Wrap synchronous method dispatch in a fiber. Celluloid now requires 1.9
Wrap synchronous method dispatch in a fiber. Celluloid now requires 1.9

The initial release of Celluloid suffered from a pretty nasty problem/bug.
Synchronous calls work fine so long as the actors in a call chain are all
unique (e.g. A calls B calls C). However, if the call chain ever loops back
around to the same actor (e.g. A calls B calls A) a deadlock occurs. A is
blocked waiting for a response from B, so it never realizes B called it.
Meanwhile B has asked A to handle a call and starts blocking waiting from
a response from A. DEADLOCK!

How can this be fixed? The only solution is to allow A to continue to service
method calls even though the method it is currently dispatching is blocked
waiting for a call response. If A could take another request while it's waiting
for a call response from B, it could in turn service the new request B just
made, and unblock the entire call chain.

To accomplish this, we need to wrap the call in a fiber. Whenever an actor
makes a synchronous call to another actor, the fiber can yield, and the
actor can wait for the call response as it processes additional incoming
method calls. As soon as the call response is received, the fiber is resumed,
and method execution continues. Actors remain sequential and only process one
method at a time, but switch back and forth between running methods whenever
synchronous calls are made to other actors.

With this approach in Celluloid, you no longer have any control of the
order methods are executed in. Several actors (each running in their own thread)
may be making method calls at the same time. The receivers process them in
a first come, first served manner, but if the receiver makes another outgoing
call, the actor suspends the method it's processing and moves on to the next
request.

The end result is you may just need to be a little more vigilant when working
with instance variables. Other methods need to work with that state too, so keep
it consistent. Don't mutate an object into an inconsistent state then make a
method call. In general, try to preserve a class invariant.

Don't write sloppy code with implementation logic coupled together between
methods through instance variables. Treat instance variables as the state of
the object, not a temporary space for passing state between method calls.
That's what the stack is for.

If you do that, which I hope you're doing anyway, then everything should be fine
and concurrent objects Celluloid's actors provide should continue functioning as
plain old Ruby objects with awesome concurrent and asynchronous capabilities.

mehta/celluloid,seuros/celluloid,TiagoCardoso1983/celluloid,davydovanton/celluloid,olleolleolle/celluloid,tarcieri/celluloid,kenichi/celluloid,marshall-lee/celluloid,jstoja/celluloid,jasonm23/celluloid,sideci-sample/sideci-sample-celluloid,dilumn/celluloid,celluloid/celluloid