SafeSemaphore-0.10.1: Much safer replacement for QSemN, QSem, and SampleVar

Copyright | (c) Chris Kuklewicz 2011
License | 3 clause BSD-style (see the file LICENSE)
Maintainer | haskell@list.mightyreason.com
Stability | experimental
Portability | non-portable (concurrency)
Safe Haskell | Safe
Language | Haskell98

Control.Concurrent.MSemN2

Description

Quantity semaphores in which each thread may wait for an arbitrary amount. This modules is intended to improve on Control.Concurrent.QSemN.

This semaphore gracefully handles threads which die while blocked waiting for quantity. The fairness guarantee is that blocked threads are FIFO. An early thread waiting for a large quantity will prevent a later thread waiting for a small quantity from jumping the queue.

If `with` is used to guard a critical section then no quantity of the semaphore will be lost if the activity throws an exception.

The functions below are generic in (Integral i) with specialization to Int and Integer.

Overflow warning: These operations do not check for overflow errors. If the Integral type is too small to accept the new total then the behavior of these operations is undefined. Using (MSem Integer) prevents the possibility of an overflow error.

Synopsis

- data MSemN i
- new :: Integral i => i -> IO (MSemN i)
- with :: Integral i => MSemN i -> i -> IO a -> IO a
- wait :: Integral i => MSemN i -> i -> IO ()
- signal :: Integral i => MSemN i -> i -> IO ()
- withF :: Integral i => MSemN i -> (i -> (i, b)) -> ((i, b) -> IO a) -> IO a
- waitF :: Integral i => MSemN i -> (i -> (i, b)) -> IO (i, b)
- signalF :: Integral i => MSemN i -> (i -> (i, b)) -> IO (i, b)
- peekAvail :: Integral i => MSemN i -> IO i

# Documentation

data MSemN i

A `MSemN` is a quantity semaphore, in which the available quantity may be signalled or waited for in arbitrary amounts.

Instances

Eq (MSemN i) |

new :: Integral i => i -> IO (MSemN i)

`new` allows positive, zero, and negative initial values. The initial value is forced here to better localize errors.

with :: Integral i => MSemN i -> i -> IO a -> IO a

`with` takes a quantity of the semaphore to take and hold while performing the provided operation. `with` ensures the quantity of the sempahore cannot be lost if there are exceptions. This uses `bracket` to ensure `wait` and `signal` get called correctly.

wait :: Integral i => MSemN i -> i -> IO ()

`wait` allow positive, zero, and negative wanted values. Waiters may block, and will be handled fairly in FIFO order.

If `wait` returns without interruption then it left the `MSemN` with a remaining quantity that was greater than or equal to zero. If `wait` is interrupted then no quantity is lost. If `wait` returns without interruption then it is known that each earlier waiter has definitely either been interrupted or has retured without interruption.

signal :: Integral i => MSemN i -> i -> IO ()

`signal` allows positive, zero, and negative values, thus this is also way to remove quantity that skips any threads in the 'wait'/'waitF' queue. If the new total is greater than the next value being waited for (if present) then the first waiter is woken. If there are queued waiters then the next one will wake after a waiter has proceeded and notice the remaining value; thus a single `signal` may result in several waiters obtaining values. Waking waiting threads is asynchronous.

`signal` may block, but it cannot be interrupted, which allows it to dependably restore value to the `MSemN`. All `signal`, `signalF`, `peekAvail`, and the head waiter may momentarily block in a fair FIFO manner.

withF :: Integral i => MSemN i -> (i -> (i, b)) -> ((i, b) -> IO a) -> IO a

`withF` takes a pure function and an operation. The pure function converts the available quantity to a pair of the wanted quantity and a returned value. The operation takes the result of the pure function. `withF` ensures the quantity of the sempahore cannot be lost if there are exceptions. This uses `bracket` to ensure `waitF` and `signal` get called correctly.

Note: A long running pure function will block all other access to the `MSemN` while it is evaluated.

waitF :: Integral i => MSemN i -> (i -> (i, b)) -> IO (i, b)

`waitWith` takes the `MSemN` and a pure function that takes the available quantity and computes the amount wanted and a second value. The value wanted is stricly evaluated but the second value is returned lazily.

`waitF` allow positive, zero, and negative wanted values. Waiters may block, and will be handled fairly in FIFO order.

If `waitF` returns without interruption then it left the `MSemN` with a remaining quantity that was greater than or equal to zero. If `waitF` or the provided function are interrupted then no quantity is lost. If `waitF` returns without interruption then it is known that each previous waiter has each definitely either been interrupted or has retured without interruption.

Note: A long running pure function will block all other access to the `MSemN` while it is evaluated.

signalF :: Integral i => MSemN i -> (i -> (i, b)) -> IO (i, b)

Instead of providing a fixed change to the available quantity, `signalF` applies a provided pure function to the available quantity to compute the change and a second value. The requested change is stricly evaluated but the second value is returned lazily. If the new total is greater than the next value being waited for then the first waiter is woken. If there are queued waiters then the next one will wake after a waiter has proceeded and notice the remaining value; thus a single `signalF` may result in several waiters obtaining values. Waking waiting threads is asynchronous.

`signalF` may block, and it can be safely interrupted. If the provided function throws an error or is interrupted then it leaves the `MSemN` unchanged. All `signal`, `signalF`, `peekAvail`, and the head waiter may momentarily block in a fair FIFO manner.

Note: A long running pure function will block all other access to the `MSemN` while it is evaluated.

peekAvail :: Integral i => MSemN i -> IO i

`peekAvail` skips the queue of any blocked `wait` and `waitF` threads, but may momentarily block on `signal`, `signalF`, other `peekAvail`, and the head waiter. This returns the amount of value available to be taken. Using this value without producing unwanted race conditions is left up to the programmer.

`peekAvail` is an optimized form of "signalF m (x -> (0,x))".

Quantity that has been passed to a blocked waiter but not picked up is not counted. If the blocked waiter is killed before picking it up then the passed quantity will be recovered by the next waiter. In this exceptional case this next waiter may see an available total that is different than returned by peekAvail.

A version of `peekAvail` that joins the FIFO queue of `wait` and `waitF` can be acheived by "waitF m (x -> (0,x))" but this will block if x is negative. On the other hand this method will see the total including any recovered quantity.