POM

Introduction

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.

Usage

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

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" });
	}
}	

Annotations

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.

Scheduler definition

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.

POM and SOM

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.

Download

The source code for POM is available in the SVN repository: http://pleiad.dcc.uchile.cl/svn/pom/.

Publications

Loading bibtex info...