CSP for Java
(JCSP) 1.1-rc4

org.jcsp.lang
Class AltingBarrier

java.lang.Object
  extended by org.jcsp.lang.Guard
      extended by org.jcsp.lang.AltingBarrier

public class AltingBarrier
extends Guard

This is the front-end for a barrier that can be used as a Guard in an Alternative.

Description

An alting barrier is represented by a family of AltingBarrier front-ends. Each process using the barrier must do so via its own front-end. A new alting barrier is created by the static create method, which returns an array of front-ends. If new processes need to be enrolled, further front-ends may be made from an existing one (see expand and contract). A process may temporarilly resign from a barrier and, later, re-enroll.

To use this barrier, a process simply includes its given AltingBarrier front-end in a Guard array associated with an Alternative. Its index will be selected if and only if all parties (processes) to the barrier similarly select it (using their own front-ends).

If a process wishes to commit to this barrier (i.e. not offer it as a choice in an Alternative), it may sync on it. However, if all parties only do this, a non-alting Barrier would be more efficient. A further shortcut (over using an Alternative) is provided to poll this barrier for completion.

An AltingBarrier front-end may only be used by one process at a time (and this is checked at run-time). A process may communicate a non-resigned front-end to another process; but the receiving process must mark it before using it and, of course, the sending process must not continue to use it. If a process terminates holding a front-end, it may be recycled for use by another process via reset.

Priorities

These do not -- and cannot -- apply to selection between barriers. The priSelect() method works locally for the process making the offer. If this were allowed, one process might offer barrier x with higher priority than barrier y ... and another process might offer them with its priorities the other way around. In which case, it would be impossible to resolve a choice in favour of x or y in any way that satisfied the conflicting priorities of both processes.

However, the priSelect() method is allowed for choices including barrier guards. It honours the respective priorities defined between non-barrier guards ... and those between a barrier guard and non-barrier guards (which guarantees, for example, immediate response to a timeout from ever-active barriers). Relative priorities between barrier guards are inoperative.

Misuse

The implementation defends against misuse, throwing an AltingBarrierError error when riled. See the documentation for AltingBarrierError for circumstances.

Example 0 (a single alting barrier)

Here is a simple gadget with two modes of operation, switched by a click event (operated externally by a button in the application below). Initially, it is in individual mode -- represented here by incrementing a number and outputting it (as a String to change the label on its controlling button) as often as it can. Its other mode is group, in which it can only work if all associated gadgets are also in this mode. Group work consists of a single decrement and output of the number (to its button's label). It performs group work as often as the group will allow (i.e. until it, or one of its partner gadgets, is clicked back to individual mode).
 import org.jcsp.lang.*;
 
 public class AltingBarrierGadget0 implements CSProcess {
 
   private final AltingChannelInput click;
   private final AltingBarrier group;
   private final ChannelOutput configure;
 
   public AltingBarrierGadget0 (
     AltingChannelInput click, AltingBarrier group, ChannelOutput configure
   ) {
     this.click = click;
     this.group = group;
     this.configure = configure;
   }
 
   public void run () {
 
     final Alternative clickGroup =
       new Alternative (new Guard[] {click, group});
 
     final int CLICK = 0, GROUP = 1;
 
     int n = 0;
     configure.write (String.valueOf (n));
 
     while (true) {
 
       configure.write (Color.green)                // pretty
 
       while (!click.pending ()) {                  // individual work mode
         n++;                                       // work on our own
         configure.write (String.valueOf (n));      // work on our own
       }
       click.read ();                               // must consume the click
 
       configure.write (Color.red);                 // pretty
       
       boolean group = true;                        // group work mode
       while (group) {
         switch (clickGroup.priSelect ()) {         // offer to work with the group
           case CLICK:
             click.read ();                         // must consume the click
             group = false;                         // back to individual work mode
           break;
           case GROUP:
             n--;                                   // work with the group
             configure.write (String.valueOf (n));  // work with the group
           break;
         }
       }
       
     }
 
   }
 
 }
 
Here is code for a system of buttons and gadgets, synchronised by an alting barrier. Note that this single event needs an array of AltingBarrier front-ends to operate -- one for each gadget:
 import org.jcsp.lang.*;
 import org.jcsp.plugNplay.*;
 
 public class AltingBarrierGadget0Demo0 {
 
   public static void main (String[] argv) {
 
     final int nUnits = 8;

     // make the buttons
 
     final One2OneChannel[] event = Channel.one2oneArray (nUnits);
     
     final One2OneChannel[] configure = Channel.one2oneArray (nUnits);
 
     final boolean horizontal = true;
 
     final FramedButtonArray buttons =
       new FramedButtonArray (
         "AltingBarrier: Gadget 0, Demo 0", nUnits, 120, nUnits*100,
          horizontal, configure, event
       );
 
     // construct an array of front-ends to a single alting barrier
 
     final AltingBarrier[] group = AltingBarrier.create (nUnits);
 
     // make the gadgets
 
     final AltingBarrierGadget0[] gadgets = new AltingBarrierGadget0[nUnits]; 
     for (int i = 0; i < gadgets.length; i++) {
       gadgets[i] = new AltingBarrierGadget0 (event[i], group[i], configure[i]);
     }
 
     // run everything
 
     new Parallel (
       new CSProcess[] {
         buttons, new Parallel (gadgets)
       }
     ).run ();
 
   }
 
 }
 
The very simple "group" work in the above example consists of actions performed independently by each gadget (decrementing the number on its button's label). The (alting) barrier synchronisation ensures that these decrements keep in step with each other.

A more interesting gadget would work with other gadgets for group work that really did require them all to be engaged. For example, they resume operation of a machine that would be dangerous if some gadgets (perhaps those responsible for safety aspects) were doing their individual work.

Example 1 (lots of alting barriers)

This example derives from a pathological challenge to the management of choice between multiway synchronisations raised by Michael Goldsmith (Formal Systems Europe). There are three processes (P, Q and R) and theee events (a, b and c). P offers events a and b; Q offers events b and c; and R offers events c and a. If P and Q synchronise on b, they do something (possibly together) then start again. Similarly if Q and R synchronise on c or if R and P synchronise on a. In CSP, the expression is trivial:
  P = ((a -> P0); P) [] ((b -> P1); P), and where c is not in the alphabet of P
  Q = ((b -> Q0); Q) [] ((c -> Q1); Q), and where a is not in the alphabet of Q
  R = ((c -> R0); R) [] ((a -> R1); R), and where b is not in the alphabet of R

  SYSTEM = (P || Q || R) \ {a, b, c}
 
To impact their environment (and avoid divergence), the sub-processes P0, P1, Q0, Q1, R0 and R1 will engage in external events (i.e. not just a, b or c). Additionally, P1 and Q0 (triggered by b) may engage in other hidden events, not given in the above. The same for Q1 and R0 (triggered by c) and for R1 and P0 (triggered by a).

In our version, there are N processes and events arranged (logically) around a circle. Each process is either off or on standby, switching between these states on random timeouts. Each process is also attached to a personal button that it uses to indicate its state. When off, it colours its button black; when on standby, light gray.

When on standby, each process offers (span+1) events: a timeout, the event with it on the circle and the next (span-1) events going (say) clockwise. It the timeout occurs, it switches to its off state. If one of the events (AltingBarrier) occurs, it must have occured for a consecutive block of span processes (including this one ... somewhere) around the circle. This group now go into a playing state.

Not mentioned before is a rail track, made of channels, running round the circle. When playing, the process furthest uptrack choses a colour and sends this to its partners down the track (to which the mulitway synchronisation ensures this group has exclusive access). Each process in the playing group then flashes its button with that colour a fixed (parametrised) number of times. The rate of flashing is coordinated by the AltingBarrier multiway synchronisation event common to the group -- the furthest uptrack process only keeping time for this. After playing, the process switches to its off state.

[Note: the above SYSTEM has N equal to 3, span equal to 2, no off state and no timeout on standby. The channels used to flash the buttons are the external events mentioned and the rail track channels are the hidden extras.]

Here is the code for these processes. As usual, the constructor just saves all parameters:

 import org.jcsp.lang.*;
 import org.jcsp.awt.*;
 
 import java.awt.Color;
 import java.util.Random;
 
 public class AltingBarrierGadget1 implements CSProcess {
 
   private final AltingBarrier[] barrier;
   private final AltingChannelInput in, click;
   private final ChannelOutput out, configure;
   private final Color offColour, standbyColour;
   private final int offInterval, standbyInterval;
   private final int playInterval, flashInterval;
 
   public AltingBarrierGadget1 (
     AltingBarrier[] barrier,
     AltingChannelInput in, ChannelOutput out,
     AltingChannelInput click, ChannelOutput configure,
     Color offColour, Color standbyColour,
     int offInterval, int standbyInterval,
     int playInterval, int flashInterval
   ) {
     this.barrier = barrier;
     this.in = in;  this.out = out;
     this.click = click;  this.configure = configure;
     this.offColour = offColour;  this.standbyColour = standbyColour;
     this.offInterval = offInterval;  this.standbyInterval = standbyInterval;
     this.playInterval = playInterval;  this.flashInterval = flashInterval;
   }
 
The barrier array gives this gadget access to the multiway events shared with adjacent siblings. The in and out channel ends are part of the rail track this gadget uses later (when playing). The click and configure channels attach this gadget to its button. The click channel is never used by this gadget -- it's included for completeness should anyone wish to enhance its behaviour. The other parameters are just data.

The run() method controls switching between off, standby and playing states. The latter is the choice between all the multiway syncs (and the timeout). It is handled by a fair select on the Alternative, constructed just once (before loop entry):

   public void run () {
  
     CSTimer tim = new CSTimer ();
 
     final Random random = new Random ();
 
     final Guard[] standbyGuard = new Guard[barrier.length + 1];
     for (int i = 0; i < barrier.length; i++) {
       standbyGuard[i] = barrier[i];
     }
     standbyGuard[barrier.length] = tim;
     final int TIMEOUT = barrier.length;
     Alternative standbyAlt = new Alternative (standbyGuard);
 
     configure.write (Boolean.FALSE);               // disable mouse clicks
                                                    // (not used by this gadget)
     while (true) {
 
       configure.write (offColour);
       tim.sleep (random.nextInt (offInterval));
 
       configure.write (standbyColour);
       tim.setAlarm (tim.read () + random.nextInt (standbyInterval));
 
       int choice = standbyAlt.fairSelect ();       // magic synchronisation
 
       if (choice != TIMEOUT) {
         play (choice, random, tim);
       }
       
     }
  
   }
 
Here is the playing code. Initially, a colour is chosen and passed down the playing group's section of rail track, to which it has exclusive access. The flashing group is coordinated through the group's common event, with just one of them keeping time.
   private void play (int choice, Random random, CSTimer tim) {
     
     final boolean RIGHTMOST = (choice == 0);
     final boolean LEFTMOST = (choice == (barrier.length - 1));
 
     Color colour = null;
     if (RIGHTMOST) {
       colour = new Color (random.nextInt ());
     } else {
       colour = (Color) in.read ();
     }
     Color bright = colour.brighter ();
 
     if (!LEFTMOST) out.write (colour);             // pass it on
 
     final AltingBarrier focus = barrier[choice];
 
     final int count = playInterval/flashInterval;
 
     long timeout = tim.read () + flashInterval;
     
     boolean bright = true;
 
     for (int i = 0; i < count; i++) {
       configure.write (bright ? brighter : colour);
       bright = !bright;
       if (RIGHTMOST) {
         tim.after (timeout);
         timeout += flashInterval;
       }
       focus.sync ();
     }
 
   }
 
 }
 
Here is code setting up a "circle" of these gadgets, buttons and alting barriers. The buttons are laid out in a row, so that the rightmost button is actually on the "left" of the leftmost button. Care needs to be taken to distribute the span front-ends for each AltingBarrier to the correct gadgets -- see the re-arrangement below:
 import org.jcsp.lang.*;
 import org.jcsp.util.*;
 import org.jcsp.plugNplay.*;
 
 import java.awt.Color;
 import java.util.Random;
 
 public class AltingBarrierGadget1Demo0 {
 
   public static void main (String[] argv) {
 
     final int nUnits = 30, span = 6;
     
     final int offInterval = 800, standbyInterval = 1000;    // milliseconds
     
     final int playInterval = 10000, flashInterval = 500;    // milliseconds
 
     final Color offColour = Color.black, standbyColour = Color.lightGray;
 
     // make the buttons
 
     final One2OneChannel[] click =
       Channel.one2oneArray (nUnits, new OverWriteOldestBuffer (1));
 
     final One2OneChannel[] configure = Channel.one2oneArray (nUnits);
 
     final boolean horizontal = true;
 
     final FramedButtonArray buttons =
       new FramedButtonArray (
         "AltingBarrier: Gadget 1, Demo 0", nUnits, 100, nUnits*50,
          horizontal, configure, click
       );
 
     // construct nUnits barriers, each with span front-ends ...
 
     AltingBarrier[][] ab = new AltingBarrier[nUnits][];
     for (int i = 0; i < nUnits; i++) {
       ab[i] = AltingBarrier.create (span);
     }
 
     // re-arrange front-ends, ready for distribution to processes ...
 
     AltingBarrier[][]barrier = new AltingBarrier[nUnits][span];
     for (int i = 0; i < nUnits; i++) {
       for (int j = 0; j < span; j++) {
         barrier[i][j] = ab[(i + j) % nUnits][j];
       }
     }
 
     // make the track and the gadgets
 
     One2OneChannel[] track = Channel.one2oneArray (nUnits);
 
     AltingBarrierGadget1[] gadgets = new AltingBarrierGadget1[nUnits];
     for (int i = 0; i < nUnits; i++) {
       gadgets[i] =
         new AltingBarrierGadget1 (
           barrier[i],
           track[(i + 1)%nUnits], track[i],
           click[i], configure[i],
           offColour, standbyColour,
           offInterval, standbyInterval,
           playInterval, flashInterval
         );
     }
 
     // run everything
 
     new Parallel (
       new CSProcess[] {
         buttons, new Parallel (gadgets)
       }
     ).run ();
 
   }
 
 }
 
For fun, here is another application program for the same gadget. It allows a much larger system to be built, laying out the circle of buttons in a grid, row by row. The rightmost button on each row is to the left of the leftmost button on the next row down. The next row down from the bottom row is the top row. The buttons and its click and configure channels are now two dimensional structures. The barriers, gadgets and rail track are still one dimensional. Only code differences from the above are shown:
 import org.jcsp.lang.*;
 import org.jcsp.util.*;
 import org.jcsp.plugNplay.*;
 
 import java.awt.Color;
 import java.util.Random;
 
 public class AltingBarrierGadget1Demo1 {
 
   public static void main (String[] argv) {
 
     final int width = 30, depth = 20;
     final int nUnits = width*depth;
     
     ...  the other system parameters (final ints and Colors)
     
     final One2OneChannel[][] click = new One2OneChannel[depth][];
     for (int i = 0; i < depth; i++) {
       click[i] = Channel.one2oneArray (width, new OverWriteOldestBuffer (1));
     }
     
     final One2OneChannel[][] configure = new One2OneChannel[depth][];
     for (int i = 0; i < depth; i++) {
       configure[i] = Channel.one2oneArray (width);
     }
 
     final FramedButtonGrid buttons =
       new FramedButtonGrid (
         "AltingBarrier: Gadget 1, Demo 1", depth, width,
         20 + (depth*50), width*50, configure, click
       );
 
     ...  construct nUnits barriers and the track exactly as before
 
     AltingBarrierGadget1[] gadgets = new AltingBarrierGadget1[nUnits];
     for (int i = 0; i < nUnits; i++) {
       gadgets[i] =
         new AltingBarrierGadget1 (
           barrier[i],
           track[(i + 1)%nUnits], track[i],
           click[i/width][i%width],
           configure[i/width][i%width],
           offColour, standbyColour,
           offInterval, standbyInterval,
           playInterval, flashInterval
         );
     }
 
     ...  build and run the buttons and gadgets in parallel
 
   }
 
 }
 

Other Examples

The alting-barriers directory in jcsp-demos contains other gadgets in a similar vein.

AltingBarrierGadget2

These are similar to AltingBarrierGadget1, but sit on a 2-way circular railtrack offering to synchronise in the same span-groups. Their difference is the game they play when synchronised: pass-the-parcel up and down their section of track, with the parcel's (rapid) progress indicated by writing on the button labels. These gadgets also enable their buttons when playing and finish their game when any one, or more, of their buttons is clicked -- or they get bored and timeout.

AltingBarrierGadget3

Along with their attached buttons, these form a two dimensional structure covering the surface of a torus. The gadgets on the top row are adjacent to the gadgets on the bottom row. The gadgets on the left column are adjacent to the gadgets on the right column. The demonstration program, AltingBarrierGadget3Demo0, asks the user to choose between various shapes and sizes for the synchronisation groups (pluses, crosses and circles) -- but all groups have the same shape and size.

Creation and distribution of the barriers is not done by the demonstration program but, more simply, by the gadgets themselves. Each AltingBarrierGadget3 belongs to many synchronisation groups, but has lead responsibility for one. It services the input end of a single channel and is given the (shared) output ends of the service channels to the other gadgets in the group it is leading. [Note: the giver of those output ends is the demonstration program.] It creates the alting barrier (and some other things -- see below) for its lead group. Distribution is by I/O-PAR exchange over their service channels as the gadgets initialise. Each gadget sends the things it made to the gadgets in the group it is leading and receives the same from the leaders of its other synchronisation groups. This is the only use they make of these channels.

A synchronised group plays a simple counting game until one of its buttons is clicked or the countdown reaches zero. Termination of the game is, and has to be, simultaneous. This is managed by a shared termination flag, safely operated through phased barrier synchronisation (which lets any process in the group set it in an even phase, with all processes acting on it in the odd phases). Shared label and colour variables (for the group's buttons) are operated similarly. The shared variables are distributed (as fields of a shared object) by the leader gadget, along with the group's alting barrier front-ends, during initialisation.

The group's AltingBarrier is used to separate the phases. Alting capability on this barrier enables rapid response to any button click on the group to end the game. The lead gadget controls timing: it alts between a countdown timeout, its button click and a cancel message from the rest of the group (should any of their buttons be clicked) -- following any of these with the barrier sync, scheduling the next phase. The other gadgets alt between their button click and the barrier: response to a click being the (timeout) cancel message to the leader then wait for the barrier; response to the barrier being the next phase.

The Any2One cancel channel is a mulitplexing relay from the non-lead buttons to the leader gadget. It is constructed by the lead gadget and distributed to its team alongside the shared variables. The cancel channel must be overwriting buffered to avoid deadlock -- the same as the click channels from buttons. The cancel channel must be cleared at the start of each game -- same as the click channels.

USER GAME: run the demo program on a 30x20 grid (expand to full screen), with circle shapes (say), a radius of 3, off and standBy intervals of 1000 (millisecs), play interval of 10000 (millisecs) and a count interfavl of 200 (millisecs). Your challenge is to zap all the coloured shapes away before any of their counts reach zero and the end of the world happens, :). How long can you survive?!!

AltingBarrierGadget4

These are the same as the previous (AltingBarrierGadget3) gadgets, except that they do not assume that all the synchronisation groups to which they belong have the same shape or size. While they do know the size of the group they lead (from the length of the channel of output ends they are given), that is all they know. In particular, they do not know how many items (barriers etc.) to expect from the leaders of their other groups in the opening exchange.

This is solved by giving each gadget a global barrier on which their parallel outputting processes synchronise when they finish their distribution. After this synchronisation, all exchanges must have finished and they can tell their gadgets to proceed.

The demonstration program for these gadgets just asks for a size for synchronisation groups and allocates (lead) shapes randomly. It could randomise the sizes as well, but the smaller patterns would always emerge dominant in the synchronisations achieved (simply because they require fewer gadgets to be simultaneously on standby -- i.e. offering mode) and the consequent games.

USER GAME: same as before, except with a span (rather than radius) of 3.

AltingBarrierGadget5

These are the same as AltingBarrierGadget4, except for the technique used to signal the end of the initial exchange of information amongst the synchronisation groups. They use a global alting barrier on which both the outputting and inputting partners in the exchange offer to synchronise -- the former when finished outputting and the latter as an alternative to inputting. This is, perhaps, more elegant than the conventional barrier and channel used by the AltingBarrierGadget4 gadgets and exercises the expand() and contract() methods.

USER GAME: same as before, except with a span (rather than radius) of 3.

AltingBarrierGadget6

The unbuffered service channels used by AltingBarrierGadget3, AltingBarrierGadget4 and AltingBarrierGadget5 (in the opening I/O-PAR exchange of information amongst each playing group) force the leaders to synchronise with each of their team members. This is no problem for AltingBarrierGadget3, since all groups have the same size and it receives as many messages as it sends; knowing how many messages it is sending, it knows how large its reception arrays should be. This is not the case for AltingBarrierGadget4 and AltingBarrierGadget5, which must first receive into collection objects that can expand to any size.

AltingBarrierGadget6 performs an asynchronous opening exchange of information, using the collection objects directly to buffer the communication messages and no channels. Further, no internal parallelism is needed for this exchange: the gadgets first add all their messages to the collections held by the rest of their team; then, they synchronise on the global barrier (a non-alting one, the same as used by AltingBarrierGadget4s); finally, they extract the information sent by the leaders of the other teams to which they belong. As before, the barrier synchronisation is crucial for the correctness of this exchange, ensuring that all messages are in place before they are gathered.

USER GAME: same as before, except with a span (rather than radius) of 3.

Author:
P.H. Welch
See Also:
Barrier,


Method Summary
 void contract()
          This contracts by one the number of processes enrolled in this alting barrier.
 void contract(AltingBarrier[] ab)
          This contracts the number of processes enrolled in this alting barrier.
static AltingBarrier create()
          This creates a new alting barrier with an (initial) enrollment count of 1.
static AltingBarrier[] create(int n)
          This creates a new alting barrier with an (initial) enrollment count of n.
 void enroll()
          A process may enroll only if it is resigned.
 AltingBarrier expand()
          This expands by one the number of processes enrolled in this alting barrier.
 AltingBarrier[] expand(int n)
          This expands the number of processes enrolled in this alting barrier.
 void mark()
          A process may hand its barrier front-end over to another process, but the receiving process must invoke this method before using it.
 boolean poll(long offerTime)
          This is a simple way to poll for synchonisation on an AltingBarrier without having to set up an Alternative.
 void reset()
          This resets a front-end for reuse.
 void resign()
          A process may resign only if it is enrolled.
 void sync()
          This is a simple way to perform a committed synchonisation on an AltingBarrier without having to set up an Alternative.
 
Methods inherited from class org.jcsp.lang.Guard
schedule
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Method Detail

create

public static AltingBarrier[] create(int n)
This creates a new alting barrier with an (initial) enrollment count of n. It provides an array of n front-ends to this barrier. It is the invoker's responsibility to install one of these (by constructor or set method) in each process that will be synchronising on the barrier, before firing up those processes.

Note: each process must use a different front-end to the barrier. Usually, a process retains an AltingBarrier front-end throughout its lifetime -- however, see mark.

Parameters:
n - the number of processes enrolled (initially) on this barrier.

Returns:
an array of n front-ends to this barrier.

Throws:
IllegalArgumentException - if n <= 0.

create

public static AltingBarrier create()
This creates a new alting barrier with an (initial) enrollment count of 1. It provides a single front-end to the barrier, from which others may be generated (see expand()) -- usually one-at-a-time to feed processes individually forked (by a ProcessManager). It is the invoker's responsibility to install each one (by constructor or set method) in the process that will be synchronising on the barrier, before firing up that process. Usually, a process retains an AltingBarrier front-end throughout its lifetime -- however, see mark.

Note: if a known number of processes needing the barrier are to be run (e.g. by a Parallel), creating the barrier with an array of front-ends using create(n) would be more convenient.

Returns:
a single front-end for this barrier.

expand

public AltingBarrier[] expand(int n)
This expands the number of processes enrolled in this alting barrier.

Use it when an enrolled process is about to go Parallel itself and some/all of those sub-processes also need to be enrolled. It returns an array new front-ends for this barrier. It is the invoker's responsibility to pass these on to those sub-processes.

Note that if there are x sub-processes to be enrolled, this method must be invoked with an argument of (x - 1). Pass the returned AltingBarriers to any (x - 1) of those sub-processes. Pass this AltingBarrier to the last one.

Before using its given front-end to this barrier, each sub-process must mark it to take ownership. [Actually, only the sub-process given the original front-end (which may be running in a different thread) really has to do this.]

Following termination of the Parallel, the original process must take back ownership of its original AltingBarrier (loaned to one of the sub-processes, which may have been running on a different thread) by marking it again.

Also following termination of the Parallel, the original process must contract the number of processes enrolled on the barrier. To do this, it must have retained the front-end array returned by this method and pass it to contract.

Parameters:
n - the number of processes to be added to this barrier.

Returns:
an array of new front-ends for this barrier.

Throws:
IllegalArgumentException - if n <= 0.

AltingBarrierError - if currently resigned or not owner of this front-end.

expand

public AltingBarrier expand()
This expands by one the number of processes enrolled in this alting barrier.

Use it when an enrolled process is about to fork a new process (using ProcessManager) that also needs to be enrolled. It returns an new front-end for this barrier. It is the invoker's responsibility to pass it to the new process.

Before terminating, the forked process should contract (by one) the number of processes enrolled in this barrier. Otherwise, no further synchronisations on this barrier would be able to complete.

Returns:
a new front-end for this barrier.

Throws:
AltingBarrierError - if currently resigned or not owner of this front-end.

contract

public void contract(AltingBarrier[] ab)
This contracts the number of processes enrolled in this alting barrier. The given front-ends are discarded.

Use it following termination of a Parallel, some/all of whose sub-processes were enrolled by being given front-ends returned by expand. See the documentation for expand.

Warning: only the process that went Parallel should invoke this method -- never one of the sub-processes.

Warning: never invoke this method whilst processes using its argument's front-ends are running.

Warning: do not attempt to reuse any of the argument elements afterwards -- they front-end nothing.

Parameters:
ab - the front-ends being discarded from this barrier. This array must be unaltered from one previously delivered by an expand.

Throws:
IllegalArgumentException - if ab is null or zero length.

AltingBarrierError - if the given array is not one previously delivered by an expand(n), or the invoking process is currently resigned or not the owner of this front-end.

contract

public void contract()
This contracts by one the number of processes enrolled in this alting barrier. This front-end cannot not be used subsequently.

This method should be used on individually created front-ends (see expand()) when, and only when, the process holding it is about to terminate. Normally, that process would have been forked by the process creating this barrier.

Warning: do not try to use this front-end following invocation of this method -- it no longer fronts anything.

Throws:
AltingBarrierError - if currently resigned or not the owner of this front-end.

resign

public void resign()
A process may resign only if it is enrolled. A resigned process may not offer to synchronise on this barrier (until a subsequent enroll). Other processes can complete the barrier (represented by this front-end) without participation by the resigned process.

Unless all processes synchronising on this barrier terminate in the same phase, it is usually appropriate for a terminating process to resign first. Otherwise, its sibling processes will never be able to complete another synchronisation.

Note: a process must not transfer its front-end to another process whilst resigned from the barrier -- see mark.

Throws:
AltingBarrierError - if currently resigned.

enroll

public void enroll()
A process may enroll only if it is resigned. A re-enrolled process may resume offering to synchronise on this barrier (until a subsequent resign). Other processes cannot complete the barrier (represented by this front-end) without participation by the re-enrolled process.

Note: timing re-enrollment on a barrier usually needs some care. If the barrier is being used for synchronising phases of execution between a set of processes, it is crucial that re-enrollment occurs in an appropriate (not arbitrary) phase. If the trigger for re-enrollment comes from another enrolled process, that process should be in such an appropriate phase. The resigned process should re-enroll and, then, acknowledge the trigger. The triggering process should wait for that acknowledgement. If the decision to re-enroll is internal (e.g. following a timeout), a buddy process, enrolled on the barrier, should be asked to provide that trigger when in an appropriate phase. The buddy process, perhaps specially built just for this purpose, polls a service channel for that question when in that phase.

Throws:
AltingBarrierError - if currently enrolled.

mark

public void mark()
A process may hand its barrier front-end over to another process, but the receiving process must invoke this method before using it. Beware that the process that handed it over must no longer use it.

Note: a process must not transfer its front-end to another process whilst resigned from the barrier -- see resign. The receiving process assumes this is the case. This mark will fail if it is not so.

See expand(n) for an example pattern of use.

Throws:
AltingBarrierError - if the front-end is resigned.

reset

public void reset()
This resets a front-end for reuse. It still fronts the same barrier. Following this method, this front-end is enrolled on the barrier and not owned by any process.

Warning: this should only be used to recycle a front-end whose process has terminated. It should not be used to transfer a front-end between running processes (for which mark should be used).

Example:

   AltingBarrier[] action = AltingBarrier.create (n);
 
   Parallel[] system = new Parallel[n];
   for (int i = 0; i < system.length; i++) {
     system[i] = new Something (action[i], ...);
   }
 
   while (true) {
     // invariant: all 'action' front-ends are enrolled on the barrier.
     // invariant: all 'action' front-ends are not yet owned by any process.
     system.run ();
     // assume: no 'system' process discards (contracts) its 'action' front-end.
     // note: some 'system' processes may have resigned their 'action' front-ends.
     // note: in the next run of 'system', its processes may be different
     //       from the point of view of the 'action' front-ends.
     for (int i = 0; i < action.length; i++) {
       action[i].reset ();
     }
     // deduce: loop invariant re-established.
   }
 


sync

public void sync()
This is a simple way to perform a committed synchonisation on an AltingBarrier without having to set up an Alternative. For example, if group is an AltingBarrier, then:
     group.sync ();
 
saves first having to construct the single guarded:
     Alternative groupCommit = new Alternative (new Guard[] {group});
 
and then:
     groupCommit.select ();
 
If this is the only method of synchronisation performed by all parties to this barrier, a non-alting Barrier would be more efficient.

Important note: following a select, priSelect or fairSelect on an Alternative that returns the index of an AltingBarrier, that barrier synchronisation has happened. Do not proceed to invoke this sync method -- unless, of course, you want to wait for a second synchronisation.


poll

public boolean poll(long offerTime)
This is a simple way to poll for synchonisation on an AltingBarrier without having to set up an Alternative. The parameter specifies how long this poll should leave its offer to synchronise on the table. If true is returned, the barrier has completed. If false, the barrier was unable to complete within the time specified (i.e. at no time were all parties making an offer).

For example, if group is an AltingBarrier, then:

     if (group.poll (offerTime)) {
       ...  group synchronisation achieved
     } else {
       ...  group synchronisation failed (within offerTime millisecs)
     }
 
is equivalent to:
     groupTimer.setAlarm (groupTimer.read () + offerTime);
     if (groupPoll.priSelect () == 0) {
       ...  group synchronisation achieved
     } else {
       ...  group synchronisation failed (within offerTime millisecs)
     }
 
where first would have to have been constructed:
     CSTimer groupTimer = new CSTimer ();
     Alternative groupPoll =
       new Alternative (new Guard[] {group, groupTimer});
 
Note: polling algorithms should generally be a last resort! If all parties to this barrier only use this method, synchronisation depends on all their poll periods coinciding. An offerTime of zero is allowed: if all other parties are offering, the barrier will complete -- otherwise, the poll returns immediately. However, if more than one party only ever polls like this, no synchronisation will ever take place.

Parameters:
offerTime - the time (in milliseconds) that this offer to synchronise should be left on the table.

Returns:
true if and only if the barrier completes within time specifed.

CSP for Java
(JCSP) 1.1-rc4

Submit a bug or feature to jcsp-team@kent.ac.uk
Version 1.1-rc4 of the JCSP API Specification (Copyright 1997-2008 P.D.Austin and P.H.Welch - All Rights Reserved)
Java is a trademark or registered trademark of Sun Microsystems, Inc. in the US and other countries.