extra-1.8.1: Extra functions I use.

Safe Haskell | Safe-Inferred
Language | Haskell2010

Control.Concurrent.Extra

Contents

- Lock
- Var
- Barrier

Description

Extra functions for Control.Concurrent.

This module includes three new types of `MVar`, namely `Lock` (no associated value), `Var` (never empty) and `Barrier` (filled at most once). See this blog post for examples and justification.

If you need greater control of exceptions and threads see the slave-thread package. If you need elaborate relationships between threads see the async package.

Synopsis

- module Control.Concurrent
- withNumCapabilities :: Int -> IO a -> IO a
- once :: IO a -> IO (IO a)
- onceFork :: IO a -> IO (IO a)
- data Lock
- newLock :: IO Lock
- withLock :: Lock -> IO a -> IO a
- withLockTry :: Lock -> IO a -> IO (Maybe a)
- data Var a
- newVar :: a -> IO (Var a)
- readVar :: Var a -> IO a
- writeVar :: Var a -> a -> IO ()
- writeVar' :: Var a -> a -> IO ()
- modifyVar :: Var a -> (a -> IO (a, b)) -> IO b
- modifyVar' :: Var a -> (a -> IO (a, b)) -> IO b
- modifyVar_ :: Var a -> (a -> IO a) -> IO ()
- modifyVar_' :: Var a -> (a -> IO a) -> IO ()
- withVar :: Var a -> (a -> IO b) -> IO b
- data Barrier a
- newBarrier :: IO (Barrier a)
- signalBarrier :: Partial => Barrier a -> a -> IO ()
- waitBarrier :: Barrier a -> IO a
- waitBarrierMaybe :: Barrier a -> IO (Maybe a)

# Documentation

module Control.Concurrent

withNumCapabilities :: Int -> IO a -> IO a Source #

On GHC 7.6 and above with the `-threaded` flag, brackets a call to `setNumCapabilities`. On lower versions (which lack `setNumCapabilities`) this function just runs the argument action.

once :: IO a -> IO (IO a) Source #

Given an action, produce a wrapped action that runs at most once. If the function raises an exception, the same exception will be reraised each time.

    let x ||| y = do t1 <- onceFork x; t2 <- onceFork y; t1; t2
    \(x :: IO Int) -> void (once x) == pure ()
    \(x :: IO Int) -> join (once x) == x
    \(x :: IO Int) -> (do y <- once x; y; y) == x
    \(x :: IO Int) -> (do y <- once x; y ||| y) == x

onceFork :: IO a -> IO (IO a) Source #

Like `once`, but immediately starts running the computation on a background thread.

    \(x :: IO Int) -> join (onceFork x) == x
    \(x :: IO Int) -> (do a <- onceFork x; a; a) == x

# Lock

data Lock Source #

Like an `MVar`, but has no value. Used to guarantee single-threaded access, typically to some system resource. As an example:

    lock <- newLock
    let output = withLock lock . putStrLn
    forkIO $ do ...; output "hello"
    forkIO $ do ...; output "world"

Here we are creating a lock to ensure that when writing output our messages do not get interleaved. This use of `MVar` never blocks on a put. It is permissible, but rare, that a withLock contains a withLock inside it - but if so, watch out for deadlocks.

newLock :: IO Lock Source #

Create a new `Lock`.

withLock :: Lock -> IO a -> IO a Source #

Perform some operation while holding `Lock`. Will prevent all other operations from using the `Lock` while the action is ongoing.

withLockTry :: Lock -> IO a -> IO (Maybe a) Source #

Like `withLock` but will never block. If the operation cannot be executed immediately it will return `Nothing`.

# Var

data Var a Source #

Like an `MVar`, but must always be full. Used to operate on a mutable variable in a thread-safe way. As an example:

    hits <- newVar 0
    forkIO $ do ...; modifyVar_ hits (+1); ...
    i <- readVar hits
    print ("HITS",i)

Here we have a variable which we modify atomically, so modifications are not interleaved. This use of `MVar` never blocks on a put. No modifyVar operation should ever block, and they should always complete in a reasonable timeframe. A `Var` should not be used to protect some external resource, only the variable contained within. Information from a `readVar` should not be subsequently inserted back into the `Var`.

newVar :: a -> IO (Var a) Source #

Create a new `Var` with a value.

readVar :: Var a -> IO a Source #

Read the current value of the `Var`.

writeVar :: Var a -> a -> IO () Source #

Write a value to become the new value of `Var`.

writeVar' :: Var a -> a -> IO () Source #

Strict variant of `writeVar`

modifyVar :: Var a -> (a -> IO (a, b)) -> IO b Source #

Modify a `Var` producing a new value and a return result.

modifyVar' :: Var a -> (a -> IO (a, b)) -> IO b Source #

Strict variant of `modifyVar'`

modifyVar_ :: Var a -> (a -> IO a) -> IO () Source #

Modify a `Var`, a restricted version of `modifyVar`.

modifyVar_' :: Var a -> (a -> IO a) -> IO () Source #

Strict variant of `modifyVar_`

withVar :: Var a -> (a -> IO b) -> IO b Source #

Perform some operation using the value in the `Var`, a restricted version of `modifyVar`.

# Barrier

data Barrier a Source #

Starts out empty, then is filled exactly once. As an example:

    bar <- newBarrier
    forkIO $ do ...; val <- ...; signalBarrier bar val
    print =<< waitBarrier bar

Here we create a barrier which will contain some computed value. A thread is forked to fill the barrier, while the main thread waits for it to complete. A barrier has similarities to a future or promise from other languages, has been known as an IVar in other Haskell work, and in some ways is like a manually managed thunk.

newBarrier :: IO (Barrier a) Source #

Create a new `Barrier`.

signalBarrier :: Partial => Barrier a -> a -> IO () Source #

Write a value into the Barrier, releasing anyone at `waitBarrier`. Any subsequent attempts to signal the `Barrier` will throw an exception.

waitBarrier :: Barrier a -> IO a Source #

Wait until a barrier has been signaled with `signalBarrier`.

waitBarrierMaybe :: Barrier a -> IO (Maybe a) Source #

A version of `waitBarrier` that never blocks, returning `Nothing` if the barrier has not yet been signaled.