chp-1.0.1: An implementation of concurrency ideas from Communicating Sequential Processes

Control.Concurrent.CHP.Barriers

Description

A module containing barriers.

A barrier is a synchronisation primitive. When N processes are enrolled
on a barrier, all N must synchronise on the barrier before any synchronisations
may complete, at which point they all complete. That is, when a single
process synchronises on a barrier, it must then wait until all the other
enrolled processes also synchronise before it can finish.

Only processes enrolled on a barrier may synchronise on it. Enrolled barriers
should not be passed around between processes, or used twice in a parallel
composition. Instead, each process should enroll on the barrier itself.

Barriers support choice (alting). This can lead to a lot of non-determinism
and some confusion. Consider these two processes, both enrolled on barriers a and b:

(sync a <-> sync b)
(sync b <-> sync a)

Which barrier completes is determined by the run-time, and will be an arbitrary
choice. This is even the case when priority is involved:

(sync a </> sync b)
(sync b </> sync a)

Clearly there is no way to resolve this to satisfy both priorities; the
run-time will end up choosing.

Barrier poison can be detected when syncing, enrolling or resigning. You
may only poison a barrier that you are currently enrolled on.

Barriers can also support phases. The idea behind a phased barrier is that
a barrier is always on a certain phase P. Whenever a barrier successfully
completes, the phase is incremented (but it does not have to be an integer).
Everyone is told the new phase once they complete a synchronisation, and
may query the current phase for any barrier that they are currently enrolled
on.

A phased barrier that is capable of being poisoned and throwing poison.
You will need to enroll on it to do anything useful with it.
For the phases you can use any type that satisfies Enum, Bounded and Eq.
The phase increments every time the barrier completes. Incrementing consists
of: if p == maxBound then minBound else succ p. Examples of things that
make sense for phases:

The () type (see the Barrier type). This effectively has a single repeating
phase, and acts like a non-phased barrier.

A bounded integer type. This increments the count every time the barrier completes.
But don't forget that the count will wrap round when it reaches the end.
You cannot use Integer for a phase because it is unbounded. If you really
want to have an infinitely increasing count, you can wrap Integer in a newtype and
provide a Bounded instance for it (with minBound and maxBound set to -1,
if you start on 0).

A boolean. This implements a simple black-white barrier, where the state
flips on each iteration.

Creates a new barrier with no processes enrolled, that will be on the
given phase. You will often want to pass in the last value in your phase
cycle, so that the first synchronisation moves it on to the first