
Hello, On 6/13/07, Scott Woods <scottw@qbik.com> 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.
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
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 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