Control.Concurrent.RLock

concurrent-extra-0.7.0.9: Extra concurrency primitives

Copyright | (c) 2010-2011 Bas van Dijk & Roel van Dijk
License | BSD3 (see the file LICENSE)
Maintainer | Bas van Dijk <v.dijk.bas@gmail.com> , Roel van Dijk <vandijk.roel@gmail.com>
Safe Haskell | Safe
Language | Haskell98

Control.Concurrent.RLock

Contents

- Creating reentrant locks
- Locking and unlocking
- Convenience functions
- Querying reentrant locks

Description

This module provides the `RLock` synchronisation mechanism. It was inspired by the Python `RLock` and Java `ReentrantLock` objects and should behave in a similar way. See:

http://docs.python.org/3.1/library/threading.html#rlock-objects

and:

http://java.sun.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html

All functions are exception safe. Throwing asynchronous exceptions will not compromise the internal state of an `RLock`.

This module is intended to be imported qualified. We suggest importing it like:

    import           Control.Concurrent.RLock          ( RLock )
    import qualified Control.Concurrent.RLock as RLock ( ... )

Synopsis

- data RLock
- new :: IO RLock
- newAcquired :: IO RLock
- acquire :: RLock -> IO ()
- tryAcquire :: RLock -> IO Bool
- release :: RLock -> IO ()
- with :: RLock -> IO a -> IO a
- tryWith :: RLock -> IO a -> IO (Maybe a)
- wait :: RLock -> IO ()
- type State = Maybe (ThreadId, Integer)
- state :: RLock -> IO State

# Documentation

data RLock

A reentrant lock is in one of two states: "locked" or "unlocked". When the lock is in the "locked" state it has two additional properties:

- Its owner: the thread that acquired the lock.
- Its acquired count: how many times its owner acquired the lock.

Instances

Eq RLock |

# Creating reentrant locks

new :: IO RLock

Create a reentrant lock in the "unlocked" state.

newAcquired :: IO RLock

Create a reentrant lock in the "locked" state (with the current thread as owner and an acquired count of 1).

# Locking and unlocking

acquire :: RLock -> IO ()

Acquires the `RLock`. Blocks if another thread has acquired the `RLock`.

`acquire` behaves as follows:

- When the state is "unlocked", `acquire` changes the state to "locked" with the current thread as owner and an acquired count of 1.
- When the state is "locked" and the current thread owns the lock `acquire` only increments the acquired count.
- When the state is "locked" and the current thread does not own the lock `acquire` blocks until the owner releases the lock. If the thread that called `acquire` is woken upon release of the lock it will take ownership and change the state to "locked" with an acquired count of 1.

There are two further important properties of `acquire`:

- `acquire` is single-wakeup. That is, if there are multiple threads blocked on `acquire`, and the lock is released, only one thread will be woken up. The runtime guarantees that the woken thread completes its `acquire` operation.
- When multiple threads are blocked on `acquire` they are woken up in FIFO order. This is useful for providing fairness properties of abstractions built using locks. (Note that this differs from the Python implementation where the wake-up order is undefined.)

tryAcquire :: RLock -> IO Bool

A non-blocking `acquire`.

- When the state is "unlocked" `tryAcquire` changes the state to "locked" (with the current thread as owner and an acquired count of 1) and returns `True`.
- When the state is "locked" `tryAcquire` leaves the state unchanged and returns `False`.

release :: RLock -> IO ()

`release` decrements the acquired count. When a lock is released with an acquired count of 1 its state is changed to "unlocked".

Note that it is both an error to release a lock in the "unlocked" state and to release a lock that is not owned by the current thread.

If there are any threads blocked on `acquire` the thread that first called `acquire` will be woken up.

# Convenience functions

with :: RLock -> IO a -> IO a

A convenience function which first acquires the lock and then performs the computation. When the computation terminates, whether normally or by raising an exception, the lock is released.

Note that: `with = `liftA2` `bracket_` `acquire` `release``.

tryWith :: RLock -> IO a -> IO (Maybe a)

A non-blocking `with`. `tryWith` is a convenience function which first tries to acquire the lock. If that fails, `Nothing` is returned. If it succeeds, the computation is performed. When the computation terminates, whether normally or by raising an exception, the lock is released and `Just` the result of the computation is returned.

wait :: RLock -> IO ()

- When the state is "locked" `wait` blocks until a call to `release` in another thread changes it to "unlocked".
- When the state is "unlocked" `wait` returns immediately.

`wait` does not alter the state of the lock.

Note that `wait` is just a convenience function defined as:

    wait l = block $ acquire l >> release l

# Querying reentrant locks

type State = Maybe (ThreadId, Integer)

The state of an `RLock`.

- `Nothing` indicates an "unlocked" state.
- ``Just` (tid, n)` indicates a "locked" state where the thread identified by `tid` acquired the lock `n` times.

state :: RLock -> IO State

Determine the state of the reentrant lock.

Note that this is only a snapshot of the state. By the time a program reacts on its result it may already be out of date.