Any actor or coroutine implementations for Cocoa?

  • Anyone know if the "Actor" design pattern for concurrent programming
    has been implemented for Cocoa?

    In a nutshell, an Actor is an object that has its own [cooperative]
    thread and message queue. Actors interact by message-passing instead
    of shared state. The idea is to eliminate the need for standard
    synchronization primitives like semaphors and locks, and get rid of
    the race conditions and deadlocks that plague multi-threaded programs.
    There's a very good overview on the website for the new Ruby library
    Revactor:
    http://revactor.org/philosophy
    Actors are also built into languages like Erlang and Io.

    The only hard part about implementing Actors in Obj-C appears to be
    the underlying dependency on coroutines. Steve Dekorte [author of Io]
    has a newish coroutine implementation in C that works on OS X. When I
    discovered that last year, I then found an Objective-C wrapper by
    dPompa, but that relies on his HigherOrderMessaging library, which is
    incompatible with 10.5 and hasn't yet been updated.

    If nothing's currently available, I'd gladly work together with one or
    more other motivated people to get coroutines and/or Actors working.
    Anyone else interested?

    —Jens
  • Hi Jens,

    I've been researching the Actor model for the last couple of months. I
    too thought Erlang used Actors, because that's what everyone says, but
    after a lot of looking I cannot find a single reference where the
    creators of Erlang say it uses the Actor model! They do say it
    implements the Cooperating Sequential Processes (CSP) model, which is
    quite close to the Actor model. I think this is largely academic,
    perhaps the current (incorrect but defacto???) understanding of what
    the Actor model is is just based on "Erlang-style concurrency".

    More comparing CSP and Actors here: http://c2.com/cgi/wiki?ActorsModel

    Also check out Scala Actors and Rubinius for other implementations of
    the Actor model (which appears really to be implementing Erlang-style
    concurrency.  I wrote a producer-consumer program in Erlang, Io,
    Rubinius and Scala for my research and compared those implementations.
    Of those, Io uses coroutines, Erlang uses its "processes" (very
    light/fast user level threads), Scala uses a hybrid model of threads
    and events, and Rubinius (Ruby) uses Rubinius' green (user level)
    threads.

    Of the above, I found Io's syntactic approach be the most
    "interesting" overall, but Erlang's is certainly the best overall
    implementation by a long shot.

    Using coroutines or some other kind of user-level threading approach
    would certainly be important for high performance, but you could start
    with a thread-based approach like Rubinius or Scala. When I looked at
    Io's coroutine code, I was shocked because it looked incredibly
    similar to the user-level thread library I wrote for my undergraduate
    Concurrency class... wasn't sure if that meant my library was really
    robust or Steve's coroutine code was really simple... regardless, Io's
    coroutines had very good performance in my tests, as did mine. ;-)

    I'm definitely interested in collaborating with you on this, ping me
    at <jim...> at your convenience.

    Jim

    On Tue, Apr 29, 2008 at 10:04 AM, Jens Alfke <jens...> wrote:
    > Anyone know if the "Actor" design pattern for concurrent programming has
    > been implemented for Cocoa?
    >
    > In a nutshell, an Actor is an object that has its own [cooperative] thread
    > and message queue. Actors interact by message-passing instead of shared
    > state. The idea is to eliminate the need for standard synchronization
    > primitives like semaphors and locks, and get rid of the race conditions and
    > deadlocks that plague multi-threaded programs. There's a very good overview
    > on the website for the new Ruby library Revactor:
    > http://revactor.org/philosophy
    > Actors are also built into languages like Erlang and Io.
    >
    > The only hard part about implementing Actors in Obj-C appears to be the
    > underlying dependency on coroutines. Steve Dekorte [author of Io] has a
    > newish coroutine implementation in C that works on OS X. When I discovered
    > that last year, I then found an Objective-C wrapper by dPompa, but that
    > relies on his HigherOrderMessaging library, which is incompatible with 10.5
    > and hasn't yet been updated.
    >
    > If nothing's currently available, I'd gladly work together with one or more
    > other motivated people to get coroutines and/or Actors working. Anyone else
    > interested?
    >
    > —Jens
  • I completely derailed my regular project and just worked on coroutines
    all day. Here's what I've got so far.

    * libCoroutine does not build as-is on Mac OS X 10.5. (It's trying to
    implement its own ucontext API, which then has name collisions with
    the one that's already in the OS.)

    * I tweaked some #ifdefs and got libCoroutine to run, but any
    nontrivial uses of it crashed hard. Several hours later I tracked this
    down to what appears to be a bug in the system header <sys/
    _structs.h>. Fortunately it was easy to work around. (I've posted a
    message to the darwin-userlevel mailing list asking about this.)

    * Since I was already modifying libCoroutine, and since 90% of its
    code is just for cross-platform compatibility, I tweezed out just the
    part that's necessary for Mac OS X and made my own "CoroX.c". It's
    only 150 lines of code. I can actually read it and understand what
    it's doing. (I've kept Steve's copyright notice and made it clear this
    is a derivative work.)

    * Then I wrote an Objective-C API around it. Only 140 lines. Here's
    the header:
    http://mooseyard.com/hg/hgwebdir.cgi/Actors/file/tip/Coroutines/MYCoroutine
    .h


    * Finally I put this into a Mercurial repository and hosted it on my
    website:
    http://mooseyard.com/hg/hgwebdir.cgi/Actors/
    If you use Mercurial, you can just clone from the above URL to get a
    local repo. If you don't, you can use the "files" or "zip" links on
    that web page to browse or download the code.

    Next I'd like to implement a simpler "generator" style API (where a
    coroutine calls "yield" to return a value to its caller) since that's
    easier for me to get my head around than the general model where every
    coroutine chooses which one to run next. From there, actors shouldn't
    be too hard to do.

    One potential problem is the interaction with Cocoa features that
    implicitly rely on the stack: exceptions and autorelease pools. I
    suspect that coroutines will confuse both of those.

    —Jens
  • On Tue, Apr 29, 2008 at 8:26 PM, Jens Alfke <jens...> wrote:
    > I completely derailed my regular project and just worked on coroutines all
    > day. Here's what I've got so far.
    >
    > * libCoroutine does not build as-is on Mac OS X 10.5. (It's trying to
    > implement its own ucontext API, which then has name collisions with the one
    > that's already in the OS.)
    >
    > * I tweaked some #ifdefs and got libCoroutine to run, but any nontrivial
    > uses of it crashed hard. Several hours later I tracked this down to what
    > appears to be a bug in the system header <sys/_structs.h>. Fortunately it
    > was easy to work around. (I've posted a message to the darwin-userlevel
    > mailing list asking about this.)
    >
    > * Since I was already modifying libCoroutine, and since 90% of its code is
    > just for cross-platform compatibility, I tweezed out just the part that's
    > necessary for Mac OS X and made my own "CoroX.c". It's only 150 lines of
    > code. I can actually read it and understand what it's doing. (I've kept
    > Steve's copyright notice and made it clear this is a derivative work.)
    >
    > * Then I wrote an Objective-C API around it. Only 140 lines. Here's the
    > header:
    >
    > http://mooseyard.com/hg/hgwebdir.cgi/Actors/file/tip/Coroutines/MYCoroutine
    .h

    >
    > * Finally I put this into a Mercurial repository and hosted it on my
    > website:
    > http://mooseyard.com/hg/hgwebdir.cgi/Actors/
    > If you use Mercurial, you can just clone from the above URL to get a local
    > repo. If you don't, you can use the "files" or "zip" links on that web page
    > to browse or download the code.
    >
    > Next I'd like to implement a simpler "generator" style API (where a
    > coroutine calls "yield" to return a value to its caller) since that's easier
    > for me to get my head around than the general model where every coroutine
    > chooses which one to run next. From there, actors shouldn't be too hard to
    > do.
    >
    > One potential problem is the interaction with Cocoa features that
    > implicitly rely on the stack: exceptions and autorelease pools. I suspect
    > that coroutines will confuse both of those.

    I once experimented with coroutines in Cocoa with the goal of turning
    asynchronous methods with callbacks (such as the sheet methods) into
    synchronous methods. (I should note that I didn't write the coroutines
    library, I just played with one that somebody else put together.)

    There were a lot of problems with Cocoa and returning to the runloop
    without returning back the way I came. Exceptions and autorelease
    pools were two of them, although they were easy enough to fix by
    manually swapping out the private NSThread ivars. A bigger problem was
    handling stuff like menu and button highlighting; they highlight when
    you click, and unhighlight when your code returns back to them. With
    coroutines this assumption is blown away and things start to fall down
    badly.

    You could probably do fairly well if you avoid using them on the main
    thread. You'll still have to do a bit of evil but not as much, and I
    think it's all reasonable to work around. For this sort of actor stuff
    it may be entirely reasonable to proxy out to a secondary thread which
    contains the coroutines so you don't mess with the main thread like I
    was doing.

    Mike
  • On 29 Apr '08, at 7:16 PM, Michael Ash wrote:

    > There were a lot of problems with Cocoa and returning to the runloop
    > without returning back the way I came

    Yes, I can see that there would have to be a rule that only the main
    coroutine (i.e. the original 'real' stack) gets to use the runloop.

    I think this can be dealt with by having (in your example) a separate
    coroutine that brings up the sheet; then that coroutine blocks until
    the sheet is dismissed, returning control to the main routine that
    returns back to the runloop.

    In the actor model, most of the application code would be in the form
    of actors waiting for specific events (menus, buttons, etc.), that
    would get a message when the event occurred, do their thing, then
    return or yield. The main routine would mostly be receiving the events
    from the runloop and dispatching them as messages to actors.

    —Jens
  • On Wed, Apr 30, 2008 at 12:18 PM, Jens Alfke <jens...> wrote:
    > On 29 Apr '08, at 7:16 PM, Michael Ash wrote:
    >> There were a lot of problems with Cocoa and returning to the runloop
    >> without returning back the way I came
    >>
    >
    > Yes, I can see that there would have to be a rule that only the main
    > coroutine (i.e. the original 'real' stack) gets to use the runloop.
    >
    > I think this can be dealt with by having (in your example) a separate
    > coroutine that brings up the sheet; then that coroutine blocks until the
    > sheet is dismissed, returning control to the main routine that returns back
    > to the runloop.

    This doesn't work because you can have multiple sheets which get
    dismissed out of order.

    For example, method X shows sheet A, method Y shows sheet B, user
    dismisses sheet A. Now you need to return back to method X but method
    Y is still on the call stack. There's no real way to resolve this
    while still presenting a synchronous interface to the sheets. This is
    the sort of thing coroutines are made for, but it doesn't work with
    "foreign" code on the stack.

    > In the actor model, most of the application code would be in the form of
    > actors waiting for specific events (menus, buttons, etc.), that would get a
    > message when the event occurred, do their thing, then return or yield. The
    > main routine would mostly be receiving the events from the runloop and
    > dispatching them as messages to actors.

    Right, so you ought to have an easier time of it here. You'll still
    have to deal with exceptions and autorelease pools but they're not too
    bad.

    Mike
  • On 30 Apr '08, at 12:35 PM, Michael Ash wrote:

    > This doesn't work because you can have multiple sheets which get
    > dismissed out of order.
    > For example, method X shows sheet A, method Y shows sheet B, user
    > dismisses sheet A. Now you need to return back to method X but method
    > Y is still on the call stack.

    That's what would happen without coroutines. But with coroutines it's
    no problem, because X and Y aren't on the same call stack:

    Coroutine 1 is running:
    ...
    method X calls runSheet(A)
    runSheet displays the sheet, then blocks and transfers control
    Now coroutine 2 is running:
    ...
    method Y calls runSheet(B)
    runSheet displays the sheet, then blocks and transfers control
    Main coroutine is running:
    ...events being handled...
    User clicks on OK button in sheet A
    Main coroutine wakes up coroutine 1
    Coroutine 1 is running:
    sheet goes away, runSheet(A) returns
    back to method X
    ...

    The main coroutine would handle the runloop and events as usual. But
    it would respond to events by messaging their associated coroutines/
    actors.

    —Jens

    PS: I just updated the coroutine library. I rewrote the underpinnings
    to make them simpler, and added a call/yield style interface similar
    to Ruby's and Lua's.
  • On Wed, Apr 30, 2008 at 5:37 PM, Jens Alfke <jens...> wrote:
    >
    > On 30 Apr '08, at 12:35 PM, Michael Ash wrote:
    >
    >
    >> This doesn't work because you can have multiple sheets which get
    >> dismissed out of order.
    >> For example, method X shows sheet A, method Y shows sheet B, user
    >> dismisses sheet A. Now you need to return back to method X but method
    >> Y is still on the call stack.
    >>
    >
    > That's what would happen without coroutines. But with coroutines it's no
    > problem, because X and Y aren't on the same call stack:
    >
    > Coroutine 1 is running:
    > ...
    > method X calls runSheet(A)
    > runSheet displays the sheet, then blocks and transfers control
    > Now coroutine 2 is running:
    > ...
    > method Y calls runSheet(B)
    > runSheet displays the sheet, then blocks and transfers control
    > Main coroutine is running:
    > ...events being handled...
    > User clicks on OK button in sheet A
    > Main coroutine wakes up coroutine 1
    > Coroutine 1 is running:
    > sheet goes away, runSheet(A) returns
    > back to method X
    > ...
    >
    > The main coroutine would handle the runloop and events as usual. But it
    > would respond to events by messaging their associated coroutines/actors.

    The goal was to use coroutines to give sheets a modal interface
    without changing the rest of the application's structure. Therefore
    the sheet-calling code would still run on the main thread, preventing
    this use of coroutines. If you run all of your coroutines on secondary
    threads and proxy back to the main thread then you can do this kind of
    thing, but that adds a great deal of additional complication.

    Mike
previous month april 2008 next month
MTWTFSS
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        
Go to today