packages feed

base-io-access 0.3.0.1 → 0.4.0.0

raw patch · 21 files changed

+610/−119 lines, 21 filesdep ~base

Dependency ranges changed: base

Files

Access/Control/Concurrent.hs view
@@ -1,9 +1,20 @@ {-# 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(..)@@ -11,42 +22,312 @@     ) where  -import Control.Concurrent-import System.Posix.Types (Fd)+import           Control.Concurrent+import           GHC.Conc                        (STM)+import           System.Posix.Types              (Fd) -import Access.System.Mem.Weak-import Access.Control.Exception-import Access.Control.Concurrent.MVar-import Access.Control.Concurrent.Chan+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)  @@ -66,6 +347,8 @@     threadDelay'        = threadDelay     threadWaitRead'     = threadWaitRead     threadWaitWrite'    = threadWaitWrite+    threadWaitReadSTM'  = threadWaitReadSTM+    threadWaitWriteSTM' = threadWaitWriteSTM  instance BoundThreadAccess IO where     forkOS'               = forkOS
Access/Control/Concurrent/Chan.hs view
@@ -5,17 +5,30 @@     ) where  -import Control.Concurrent.Chan+import           Control.Concurrent.Chan -import Access.Core+import           Access.Core   class Access io => ChanAccess io where+    -- |Build and returns a new instance of 'Chan'.     newChan'            :: io (Chan a)+    -- |Write a value to a 'Chan'.     writeChan'          :: Chan a -> a -> io ()+    -- |Read the next value from the 'Chan'.     readChan'           :: Chan a -> io a+    -- |Duplicate a 'Chan': the duplicate channel begins empty, but data written to+    -- either channel from then on will be available from both.  Hence this creates+    -- a kind of broadcast channel, where data written by anyone is seen by+    -- everyone else.+    --+    -- (Note that a duplicated channel is not equal to its original.+    -- So: @fmap (c /=) $ dupChan c@ returns @True@ for all @c@.)     dupChan'            :: Chan a -> io (Chan a)+    -- |Return a lazy list representing the contents of the supplied+    -- 'Chan', much like 'System.IO.hGetContents'.     getChanContents'    :: Chan a -> io [a]+    -- |Write an entire list of items to a 'Chan'.     writeList2Chan'     :: Chan a -> [a] -> io ()  
Access/Control/Concurrent/MVar.hs view
@@ -6,28 +6,146 @@     ) where  -import Control.Concurrent.MVar+import           Control.Concurrent.MVar -import Access.Core-import Access.System.Mem.Weak+import           Access.Core+import           Access.System.Mem.Weak  class Access io => MVarAccess io where+    -- |Create an 'MVar' which is initially empty.     newEmptyMVar'       :: io (MVar a)+    -- |Create an 'MVar' which contains the supplied value.     newMVar'            :: a -> io (MVar a)+    -- |Return the contents of the 'MVar'.  If the 'MVar' is currently+    -- empty, 'takeMVar' will wait until it is full.  After a 'takeMVar',+    -- the 'MVar' is left empty.+    --+    -- There are two further important properties of 'takeMVar':+    --+    --   * 'takeMVar' is single-wakeup.  That is, if there are multiple+    --     threads blocked in 'takeMVar', and the 'MVar' becomes full,+    --     only one thread will be woken up.  The runtime guarantees that+    --     the woken thread completes its 'takeMVar' operation.+    --+    --   * When multiple threads are blocked on an 'MVar', they are+    --     woken up in FIFO order.  This is useful for providing+    --     fairness properties of abstractions built using 'MVar's.+    --     takeMVar'           :: MVar a -> io a+    -- |Put a value into an 'MVar'.  If the 'MVar' is currently full,+    -- 'putMVar' will wait until it becomes empty.+    --+    -- There are two further important properties of 'putMVar':+    --+    --   * 'putMVar' is single-wakeup.  That is, if there are multiple+    --     threads blocked in 'putMVar', and the 'MVar' becomes empty,+    --     only one thread will be woken up.  The runtime guarantees that+    --     the woken thread completes its 'putMVar' operation.+    --+    --   * When multiple threads are blocked on an 'MVar', they are+    --     woken up in FIFO order.  This is useful for providing+    --     fairness properties of abstractions built using 'MVar's.+    --     putMVar'            :: MVar a -> a -> io ()+    -- |Atomically read the contents of an 'MVar'.  If the 'MVar' is+    -- currently empty, 'readMVar' will wait until its full.+    -- 'readMVar' is guaranteed to receive the next 'putMVar'.+    --+    -- 'readMVar' is multiple-wakeup, so when multiple readers are+    -- blocked on an 'MVar', all of them are woken up at the same time.+    --+    -- /Compatibility note:/ Prior to base 4.7, 'readMVar' was a combination+    -- of 'takeMVar' and 'putMVar'.  This mean that in the presence of+    -- other threads attempting to 'putMVar', 'readMVar' could block.+    -- Furthermore, 'readMVar' would not receive the next 'putMVar' if there+    -- was already a pending thread blocked on 'takeMVar'.  The old behavior+    -- can be recovered by implementing 'readMVar as follows:+    --+    -- @+    --  readMVar :: MVar a -> IO a+    --  readMVar m =+    --    mask_ $ do+    --      a <- takeMVar m+    --      putMVar m a+    --      return a+    -- @     readMVar'           :: MVar a -> io a+    {-|+      Take a value from an 'MVar', put a new value into the 'MVar' and+      return the value taken. This function is atomic only if there are+      no other producers for this 'MVar'.+    -}     swapMVar'           :: MVar a -> a -> io a+    -- |A non-blocking version of 'takeMVar'.  The 'tryTakeMVar' function+    -- returns immediately, with 'Nothing' if the 'MVar' was empty, or+    -- @'Just' a@ if the 'MVar' was full with contents @a@.  After 'tryTakeMVar',+    -- the 'MVar' is left empty.     tryTakeMVar'        :: MVar a -> io (Maybe a)+    -- |A non-blocking version of 'putMVar'.  The 'tryPutMVar' function+    -- attempts to put the value @a@ into the 'MVar', returning 'True' if+    -- it was successful, or 'False' otherwise.     tryPutMVar'         :: MVar a -> a -> io Bool+    -- |A non-blocking version of 'readMVar'.  The 'tryReadMVar' function+    -- returns immediately, with 'Nothing' if the 'MVar' was empty, or+    -- @'Just' a@ if the 'MVar' was full with contents @a@.+    --+    -- /Since: 4.7.0.0/+    tryReadMVar'        :: MVar a -> io (Maybe a)+    -- |Check whether a given 'MVar' is empty.+    --+    -- Notice that the boolean value returned  is just a snapshot of+    -- the state of the MVar. By the time you get to react on its result,+    -- the MVar may have been filled (or emptied) - so be extremely+    -- careful when using this operation.   Use 'tryTakeMVar' instead if possible.     isEmptyMVar'        :: MVar a -> io Bool+    {-|+      'withMVar' is an exception-safe wrapper for operating on the contents+      of an 'MVar'.  This operation is exception-safe: it will replace the+      original contents of the 'MVar' if an exception is raised (see+      "Control.Exception").  However, it is only atomic if there are no+      other producers for this 'MVar'.+    -}     withMVar'           :: MVar a -> (a -> IO b) -> io b+    {-|+      Like 'withMVar', but the @IO@ action in the second argument is executed+      with asynchronous exceptions masked.++      /Since: 4.7.0.0/+    -}+    withMVarMasked'     :: MVar a -> (a -> IO b) -> io b+    {-|+      An exception-safe wrapper for modifying the contents of an 'MVar'.+      Like 'withMVar', 'modifyMVar' will replace the original contents of+      the 'MVar' if an exception is raised during the operation.  This+      function is only atomic if there are no other producers for this+      'MVar'.+    -}     modifyMVar_'        :: MVar a -> (a -> IO a) -> io ()+    {-|+      A slight variation on 'modifyMVar_' that allows a value to be+      returned (@b@) in addition to the modified value of the 'MVar'.+    -}     modifyMVar'         :: MVar a -> (a -> IO (a, b)) -> io b+    {-|+      Like 'modifyMVar_', but the @IO@ action in the second argument is executed with+      asynchronous exceptions masked.++      /Since: 4.6.0.0/+    -}     modifyMVarMasked_'  :: MVar a -> (a -> IO a) -> io ()+    {-|+      Like 'modifyMVar', but the @IO@ action in the second argument is executed with+      asynchronous exceptions masked.++      /Since: 4.6.0.0/+    -}     modifyMVarMasked'   :: MVar a -> (a -> IO (a, b)) -> io b  class (WeakMemAccess io, MVarAccess io) => WeakMVarAccess io where+    -- | Make a 'Weak' pointer to an 'MVar', using the second argument as+    -- a finalizer to run when 'MVar' is garbage-collected+    --+    -- /Since: 4.6.0.0/     mkWeakMVar' :: MVar a -> IO () -> io (Weak (MVar a))  @@ -40,8 +158,10 @@     swapMVar'          = swapMVar     tryTakeMVar'       = tryTakeMVar     tryPutMVar'        = tryPutMVar+    tryReadMVar'       = tryReadMVar     isEmptyMVar'       = isEmptyMVar     withMVar'          = withMVar+    withMVarMasked'    = withMVarMasked     modifyMVar_'       = modifyMVar_     modifyMVar'        = modifyMVar     modifyMVarMasked_' = modifyMVarMasked_
+ Access/Control/Concurrent/QSem.hs view
@@ -0,0 +1,26 @@+module Access.Control.Concurrent.QSem+    ( module Control.Concurrent.QSem++    , QSemAccess(..)+    ) where+++import Control.Concurrent.QSem++import Access.Core+++class Access io => QSemAccess io where+    -- |Build a new 'QSem' with a supplied initial quantity.+    --  The initial quantity must be at least 0.+    newQSem'    :: Int -> io QSem+    -- |Wait for a unit to become available+    waitQSem'   :: QSem -> io ()+    -- |Signal that a unit of the 'QSem' is available+    signalQSem' :: QSem -> io ()+++instance QSemAccess IO where+    newQSem'    = newQSem+    waitQSem'   = waitQSem+    signalQSem' = signalQSem
+ Access/Control/Concurrent/QSemN.hs view
@@ -0,0 +1,26 @@+module Access.Control.Concurrent.QSemN+    ( module Control.Concurrent.QSemN++    , QSemNAccess(..)+    ) where+++import Control.Concurrent.QSemN++import Access.Core+++class Access io => QSemNAccess io where+    -- |Build a new 'QSemN' with a supplied initial quantity.+    --  The initial quantity must be at least 0.+    newQSemN'    :: Int -> io QSemN+    -- |Wait for the specified quantity to become available+    waitQSemN'   :: QSemN -> Int -> io ()+    -- |Signal that a given quantity is now available from the 'QSemN'.+    signalQSemN' :: QSemN -> Int -> io ()+++instance QSemNAccess IO where+    newQSemN'    = newQSemN+    waitQSemN'   = waitQSemN+    signalQSemN' = signalQSemN
Access/Control/Exception.hs view
@@ -7,10 +7,10 @@     ) where  -import Control.Exception-import Control.Concurrent (ThreadId)+import           Control.Concurrent (ThreadId)+import           Control.Exception -import Access.Core+import           Access.Core  class Access io => ExceptionAccess io where     throwIO'        :: Exception e => e -> io a
Access/Core.hs view
@@ -1,15 +1,26 @@+--------------------------------------------------------------------------------+-- |+-- Module       :   Access.Core+-- Copyright    :   (c) Aaron Stevens, 2014+-- License      :   GPL2+--+-- Maintainer   :   bheklilr2@gmail.com+--------------------------------------------------------------------------------+ module Access.Core     ( Access     ) where  -import Data.Typeable.Internal (Typeable1)-import Control.Applicative    (Applicative)-import Control.Monad.Fix      (MonadFix)+import           Control.Applicative (Applicative)+import           Control.Monad.Fix   (MonadFix)+import           Data.Typeable       (Typeable) +-- | The 'Access' type class.  It belongs to several of the same type classes+-- as 'IO'.  Notably, it is a 'Monad', a 'Functor', and an 'Applicative'. class ( Monad io       , Functor io-      , Typeable1 io+      , Typeable io       , MonadFix io       , Applicative io) => Access io where 
Access/Data/IORef.hs view
@@ -6,10 +6,10 @@     ) where  -import Data.IORef+import           Data.IORef -import Access.Core-import Access.System.Mem.Weak+import           Access.Core+import           Access.System.Mem.Weak   class Access io => IORefAccess io where
Access/Data/Unique.hs view
@@ -5,9 +5,9 @@     ) where  -import Data.Unique+import           Data.Unique -import Access.Core+import           Access.Core   class Access io => UniqueAccess io where
Access/Debug/Trace.hs view
@@ -5,16 +5,18 @@     ) where  -import Debug.Trace+import           Debug.Trace -import Access.Core+import           Access.Core   class Access io => TraceAccess io where     traceIO'        :: String -> io ()     traceEventIO'   :: String -> io ()+    traceMarkerIO'  :: String -> io ()   instance TraceAccess IO where-    traceIO'      = traceIO-    traceEventIO' = traceEventIO+    traceIO'       = traceIO+    traceEventIO'  = traceEventIO+    traceMarkerIO' = traceMarkerIO
Access/System/CPUTime.hs view
@@ -5,12 +5,15 @@     ) where  -import System.CPUTime+import           System.CPUTime -import Access.Core+import           Access.Core   class Access io => CPUTimeAccess io where+    -- |Computation 'getCPUTime' returns the number of picoseconds CPU time+    -- used by the current program.  The precision of this result is+    -- implementation-dependent.     getCPUTime' :: io Integer  
Access/System/Environment.hs view
@@ -5,9 +5,9 @@     ) where  -import System.Environment+import           System.Environment -import Access.Core+import           Access.Core   class Access io => EnvironmentAccess io where@@ -16,6 +16,8 @@     getExecutablePath'  :: io FilePath     getEnv'             :: String -> io String     lookupEnv'          :: String -> io (Maybe String)+    setEnv'             :: String -> String -> io ()+    unsetEnv'           :: String -> io ()     withArgs'           :: [String] -> IO a -> io a     withProgName'       :: String -> IO a -> io a     getEnvironment'     :: io [(String, String)]@@ -27,6 +29,8 @@     getExecutablePath' = getExecutablePath     getEnv'            = getEnv     lookupEnv'         = lookupEnv+    setEnv'            = setEnv+    unsetEnv'          = unsetEnv     withArgs'          = withArgs     withProgName'      = withProgName     getEnvironment'    = getEnvironment
Access/System/Exit.hs view
@@ -5,14 +5,46 @@     ) where  -import System.Exit+import           System.Exit -import Access.Core+import           Access.Core   class Access io => ExitAccess io where+    -- | Computation 'exitWith' @code@ throws 'ExitCode' @code@.+    -- Normally this terminates the program, returning @code@ to the+    -- program's caller.+    --+    -- On program termination, the standard 'Handle's 'stdout' and+    -- 'stderr' are flushed automatically; any other buffered 'Handle's+    -- need to be flushed manually, otherwise the buffered data will be+    -- discarded.+    --+    -- A program that fails in any other way is treated as if it had+    -- called 'exitFailure'.+    -- A program that terminates successfully without calling 'exitWith'+    -- explicitly is treated as it it had called 'exitWith' 'ExitSuccess'.+    --+    -- As an 'ExitCode' is not an 'IOError', 'exitWith' bypasses+    -- the error handling in the 'IO' monad and cannot be intercepted by+    -- 'catch' from the "Prelude".  However it is a 'SomeException', and can+    -- be caught using the functions of "Control.Exception".  This means+    -- that cleanup computations added with 'Control.Exception.bracket'+    -- (from "Control.Exception") are also executed properly on 'exitWith'.+    --+    -- Note: in GHC, 'exitWith' should be called from the main program+    -- thread in order to exit the process.  When called from another+    -- thread, 'exitWith' will throw an 'ExitException' as normal, but the+    -- exception will not cause the process itself to exit.+    --     exitWith'       :: ExitCode -> io a+    -- | The computation 'exitFailure' is equivalent to+    -- 'exitWith' @(@'ExitFailure' /exitfail/@)@,+    -- where /exitfail/ is implementation-dependen     exitFailure'    :: io a+    -- | The computation 'exitSuccess' is equivalent to+    -- 'exitWith' 'ExitSuccess', It terminates the program+    -- successfully.     exitSuccess'    :: io a  
Access/System/IO.hs view
@@ -24,10 +24,10 @@     ) where  -import System.IO-import Foreign.Ptr (Ptr)+import           Foreign.Ptr (Ptr)+import           System.IO -import Access.Core+import           Access.Core  -- | Provides access to 'Handle' write functions class Access io => HandleWriteAccess io where
Access/System/IO/Error.hs view
@@ -5,9 +5,9 @@     ) where  -import System.IO.Error+import           System.IO.Error -import Access.Core+import           Access.Core   class Access io => IOErrorAccess io where
Access/System/Mem.hs view
@@ -5,14 +5,25 @@     ) where  -import System.Mem+import           System.Mem -import Access.Core+import           Access.Core   class Access io => MemAccess io where-    performGC' :: io ()+    -- | Triggers an immediate garbage collection.+    performGC'      :: io ()+    -- | Triggers an immediate garbage collection.+    --+    -- /Since: 4.7.0.0/+    performMajorGC' :: io ()+    -- | Triggers an immediate minor garbage collection.+    --+    -- /Since: 4.7.0.0/+    performMinorGC' :: io ()   instance MemAccess IO where     performGC' = performGC+    performMajorGC' = performMajorGC+    performMinorGC' = performMinorGC
Access/System/Mem/StableName.hs view
@@ -5,9 +5,9 @@     ) where  -import System.Mem.StableName+import           System.Mem.StableName -import Access.Core+import           Access.Core   class Access io => StableNameAccess io where
Access/System/Mem/Weak.hs view
@@ -5,9 +5,9 @@     ) where  -import System.Mem.Weak+import           System.Mem.Weak -import Access.Core+import           Access.Core   class Access io => WeakMemAccess io where
Access/System/Timeout.hs view
@@ -5,9 +5,9 @@     ) where  -import System.Timeout+import           System.Timeout -import Access.Core+import           Access.Core   class Access io => TimeoutAccess io where@@ -15,4 +15,32 @@   instance TimeoutAccess IO where+    -- |Wrap an 'IO' computation to time out and return @Nothing@ in case no result+    -- is available within @n@ microseconds (@1\/10^6@ seconds). In case a result+    -- is available before the timeout expires, @Just a@ is returned. A negative+    -- timeout interval means \"wait indefinitely\". When specifying long timeouts,+    -- be careful not to exceed @maxBound :: Int@.+    --+    -- The design of this combinator was guided by the objective that @timeout n f@+    -- should behave exactly the same as @f@ as long as @f@ doesn't time out. This+    -- means that @f@ has the same 'myThreadId' it would have without the timeout+    -- wrapper. Any exceptions @f@ might throw cancel the timeout and propagate+    -- further up. It also possible for @f@ to receive exceptions thrown to it by+    -- another thread.+    --+    -- A tricky implementation detail is the question of how to abort an @IO@+    -- computation. This combinator relies on asynchronous exceptions internally.+    -- The technique works very well for computations executing inside of the+    -- Haskell runtime system, but it doesn't work at all for non-Haskell code.+    -- Foreign function calls, for example, cannot be timed out with this+    -- combinator simply because an arbitrary C function cannot receive+    -- asynchronous exceptions. When @timeout@ is used to wrap an FFI call that+    -- blocks, no timeout event can be delivered until the FFI call returns, which+    -- pretty much negates the purpose of the combinator. In practice, however,+    -- this limitation is less severe than it may sound. Standard I\/O functions+    -- like 'System.IO.hGetBuf', 'System.IO.hPutBuf', Network.Socket.accept, or+    -- 'System.IO.hWaitForInput' appear to be blocking, but they really don't+    -- because the runtime system uses scheduling mechanisms like @select(2)@ to+    -- perform asynchronous I\/O, so it is possible to interrupt standard socket+    -- I\/O or file I\/O using this combinator.     timeout' = timeout
− README.md
@@ -1,69 +0,0 @@-base-io-access-==============--An attempt to break up the monolithic IO monad into small, composable classes-that can be used to restrict a function to only having access to, say, functions-to work with the standard pipes, or a function that can access the environment.-The motivation for this library is to allow people to make a stricter contract-than simply "this function does IO", and express through the type system exactly-what IO is being performed.--Implementation-==============--The method used is to make a thinly veiled class breaking up groups of `IO`-actions into smaller sub-groups.  As an example:--```haskell-class Access io => ExitAccess io where-    exitWith'       :: ExitCode -> io a-    exitFailure'    :: io a-    exitSuccess'    :: io a--instance ExitAccess IO where-    exitWith'    = exitWith-    exitFailure' = exitFailure-    exitSuccess' = exitSuccess-```--Here the `Access` class is an empty super class that combines `Monad`, -`Applicative`, `Functor`, `Typeable1`, and `MonadFix`.  It is the super class -for all the others in this library.  Maybe eventually I will make it more -powerful, or add in special support for handling generic `Access` actions, but -for now it's going to remain very simplistic.--In this example taken from `Access.System.Exit`, we have defined a small class-`ExitAccess` that has three functions, each corresponding to their -implementation in `System.Exit` but with an extra `'` appended to the name.-By itself this class isn't very interesting, but when combined with (the larger-) `StdIOAccess` class, we could write a function like--```haskell-helpMessage :: (ExitAccess io, StdIOAcess io) => io ()-helpMessage = do-    putStrLn' "Usage:"-    putStrLn' "    mytool [options]"-    putStrLn' "Options:"-    putStrLn' "    --help       Display this help message"-    exitSuccess'--main :: IO ()-main = do-    args <- getArgs-    if "--help" `elem` args-        then helpMessage-        else mainLoop-```--While this is a simple and contrived example, it illustrates that a function's -capabilities are greatly reduced through this method.  Since we often want to-use the most restrictive type we can in Haskell, this provides an easy way to-achieve this when working with the all-encompassing `IO` monad.--Performance-===========--Since the entire library is based on just using typeclasses, there is no runtime-overhead whatsoever.  The compiler will optimize away the class lookups, and-every function is simply a thin wrapper around its actual implementation in-`base`.
base-io-access.cabal view
@@ -1,5 +1,5 @@ name:                   base-io-access
-version:                0.3.0.1
+version:                0.4.0.0
 synopsis:               The IO functions included in base delimited into small, composable classes
 description:            An attempt to break up the monolithic IO monad into small, composable classes
                         that can be used to restrict a function to only having access to, say, functions
@@ -15,7 +15,6 @@ category:               System
 build-type:             Simple
 cabal-version:          >=1.10
-extra-source-files:     README.md
 
 source-repository head
     type:               git
@@ -25,6 +24,8 @@     exposed-modules:    Access.Control.Concurrent,
                         Access.Control.Concurrent.Chan,
                         Access.Control.Concurrent.MVar,
+                        Access.Control.Concurrent.QSem,
+                        Access.Control.Concurrent.QSemN,
                         Access.Control.Exception,
 
                         Access.Data.IORef,
@@ -44,5 +45,5 @@ 
                         Access.Core
 
-    build-depends:      base >=4.6 && <4.7
+    build-depends:      base >=4.7 && <4.8
     default-language:   Haskell2010