The join-calculus C# library


In this post I will take you through the main features of my join-calculus library.

Terminology

Chord

A chord represents a pattern of methods, it is always associated to a block of code to be executed on a match.

Method

Synchronous and asynchronous methods are exposed as public instance methods of the join class. Internally they are defined as instances of the SyncMethod or AsyncMethod classes which are then used to express the chord.

Message

A message is sent to a join class by calling one of its methods. Messages are queued up and pattern matched on the chords defined in the join class.

Primary method

The primary method is the first method defined in a chord.

Body execution

The library offers several options when it comes to the execution of the body of a chord.

Sync

Synchronous chords only. In this case the body will be executed on the thread of the primary method, i.e. the first synchronous method of the chord.

    (get & put).Do((T t) => {
        return t;
    });

Pool

Asynchronous chords only. The chord body will be executed on a thread of the specified ThreadPool.

    (signalOne & signalTwo).Pool(() => {
        ...
    });

Spawn

Asynchronous chords only. The chord body will be executed on a new thread.

    (signalOne & signalTwo).Spawn(() => {
        OnSignals();
    });

Continue

Asynchronous chords only. The chord body will be executed on the thread of the message which completes the chord. Must be used with caution as this risks blocking the asynchronous message. It is however very convenient to use when one wants to avoid the overhead cost of creating a new thread.

    (signalOne & signalTwo).Continue(() => {
        OnSignals();
    });

SyncContext

Synchronous and asynchronous chords. The chord body will be dispatched to the specified SynchronizationContext. The body is dispatched on the Send/Post method for synchronous/asynchronous chords. By default the WindowsFormsSynchronizationContext.Current is picked up by the Join constructor, otherwise it can be explicitly specified:

    mJoin.SynchronizationContext = WindowsFormsSynchronizationContext.Current; // this is redundant
    (signalOne & signalTwo).SyncContext(() => {
        OnSignals();
    });

Asynchronous chords

A chord consisting of only asynchronous messages is called an asynchronous chord. Since all the messages are asynchronous there is no synchronous message on which to execute the body of the chord. Usually the body of the chord will be executed on a new thread. Depending on the amount of work performed in the body, the creation of a new thread might present too much of an overhead and it is convenient to execute it on the thread of the last message.

    (async1 & async2). Continue(() => {
        ...
    });

Synchronous chords

A chord which contains at least one synchronous method is called a synchronous chord. The synchronous method must be first in the chord definition and will block until the chord is matched. The body of the chord is executed on the thread of the synchronous message. The synchronous message returns the value returned by the block:

    (sync & async).Do(() => {
        return 1;
    });

Multiple synchronous chords

A chord can have multiple synchronous methods. All synchronous messages will block until either the end of the chord’s body or until they are “returned” from the body of the chord:

    (sync1 & sync2).Do(() => {
        sync2.Return(2);
        return 1;
    });

Where sync2 returns 2 when the Return method is invoked. All methods which return a value must call their Return method before the body ends or an exception will be thrown.

Exceptions

Exceptions thrown in synchronous chords are passed to the thread of the primary method. For asynchronous chords the body must implement its own exception handling; exceptions which are allowed to escape the body are ignored. For example:

    public async One (int i, Action onException) { ... }
    public async Two (int j) { ... }

    (one & two).Do((int i, int j, Action onException) => {
        try {
            ...
        } catch (Exception exception) {
            onException(exception);
        }
    });

Multipliers

When defining a chord, any method can include a multiplier, e.g.

    (wait & 2*signal).Do(() => { });

which is logically equivalent to

    (wait & signal & signal).Do(() => { });

Arguments to multiplied methods are passed in arrays:

    public async SignalInteger(int i) { ... }

    (wait & n*signalInteger).Do((int[] ints) => { ... });

or

    (wait & 2*signalInteger & 3*signalInteger).Do((int[] twoInts, int[] threeInts) => { ... });

Timeout

Every synchronous method can have an associated timeout and OnTimeout delegate configured. This delegate will be executed whenever an associated synchronous message has been received but not been used in a match within the specified time span. Note that this is not fully implemented yet.

Performance

The goal of this library implementation of join-calculus is to give all C# users a chance to try out join-calculus and investigate its potential in the real world. The implementation is not at all optimized and it should be fairly easy to do some simple optimization. A better implementation could take advantage of runtime code generation to pre-compile a join class before use. This would allow a number of optimizations which are not possible in the current implementation due to its generic nature.

Warning

Please remember that the current implementation is incomplete and has only been tested very superficially.

The future

There are a few features I would like to add to this library. The main one is probably to add a way of including the message arguments in the pattern:

    public async One (int i) { ... }
    public async Two (int j) { ... }

    (one & two).Where((int i int,j) => i >  j).Do((int i, int j) => { Console.WriteLine(“i > j”); });
    (one & two).Where((int i int,j) => i <= j).Do((int i, int j) => { Console.WriteLine(“i <= j”); });

Such a feature will make the library much more powerful and expressive, but it is likely to come at a rather high run-time cost.

Otherwise I would like to integrate the library better with the new concurrency features in .Net 4.0 and the upcoming async feature.

In my next post I will look at more examples.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Awelon Blue

Thoughts on Programming Experience Design

Joe Duffy's Blog

Adventures in the High-tech Underbelly

Design Matters

Furniture design blog from Design Matters author George Walker

Shavings

A Blog for Woodworkers by Gary Rogowski

randallnatomagan

Woodworking, life and all things between

%d bloggers like this: