POM (Parallel Object Monitors) is a concurrency abstraction for the coordination of parallel activities over single objects and groups of objects in shared memory systems. It allows a clean separation of the coordination concern from the base application code.
A POM is a monitor defining a scheduling method responsible for specifying how concurrent requests should be scheduled, possibly in parallel. A POM also defines a leaving method which is executed by each thread once it has executed a request. Such methods are essential to the proposed abstraction as it makes it possible to reuse functional code as it is, adding necessary synchronization actions externally.
When a thread invokes a method on an object controlled by a POM, the thread is blocked and the invocation is reified and turned into a request object. Requests are then queued in a pending queue until the scheduling method grants them permission to execute. The scheduling method can trigger the execution of several requests; all selected requests are free to execute in parallel, and are run by the thread that originated the call. When a thread has finished the execution of a request, it has to run the leaving method before leaving the POM. Note that although requests can be executed in parallel, the scheduling and leaving methods are always executed in mutual exclusion.
POM is based on Reflex and therefore Reflex should be installed and configured prior to using POM. POM can be used through direct configuration or through annotations. In both cases, the scheduler is defined in the same way.
Direct configuration consists in explicitly using the POM API in a POM configuration class so as to create the appropriate Reflex links, hooksets and metaobjects. An example of such a configuration can be found in the SVN repository: Config.java.
The first step is to create a configuration class that extends POMConfig
and override the initPOM()
method:
public class Config extends POMConfig { protected void initPOM() {
The initPOM()
method must call one of the schedule()
method to specify the scheduling policies (the different variants of the schedule
method are described in details in the Javadoc of the POMConfig
class). In the following, all instances of the POMBuffer
class are scheduled by a single POMBufferScheduler
:
schedule("reflex.lib.pom.test.bench.pc.POMBuffer", "reflex.lib.pom.test.bench.pc.POMBufferScheduler"); }
Finally create a main()
method that sets up Reflex. In the following, the link provider (-lp) is set to be the current configuration class; the scope (–working-set) is reduced to the classes of the reflex.lib.pom
package and its subpackages, and the class that contains the actual main()
method is specified:
public static void main(String[] args) throws Throwable { LogLevel.set(LogLevel.VERBOSE); Run.main(new String[] { "-lp", Config.class.getName(), "--working-set", "[+reflex.lib.pom.**]", "reflex.lib.pom.test.bench.Main" }); } }
Another, and often easier, way to configure POM is to use annotations. In this case, the POMConfig
class itself can be used as a link provider and the classes and methods to synchronize are marked with annotations:
@POMSyncClass( scheduler = Scheduler.class, group = Scheduler.class, syncAll = false) public class MyClass { @POMSync(category="C1") public void foo() { ... } @POMSync public void bar() { ... } public void bar2() { ... } }
In the above example, the MyClass
class is synchronized by Scheduler
. Instances of MyClass
are divided into groups as specified by a POMGroupDef
(in this case, Scheduler
implements POMGroupDef
) . The syncAll
attribute of POMSyncClass
indicates that by default no method is synchronized: methods to synchronize are explicitly specified using the @POMSync
attribute. Here the foo()
and bar()
methods are synchronized, while the bar2()
method is not.
The scheduler is responsible for granting requests the permission to execute. A POM scheduler must extend the POMScheduler
class and at least override the schedule()
method; it can also override the leave()
method if necessary.
Example 1: a simple mutual exclusion scheduler.
public class MutualExclusionSched extends POMScheduler { private boolean working = false; public void schedule() { if(!working) working = executeOldest(); } public void leave(Request req) { working = false; } }
The state of MutualExclusionSched
consists of a boolean variable (working
) that is true if and only if there is one thread executing a method in one of the monitored objects. The scheduling method only grants permission to execute a request when working
is false: the oldest request is given permission to execute through the call to executeOldest()
; this method returns a boolean indicating whether some request was actually given permission to execute. The leave()
method updates the value of working
to false
in order to indicate that no request is in execution anymore.
Many more examples are provided in 2007-CCPE.
Sequential Object Monitors, or SOM, is a concurrency abstraction similar to POM but geared towards sequential activities. Actually, a SOM is a particular case of a POM where only one request is executed at a time, and there is only one object controlled by the scheduler. POM provides the SOMScheduler
class for implementing SOM schedulers.
The source code for POM is available in the SVN repository: http://pleiad.dcc.uchile.cl/svn/pom/.