Archive

Last week, we announced the pre-release of PostSharp Threading Toolkit, a library of aspects implementing best-practice patterns of multithreaded applications. The toolkit is available on NuGet and its source code is hosted on GitHub.

Note: the source code of the ping-pong example is available on GitHub too.

The Right Level of Abstraction

The motto of this toolkit is that direct use of the System.Threading namespace brings so much complexity that it cannot scale to large object models. In other words, if you’re using System.Threading or just locks in your business or UI layer (except in a few very specific classes), you are probably addressing the threading concern at the wrong level of abstraction, and you and your co-workers will perhaps have difficulties to handle that complexity in large applications.

A far better approach to multithreading is to grab a few threading models and to make sure everyone understands them. Then, select the proper threading model for every part of your application (UI and domain will probably require a different threading model). Finally, implement the threading model in all your classes. That final step is obviously the most complex and time-consuming (there can be thousands of classes) and cancels the benefits of abstraction – unless you are getting the tools for the job: Erlang, F#, or… PostSharp Threading Toolkit.

The Bad: Mutable Shared State

Imagine you have a room full of accounting assistants all working on the yearly balance of the same company. To make it easier to share information among workers, the manager thought it was smart to decide that all accounts will be maintained on a blackboard, so that many workers can update it simultaneously. (I chose a blackboard on purpose, because if whiteboards existed, they would probably have chosen a computer.)

It’s clear that workers of this team need to cooperate (i.e., communicate) in order to get their jobs done, otherwise they are going to overwrite each other’s accounting lines and mess with the balance.

The moral in the fable: concurrent access by several workers to shared mutable state causes mess.

A state (typically: a field or an element array) is mutable if it can be modified. It is shared if it can be read or updated by several workers concurrently. Immutable shared state does not cause conflict (all workers can concurrently look at the President’s picture on the wall). Private mutable state is safe too (there would be no issue if every worker was assigned a single account).

Actors Against Mutable Shared State

Actor-Based Programming (or simply actors) address the problem of multithreaded software by eradicating mutable shared state. Formally, the Actor model wants to ensure that, for any state:

     NOT (state is mutable AND state is shared).

In other words, the Actor model ensures that:

     state is mutable XOR state is shared.

The Actor model implements these restrictions using – guess what: actors! Think of as Actor as an object-oriented class (with some children objects such as collections, perhaps).

The Actor model is based on the following principles:

  • Actors communicate between each other and with the rest of the world using messages.
  • Messages are immutable.
  • An actor process only one message at a time.
  • Processing messages may cause a change of the state of the actor that processes it. Other state changes must be ordered through message passing.

The principles ensure that the only mutable state is private to an actor, and since the message-passing mechanism guarantees actors are single-threaded, the mutable state is not shared among different processors.

These simple principles are at the origin of the success of Erlang and F#, but have traditionally been difficult to implement in C#. With the new C# 5.0 and PostSharp Threading Toolkit, this is now much easier!

A Simple Ping-Pong Game

Let’s see these principles in action on the simplest multi-actor game invented: ping-pong. To make the game a little more interesting (and show that execution is actually asynchronous), I’m throwing two balls in the game.

class Player : Actor
{
    string name;
    int counter;

    public Player(string name)
    {
        this.name = name;
    }

    public void Ping(Player peer,  int countdown)
    {
        Console.WriteLine("{0}.Ping from thread {1}", this.name, 
Thread.CurrentThread.ManagedThreadId); if (countdown >= 1) { peer.Ping(this, countdown - 1); } this.counter++; } } class Program { static void Main(string[] args) { Player ping = new Player("Sarkozy"); Player pong = new Player("Hollande"); ping.Ping(pong, 4); ping.Ping(pong, 4); Console.ReadLine(); } }

This program gives the following output:

Sarkozy.Ping from thread 3
Sarkozy.Ping from thread 3
Hollande.Ping from thread 4
Hollande.Ping from thread 4
Sarkozy.Ping from thread 3
Sarkozy.Ping from thread 3
Hollande.Ping from thread 4
Hollande.Ping from thread 4
Sarkozy.Ping from thread 6
Sarkozy.Ping from thread 6

As you can see, actors run on a different thread than the Main method. Calling the Ping method results in a message to be appended to the actor’s message queue. When a message queue was empty and got its first message, the dispatcher creates a Task to process the message queue. Therefore, actors run on the thread pool and there’s no need to have one thread per actor.

Note that the field Player.counter is incremented by the Ping method. Incrementing consists in reading and writing a field atomically. In a concurrent environment, we should have used Interlocked.Increment. But since the field is private to the actor, we don’t have to think about data races.

Build-Time Validation

I’ve said that the Player.counter field is safe because it’s private. I meant that logically it belongs to the private state of the actor, as opposed to the state that is shared among actors and threads. That said, private is also a C# concept, so what if the field was public?

We thought about this issue, and we designed the toolkit to minimize the level of thinking necessary to apply a threading model, so the toolkit will warn you of any potential error with the following build-time error message:

1>------ Build started: Project: TestAsync, Configuration: Debug Any CPU ------
1>POSTSHARP : postsharp error THR005: Field Player.counter must be private or 
protected because the Player class is an Actor. 1>POSTSHARP : postsharp error PS0060: The processing of module "TestAsync.exe" was
not successful. ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

The toolkit does not claim it’s smarter than you, but it prevents you (and more likely your co-workers) from obvious mistakes. If you decide that a public field is really what you want, you have to annotate the field with the [ThreadUnsafe] custom attribute, which opts out from the safety net enforced by the toolkit.

In a similar spirit, the toolkit will prevent an actor to expose public method that return a value or have ref or out parameters. Why? Because these methods are modified to run asynchronously, so output values could not be known at the time the asynchronous call completes (asynchronous means that the execution continues after the call completes).

If you think this is an awful limitation, you’re right. But we have some good news for you.

Where C# 5.0 Saves the Day

The C# feature of the year is the ability to work with asynchronous methods thanks to two new keywords: async and await. The async keyword basically allows the use of the await keyword, which suspends the execution of the method until some task (or anything “awaitable”) completes. This is exactly what we need to get actor methods return some output to the caller.

Suppose we want to add a GetCounter method to the Player class. We could simply do that:

public async Task<int> GetCounter()
{
    return this.counter;
}

Intellisense gives some warning that there is no async keyword and the method will therefore run synchronously, but PostSharp adds some magic to make the method run asynchronously from the message queue of the Actor. You don’t have to take care of that and can safely ignore the compiler warning.

Now, suppose we call GetCounter just after we call Ping. The message is going to be queued just after the first Ping messages, we we’re probably going to get a counter value of 1. This is not what we want: we want to get the counter after the ping-pong game is finished. To achieve that, we need to make the Ping method awaitable by using the async and await keywords.

Here is the updated source code of our new ‘awaitable’ actor game:

class Player : Actor
{
    string name;
    private int counter;

    public Player(string name)
    {
        this.name = name;
    }

    public async Task<int> GetCounter()
    {
        return this.counter;
    }

    public async Task Ping(Player peer, int countdown)
    {
        Console.WriteLine("{0}.Ping({1}) from thread {2}", this.name, countdown, 
Thread.CurrentThread.ManagedThreadId); if (countdown > 1) { await peer.Ping(this, countdown - 1); } this.counter++; } [ThreadSafe] public override string ToString() { return this.name; } } class Program { static void Main(string[] args) { AsyncMain().Wait(); } private static async Task AsyncMain() { Player ping = new Player("Sarkozy"); Player pong = new Player("Hollande"); Task pingTask = ping.Ping(pong, 4); Task pongTask = pong.Ping(ping, 4); await pingTask; await pongTask; Console.WriteLine("{0} Counter={1}", ping, await ping.GetCounter()); Console.WriteLine("{0} Counter={1}", pong, await pong.GetCounter()); } }

 

Is PostSharp Necessary Anyway?

You could wonder where is PostSharp in this code. After all, isn’t C# 5.0 all you need? Actually, PostSharp is hidden behind the Actor class. It will modify the code of all public instance methods of actors to make them compatible with the Actor model. If you remove PostSharp, you will perhaps still have asynchronous methods, but they will not respect the Actor model and C# 5.0 alone isn’t going to clean up the multithreaded mess.

Actually, PostSharp adds the following code on the top of the main method of the asynchronous state machine. That seems complex, but it’s actually approximately equivalent to doing an “await Task.Yield()” if the code executes from the wrong context, to force it to be rescheduled in the right context.

if (!this.<>4__this.Dispatcher.CheckAccess())
{
  this.<>t__dispatchAwaiter = Task.Yield().GetAwaiter();
  SynchronizationContext oldContext = SynchronizationContext.Current;
  SynchronizationContext.SetSynchronizationContext
(this.<>4__this.Dispatcher.SynchronizationContext); this.<>t__builder.AwaitUnsafeOnCompleted<YieldAwaitable.YieldAwaiter,
Player.<GetCounter>d__0>(ref this.<>t__dispatchAwaiter, ref this); } else {
// Your original code.
}

Additionally to adding behavior to public methods, PostSharp (the Threading Toolkit, more specifically) ensures that your source code respects some safety rules for the Actor model, and emits build-time errors or run-time exceptions if necessary.

To-Do List

Although the main functionalities are implemented and work fine, there is still a lot of work on the to-do list. For instance, the toolkit does not enforce dispatching of private interface implementation yet, neither callback methods (delegate). Not all build-time and run-time checking are already implemented. Specifically, we don’t check that all messages (method parameters and return values) are immutable or otherwise thread-safe.

Summary

I’ve shown how PostSharp Threading Toolkit, together with C# 5.0, offer a compelling implementation of the Actor threading model. As you can see, proper compiler support (PostSharp is a compiler extension) makes programming much easier because it allows to address a problem at the proper level of abstraction.

In later posts, we’ll talk about other threading models supported by PostSharp Threading Toolkit: reader-writer synchronized, and thread unsafe (or singlethreaded).

Happy PostSharping!


-gael

Comments (5) -

Rajesh Karmani
Rajesh Karmani
6/13/2012 7:09:54 PM #

I possibly see a deviation from the Actor model here, so want to clarify. Is it possible for an Actor to process a new message while it "awaiting" inside a previous message?

Gael Fraiteur
Gael Fraiteur
6/13/2012 9:18:25 PM #

Very interesting question. Yes, this implementation will continue processing a new message while awaiting for inside a previous message. That means that the implementation must ensure invariants are valid at each awaiting point.

Rajesh Karmani
Rajesh Karmani
6/13/2012 9:35:13 PM #

I see where it can be useful, for example, to ensure request-reply patterns do not deadlock. However, the downside is as you mentioned ensuring invariants are valid at each awaiting point, and as a result the control flow is all over. I am thinking what would be a good trade-off. I expect this design issue to show up frequently in actor applications.

Gael Fraiteur
Gael Fraiteur
6/13/2012 9:54:14 PM #

I can think of a few approaches here:

1. Define a virtual method CheckInvariants() in the actor, and check invariants at each await. Of course, programmers will never implement invariants. But the compiler could enforce that the method has been implemented, for instance.

2. Define a custom attribute [Interruptible] that, when applied to an async method, "promises" that invariants are respected, and allows other messages to be processed during a wait point (by default, we would wait for the previous method to complete). We can have the opposite default behavior.

3. Add this case to the deadlock detection algorithm that we implemented in the toolkit but did not blog about yet. This does not solve the problem, of course.

Also, I think a major issue in Actor-based programming is debugging. Typically, I would like a breakpoint or a "step" to be executed inside the current actor only, and accross wait points. Current debugging techniques make that quite difficult.

Rajesh Karmani
Rajesh Karmani
6/13/2012 10:27:50 PM #

Great, we have a list of different options to think more about. I 'll add one more to it:

4. Allow only "read-only" async  methods to be processed while any other message is awaiting. (I believe this is a stricter variation of 2 in your list.)

Regarding 1, one problem that I see is that at times, these invariants could be local to a method instead of actor-wide invariants.

Good point about debugging actor programs. May be need an actor-aware debugger?

Comments are closed