Boost logo

Boost :

From: Yigong Liu (yigongliu_at_[hidden])
Date: 2007-06-17 13:47:00


Hello,

On 6/13/07, Scott Woods <scottw_at_[hidden]> wrote:
>
>
> Yes. I have this lingering feeling that it is a cute abstraction at
> language
> level that relieves the developer from the headache of concurrency. Just
> right at
> this moment I think I have a chord-ache.

 A comparison of Java/C#'s synchronization model and Join's model may help a
little. Both models use a object-wise lock. However the lock is used
differently.

Java/C#'s model is based on "monitor", the whole object state (all
application data too) is the critical region protected by this lock, if a
synchronized method is called, the lock is hold during the execution of
method body, the callings of all other synchronized methods will be blocked
till the first calling returns.
In Join, the object-wise lock is only used to protect the synchronization
state of the object, embedded in actor<> / async<> / synch<> members. When a
async / synch method is invoked, the lock is only hold for buffering
messages and checking if any chords are ready to fire (all its messages have
arrived) and then it is released. When chord bodies execute (either in
calling thread of a synch<> method or a task in thread pool), this lock is
not held anymore.
We can explore the implications of this difference in the following
directions:

1> Join's object-wise lock is not used to synchronize access to application
code, instead this synchronization should be specified explicitly in chords
or join-patterns. For example, if two methods may access / modify an
internal data (data_A) concurrently, we could define an async method
data_A_lock() to synchronize the access:
   chord(method1, data_A_lock, chord_body1);
   chord(method2, data_A_lock, chord_body2);
By calling data_A_lock(), data_A is unlocked. Then the calling of
method1/chord_body1 and method2/chord_body2 are synchronized regarding to
data_A.

2> Join's synchronization is more fine-grained. In Java/C# model, calling a
synchronized method will lock the whole object. For some object, its
synchronized methods could be divided into diff groups because they depends
on diff resources or states. Theoretically, one method from each of these
diff groups should be able to run concurrently without interfering. In Java
we cannot do it since the whole object is locked. To do it we need to split
the original object into smaller objects, one for each group, to use
different locks. This issue can be easily resolve in Join (with one lock) by
defining a separate async<> method for each group and diff methods can join
with a proper async<> method to acquire the exclusive access to the resource
it needs.

3> Join's model can help avoiding some important cases of dead-locks. Some
actions need exclusive access to multiple resources. Traditionally we need
first lock all these resources one by one and then perform the action. This
is problematic design, since some other actions may need exclusive access to
(some of) these resources too. We have to make sure all these resources /
locks are acquired in the same order otherwise dead-lock will occur.
   In Join, we can resolve this issue by defining an async method (lock1(),
lock2(),...) for each resource and defining chord to join the action method
with the set of async methods represents the set of resources it requires:
   chord(action1, lock1, lock2, lock3,..., chord_body);
   Please note that when message arrive, Join will check the availablity of
all resources (lock1, lock2, ...) in one "atomic" transaction, so the order
of how lock1(), lock2(),... are called are not relevant or important
anymore.

My interest in Join arises from a perceived overlap of concurrency and
> FSMs.
>
> Yes, we can use Join to encode some state-machine. The basic idea is that
we use async methods to represent the states of objects and using chord to
define which actions should be performed in which states. e.g.
     async<void()> state1;
     async<void()> state2;
     ...
     chord(state1, method1, chord_body1);
     chord(state2, method2, chord_body2);
     ...
     In constructor, we can init object state by calling state1(); To
transition to state2 from inside a chord_body1, we can call state2(). Please
refer to the active_object tutorial for a sample.

Also i found Join is a close match to the "reactive" model we use to design
network servers. Async<> methods can be used to represent incoming requests
and chords defining the reaction rules about how to handle requests. By
properly choosing synch<> / asynch<> to define the process method, we can
achieve different server designs such as "iterative server" or "a task per
request"/thread-pool design.

Thanks
Yigong


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk