[Eventmachine-talk] Erlang-like Processes

Francis Cianfrocca
Tue, 28 Aug 2007 04:53:56 -0000

On 8/27/07, hemant <gethemant at gmail.com> wrote:


Ok, hemant, lots of great questions here, let''s see if I can answer
most of
them.

This is a working code sample tested on Ruby 1.8 that produces the expected
result:

#------------------------------
require ''rubygems''
require ''eventmachine''

x=1
y=1

EM.run {
        25.times {
                s = EM.spawn {x,y = y,x+y}
                s.notify
        }

        t = EM.spawn { EM.stop }
        t.notify
}

puts y
#------------------------------

Again, it''s not illustrative of how you would write a real program,
which
would not involve variables like x,y that are visible to every process and
every block. A better example would use the capability of passing parameters
in messages to processes (the parameters are passed to the #notify method).
And of course, in a real EM program, you probably wouldn''t be stopping
the
reactor loop just to get the result!

Here''s a considerably more complex alternative, in which 25 processes
are
spawned, each with a binding to the next. You notify the first one, passing
the initial fibonacci parameters, each subsequent process passes its results
to the next process in the chain. The last one prints the result and stops
the program:

#-------------------------------
require ''rubygems''
require ''eventmachine''

EM.run {
        nextpid = EM.spawn {|x,y|
                puts "#{x}, #{y}"
                EM.stop
        }

        25.times {
                # variable n is block-local, ensuring that every process
                # binds to a different value of nextpid. We create the
processes
                # "backwards," so that each one has a reference to the
last-created
                # one. And we start the chain by notifying the last one we
create.
                n = nextpid
                nextpid = EM.spawn {|x,y| n.notify( y, x+y )}
        }

        nextpid.notify( 1, 1 )
}
#-------------------------------


There''s nothing magical about this implementation, in fact
it''s almost
ridiculously simple. All the magic happens in the standard EM reactor core,
which schedules spawned processes to run their code block whenever they have
notifications, in exactly the same way that it schedules I/O handlers to run
when there are I/O events. There is no concern about thread-locking
whatsoever, because a notification runs to completion and is never
interrupted by EM. As in Erlang (and also in Ruby 1.9 "Fibers"), the
concurrency is co-operative rather than pre-emptive.

As regards the coding style: the block you pass to EM#spawn is the code that
executes whenever you "notify" (send a message to) an EM spawned
process.
You can send any number of messages to a given process. At some point in the
future it may make sense to augment this interface so that a programmer can
specify different blocks of code in the same process, with a choice being
made a runtime which block to run, based on the parameters or something
else. But I have a feeling it may just be easier to spawn another process.
After all, these processes are nothing more than Ruby objects, so you can
easily have millions of them.

The next thing I''m going to add is an Erlang-like ability to specify
message
"filters." That is, when you spawn a process, you can specify a regex
or
logical expression on the parameters of a notification. Then you''ll be
able
to say EM.notify( parameters ). Notice, there is no pid specified as the
receiver of the notify! The reactor core will inspect all of the
currently-active processes and notify the one(s) whose parameter filter
matches the notification.

Multi-machine concurrency: Unless you can convince me otherwise, I believe
the right answer to this is to leave EM single-threaded and avoid the
thread-locking issues altogether. (EM does currently implement a thread-pool
(EventMachine#defer) which operates independently of spawned processes.)
You''ll be able to take advantage or multi-core or multi-CPU hardware
simply
by running more instances of your single-threaded program. This will work
because spawned processes will soon be able to receive notifications from
other running programs, just as in Erlang.

Remember that, like Erlang processes, EM spawned processes have no concept
of a return value when they process a notification. They''re strictly
fire
and forget. As you saw in the example above, it''s easy to make
processes
aware of other processes (by passing pids around), so you can simulate the
effect of sending replies to messages.

If you really want to use that style, however, EM has another
lightweight-concurrency feature called "Deferrable." This is inspired
directly by the "deferred" object in Python''s Twisted. EM
Deferrable is very
useful and very graceful, but rather weird conceptually, so it needs its own
documentation (which hasn''t been written yet). It ultimately provides
the
same functionality as spawned processes, but packaged in a way that makes it
easier to "return values" to code that invokes lengthy operations. And
of
course, it also runs without threads.
-------------- next part --------------
An HTML attachment was scrubbed...