Actors Guild

Saturday, December 27, 2008

Behind the Scenes: Actor Proxies

It is not possible to implement Actors Guild's functionality without generating or manipulating Java bytecode at runtime. AG intercepts invocations to @Message methods, queues the invocations and later invokes the original implementation. As AG does not need any preprocessor, it needs to create several dynamic helper classes for each actor at runtime.

Let's take the following Actor as an example to show the dynamic classes that AG will generate:
public class StringJoiner {
@Message
public AsyncResult<String> join(String separator, String[] parts) {
if (parts.length == 0)
return result("");

StringBuilder sb = new StringBuilder();
sb.append(part[0]);
for (int i = 1; i < parts.length; i++)
sb.append(separator).append(parts[i]);

return result(sb.toString());
}


Helper classes will be generated the first time the actor is created using Agent.create():
Agent ag = new DefaultAgent();
StringJoiner sj = ag.create(StringJoiner.class);


The most important helper class is the actor proxy. It is a sub-class of the original actor implementation that intercepts the messages. Actors Guild creates the byte code of its dynamic classes directly. As this would not be very readable, this is what the actor proxy class would look like in Java:
public final class StringJoiner__ACTORPROXY extends StringJoiner implements ActorProxy { // (1)
private final ActorState actorState__ACTORPROXY; // (2)
private final static MessageCaller messageCaller_0__ACTORPROXY; // (3)

static {
messageCaller_0__ACTORPROXY = new StringJoiner_join_0__MESSAGECALLER(); // (3)
}

public StringJoiner__ACTORPROXY(Controller controller) {
actorState__ACTORPROXY = new ActorState(controller, this, false); // (2)
}

public ActorState getState__ACTORPROXYMETHOD() { // (1)
return actorState__ACTORPROXY;
}

public AsyncResult join(String separator, String[] parts) { // (4)
Object[] args = new Object[2];
args[0] = separator;
args[1] = SerializableFreezer.freeze(parts);
actorState__ACTORPROXY.queueMessage(messageCaller_0__ACTORPROXY,
ConcurrencyModel.SingleThreaded, ThreadUsage.CpuBound, args);
}

public AsyncResult join__ACTORPROXYMETHOD_original(String separator, String[] parts) { // (5)
super.join(separator, parts);
}
}

(1)The proxy class has the name of the original actor with the postfix "__ACTORPROXY". The dynamic classes use this and other postfixes to prevent name conflicts with the user's own classes, because the generated classes always live in the same package as the user's classes. In order to override the message implementations (in this example, only join()), the proxy extends the Actor class. It also implements the ActorProxy interface which has only a single method getState_ACTORPROXYMETHOD(). The method allows retrieving the actor's ActorState object.

(2)The ActorState object contains the current state of the Actor. It contains things such as the actor's message queue, locking mechanisms and initialization state.

(3)For every message that the actor implements, there is one dynamically generated implementation of the MessageCaller interface. MessageCaller allows invoking the message implementations without using Java's reflection mechanisms. The implementation of the class StringJoiner_join_0__MESSAGECALLER will be shown below.

(4)The original implementation of the message will be overwritten by a message that puts the arguments into an object array and then calls the ActorState to queue the message. Arguments that are not immutable, such as the String array, will be frozen and copied using the helper class SerializableFreezer.

(5)This helper method is used by the MessageCaller to invoke the original message implementation, as an overridden method can only be called from sub-classes in Java.


Actors Guild generates an implementation of the MessageCaller interface for every message implementation in the actor. MessageCaller allows invocations of the method without the use of reflection. It is also responsible for unwrapping values in SerializableFreezers. The implementation for StringJoiner's join() method would look like this:
public final class StringJoiner_join_0__MESSAGECALLER implements MessageCaller {

public AsyncResult invoke(Actor actor, Object[] args) {
StringJoiner__ACTORPROXY a = (StringJoiner__ACTORPROXY) actor;
return a.invoke((String) args[0], (String[])((SerializableFreezer)args[1]).get());
}

public String getMessageName() {
return "join";
}
}

Finally, a third class is created for every Actor. It is a simple factory for the actor proxy and implements the ActorProxyFactory interface. ActorProxyFactory's only purpose is to avoid the use of reflection at runtime:
public final class StringJoiner__ACTORPROXYFACTOR implements ActorProxyFactory {

public ActorProxy createNewActor(Controller controller) {
return new StringJoiner__ACTORPROXY(controller);
}
}



Now that all the classes have been created for StringJoiner, the agent can start using them. First it obtains an ActorProxyFactory to create a new instance of the proxy class and return it:

Agent ag = new DefaultAgent();
StringJoiner sj = ag.create(StringJoiner.class);
System.out.println(sj.getClass());
// output: class StringJoiner__ACTORPROXY

When the join() method of the actor is invoked, the overriding implementation StringJoiner__ACTORPROXY.join() will intercept this call and put it in the ActorState's queue.

StringJoiner sj = ....;
AsyncResult<String> s = sj.join(",", new String[] { "a", "b", "c" });
System.out.println(s.get());
// output: a, b, c

The queued message will be either picked up by a worker thread that is looking for work, or it will be executed by the AsyncResult implementation when its await() or get() method is invoked - whatever comes first. In both cases, the generated MessageCaller invocation StringJoiner_join_0_MESSAGECALLER will be used to call the method StringJoiner__ACTORPROXY.join__ACTORPROXYMETHOD_original(), which can then invoke the original implementation StringJoiner.join(). Actors Guild's internal AsyncResult implementation will store the result and return it in get().

Sunday, December 21, 2008

Actors Guild: First Release (0.5)

I just released the first version of the Actors Guild Framework! Version 0.5 is ready for download. You can also visit the new site and read the tutorial.

Version 0.5 contains all the main features that I want to have in version 1.0. Still, please note that it is experimental software. There may be backward-incompatible changes in the API, in particular in the way actors are initialized. I would appreciate any feedback, especially concerning the API and how to make it easier to use.

This is my current to-do list for version 1.0:
  • support for dependency-injection in Actors
  • Spring support
  • run-time checks at the bytecode level to enforce the rules for Stateless messages
  • faster copying of mutable (Serializable) message arguments
I hope that you give give Actors Guild a try, and please leave some feedback either here on the blog, or write to tim@actorsguildframework.org. Thanks!

Saturday, November 29, 2008

Actors Guild Framework

This is the start of my Actors Guild blog. Actors Guild is a framework for concurrent programming in Java. A short introduction can be found on the Google Code project page, which also contains the source code (SVN) and JavaDocs.
I am currently working on a longer tutorial, the real homepage as well as some example code. Once this is done, I plan to release the first version (called 0.5, probably). If everything goes well, it's out on January 1.