|
CSP for Java (JCSP) 1.1-rc4 |
||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectorg.jcsp.lang.Guard
org.jcsp.lang.AltingBarrier
public class AltingBarrier
This is the front-end for a barrier that can be used as
a Guard
in an Alternative
.
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
.
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.
AltingBarrierError
error when riled. See the documentation for AltingBarrierError
for
circumstances.
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.
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 } }
alting-barriers
directory in jcsp-demos
contains other gadgets in a similar vein.
AltingBarrierGadget2
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
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
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
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
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 AltingBarrierGadget4
s); 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.
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 |
---|
public static AltingBarrier[] create(int n)
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
.
n
- the number of processes enrolled (initially) on this barrier.
n
front-ends to this barrier.
IllegalArgumentException
- if n
<= 0
.public static AltingBarrier create()
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.
public AltingBarrier[] expand(int n)
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 AltingBarrier
s 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 mark
ing 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
.
n
- the number of processes to be added to this barrier.
IllegalArgumentException
- if n
<= 0
.
AltingBarrierError
- if currently resigned or not owner of this
front-end.public AltingBarrier expand()
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.
AltingBarrierError
- if currently resigned or not owner of this
front-end.public void contract(AltingBarrier[] ab)
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.
ab
- the front-ends being discarded from this barrier.
This array must be unaltered from one previously delivered by
an expand
.
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.public void contract()
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.
AltingBarrierError
- if currently resigned or not the owner of
this front-end.public void resign()
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
.
AltingBarrierError
- if currently resigned.public void enroll()
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.
AltingBarrierError
- if currently enrolled.public void mark()
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.
AltingBarrierError
- if the front-end is resigned.public void reset()
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. }
public void sync()
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.
public boolean poll(long offerTime)
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.
offerTime
- the time (in milliseconds) that this offer to synchronise
should be left on the table.
true
if and only if the barrier completes within
time specifed.
|
CSP for Java (JCSP) 1.1-rc4 |
||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |