Safe Haskell | None |
---|---|
Language | Haskell2010 |
Deterministic traced execution of concurrent computations which
may do IO
.
Warning: Blocking on the action of another thread in liftIO
cannot be detected! So if you perform some potentially blocking
action in a liftIO
the entire collection of threads may deadlock!
You should therefore keep IO
blocks small, and only perform
blocking operations with the supplied primitives, insofar as
possible.
- data ConcIO t a
- data Failure
- runConcIO :: Scheduler s -> s -> (forall t. ConcIO t a) -> IO (Either Failure a, s, Trace)
- runConcIO' :: Scheduler s -> s -> (forall t. ConcIO t a) -> IO (Either Failure a, s, Trace')
- liftIO :: IO a -> ConcIO t a
- fork :: ConcIO t () -> ConcIO t ThreadId
- forkFinally :: ConcIO t a -> (Either SomeException a -> ConcIO t ()) -> ConcIO t ThreadId
- forkWithUnmask :: ((forall a. ConcIO t a -> ConcIO t a) -> ConcIO t ()) -> ConcIO t ThreadId
- forkOn :: Int -> ConcIO t () -> ConcIO t ThreadId
- getNumCapabilities :: ConcIO t Int
- myThreadId :: ConcIO t ThreadId
- spawn :: ConcIO t a -> ConcIO t (CVar t a)
- atomically :: STMLike t IO IORef a -> ConcIO t a
- throw :: Exception e => e -> ConcIO t a
- throwTo :: Exception e => ThreadId -> e -> ConcIO t ()
- killThread :: ThreadId -> ConcIO t ()
- catch :: Exception e => ConcIO t a -> (e -> ConcIO t a) -> ConcIO t a
- mask :: ((forall a. ConcIO t a -> ConcIO t a) -> ConcIO t b) -> ConcIO t b
- uninterruptibleMask :: ((forall a. ConcIO t a -> ConcIO t a) -> ConcIO t b) -> ConcIO t b
- data CVar t a
- newEmptyCVar :: ConcIO t (CVar t a)
- putCVar :: CVar t a -> a -> ConcIO t ()
- tryPutCVar :: CVar t a -> a -> ConcIO t Bool
- readCVar :: CVar t a -> ConcIO t a
- takeCVar :: CVar t a -> ConcIO t a
- tryTakeCVar :: CVar t a -> ConcIO t (Maybe a)
- data CRef t a
- newCRef :: a -> ConcIO t (CRef t a)
- readCRef :: CRef t a -> ConcIO t a
- writeCRef :: CRef t a -> a -> ConcIO t ()
- modifyCRef :: CRef t a -> (a -> (a, b)) -> ConcIO t b
- _concNoTest :: ConcIO t a -> ConcIO t a
- _concKnowsAbout :: Either (CVar t a) (CTVar t IORef a) -> ConcIO t ()
- _concForgets :: Either (CVar t a) (CTVar t IORef a) -> ConcIO t ()
- _concAllKnown :: ConcIO t ()
- type Trace = [(Decision, [(Decision, Lookahead)], ThreadAction)]
- type Trace' = [(Decision, [(Decision, NonEmpty Lookahead)], ThreadAction)]
- data Decision
- data ThreadAction
- = Fork ThreadId
- | MyThreadId
- | New CVarId
- | Put CVarId [ThreadId]
- | BlockedPut CVarId
- | TryPut CVarId Bool [ThreadId]
- | Read CVarId
- | BlockedRead CVarId
- | Take CVarId [ThreadId]
- | BlockedTake CVarId
- | TryTake CVarId Bool [ThreadId]
- | NewRef CRefId
- | ReadRef CRefId
- | ModRef CRefId
- | STM [ThreadId]
- | FreshSTM
- | BlockedSTM
- | Catching
- | PopCatching
- | Throw
- | ThrowTo ThreadId
- | BlockedThrowTo ThreadId
- | Killed
- | SetMasking Bool MaskingState
- | ResetMasking Bool MaskingState
- | Lift
- | NoTest
- | KnowsAbout
- | Forgets
- | AllKnown
- | Stop
- data Lookahead
- = WillFork
- | WillMyThreadId
- | WillNew
- | WillPut CVarId
- | WillTryPut CVarId
- | WillRead CVarId
- | WillTake CVarId
- | WillTryTake CVarId
- | WillNewRef
- | WillReadRef CRefId
- | WillModRef CRefId
- | WillSTM
- | WillCatching
- | WillPopCatching
- | WillThrow
- | WillThrowTo ThreadId
- | WillSetMasking Bool MaskingState
- | WillResetMasking Bool MaskingState
- | WillLift
- | WillNoTest
- | WillKnowsAbout
- | WillForgets
- | WillAllKnown
- | WillStop
- type CVarId = Int
- data MaskingState :: *
- showTrace :: Trace -> String
- toTrace :: Trace' -> Trace
- module Test.DejaFu.Deterministic.Schedule
The ConcIO
Monad
An indication of how a concurrent computation failed.
InternalError | Will be raised if the scheduler does something bad. This should never arise unless you write your own, faulty, scheduler! If it does, please file a bug report. |
Deadlock | The computation became blocked indefinitely on |
STMDeadlock | The computation became blocked indefinitely on |
UncaughtException | An uncaught exception bubbled to the top of the computation. |
FailureInNoTest | A computation annotated with |
runConcIO :: Scheduler s -> s -> (forall t. ConcIO t a) -> IO (Either Failure a, s, Trace) Source
Run a concurrent computation with a given Scheduler
and initial
state, returning an failure reason on error. Also returned is the
final state of the scheduler, and an execution trace.
Concurrency
forkFinally :: ConcIO t a -> (Either SomeException a -> ConcIO t ()) -> ConcIO t ThreadId Source
Fork a thread and call the supplied function when the thread is about to terminate, with an exception or a returned value. The function is called with asynchronous exceptions masked.
This function is useful for informing the parent when a child terminates, for example.
forkWithUnmask :: ((forall a. ConcIO t a -> ConcIO t a) -> ConcIO t ()) -> ConcIO t ThreadId Source
Like fork
, but the child thread is passed a function that can
be used to unmask asynchronous exceptions. This function should not
be used within a mask
or uninterruptibleMask
.
forkOn :: Int -> ConcIO t () -> ConcIO t ThreadId Source
Fork a computation to happen on a specific processor. This implementation only has a single processor.
getNumCapabilities :: ConcIO t Int Source
Get the number of Haskell threads that can run simultaneously. This implementation lies and always returns 2. There is no way to verify in the computation that this is a lie, and will potentially avoid special-case behaviour for 1 capability, so it seems a sane choice.
myThreadId :: ConcIO t ThreadId Source
Get the ThreadId
of the current thread.
spawn :: ConcIO t a -> ConcIO t (CVar t a) Source
Run the provided computation concurrently, returning the result.
atomically :: STMLike t IO IORef a -> ConcIO t a Source
Run the provided MonadSTM
transaction atomically. If retry
is
called, it will be blocked until any of the touched CTVar
s have
been written to.
throw :: Exception e => e -> ConcIO t a Source
Raise an exception in the ConcIO
monad. The exception is raised
when the action is run, not when it is applied. It short-citcuits
the rest of the computation:
throw e >> x == throw e
throwTo :: Exception e => ThreadId -> e -> ConcIO t () Source
Throw an exception to the target thread. This blocks until the
exception is delivered, and it is just as if the target thread had
raised it with throw
. This can interrupt a blocked action.
killThread :: ThreadId -> ConcIO t () Source
Raise the ThreadKilled
exception in the target thread. Note
that if the thread is prepared to catch this exception, it won't
actually kill it.
mask :: ((forall a. ConcIO t a -> ConcIO t a) -> ConcIO t b) -> ConcIO t b Source
Executes a computation with asynchronous exceptions
masked. That is, any thread which attempts to raise an exception
in the current thread with throwTo
will be blocked until
asynchronous exceptions are unmasked again.
The argument passed to mask is a function that takes as its
argument another function, which can be used to restore the
prevailing masking state within the context of the masked
computation. This function should not be used within an
uninterruptibleMask
.
uninterruptibleMask :: ((forall a. ConcIO t a -> ConcIO t a) -> ConcIO t b) -> ConcIO t b Source
Like mask
, but the masked computation is not
interruptible. THIS SHOULD BE USED WITH GREAT CARE, because if a
thread executing in uninterruptibleMask
blocks for any reason,
then the thread (and possibly the program, if this is the main
thread) will be unresponsive and unkillable. This function should
only be necessary if you need to mask exceptions around an
interruptible operation, and you can guarantee that the
interruptible operation will only block for a short period of
time. The supplied unmasking function should not be used within a
mask
.
CVar
s
The concurrent variable type used with the ConcIO
monad. These
behave the same as Conc
's CVar
s
newEmptyCVar :: ConcIO t (CVar t a) Source
Create a new empty CVar
.
tryPutCVar :: CVar t a -> a -> ConcIO t Bool Source
Put a value into a CVar
if there isn't one, without blocking.
readCVar :: CVar t a -> ConcIO t a Source
Block on a CVar
until it is full, then read from it (without
emptying).
takeCVar :: CVar t a -> ConcIO t a Source
Block on a CVar
until it is full, then read from it (with
emptying).
tryTakeCVar :: CVar t a -> ConcIO t (Maybe a) Source
Read a value from a CVar
if there is one, without blocking.
CRef
s
The mutable non-blocking reference type. These behave the same as
Conc
's CRef
s
modifyCRef :: CRef t a -> (a -> (a, b)) -> ConcIO t b Source
Atomically modify the value inside a CRef
.
Testing
_concNoTest :: ConcIO t a -> ConcIO t a Source
Run the argument in one step. If the argument fails, the whole computation will fail.
_concKnowsAbout :: Either (CVar t a) (CTVar t IORef a) -> ConcIO t () Source
Record that the referenced variable is known by the current thread.
_concForgets :: Either (CVar t a) (CTVar t IORef a) -> ConcIO t () Source
Record that the referenced variable will never be touched by the current thread.
_concAllKnown :: ConcIO t () Source
Record that all CVar
s and CTVar
s known by the current thread
have been passed to _concKnowsAbout
.
Execution traces
type Trace = [(Decision, [(Decision, Lookahead)], ThreadAction)] Source
One of the outputs of the runner is a Trace
, which is a log of
decisions made, alternative decisions (including what action would
have been performed had that decision been taken), and the action a
thread took in its step.
type Trace' = [(Decision, [(Decision, NonEmpty Lookahead)], ThreadAction)] Source
Like a Trace
, but gives more lookahead (where possible) for
alternative decisions.
Scheduling decisions are based on the state of the running program, and so we can capture some of that state in recording what specific decision we made.
data ThreadAction Source
All the actions that a thread can perform.
Fork ThreadId | Start a new thread. |
MyThreadId | Get the |
New CVarId | Create a new |
Put CVarId [ThreadId] | Put into a |
BlockedPut CVarId | Get blocked on a put. |
TryPut CVarId Bool [ThreadId] | Try to put into a |
Read CVarId | Read from a |
BlockedRead CVarId | Get blocked on a read. |
Take CVarId [ThreadId] | Take from a |
BlockedTake CVarId | Get blocked on a take. |
TryTake CVarId Bool [ThreadId] | Try to take from a |
NewRef CRefId | Create a new |
ReadRef CRefId | Read from a |
ModRef CRefId | Modify a |
STM [ThreadId] | An STM transaction was executed, possibly waking up some threads. |
FreshSTM | An STM transaction was executed, and all it did was create and
write to new |
BlockedSTM | Got blocked in an STM transaction. |
Catching | Register a new exception handler |
PopCatching | Pop the innermost exception handler from the stack. |
Throw | Throw an exception. |
ThrowTo ThreadId | Throw an exception to a thread. |
BlockedThrowTo ThreadId | Get blocked on a |
Killed | Killed by an uncaught exception. |
SetMasking Bool MaskingState | Set the masking state. If |
ResetMasking Bool MaskingState | Return to an earlier masking state. If |
Lift | Lift an action from the underlying monad. Note that the
penultimate action in a trace will always be a |
NoTest | A computation annotated with |
KnowsAbout | A |
Forgets | A |
AllKnown | A |
Stop | Cease execution and terminate. |
A one-step look-ahead at what a thread will do next.
WillFork | Will start a new thread. |
WillMyThreadId | Will get the |
WillNew | Will create a new |
WillPut CVarId | Will put into a |
WillTryPut CVarId | Will try to put into a |
WillRead CVarId | Will read from a |
WillTake CVarId | Will take from a |
WillTryTake CVarId | Will try to take from a |
WillNewRef | Will create a new |
WillReadRef CRefId | Will read from a |
WillModRef CRefId | Will modify a |
WillSTM | Will execute an STM transaction, possibly waking up some threads. |
WillCatching | Will register a new exception handler |
WillPopCatching | Will pop the innermost exception handler from the stack. |
WillThrow | Will throw an exception. |
WillThrowTo ThreadId | Will throw an exception to a thread. |
WillSetMasking Bool MaskingState | Will set the masking state. If |
WillResetMasking Bool MaskingState | Will return to an earlier masking state. If |
WillLift | Will lift an action from the underlying monad. Note that the
penultimate action in a trace will always be a |
WillNoTest | Will execute a computation annotated with |
WillKnowsAbout | Will process a |
WillForgets | Will process a |
WillAllKnown | Will process a |
WillStop | Will cease execution and terminate. |
data MaskingState :: *
Describes the behaviour of a thread when an asynchronous exception is received.
Unmasked | asynchronous exceptions are unmasked (the normal state) |
MaskedInterruptible | the state during |
MaskedUninterruptible | the state during |