********************************************************************** * JCSP ("CSP for Java") libraries * Copyright (C) 1996-2006 Peter Welch and Paul Austin. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. * * Author contact: P.H.Welch@ukc.ac.uk * * ***********************************************************************
Type | Name and description |
---|---|
void |
contract(AltingBarrier[] ab) This contracts the number of processes enrolled in this alting barrier. |
void |
contract() This contracts by one the number of processes enrolled in this alting barrier. |
static AltingBarrier[] |
create(int n) This creates a new alting barrier with an (initial) enrollment count of n .
|
static AltingBarrier |
create() This creates a new alting barrier with an (initial) enrollment count of 1 .
|
void |
enroll() A process may enroll only if it is resigned. |
AltingBarrier[] |
expand(int n) This expands the number of processes enrolled in this alting barrier. |
AltingBarrier |
expand() This expands by one 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. |
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.
ab
is null
or zero length.
ab
- the front-ends being discarded from this barrier.
This array must be unaltered from one previously delivered by
an expand.
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() 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.
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.
n
<= 0
.n
front-ends to this barrier.
n
- the number of processes enrolled (initially) on this barrier.
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() 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(int) create(n) would be more convenient.
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.
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 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 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.
n
<= 0
.
n
- the number of processes to be added to this barrier.
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.
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(int) expand(n) for an example pattern of use.
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.
true
if and only if the barrier completes within
time specifed.offerTime
- the time (in milliseconds) that this offer to synchronise
should be left on the table.
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. }
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.
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.