Join - Asynchronous Message Coordination and Concurrency Library

Yigong Liu (2007-2009)

yigongliu@gmail.com

Warning:  This library is NOT an official Boost library yet


  1. Introduction
  2. Chords and Joints
  3. Samples
  4. Synopsis of Join Classes
  5. Join Internals
  6. Discussions on "Join" Concurrency
  7. Support for Shared State Concurrency
  8. Support for Message Passing Concurrency
  9. Compare Join with Futures/Promises
  10. References

Introduction

Join is an asynchronous message coordination and concurrency library based on concepts and techniques developed in JoCaml[1] and Cω[2]. It is applicable both to multithreaded applications and to the orchestration of asynchronous message flows.

Join supports message orchestration with a few simple abstractions:
  1.  typed asynchronous and synchronous message ports

These are function objects used as typed ports or interfaces to message flows. Message passing is done by invoking or calling these function objects. Ports by themselves are not complete message passing constructs yet, they are merely interfaces to these constructs which are created when ports are "joined" together thru chords. For example, a message queue has a sending port and receiving port.
A asynchronous port provides one-way, non-blocking calls. Message passing thru async<T> port is guaranteed to return immediately; internally there could be a queue to buffer the arguments or message.
A synchronous port is similar to normal method call, in that the calling thread will block till result is returned. However a normal method call just involve the calling thread, a call to synch<R,T> port could involves multiple threads and synchronization.
  1. chords (or join patterns)

A chord binds a set of ports to the message processing function which will be invoked when all ports in the set are called and messages are available.
   
In Cω, a thread-safe buffer can be defined as following:
public class Buffer {
      public async Put(string s);
      public string Get() & Put(string s) { return s; }   //a chord
}
Here a chord is defined with a synchronous Get port and asynchronous Put port; the calling of Get will block if no Put is called yet, otherwise the string sent by Put will be returned to Get.   

In Join, the buffer class can be defined in Cω style as following:
class buffer : public joint {
public:
    async<string> put;
    synch<string,void> get;
    buffer() {
        chord(get, put, &buffer::proc);
    }
    string proc(void_t g, string p) {
         return p;
    }
};

or with boost::lambda, it becomes:
class buffer {
public:
    async<string> put;
    synch<string, void> get;
    buffer() {
       joins().chord(get, put, lambda::_2);
    }
}
  
In JoCaml, such a buffer can be created with a "factory" function:
       let create_buffer() =
             def put(n) & get() = reply n to get;
             put, get

Similarly in Join, with the help of boost lambda and tuples, a factory function can be defined in JoCaml style as following:
       template <typename T>
       tuple<async<T>, synch<T,void> > create_buffer() {
             async<T> put;
             synch<T,void> get;
             joins().chord(get, put, lambda::_2);
             return make_tuple(put, get);
       }
            
such a buffer can be safely used in multithread applications:
buffer b;
b.put("Hello"); b.put("World");
cout << b.get() << b.get() << endl;

Difference of Join with Cω in Chord definitions:
  1. joint

A joint "joins" together a set of chords, each of which may bind several ports and may share some ports with each other, thus creating competing requests for messages, which are synchronized by joint's internal logic. A joint defines a complete synchronized concurrent message passing contruct. A joint is where message ports meet and processing logic are attached thru chords. All application code which use async/synch ports and chords to define concurrent behaviours should either inherit class joint or use a joint instance to join message flows together. Joint maintains the internal state about the message arrivals and synchronization, and firing chords when all of their messages are available.

In Join based applications, with the help of simple thread-pool based executors (which can be built on top of Join's primitives), we can develop concurrent applications without explicit usage of threads (thread creation and synchronization); concurrency are mostly defined and controlled by chords with only async<> ports.

License

Join is licensed under the Boost Software License.

Installation

Join is continuously being developed and tested in Linux (Fedora Core & Ubuntu) and Windows (WindowsXP/Vista and Visual C++ 2008). The implementation is solely based on standard boost facilities (Boost.Bind, Boost.Function, Boost.Shared_Ptr, etc.) which will be part of next C++ standard.
Download: http://port.sourceforge.net
Installation:
Join is a header only library. There is no need to build the library itself to use it. Please following these steps:
download or checkout boost distribution
download latest boost_join_x_x.tar.gz
tar xvzf boost_join_x_x.tar.gz
add boost's directory and Join's directory to compilers' include path
cd to <boost_join_directory>/libs/join/exmaple
bjam

Acknowlegement

The original theory and systems of Join Calculus / JoCaml are developed by Cédric Fournet, Georges Gonthier, Jean-Jacques Lévy, Luc Maranget and Didier Rémy and others at MOSCOVA INRIA.
At Microsoft Research, Cω is developed by Nick Benton, Luca Cardelli, Cédric Fournet and others. Claudio Russo developed the C# Joins library which is an efficient combinator library for Cω-style join patterns, implemented in C# 2.0 generics and accessible from multiple .Net languages.
An earlier version of the Join library described here is based on both Cω and Jocaml which supports multiple synchronous methods per chord (join-pattern) and propagation of exceptions to all synchronous callers.
The current version is mostly based on Cω, supporting only one synchronous method per chord for simplicity and better performance.
Thanks Dr. Claudio Russo at Microsoft Research for many insightful email discussions, clarifying many of my questions about Cω internals and semantics, and many helpful links to other Join based projects.
Thanks Dr. Luc Maranget at Moscova INRIA for many kind advices and links to papers, for pointing out the advantages (simplification and optimization) of the single-synch per chord design.
In the earlier version of this Join library, the functional signature API of async / synch methods is modeled after Douglas Gregor's Boost.Signals.
For earlier versions which support multiple synchronous methods and exception propagation,  the scheme of catching exceptions in one thread, and rethrowing it in another thread is based on Christopher M. Kohlhoff's idea and sample code posted in boost email list discussions.