Skip to content

Commit f9b8901

Browse files
committed
Merge pull request #52 from sardonicpresence/master
Allow cancellation of computations forked with `runAff`
2 parents b17683e + 0f21d45 commit f9b8901

File tree

2 files changed

+77
-52
lines changed

2 files changed

+77
-52
lines changed

src/Control/Monad/Aff.purs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ foreign import data Aff :: # ! -> * -> *
5252
-- | asynchronous computation.
5353
type PureAff a = forall e. Aff e a
5454

55-
-- | A canceler is asynchronous function that can be used to attempt the
55+
-- | A canceler is an asynchronous function that can be used to attempt the
5656
-- | cancelation of a computation. Returns a boolean flag indicating whether
5757
-- | or not the cancellation was successful. Many computations may be composite,
5858
-- | in such cases the flag indicates whether any part of the computation was
@@ -79,15 +79,20 @@ cancelWith aff c = runFn3 _cancelWith nonCanceler aff c
7979
-- | If you do need to handle exceptions, you can use `runAff` instead, or
8080
-- | you can handle the exception within the Aff computation, using
8181
-- | `catchError` (or any of the other mechanisms).
82-
launchAff :: forall e a. Aff e a -> Eff (err :: EXCEPTION | e) Unit
83-
launchAff = runAff throwException (const (pure unit)) <<< liftEx
82+
launchAff :: forall e a. Aff e a -> Eff (err :: EXCEPTION | e) (Canceler e)
83+
launchAff = lowerEx <<< runAff throwException (const (pure unit)) <<< liftEx
8484
where
8585
liftEx :: Aff e a -> Aff (err :: EXCEPTION | e) a
8686
liftEx = _unsafeInterleaveAff
87+
lowerEx :: Eff (err :: EXCEPTION | e) (Canceler (err :: EXCEPTION | e)) -> Eff (err :: EXCEPTION | e) (Canceler e)
88+
lowerEx = map (Canceler <<< map _unsafeInterleaveAff <<< cancel)
8789

8890
-- | Runs the asynchronous computation. You must supply an error callback and a
8991
-- | success callback.
90-
runAff :: forall e a. (Error -> Eff e Unit) -> (a -> Eff e Unit) -> Aff e a -> Eff e Unit
92+
-- |
93+
-- | Returns a canceler that can be used to attempt cancellation of the
94+
-- | asynchronous computation.
95+
runAff :: forall e a. (Error -> Eff e Unit) -> (a -> Eff e Unit) -> Aff e a -> Eff e (Canceler e)
9196
runAff ex f aff = runFn3 _runAff ex f aff
9297

9398
-- | Creates an asynchronous effect from a function that accepts error and
@@ -200,7 +205,7 @@ instance monadRecAff :: MonadRec (Aff e) where
200205
tailRecM f a = runFn3 _tailRecM isLeft f a
201206

202207
instance monadContAff :: MonadCont (Aff e) where
203-
callCC f = makeAff (\eb cb -> runAff eb cb (f \a -> makeAff (\_ _ -> cb a)))
208+
callCC f = makeAff (\eb cb -> void $ runAff eb cb (f \a -> makeAff (\_ _ -> cb a)))
204209

205210
instance semigroupCanceler :: Semigroup (Canceler e) where
206211
append (Canceler f1) (Canceler f2) = Canceler (\e -> (||) <$> f1 e <*> f2 e)
@@ -272,7 +277,7 @@ foreign import _bind :: forall e a b. Fn3 (Canceler e) (Aff e a) (a -> Aff e b)
272277

273278
foreign import _attempt :: forall e a. Fn3 (forall x y. x -> Either x y) (forall x y. y -> Either x y) (Aff e a) (Aff e (Either Error a))
274279

275-
foreign import _runAff :: forall e a. Fn3 (Error -> Eff e Unit) (a -> Eff e Unit) (Aff e a) (Eff e Unit)
280+
foreign import _runAff :: forall e a. Fn3 (Error -> Eff e Unit) (a -> Eff e Unit) (Aff e a) (Eff e (Canceler e))
276281

277282
foreign import _liftEff :: forall e a. Fn2 (Canceler e) (Eff e a) (Aff e a)
278283

test/Test/Main.purs

Lines changed: 66 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ import Prelude
55
import Control.Alt ((<|>))
66
import Control.Apply ((*>))
77
import Control.Parallel.Class (parallel, runParallel)
8-
import Control.Monad.Aff (Aff, runAff, makeAff, later, later', forkAff, forkAll, Canceler(..), cancel, attempt, finally, apathize)
8+
import Control.Monad.Aff (Aff, runAff, makeAff, launchAff, later, later', forkAff, forkAll, Canceler(..), cancel, attempt, finally, apathize)
99
import Control.Monad.Aff.AVar (AVAR, makeVar, makeVar', putVar, modifyVar, takeVar, killVar)
1010
import Control.Monad.Aff.Console (log)
1111
import Control.Monad.Cont.Class (callCC)
1212
import Control.Monad.Eff (Eff)
1313
import Control.Monad.Eff.Console (CONSOLE)
14-
import Control.Monad.Eff.Exception (EXCEPTION, throwException, error, message)
14+
import Control.Monad.Eff.Console (log) as Eff
15+
import Control.Monad.Eff.Exception (EXCEPTION, throwException, error, message, try)
1516
import Control.Monad.Error.Class (throwError)
1617
import Control.Monad.Rec.Class (tailRecM)
1718
import Data.Either (Either(..), either, fromLeft, fromRight)
@@ -137,6 +138,18 @@ test_cancelLater = do
137138
v <- cancel c (error "Cause")
138139
log (if v then "Success: Canceled later" else "Failure: Did not cancel later")
139140

141+
test_cancelLaunchLater :: forall e. Eff (console :: CONSOLE, err :: EXCEPTION | e) Unit
142+
test_cancelLaunchLater = do
143+
c <- launchAff $ later' 100 $ log ("Failure: Later was not canceled!")
144+
void $ launchAff $ (do v <- cancel c (error "Cause")
145+
log (if v then "Success: Canceled later" else "Failure: Did not cancel later"))
146+
147+
test_cancelRunLater :: forall e. Eff (console :: CONSOLE | e) Unit
148+
test_cancelRunLater = do
149+
c <- runAff (const (pure unit)) (const (pure unit)) $ later' 100 $ log ("Failure: Later was not canceled!")
150+
void $ try $ launchAff $ (do v <- cancel c (error "Cause")
151+
log (if v then "Success: Canceled later" else "Failure: Did not cancel later"))
152+
140153
test_cancelParallel :: TestAVar Unit
141154
test_cancelParallel = do
142155
c <- forkAff <<< runParallel $ parallel (later' 100 $ log "Failure: #1 should not get through") <|>
@@ -187,69 +200,76 @@ delay n = callCC \cont ->
187200
later' n (cont unit)
188201

189202
main :: Eff (console :: CONSOLE, avar :: AVAR, err :: EXCEPTION) Unit
190-
main = runAff throwException (const (pure unit)) $ do
191-
log "Testing sequencing"
192-
test_sequencing 3
203+
main = do
204+
Eff.log "Testing kill of later launched in separate Aff"
205+
test_cancelLaunchLater
206+
207+
Eff.log "Testing kill of later run in separate Aff"
208+
test_cancelRunLater
209+
210+
void $ runAff throwException (const (pure unit)) $ do
211+
log "Testing sequencing"
212+
test_sequencing 3
193213

194-
log "Testing pure"
195-
test_pure
214+
log "Testing pure"
215+
test_pure
196216

197-
log "Testing makeAff"
198-
test_makeAff
217+
log "Testing makeAff"
218+
test_makeAff
199219

200-
log "Testing attempt"
201-
test_attempt
220+
log "Testing attempt"
221+
test_attempt
202222

203-
log "Testing later"
204-
later $ log "Success: It happened later"
223+
log "Testing later"
224+
later $ log "Success: It happened later"
205225

206-
log "Testing kill of later"
207-
test_cancelLater
226+
log "Testing kill of later"
227+
test_cancelLater
208228

209-
log "Testing kill of first forked"
210-
test_killFirstForked
229+
log "Testing kill of first forked"
230+
test_killFirstForked
211231

212-
log "Testing apathize"
213-
test_apathize
232+
log "Testing apathize"
233+
test_apathize
214234

215-
log "Testing semigroup canceler"
216-
test_semigroupCanceler
235+
log "Testing semigroup canceler"
236+
test_semigroupCanceler
217237

218-
log "Testing AVar - putVar, takeVar"
219-
test_putTakeVar
238+
log "Testing AVar - putVar, takeVar"
239+
test_putTakeVar
220240

221-
log "Testing AVar killVar"
222-
test_killVar
241+
log "Testing AVar killVar"
242+
test_killVar
223243

224-
log "Testing finally"
225-
test_finally
244+
log "Testing finally"
245+
test_finally
226246

227-
log "Test Parallel (*>)"
228-
test_parError
247+
log "Test Parallel (*>)"
248+
test_parError
229249

230-
log "Testing Parallel (<|>)"
231-
test_parRace
250+
log "Testing Parallel (<|>)"
251+
test_parRace
232252

233-
log "Testing Parallel (<|>) - kill one"
234-
test_parRaceKill1
253+
log "Testing Parallel (<|>) - kill one"
254+
test_parRaceKill1
235255

236-
log "Testing Parallel (<|>) - kill two"
237-
test_parRaceKill2
256+
log "Testing Parallel (<|>) - kill two"
257+
test_parRaceKill2
238258

239-
log "Testing cancel of Parallel (<|>)"
240-
test_cancelParallel
259+
log "Testing cancel of Parallel (<|>)"
260+
test_cancelParallel
241261

242-
log "Testing synchronous tailRecM"
243-
test_syncTailRecM
262+
log "Testing synchronous tailRecM"
263+
test_syncTailRecM
244264

245-
log "pre-delay"
246-
delay 1000
247-
log "post-delay"
265+
log "pre-delay"
266+
delay 1000
267+
log "post-delay"
248268

249-
loopAndBounce 1000000
269+
loopAndBounce 1000000
250270

251-
all 100000
271+
all 100000
252272

253-
cancelAll 100000
273+
cancelAll 100000
254274

255-
log "Done testing"
275+
log "Done testing"

0 commit comments

Comments
 (0)