Join - Asynchronous Message
Coordination and
Concurrency
Library
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:
-
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.
-
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:
- Cω's chord defintion is "static", declared in class definition,
cannot change during runtime, can be optimized by compiler.
- Join's chord definition is "dynamic", created by chord()
functions, can be changed during runtime. It can support dynamic join
programming similar
to JoCaml and CCR[4].
-
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.