2 {-# LANGUAGE DeriveDataTypeable #-}
4 -- | 'Async', yet using 'MVar's.
6 -- Adopted from @async@ library
7 -- Copyright (c) 2012, Simon Marlow
8 -- Licensed under BSD-3-Clause
11 module Distribution
.Compat
.Async
18 , uninterruptibleCancel
25 import Control
.Concurrent
(ThreadId
, forkIO
)
26 import Control
.Concurrent
.MVar
(MVar
, newEmptyMVar
, putMVar
, readMVar
)
27 import Control
.DeepSeq
(NFData
, force
)
28 import Control
.Exception
29 ( BlockedIndefinitelyOnMVar
(..)
38 , uninterruptibleMask_
40 import Control
.Monad
(void
)
41 import Data
.Typeable
(Typeable
)
42 import GHC
.Exts
(inline
)
44 #if MIN_VERSION_base
(4,7,0)
45 import Control
.Exception
(asyncExceptionFromException
, asyncExceptionToException
)
48 -- | Async, but based on 'MVar', as we don't depend on @stm@.
50 { asyncThreadId
:: {-# UNPACK #-} !ThreadId
51 -- ^ Returns the 'ThreadId' of the thread running
53 , _asyncMVar
:: MVar
(Either SomeException a
)
56 -- | Spawn an asynchronous action in a separate thread, and pass its
57 -- @Async@ handle to the supplied function. When the function returns
58 -- or throws an exception, 'uninterruptibleCancel' is called on the @Async@.
60 -- > withAsync action inner = mask $ \restore -> do
61 -- > a <- async (restore action)
62 -- > restore (inner a) `finally` uninterruptibleCancel a
64 -- This is a useful variant of 'async' that ensures an @Async@ is
65 -- never left running unintentionally.
67 -- Note: a reference to the child thread is kept alive until the call
68 -- to `withAsync` returns, so nesting many `withAsync` calls requires
70 withAsync
:: IO a
-> (AsyncM a
-> IO b
) -> IO b
71 withAsync
= inline withAsyncUsing forkIO
73 withAsyncNF
:: NFData a
=> IO a
-> (AsyncM a
-> IO b
) -> IO b
74 withAsyncNF m
= inline withAsyncUsing forkIO
(m
>>= evaluateNF
)
76 evaluateNF
= evaluate
. force
78 withAsyncUsing
:: (IO () -> IO ThreadId
) -> IO a
-> (AsyncM a
-> IO b
) -> IO b
79 -- The bracket version works, but is slow. We can do better by
81 withAsyncUsing doFork
= \action inner
-> do
84 t
<- doFork
$ try (restore action
) >>= putMVar var
87 restore
(inner a
) `catchAll`
\e
-> do
88 uninterruptibleCancel a
90 uninterruptibleCancel a
93 -- | Wait for an asynchronous action to complete, and return its
94 -- value. If the asynchronous action threw an exception, then the
95 -- exception is re-thrown by 'wait'.
97 -- > wait = atomically . waitSTM
99 wait
:: AsyncM a
-> IO a
103 Left
(SomeException e
) -> throwIO e
106 -- | Wait for an asynchronous action to complete, and return either
107 -- @Left e@ if the action raised an exception @e@, or @Right a@ if it
108 -- returned a value @a@.
110 -- > waitCatch = atomically . waitCatchSTM
111 {-# INLINE waitCatch #-}
112 waitCatch
:: AsyncM a
-> IO (Either SomeException a
)
113 waitCatch
(Async _ var
) = tryAgain
(readMVar var
)
115 -- See: https://github.com/simonmar/async/issues/14
116 tryAgain f
= f `
catch`
\BlockedIndefinitelyOnMVar
-> f
118 catchAll
:: IO a
-> (SomeException
-> IO a
) -> IO a
121 -- | Cancel an asynchronous action by throwing the @AsyncCancelled@
122 -- exception to it, and waiting for the `Async` thread to quit.
123 -- Has no effect if the 'Async' has already completed.
125 -- > cancel a = throwTo (asyncThreadId a) AsyncCancelled <* waitCatch a
127 -- Note that 'cancel' will not terminate until the thread the 'Async'
128 -- refers to has terminated. This means that 'cancel' will block for
129 -- as long said thread blocks when receiving an asynchronous exception.
131 -- For example, it could block if:
133 -- * It's executing a foreign call, and thus cannot receive the asynchronous
135 -- * It's executing some cleanup handler after having received the exception,
136 -- and the handler is blocking.
137 {-# INLINE cancel #-}
138 cancel
:: AsyncM a
-> IO ()
139 cancel a
@(Async t _
) = do
140 throwTo t AsyncCancelled
143 -- | The exception thrown by `cancel` to terminate a thread.
144 data AsyncCancelled
= AsyncCancelled
151 {- FOURMOLU_DISABLE -}
152 instance Exception AsyncCancelled
where
153 #if MIN_VERSION_base
(4,7,0)
154 -- wraps in SomeAsyncException
155 -- See https://github.com/ghc/ghc/commit/756a970eacbb6a19230ee3ba57e24999e4157b09
156 fromException
= asyncExceptionFromException
157 toException
= asyncExceptionToException
159 {- FOURMOLU_ENABLE -}
161 -- | Cancel an asynchronous action
163 -- This is a variant of `cancel`, but it is not interruptible.
164 {-# INLINE uninterruptibleCancel #-}
165 uninterruptibleCancel
:: AsyncM a
-> IO ()
166 uninterruptibleCancel
= uninterruptibleMask_
. cancel