Synchronization in JavaSymphony
Lock/Unlock mechanism for JSObject already provides
a way to synchronize concurrent access to methods of a remote object. JavaSymphony
provides two additional synchronization mechanism, which we consider useful
for parallel application:
- Synchronization of asynchronous
method invocations allows the synchronization at the end of the execution
of several methods running asynchronously in parallel;
- Barrier synchronization allows synchronization while methods
are executed in parallel.
Synchronization of Asynchronous Method Invocations
JavaSymphony enables a programmer to group a set of
result handles -- each one associated with a unique asynchronous remote
method invocation -- by using a class ResultHandleSet. This class
provides several methods to block or examine (without blocking) whether
one, a certain number, or all threads finished processing their methods.
Frequently, methods of different objects -- that reside on different computing
nodes -- are executed in parallel, in order to implement load balancing.
By using a synchronization mechanism we can easily determine which object
is idle because the execution of its method has finished. The following
excerpt of code illustrates these features:
JSObject obj[n];
ResultHandleSet rhs;
ResultHandle rh;
Object[] params;
...
for(i=0; i < n; i++)
{
// add a ResultHandle and index i (optional)
to the ResultHandleSet rhs
rhs.add(obj[i].ainvoke("run", params), i);
}
...
//non-blocking test if at least 5 methods are finished
if( rhs.isReady(5) ) {...}
//non-blocking test if all methods are finished
if( rhs.isAllReady() ) {...}
//block until at least 5 methods are finished
if( rhs.waitReady(5) ) {...}
// block until all methods are finished
if( rhs.waitAll () ) {...}
// get results one by one without specific order; block
until the first method has returned results
rh = rhs.getFirstReady();
while(rh != null) {
// get the results
ResultClass result = (ResultClass)rh.getResult();
... // process results
// get index of idle object
index = rhs.getIndex(rh);
// invoke next method on idle object for
load balancing and add ResultHandle in ResultHandleSet again
rhs.add( obj[index].ainvoke("run", params), index);
// get ResultHandle of a method that finishes
next; block until results returned
rh = rhs.getNextReady();
}
Barrier Synchronization
JavaSymphony provides a barrier for methods called
in JS objects. The flow of control for a set of threads can be suspended
until all of these threads reach a certain barrier point.
A number of barriers can be defined for each JS application by using a static
method newBarrier -- part of JSRegistry class -- with an
identifier that uniquely identifies the barrier, and the number of the threads
n which have to wait at this barrier. The barrier is visible by all
objects of the application. The execution of the threads reaching a barrier
point is suspended until exactly n threads reach this point. Thereafter,
all threads can resume execution beyond the barrier.
The following code excerpt demonstrates the usage of the JS barrier operation:
// a barrierId defines a unique synchronization
point
int barrierId = 17;
// define a barrier for two (remote) threads.
JSRegistry.newBarrier(2, barrierId);
obj1.oinvoke("runThread1", params);
obj2.oinvoke("runThread2", params);
...
// inside runThread1
int barrierId = 17;
// suspend execution until runThread2 reaches the synchronization
point
JSRegistry.barrier(barrierId);
...
// inside runThread2
int barrierId = 17;
// suspend execution until runThread1 reaches the synchronization
point
JSRegistry.barrier(barrierId);
...
Events in JavaSymphony
JS follows a general event model where objects can subscribe
as consumers for various types of events. Events with a specific type can
be produced and the registered consumers will be notified. An event consumer
handles the event by providing an appropriate method.
A specific advantage of the event mechanism is that JS
does not restrict the types of objects which receive or produce events. Any
Java object distributed by using JavaSymphony can consume or produce events
without implementing dedicated interfaces or extending dedicated JS classes.
JavaSymphony supports three types of events:
- User Defined Events are generated explicitly by the user. They
are used to support asynchronous communication and interaction among arbitrary
Java objects (not restricted to JS objects). The programmer must provide
the code for the producer, which generates an event and a method which is
invoked by the consumer when the notification for the event arrives.
- Middleware Events are produced and controlled by the JRS in
the event of, for instance, VA unavailable, (un)registration of a new application,
object (un)lock or VA (un)lock, etc. The user must provide only the
method that is invoked when notification for the event arrives, whereas the
JRS produces these events.
- System Events are invoked due to changes of dynamic system parameters
such as idle time, available memory, swap space allocated, etc. All of these
parameters can be accessed by the programmer through the JS API for static/dynamic
system parameters . For the consumer object a set of constraints, a constant
that controls the generation of an event, and a method which is invoked if
the event occurs, are specified as constructor parameters. The constant determines
the event generation if the set of constraints holds, does not hold, or changes.
An object that wants to consume a user-defined or JS-middleware
event creates a JSEventConsumer which describes the type (middleware
or user-defined) and properties of the event in which it is interested. A
derived class JSSystemEventConsumer is used for system events. When
building an instance of JSEventConsumer,
the programmer provides the following information:
- a reference to the object that consumes the event;
- a unique event type identifier, and the consumer method which will
be invoked when the event occurs;
Events can be filtered by specific parameters passed to
the constructor. A set of constants for event types is defined as part of
the JSConstants class. For example:
- C_USER_TYPE implies the definition of user-defined events;
- C_APP_REGISTERED denotes an event generated when a new application
registers itself with the JRS;
- C_SYSTEM_EVENT corresponds to system events.
Similarly to filter the events:
- C_ANY_LOCATION indicates that the consumer accepts events from
any producer;
- C_LIST_VA_EVENT restricts the acceptance of events produced
at a specific list of VAs;
- C_LIST_JSOBJECT_EVENT restricts acceptance of events produced
by objects in a specific list of JSObjects.
For JSSystemEventConsumer a JSConstraints
object encapsulate the constraints which will be checked in order to produce
a system event. An additional parameter controls the generation of the event,
if the constraints become valid (JS_CONSTRAINTS_HOLD), invalid (JS_CONSTRAINTS_
NOT_HOLD), or their status changes (JS_CONSTRAINTS_CHANGE). For
every different set of constraints, a distinct system event will be generated
by the JRS which causes all consumers to be notified accordingly.
The consumer subscribes an event by using the subscribe
method of the JSConsumerEvent class.If specific events should no longer
be received, then the consumer
will use the unsubscribe method. Only user-defined events can be explicitly
produced by the programmer through the JSEventProducer object. The
first constructor parameter indicates the object that generates an event.
The second parameter refers to the unique type of the generated event which
must match with the second parameter of JSEventConsumer. Moreover,
the same list of constants defined in JSConstants is used to restrict
the list of consumers.
The following code excerpt demonstrates the usage of the
JS events:
...
// ****** Code for Event Consumer ********
...
// define types for user defined and middleware events
int userEvType = JSConstants.C_USER_TYPE + 1;
int middleEvType = JSConstants.C_APP_REGISTERED;
JSObject listObj[]=.....; // list of remotes objects
VA listVAs[]=.....; // list of VAs
JSConstraints constr1;
// subscribe for a user defined event; no restriction
on event producers; handleMethod will handle events.
JSEventConsumer cEv1 = new JSEventConsumer(this, userEvType, JSConstants.C_ANY_LOCATION,
"handleMethod");
// events can be produced only on VAs in listVAs
JSEventConsumer cEv2 = new JSEventConsumer(this, userEvType, JSConstants.C_LIST_VA_EVENT,
listVAs, "handleMethod");
// event can be produced only by JSObjects in listObj
JSEventConsumer cEv3 = new JSEventConsumer(this, userEvType, JSConstants.C_LIST_JSOBJECT_EVENT,
listObj, "handleMethod");
// subscribe for a middleware event which can be produced
anywhere;the event is generated when a new application registers with JS
JSEventConsumer cEv4 = new JSEventConsumer(this, middleEvType,JSConstants.C_ANY_LOCATION,
"handleMethod");
\> // subscribe for a system event which can be produced only by the VA
va when the validity for a set of constraints constr changes
JSSystemEventConsumer cEvSystem = new JSSystemEventConsumer(this, JSConstants.C_VA_EVENT,
va, "handleMethod", constr, JSConstants.JS_CONSTRAINTS_CHANGE);
...
// subscribe for event cEv1
cEv1.subscribe();
...
// unsubscribe for event cEv1
cEv1.unsubscribe();
...
// ****** Code for Producer of User-Defined Events ********
...
int userEvType = JSConstants.C_USER_TYPE + 1;
Object listObj[]=.....; // list of remotes objects
Object listVAs[]=.....; // list of VAs
// produces a user-defined event of type userEvType;
no restriction on event consumers
JSEventProducer pEv1 = new JSEventProducer (this, userEvType, JSConstants.C_ANY_LOCATION);
// notify only those consumers registered on VAs in listVAs
JSEventProducer pEv2 = new JSEventProducer (this, userEvType, JSConstants.C_LIST_VA_EVENT,
listVAs);
// notify only those consumers in listObj
JSEventProducer pEv3 = new JSEventProducer (this, userEvType, JSConstants.C_LIST_JSOBJECT_EVENT,
listObj);
...
// produce a user-defined event; parameters will be transmitted
to the handleMethod of matching consumer
Object params[]=.....;
pEv1.produceEvent(params)
...
Locality Control
The Locality Control Module (LCM) is a part of the JSR that applies and
manages locality constraints on the executing JS application by mapping
JS objects and tasks onto the VA nodes. In JS, We can specify locality
constraints at three levels of abstraction:
- Application-level
locality constraints are applied to all JS or SJS objects and all
future data allocations of a JS application. The locality constraints
can be specifed with the help of setAppAffinity static method of the
JSRegistry class.
- Object-level locality
constraints are applied to all method invocations and data allocations
performed by a JS or SJS object. The object-level locality constraints
override any previous application-level constraints for that object.
- Task-level locality
constraints are applied to specific task invocations and override any
previous object or application-level constraints for that task.
The LCM in coordination with the OAS job processing mechanism applies
the locality constraints. The jobs are processed by the job handler
threads within an OA. The job handlers are JVM threads that are
executed by some system-level threads such as the POSIX threads on a
Linux system. Below we show some code example related to locality
specification at three levels.
......
// Creating a level-0 VA which represents a core
VA c1 = new VA(new int[]{0},0);
// Creating a level-1 VA which represents a processors
VA p1 = new VA(new int[]{0},1);
//Creating a level-2 VA ( a machine or workstation)
VA v1 = new VA(2);
//Add core to processor, processor to machine
p1.addVA(c1);
v1.addVA(p1);
.......
//Application level locality
JSRegistry reg=new JSRegistry("MyApp",v1);
.........
//Object level locality
SJSObject obj1 = new SJSObject(false,"MyClass",new int[]{},p1);
.........
//Task level locality
ResultHandle handle = obj1.ainvoke("MyMethod", new int[]{methodID},c1};