This package is a Haskell implementation of the "Phaser," a barrier-like synchronization object. Its construction and behavior is inspired by the object of the same name that is part of the Habanero Project.
The test suite assumes that you have haskell-stack
installed on your system.
To start it, run:
make test # 'stack test' works too
Currently, there are two sets of tests -- one (PhaserSpec
)
is a test of basic properties of the Phaser
s. The other,
(ConwaySpec
) is a stress-test that is designed to
coax misbehavior or hard-to-find race conditions from the Phaser
s by computing a certain
generation of a game of Conway's Game of Life in parallel.
The Phaser
is basically a generalized barrier.
It differs in a few respects:
-
A
Phaser
is re-usable, whereas a barrier is usually used only once. -
A
Phaser
has a notion of some phase (for this package, anyEnum
), which is advanced every time all parties/threads leave thePhaser
. -
In a barrier, all parties that arrive on the barrier block until all remaining parties arrive. In
Phaser
terms, these threads bothSignal
(their arrival is necessary for the barrier to advance) andWait
(block until the barrier does advance). However, unlike a barrier, with aPhaser
, a thread can use thePhaser
in aSignal
-ling capacity, aWait
ing capacity, or both (SignalWait
). This means you can have threads which contribute towards unblocking others, even if they are not blocked by the phaser... or threads that do not contribute towards unblocking the phaser, but will still be blocked by it.
(If you'd prefer to see an example, you might want to check out the Conway's Game of Life example that's used as a stress test in the test suite.)
This package includes both an MVar
-based implementation (IOPhaser
) and a STM
-based implementation (STMPhaser
). The IOPhaser
is used by default when you import Control.Concurrent.Phaser
, but Control.Concurrent.Phaser.STM
has the alternate STMPhaser
option.
A Phaser
is declared with a 'phase' and initial number of threads/parties that will use it:
ph <- newIOPhaser 0 4 -- Starting with phase 0, 4 parties.
The number of parties can be dynamically adjusted with the register
and unregister
functions.
Synchronizing work on a Phaser
is fairly straightforward -- in each thread, state which phasers
you want to synchronize with, what your relationship (Signal/Wait) is with them, and what you want to do:
-- Single phaser, Signal/Wait mode:
runPhased ph SignalWait $ do
... something useful, blocks when done on ph until all signalling parties arrive ...
-- Or even with multiple Phasers
runMultiPhased [(ph1, Signal), (ph2, Wait)] $ do
... something useful, blocks when done on ph2 until all signalling parties arrive ...
It's important to note that any barrier-like blocking occurs after the the action specified on the phaser has been run.
The Phaser
type is semantically different from, but not entirely unlike, Java's
java.util.concurrent.Phaser class.
Some features which are currently unimplemented, but may eventually be added:
-
Asynchronous exception safety
-
Tree-structuring of Phasers to eliminate livelock for large numbers of processes