| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
System.Semaphore
Description
This library provides a cross-platform implementation of semaphores.
Its main role is to provide a cross-platform notion of semaphore that can be used to implement the jobserver/jobclient model of GHC proposal #540.
Typical usage:
- The jobserver creates a
ServerSemaphore, e.g. usingcreateSemaphore/freshSemaphore. - The jobserver retrieves the serialisable
SemaphoreIdentifierof thisServerSemaphoreby usingserverClientSemaphore,clientSemaphoreNameandsemaphoreIdentifier. - The jobserver passes the
SemaphoreIdentifierto the jobclient via IPC. - The jobclient uses
openSemaphoreto obtain theClientSemaphorecorresponding to theSemaphoreIdentifierit has been given. - The jobclient can then request resources from the semaphore using
waitOnSemaphore(and return them usingreleaseSemaphoreToken), or by using thewithAbstractSembracket pattern. - Once the jobclient is done, it uses
destroyClientSemaphoreto ensure all resources are released (including any remaining tokens). - Once the jobserver is done, it cleans up the resources underlying the
semaphore using
destroyServerSemaphore.
NB: the above usage outline deliberately omits any mention of implicit
semaphore tokens: such a notion does not exist in this library, as they are
purely an abstraction layer used by the jobserver implementation.
See destroyClientSemaphore for more details.
Synopsis
- data ClientSemaphore
- data ServerSemaphore
- serverClientSemaphore :: ServerSemaphore -> ClientSemaphore
- data SemaphoreName = SemaphoreName {}
- clientSemaphoreName :: ClientSemaphore -> SemaphoreName
- type SemaphoreIdentifier = String
- semaphoreIdentifier :: SemaphoreName -> SemaphoreIdentifier
- createSemaphore :: String -> Int -> IO (Either SemaphoreError ServerSemaphore)
- freshSemaphore :: String -> Int -> IO (Either SemaphoreError ServerSemaphore)
- data SemaphoreToken
- openSemaphore :: SemaphoreIdentifier -> IO (Either SemaphoreError ClientSemaphore)
- parseSemaphoreIdentifier :: SemaphoreIdentifier -> Maybe SemaphoreName
- newtype SemaphoreProtocolVersion = SemaphoreProtocolVersion {}
- semaphoreVersion :: SemaphoreProtocolVersion
- versionsAreCompatible :: SemaphoreProtocolVersion -> SemaphoreProtocolVersion -> Bool
- data SemaphoreError
- = SemaphoreAlreadyExists { }
- | SemaphoreDoesNotExist { }
- | SemaphoreIncompatibleVersion { }
- | SemaphoreOtherError { }
- waitOnSemaphore :: HasCallStack => ClientSemaphore -> IO SemaphoreToken
- tryWaitOnSemaphore :: HasCallStack => ClientSemaphore -> IO (Maybe SemaphoreToken)
- withSemaphoreToken :: ClientSemaphore -> (SemaphoreToken -> IO a) -> IO a
- getSemaphoreValue :: ServerSemaphore -> IO Int
- releaseSemaphoreToken :: HasCallStack => SemaphoreToken -> IO ()
- destroyClientSemaphore :: ClientSemaphore -> IO ()
- destroyServerSemaphore :: ServerSemaphore -> IO ()
- data AbstractSem = AbstractSem {
- acquireSem :: IO ()
- releaseSem :: IO ()
- withAbstractSem :: AbstractSem -> IO b -> IO b
System semaphores
Since version 2 of semaphore-compat, the library distinguishes between two
kinds of semaphores:
- When a jobserver creates a semaphore via
createSemaphore, it obtains aServerSemaphore. - Jobclients, which are passed the
SemaphoreIdentifierof a pre-existing semaphore, obtain aClientSemaphoreviaopenSemaphore.
When the jobserver wants to also act as a jobclient, it can use
serverClientSemaphore to obtain the ClientSemaphore corresponding
to its ServerSemaphore.
This architecture allows the jobserver to keep full accounting of semaphore resources held by all clients.
data ClientSemaphore Source #
A client-side semaphore: the handle a jobclient uses to acquire and release tokens from a semaphore created by a jobserver.
Retrieve the underlying name with clientSemaphoreName.
data ServerSemaphore Source #
A server-side semaphore: a ClientSemaphore together with the book-keeping
needed to track resource usage by all clients.
Retrieve the corresponding client semaphore with serverClientSemaphore.
serverClientSemaphore :: ServerSemaphore -> ClientSemaphore Source #
Retrieve the client-side semaphore corresponding to a server-side semaphore.
data SemaphoreName Source #
A semaphore name: a protocol version and an unversioned name string.
Constructors
| SemaphoreName | |
Instances
| Show SemaphoreName Source # | |
Defined in System.Semaphore.Internal.Version Methods showsPrec :: Int -> SemaphoreName -> ShowS # show :: SemaphoreName -> String # showList :: [SemaphoreName] -> ShowS # | |
| Eq SemaphoreName Source # | |
Defined in System.Semaphore.Internal.Version Methods (==) :: SemaphoreName -> SemaphoreName -> Bool # (/=) :: SemaphoreName -> SemaphoreName -> Bool # | |
clientSemaphoreName :: ClientSemaphore -> SemaphoreName Source #
Retrieve the underlying name of a client-side semaphore.
type SemaphoreIdentifier = String Source #
The identifier string of a semaphore: a serialised SemaphoreName used
for inter-process communication (e.g. as a command line argument).
The specific format of this name string depends on the protocol version:
- For version 1, this is the unadorned semaphore name.
- For version
N >= 2, the semaphore name is prefixed withv<N>-.
semaphoreIdentifier :: SemaphoreName -> SemaphoreIdentifier Source #
The serialised identifier of a SemaphoreName for inter-process
communication.
See SemaphoreIdentifier for more information.
Creating a semaphore
Arguments
| :: String | label |
| -> Int | number of tokens on the semaphore |
| -> IO (Either SemaphoreError ServerSemaphore) |
Create a new semaphore with the given label and initial token count.
Arguments
| :: String | label prefix |
| -> Int | number of tokens on the semaphore |
| -> IO (Either SemaphoreError ServerSemaphore) |
Create a fresh semaphore with a unique name and the given token count.
The name is derived from the given prefix with a random suffix.
Opening a semaphore
data SemaphoreToken Source #
A held semaphore token: evidence of one unit of resource acquired from a semaphore.
Use releaseSemaphoreToken or withSemaphoreToken to ensure prompt release
of semaphore tokens.
Platform-dependent behaviour:
- On POSIX, if all references to a
SemaphoreTokenare dropped without being released, a finalizer returns the token to the semaphore. - On Windows, tokens held by a crashed client are permanently lost.
openSemaphore :: SemaphoreIdentifier -> IO (Either SemaphoreError ClientSemaphore) Source #
Open a pre-existing semaphore.
Returns Left SemaphoreIncompatibleVersion if the semaphore protocol
version is not compatible with the current version of semaphore-compat.
parseSemaphoreIdentifier :: SemaphoreIdentifier -> Maybe SemaphoreName Source #
Parse a SemaphoreIdentifier into a SemaphoreName.
Returns Nothing for unversioned strings (which should be treated as
v1 by the caller's compatibility logic).
newtype SemaphoreProtocolVersion Source #
The semaphore protocol version currently being used.
The version tracks the IPC mechanism, not the library version:
- POSIX: 2 (domain sockets, replacing v1 system semaphores).
- Windows: 1 (Win32 named semaphores, unchanged from v1).
- Unsupported platforms: 0 (no compatible IPC backend).
Constructors
| SemaphoreProtocolVersion | |
Fields | |
Instances
semaphoreVersion :: SemaphoreProtocolVersion Source #
The protocol version used on this host platform with this version of
semaphore-compat.
versionsAreCompatible :: SemaphoreProtocolVersion -> SemaphoreProtocolVersion -> Bool Source #
Check whether two semaphore protocol versions are compatible.
data SemaphoreError Source #
Errors that can occur when creating or opening a semaphore.
Constructors
| SemaphoreAlreadyExists | Can't create a semaphore: a semaphore with this name already exists. |
Fields | |
| SemaphoreDoesNotExist | Can't open a semaphore: no semaphore with this name exists. |
Fields | |
| SemaphoreIncompatibleVersion | Protocol version incompatibility: the semaphore identifier uses a different protocol version than the library is currently using. |
| SemaphoreOtherError | An |
Fields | |
Instances
| Exception SemaphoreError Source # | |
Defined in System.Semaphore.Internal.Version Methods toException :: SemaphoreError -> SomeException # | |
| Show SemaphoreError Source # | |
Defined in System.Semaphore.Internal.Version Methods showsPrec :: Int -> SemaphoreError -> ShowS # show :: SemaphoreError -> String # showList :: [SemaphoreError] -> ShowS # | |
| Eq SemaphoreError Source # | |
Defined in System.Semaphore.Internal.Version Methods (==) :: SemaphoreError -> SemaphoreError -> Bool # (/=) :: SemaphoreError -> SemaphoreError -> Bool # | |
Requesting a token
waitOnSemaphore :: HasCallStack => ClientSemaphore -> IO SemaphoreToken Source #
Acquire a token from the semaphore, blocking until one is available.
The returned SemaphoreToken must be released with releaseSemaphoreToken.
For prompt and predictable release of resources, callers should use
withSemaphoreToken or releaseSemaphoreToken.
This operation is interruptible: it can be cancelled by
throwTo, killThread, etc.
If interrupted, any transiently acquired token is automatically returned.
tryWaitOnSemaphore :: HasCallStack => ClientSemaphore -> IO (Maybe SemaphoreToken) Source #
Try to acquire a token from the semaphore without blocking.
Returns Just token if a token was available, Nothing otherwise.
This is not an interruptible operation, but that should not be a problem: this function is not expected to block for long, as the server is supposed to respond immediately.
withSemaphoreToken :: ClientSemaphore -> (SemaphoreToken -> IO a) -> IO a Source #
Acquire a token, run an action, then release the token. Exception safe.
getSemaphoreValue :: ServerSemaphore -> IO Int Source #
Query the current semaphore value (how many tokens it has available).
This is mainly for debugging use, as it is easy to introduce race conditions when nontrivial program logic depends on the value returned by this function.
Releasing resources
releaseSemaphoreToken :: HasCallStack => SemaphoreToken -> IO () Source #
Release a semaphore token, returning it to the pool.
Idempotent: a second call on the same token is a safe no-op.
destroyClientSemaphore :: ClientSemaphore -> IO () Source #
Destroy a client-side semaphore.
This is an idempotent operation: double calls to destroyClientSemaphore
do not cause any problems.
destroyServerSemaphore :: ServerSemaphore -> IO () Source #
Destroy a server-side semaphore.
Idempotent. Not interruptible.
The GHC jobserver protocol described in GHC proposal #540 specifies the notion of implicit semaphore tokens.
- To invoke a jobclient, the jobserver must be in possession of at least one semaphore token. We consider that the jobserver passes on ownership of this token to the jobclient, even though no interaction with the semaphore takes place. This is the implicit token.
- Each jobclient thus starts with one available token (the implicit token), and can request more tokens by waiting on the semaphore.
A jobclient calling destroyClientSemaphore signals to the jobserver that the
client is done doing work. The jobserver thus regains control of the implicit
token, which it can now use to spawn other jobclients.
Implicit tokens are purely an accounting abstraction that we superimpose on top of semaphore token ownership for the purposes of implementing the jobserver protocol. They are not part of this library.
Abstract semaphores
data AbstractSem Source #
Abstraction over the operations of a semaphore.
Constructors
| AbstractSem | |
Fields
| |
withAbstractSem :: AbstractSem -> IO b -> IO b Source #
Acquire/release bracket pattern: acquire a token, perform an action, and release the token.
Guarantees that the token is released in case that the inner action throws an exception.