Plausible ActorKit

Introduction

ActorKit provides an Objective-C implementation of asynchronous inter-thread message passing.

The purpose of ActorKit is to facilitate the implementation of concurrent software on both the desktop (Mac OS X) and embedded devices (iPhone OS). On the iPhone, thread-based concurrency is a critical tool in achieving high interface responsiveness while implementing long-running and potentially computationally expensive background processing. On Mac OS X, thread-based concurrency opens the door to leveraging the power of increasingly prevalent multi-core desktop computers.

To this end, ActorKit endeavours to provide easily understandable invariants for concurrent software:

As an actor may only synchronously receive messages, no additional concurrency primitives are required, such as mutexes or condition variables.

Building on this base concurrency model, ActorKit provides facilities for proxying Objective-C method invocations between threads, providing direct, transparent, synchronous and asynchronous execution of Objective-C methods on actor threads.

Actor Creation and Simple Message Passing

Messages

The Actor model of concurrency is fundamentally based on communication between isolated actors through asynchronous message passing. In ActorKit, any Objective-C object conforming to the NSObject protocol may be used as an inter-actor message, but message objects should be immutable to ensure thread safety. ActorKit, being written in Objective-C, can not enforce message immutablity or full isolation of Actor threads. It is entirely possible to pass mutable messages, or access mutable global variables. Like many other libraries implementing Actor message passing semantics, isolation is maintained purely through convention.

While ActorKit supports messaging with any Objective-C object, the PLActorMessage class provides generally useful facilities such as unique message transaction ids, automatically determining the message sender, and including additional message payloads.

Actor Proceses

In ActorKit, all threads are fully functioning actors -- including the "main" Cocoa thread. Each actor is represented by a PLActorProcess instance, which may be passed to any other running actors, and is used to send messages asynchronously to the given process.

ActorKit ensures that all message reception within a given actor occurs serially, and provides strict guarantees on message ordering -- messages M1 and M2 sent from actor A1 will be delivered to actor A2 in the same order. However, delivery of messages from actor A1 may be interspersed with delivery of messages sent by other actors:

inline_mscgraph_1

In the future, ActorKit may be extended to leverage Apple's Grand Central [1] to provide hybrid event/thread M:N scheduling of actor execution on available cores, an approach presented by Philipp Haller and Martin Odersky and implemented in Scala's Actor library [2].

A Simple Echo Actor

  - (void) echo {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     PLActorMessage *message;

     // Loop forever, receiving messages
     while ((message = [PLActorKit receive]) != nil) {
         // Echo the same message back to the sender.
         [[message sender] send: message];

         // Flush the autorelease pool through every loop iteration
         [pool release];
         pool = [[NSAutoreleasePool alloc] init];
     }

     [pool release];
 }

 - (void) run {
     // Spawn a new actor thread. This will return a process instance which may be used
     // to deliver messages the new actor.
     id<PLActorProcess> proc = [PLActorKit spawnWithTarget: self selector: @selector(echo:)];

     // Send a simple message to the actor.
     [proc send: [PLActorMessage messageWithObject: @"Hello"]];

     // Wait for the echo
     PLActorMessage *message = [PLActorKit receive];
 }

Sending Synchronous Messages with Actors

In an Actor system where all messages are sent asynchronously, synchronous messaging may be achieved with the following steps:

Message Sequence:

inline_mscgraph_2

ActorKit provides facilities for handling this common usage scenario. Unique transaction ids may be generated via the createTransactionId (PLActorKit) method, and every PLActorMessage generates and uses a new transactionId.

The PLActorRPC class utilizes the PLActorMessage's transaction id to wait for a reply on your behalf.

Send a message, and wait for the reply:

 id<PLActorProcess> helloActor = [PLActorKit spawnWithTarget: self selector: @selector(helloActor:)];
 PLActorMessage *message = [PLActorMessage messageWithObject: @"Hello"];
 PLActorMessage *reply = [PLActorRPC sendRPC: message toProcess: helloActor];

Transparently Proxying Objective-C Messages with Actors

ActorKit provides two NSProxy subclasses which provide transparent proxying of Objective-C synchronous and asynchronous method invocations via actor messaging. PLActorRPCProxy spawns a new actor to execute Objective-C methods for a given object instance, while PLRunloopRPCProxy executes Objective-C methods on a provided NSRunLoop.

In combination, these classes allow for safely and transparenty executing methods on Objective-C instances from any thread:

 NSString *actorString = [PLActorRPCProxy proxyWithTarget: @"Hello"];
 NSString *runloopString = [PLRunloopRPCProxy proxyWithTarget: @"Hello" runLoop: [NSRunLoop mainRunLoop]];

 // Executes synchronously, via a newly created actor thread.
 [actorString description];

 // Executes synchronously, on the main runloop.
 [runloopString description];

By default, PLActorRPCProxy and PLRunloopRPCProxy will execute methods synchronously, waiting for completion prior to returning. In order to execute a method asynchronously -- allowing a long running method to execute without waiting for completion -- it is necessary to mark methods for asynchronous execution.

The Objective-C runtime provides a number of type qualifiers that were intended for use in implementing a Distributed Object system. Of particular note is the 'oneway' qualifier, which allows us to specify that a method should be invoked asynchronously.

When a method is declared with a return value of 'oneway void', the proxy classes will introspect this return value, and execute the method asynchronously, without waiting for a reply:

 - (oneway void) asyncMethod {
    // Execute, asynchronously
 }

inline_mscgraph_3

 - (NSString *) synchronousMethod {
    // Execute, synchronously
    return @"Hello";
 }

inline_mscgraph_4

A Simple Echo Actor with PLActorRPCProxy

The following actor returns a proxy from its init method, ensuring that all methods called on the object instance will occur via the actor thread.

 // An actor that responds to Objective-C messages either synchronously or asynchronously.
 @implementation EchoActor

 - (id) init {
     if ((self = [super init]) == nil)
         return nil;
 
     // Launch our actor
     id proxy = [[PLActorRPCProxy alloc] initWithTarget: self];

     // Release ourself, as the proxy has retained our object,
     // and return our proxy to the caller
     [self release];
     return proxy;
 }

 // Method is called asynchronously
 - (oneway void) asynchronousEcho: (NSString *) text listener: (EchoListener *) echoListener {
     [echoListener receiveEcho: text];
 }

 // Method is called synchronously
 - (NSString *) synchronousEcho: (NSString *) text {
     return text;
 }

 @end

Integration & Development Services

ActorKit is provided free of charge under the MIT license, and may be freely integrated with any application. We can provide assistance with integrating our code in your own iPhone or Mac application, as well as development of additional features under a license of your choosing.

Contact Plausible Labs for more information: http://www.plausiblelabs.com

References

[1] Grand Central (technology), Wikipedia contributors, Wikipedia, The Free Encyclopedia, November 19, 2008, 17:13 UTC. Available from http://en.wikipedia.org/w/index.php?title=Grand_Central_(technology)&oldid=252808705. Accessed November 19, 2008.

[2] Actors that Unify Threads and Events, Philipp Haller and Martin Odersky, LAMP-REPORT-2007-001, EPFL, January 2007. Available from http://lamp.epfl.ch/~phaller/actors.html.


Generated on Wed Feb 18 14:56:37 2009 for PlausibleActorKit by  doxygen 1.5.5