base-io-access-0.4.0.0: Access/Control/Concurrent.hs
{-# LANGUAGE RankNTypes #-}
--------------------------------------------------------------------------------
-- |
-- Module : Access.Control.Concurrent
-- Copyright : (c) Aaron Stevens, 2014
-- License : GPL2
--
-- Maintainer : bheklilr2@gmail.com
--------------------------------------------------------------------------------
module Access.Control.Concurrent
( module Control.Concurrent
, module Access.Control.Concurrent.MVar
, module Access.Control.Concurrent.Chan
, module Access.Control.Concurrent.QSem
, module Access.Control.Concurrent.QSemN
, ThreadAccess(..)
, BoundThreadAccess(..)
, WeakThreadAccess(..)
) where
import Control.Concurrent
import GHC.Conc (STM)
import System.Posix.Types (Fd)
import Access.Control.Concurrent.Chan
import Access.Control.Concurrent.MVar
import Access.Control.Concurrent.QSem
import Access.Control.Concurrent.QSemN
import Access.Control.Exception
import Access.System.Mem.Weak
-- | Inherits from 'ExceptionAccess', and gives access to Thread related functions
class ExceptionAccess io => ThreadAccess io where
-- | Wraps 'Control.Concurrent.myThreadId'
--
-- Returns the 'ThreadId' of the calling thread (GHC only)
myThreadId' :: io ThreadId
-- | Wraps 'Control.Concurrent.forkIO'
--
-- Sparks off a new thread to run the 'IO' computation passed as the
-- first argument, and returns the 'ThreadId' of the newly created
-- thread.
--
-- The new thread will be a lightweight thread; if you want to use a foreign
-- library that uses thread-local storage, use 'forkOS'' instead.
--
-- GHC note: the new thread inherits the /masked/ state of the parent
-- (see 'Access.Control.Exception.mask'').
--
-- The newly created thread has an exception handler that discards the
-- exceptions 'BlockedIndefinitelyOnMVar', 'BlockedIndefinitelyOnSTM', and
-- 'ThreadKilled', and passes all other exceptions to the uncaught
-- exception handler.
forkIO' :: io () -> io ThreadId
-- | Wraps 'Control.Concurrent.forkFinally'
--
-- 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.
--
-- > forkFinally' action and_then =
-- > mask $ \restore ->
-- > forkIO' $ try (restore action) >>= and_then
--
-- This function is useful for informing the parent when a child
-- terminates, for example.
forkFinally' :: io a -> (Either SomeException a -> io ()) -> io ThreadId
-- | Wraps 'Control.Concurrent.forkIOWithUnmask'
--
-- Like 'forkIO'', but the child thread is passed a function that can
-- be used to unmask asynchronous exceptions. This function is
-- typically used in the following way
--
-- > ... mask_ $ forkIOWithUnmask' $ \unmask ->
-- > catch' (unmask ...) handler
--
-- so that the exception handler in the child thread is established
-- with asynchronous exceptions masked, meanwhile the main body of
-- the child thread is executed in the unmasked state.
--
-- Note that the unmask function passed to the child thread should
-- only be used in that thread; the behaviour is undefined if it is
-- invoked in a different thread.
forkIOWithUnmask' :: ((forall a. io a -> io a) -> io ()) -> io ThreadId
-- | Wraps 'Control.Concurrent.killThread'
--
-- 'killThread'' raises the 'ThreadKilled' exception in the given
-- thread (GHC only).
--
-- > killThread' tid = throwTo' tid ThreadKilled
killThread' :: ThreadId -> io ()
-- | Wraps 'Control.Concurrent.throwTo'
--
-- 'throwTo'' raises an arbitrary exception in the target thread (GHC only).
--
-- 'throwTo'' does not return until the exception has been raised in the
-- target thread.
-- The calling thread can thus be certain that the target
-- thread has received the exception. This is a useful property to know
-- when dealing with race conditions: eg. if there are two threads that
-- can kill each other, it is guaranteed that only one of the threads
-- will get to kill the other.
--
-- Whatever work the target thread was doing when the exception was
-- raised is not lost: the computation is suspended until required by
-- another thread.
--
-- If the target thread is currently making a foreign call, then the
-- exception will not be raised (and hence 'throwTo'' will not return)
-- until the call has completed. This is the case regardless of whether
-- the call is inside a 'mask'' or not. However, in GHC a foreign call
-- can be annotated as @interruptible@, in which case a 'throwTo'' will
-- cause the RTS to attempt to cause the call to return; see the GHC
-- documentation for more details.
--
-- Important note: the behaviour of 'throwTo'' differs from that described in
-- the paper \"Asynchronous exceptions in Haskell\"
-- (<http://research.microsoft.com/~simonpj/Papers/asynch-exns.htm>).
-- In the paper, 'throwTo'' is non-blocking; but the library implementation
-- adopts a more synchronous design in which 'throwTo'' does not return
-- until the exception is received by the target thread. The trade-off is
-- discussed in Section 9 of the paper. Like any blocking operation,
-- 'throwTo'' is therefore interruptible (see Section 5.3 of the paper).
-- Unlike other interruptible operations, however, 'throwTo'' is /always/
-- interruptible, even if it does not actually block.
--
-- There is no guarantee that the exception will be delivered promptly,
-- although the runtime will endeavour to ensure that arbitrary
-- delays don't occur. In GHC, an exception can only be raised when a
-- thread reaches a /safe point/, where a safe point is where memory
-- allocation occurs. Some loops do not perform any memory allocation
-- inside the loop and therefore cannot be interrupted by a 'throwTo''.
--
-- If the target of 'throwTo'' is the calling thread, then the behaviour
-- is the same as 'Access.Control.Exception.throwIO'', except that the
-- exception is thrown as an asynchronous exception. This means that if
-- there is an enclosing pure computation, which would be the case if the
-- current IO operation is inside 'unsafePerformIO' or 'unsafeInterleaveIO',
-- that computation is not permanently replaced by the exception, but is
-- suspended as if it had received an asynchronous exception.
--
-- Note that if 'throwTo'' is called with the current thread as the
-- target, the exception will be thrown even if the thread is currently
-- inside 'mask'' or 'uninterruptibleMask''.
throwTo' :: Exception e => ThreadId -> e -> io ()
throwTo' = Access.Control.Exception.throwTo'
-- | Wraps 'Control.Concurrent.forkOn'
--
-- Like ''forkIO'', but lets you specify on which processor the thread
-- should run. Unlike a `'forkIO'` thread, a thread created by `forkOn'`
-- will stay on the same processor for its entire lifetime (`'forkIO'`
-- threads can migrate between processors according to the scheduling
-- policy). `forkOn'` is useful for overriding the scheduling policy when
-- you know in advance how best to distribute the threads.
--
-- The `Int` argument specifies a /capability number/ (see
-- 'getNumCapabilities''). Typically capabilities correspond to physical
-- processors, but the exact behaviour is implementation-dependent. The
-- value passed to 'forkOn'' is interpreted modulo the total number of
-- capabilities as returned by 'getNumCapabilities''.
--
-- GHC note: the number of capabilities is specified by the @+RTS -N@
-- option when the program is started. Capabilities can be fixed to
-- actual processor cores with @+RTS -qa@ if the underlying operating
-- system supports that, although in practice this is usually unnecessary
-- (and may actually degrade perforamnce in some cases - experimentation
-- is recommended).
forkOn' :: Int -> io () -> io ThreadId
-- | Wraps 'Control.Concurrent.forkOnWithUnmask'
--
-- Like 'forkIOWithUnmask'', but the child thread is pinned to the
-- given CPU, as with 'forkOn''.
forkOnWithUnmask' :: Int -> ((forall a. io a -> io a) -> io ()) -> io ThreadId
-- | Wraps 'Control.Concurrent.getNumCapabilities'
--
-- Returns the number of Haskell threads that can run truly
-- simultaneously (on separate physical processors) at any given time. To
-- change this value, use 'setNumCapabilities''.
getNumCapabilities' :: io Int
-- | Wraps 'Control.Concurrent.setNumCapabilities'
--
-- Set the number of Haskell threads that can run truly simultaneously
-- (on separate physical processors) at any given time. The number
-- passed to `forkOn'` is interpreted modulo this value. The initial
-- value is given by the @+RTS -N@ runtime flag.
--
-- This is also the number of threads that will participate in parallel
-- garbage collection. It is strongly recommended that the number of
-- capabilities is not set larger than the number of physical processor
-- cores, and it may often be beneficial to leave one or more cores free
-- to avoid contention with other processes in the machine.
setNumCapabilities' :: Int -> io ()
-- | Wraps 'Control.Concurrent.threadCapability'
--
-- Returns the number of the capability on which the thread is currently
-- running, and a boolean indicating whether the thread is locked to
-- that capability or not. A thread is locked to a capability if it
-- was created with @forkOn'@.
threadCapability' :: ThreadId -> io (Int, Bool)
-- | Wraps 'Control.Concurrent.yield'
--
-- The 'yield'' action allows (forces, in a co-operative multitasking
-- implementation) a context-switch to any other currently runnable
-- threads (if any), and is occasionally useful when implementing
-- concurrency abstractions.
yield' :: io ()
-- | Wraps 'Control.Concurrent.threadDelay'
--
-- Suspends the current thread for a given number of microseconds
-- (GHC only).
--
-- There is no guarantee that the thread will be rescheduled promptly
-- when the delay has expired, but the thread will never continue to
-- run /earlier/ than specified.
threadDelay' :: Int -> io ()
-- | Wraps 'Control.Concurrent.threadWaitRead'
--
-- Block the current thread until data is available to read on the
-- given file descriptor (GHC only).
--
-- This will throw an 'IOError' if the file descriptor was closed
-- while this thread was blocked. To safely close a file descriptor
-- that has been used with 'threadWaitRead', use
-- 'GHC.Conc.closeFdWith'.
threadWaitRead' :: Fd -> io ()
-- | Wraps 'Control.Concurrent.threadWaitWrite'
--
-- Block the current thread until data can be written to the
-- given file descriptor (GHC only).
--
-- This will throw an 'IOError' if the file descriptor was closed
-- while this thread was blocked. To safely close a file descriptor
-- that has been used with 'threadWaitWrite', use
-- 'GHC.Conc.closeFdWith'.
threadWaitWrite' :: Fd -> io ()
-- | Returns an STM action that can be used to wait for data
-- to read from a file descriptor. The second returned value
-- is an IO action that can be used to deregister interest
-- in the file descriptor.
--
-- /Since: 4.7.0.0/
threadWaitReadSTM' :: Fd -> io (STM (), io ())
-- | Returns an STM action that can be used to wait until data
-- can be written to a file descriptor. The second returned value
-- is an IO action that can be used to deregister interest
-- in the file descriptor.
--
-- /Since: 4.7.0.0/
threadWaitWriteSTM' :: Fd -> io (STM (), io ())
-- | Provides access to bounded thread functions
class ThreadAccess io => BoundThreadAccess io where
-- | Wraps 'Control.Concurrent.forkOS'
--
-- Like 'forkIO'', this sparks off a new thread to run the 'IO'
-- computation passed as the first argument, and returns the 'ThreadId'
-- of the newly created thread.
--
-- However, 'forkOS'' creates a /bound/ thread, which is necessary if you
-- need to call foreign (non-Haskell) libraries that make use of
-- thread-local state, such as OpenGL (see "Control.Concurrent#boundthreads").
--
-- Using 'forkOS'' instead of 'forkIO'' makes no difference at all to the
-- scheduling behaviour of the Haskell runtime system. It is a common
-- misconception that you need to use 'forkOS'' instead of 'forkIO'' to
-- avoid blocking all the Haskell threads when making a foreign call;
-- this isn't the case. To allow foreign calls to be made without
-- blocking all the Haskell threads (with GHC), it is only necessary to
-- use the @-threaded@ option when linking your program, and to make sure
-- the foreign import is not marked @unsafe@.
forkOS' :: io () -> io ThreadId
-- | Wraps 'Control.Concurrent.isCurrentThreadBound'
--
-- Returns 'True' if the calling thread is /bound/, that is, if it is
-- safe to use foreign libraries that rely on thread-local state from the
-- calling thread.
isCurrentThreadBound' :: io Bool
-- | Wraps 'Control.Concurrent.runInBoundThread'
--
-- Run the 'IO' computation passed as the first argument. If the calling
-- thread is not /bound/, a bound thread is created temporarily.
-- @runInBoundThread'@ doesn't finish until the 'IO' computation finishes.
--
-- You can wrap a series of foreign function calls that rely on thread-local
-- state with @runInBoundThread'@ so that you can use them without knowing
-- whether the current thread is /bound/.
runInBoundThread' :: io a -> io a
-- | Wraps 'Control.Concurrent.runInUnboundThread'
--
--Run the 'IO' computation passed as the first argument. If the calling
-- thread is /bound/, an unbound thread is created temporarily using
-- 'forkIO''. @runInBoundThread'@ doesn't finish until the 'IO' computation
-- finishes.
--
-- Use this function /only/ in the rare case that you have actually observed
-- a performance loss due to the use of bound threads. A program that
-- doesn't need it's main thread to be bound and makes /heavy/ use of
-- concurrency (e.g. a web server), might want to wrap it's @main@ action in
-- @runInUnboundThread'@.
--
-- Note that exceptions which are thrown to the current thread are thrown in
-- turn to the thread that is executing the given computation. This ensures
-- there's always a way of killing the forked thread.
runInUnboundThread' :: io a -> io a
-- | Provides access to the 'mkWeakThreadId' function, inherits from 'WeakMemAccess'
class WeakMemAccess io => WeakThreadAccess io where
-- | Wraps 'Control.Concurrent.mkWeakThreadId'
--
-- Make a weak pointer to a 'ThreadId'. It can be important to do
-- this if you want to hold a reference to a 'ThreadId' while still
-- allowing the thread to receive the @BlockedIndefinitely@ family of
-- exceptions (e.g. 'BlockedIndefinitelyOnMVar'). Holding a normal
-- 'ThreadId' reference will prevent the delivery of
-- @BlockedIndefinitely@ exceptions because the reference could be
-- used as the target of 'throwTo'' at any time, which would unblock
-- the thread.
--
-- Holding a @Weak ThreadId@, on the other hand, will not prevent the
-- thread from receiving @BlockedIndefinitely@ exceptions. It is
-- still possible to throw an exception to a @Weak ThreadId@, but the
-- caller must use @deRefWeak'@ first to determine whether the thread
-- still exists.
mkWeakThreadId' :: ThreadId -> io (Weak ThreadId)
instance ThreadAccess IO where
myThreadId' = myThreadId
forkIO' = forkIO
forkFinally' = forkFinally
forkIOWithUnmask' = forkIOWithUnmask
killThread' = killThread
throwTo' = throwTo
forkOn' = forkOn
forkOnWithUnmask' = forkOnWithUnmask
getNumCapabilities' = getNumCapabilities
setNumCapabilities' = setNumCapabilities
threadCapability' = threadCapability
yield' = yield
threadDelay' = threadDelay
threadWaitRead' = threadWaitRead
threadWaitWrite' = threadWaitWrite
threadWaitReadSTM' = threadWaitReadSTM
threadWaitWriteSTM' = threadWaitWriteSTM
instance BoundThreadAccess IO where
forkOS' = forkOS
isCurrentThreadBound' = isCurrentThreadBound
runInBoundThread' = runInBoundThread
runInUnboundThread' = runInUnboundThread
instance WeakThreadAccess IO where
mkWeakThreadId' = mkWeakThreadId