With the 1.3.0 release of the MASS library, a new feature has been introduced to make it easier to develop applications that simulate the passing of time or synchronization of events based upon the notion of a global clock, the value of which being consistent among all cluster nodes.
The Global Logical Clock is a long datatype value that increments with each call to Agents.manageAll, .doAll, .doWhile, and .doUntil. When the clock value increments, all cluster nodes will receive this new value automatically.
Agents may contain methods that are invoked upon the clock reaching:
In addition to triggering events (method invocation) upon the clock value reaching some value, applications may query and even control the clock in a limited manner:
For each clock iteration, every node will compute the next value at which an event is scheduled to occur and will automatically “fast forward” the clock to skip over values that would not trigger an event. This prevents the clock from incrementing through cycles that would not trigger a change in the simulation.
Annotations are used to determine which methods are invoked when the clock reaches specified values. Although there is only one annotation (“@Clocked”) used to describe the Agent methods to be invoked by the clock, there are three different ways to specify when the method is to be invoked:
If you would like to have an Agent method invoked on each iteration of the clock value (on every “tick”), simply decorate the method with the “@Clocked” annotation with no arguments as follows:
#!java
@Clocked
public void onEveryClockCycle( ) {
// simulate movement down the path
myDistanceTravelled += pathLength;
}
You may want to have a method invoked on specific values of the clock and not any other time. Here are some examples of annotating methods to occur at specific “times”:
#!java
@Clocked( onValuesOf = { 2 } )
public void doDelayedInitialization( ) {
// perform second series of initialization functions, now that the first set has completed
doSecondStageInit();
}
@Clocked( onValuesOf = { 5, 10, 15, 20 } )
public void checkForCollision( ) {
// only at clock cycles 5, 10, 15, and 20 should we check for collision
doCollisionCheck();
}
The final type of clocked method invocation is on multiples of the clock value. Here is an example of a method that would be called every fifth clock cycle:
#!java
@Clocked( onMultiplesOf = { 5 } )
public void onEveryFifthClockCycle( ) {
// is this Agent being followed?
if ( isFollowed() ) {
doHide();
}
}
Just like “onValuesOf”, you can specify several multiples for invocation:
#!java
@Clocked( onMultiplesOf = { 2, 3, 5, 7, 11 } )
public void onFirstPrimes( ) {
// check for local minima
if ( currentValue < minValue ) {
minValue = currentValue;
}
}
As described previously, the clock will increment after each call to Agents.manageAll, .doAll, .doWhile, and .doUntil. It is possible, though, to control the clock from your main application. There are a few methods that you may use to control the clock during the simulation:
Not a controlling method, but you can obtain the current clock value using this method.
Calling this will cause the Global Logical Clock to increment by one.
Use this method to reset the clock to a value of zero.
You can force the clock to a specified value using this method. After each node is set to this new value, all Agents will be evaluated to see if @Clocked method invocations are in order.