Skip to content

Commit a3e7e9b

Browse files
committed
Add canceler to forkAll
1 parent 73cff86 commit a3e7e9b

File tree

4 files changed

+61
-15
lines changed

4 files changed

+61
-15
lines changed

docs/Control.Monad.Aff.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,14 @@ forked computation.
162162
#### `forkAll`
163163

164164
``` purescript
165-
forkAll :: forall f e a. (Foldable f) => f (Aff e a) -> Aff e Unit
165+
forkAll :: forall f e a. (Foldable f) => f (Aff e a) -> Aff e (Canceler e)
166166
```
167167

168-
Forks many asynchronous computation at once, ignoring the results.
168+
Forks many asynchronous computation in a synchronous manner while being
169+
stack-safe up to the selected Foldable instance.
169170

170-
This function is stack-safe up to the selected Foldable instance.
171+
Returns a canceler that can be used to attempt cancellation of all
172+
forked computations.
171173

172174
#### `attempt`
173175

src/Control/Monad/Aff.js

+39-4
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,49 @@ exports._forkAll = function (nonCanceler, foldl, affs) {
100100
var voidF = function(){};
101101

102102
return function(success, error) {
103-
foldl(function(_) {
103+
var cancelers = foldl(function(acc) {
104104
return function(aff) {
105-
aff(voidF, voidF);
105+
acc.push(aff(voidF, voidF));
106+
return acc;
107+
}
108+
})([])(affs);
109+
110+
var canceler = function(e) {
111+
return function(success, error) {
112+
var cancellations = 0;
113+
var result = false;
114+
var errored = false;
115+
116+
var s = function(bool) {
117+
cancellations = cancellations + 1;
118+
result = result || bool;
119+
120+
if (cancellations === cancelers.length && !errored) {
121+
try {
122+
success(result);
123+
} catch (e) {
124+
error(e);
125+
}
126+
}
127+
};
128+
129+
var f = function(err) {
130+
if (!errored) {
131+
errored = true;
132+
error(err);
133+
}
134+
};
135+
136+
for (var i = 0; i < cancelers.length; i++) {
137+
cancelers[i](e)(s, f);
138+
}
139+
140+
return nonCanceler;
106141
};
107-
})({})(affs);
142+
};
108143

109144
try {
110-
success({});
145+
success(canceler);
111146
} catch(e) {
112147
error(e);
113148
}

src/Control/Monad/Aff.purs

+6-4
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,12 @@ finally aff1 aff2 = do
122122
forkAff :: forall e a. Aff e a -> Aff e (Canceler e)
123123
forkAff aff = runFn2 _forkAff nonCanceler aff
124124

125-
-- | Forks many asynchronous computation at once, ignoring the results.
125+
-- | Forks many asynchronous computation in a synchronous manner while being
126+
-- | stack-safe up to the selected Foldable instance.
126127
-- |
127-
-- | This function is stack-safe up to the selected Foldable instance.
128-
forkAll :: forall f e a. (Foldable f) => f (Aff e a) -> Aff e Unit
128+
-- | Returns a canceler that can be used to attempt cancellation of all
129+
-- | forked computations.
130+
forkAll :: forall f e a. (Foldable f) => f (Aff e a) -> Aff e (Canceler e)
129131
forkAll affs = runFn3 _forkAll nonCanceler foldl affs
130132

131133
-- | Promotes any error to the value level of the asynchronous monad.
@@ -215,7 +217,7 @@ foreign import _unsafeInterleaveAff :: forall e1 e2 a. Aff e1 a -> Aff e2 a
215217

216218
foreign import _forkAff :: forall e a. Fn2 (Canceler e) (Aff e a) (Aff e (Canceler e))
217219

218-
foreign import _forkAll :: forall f e a b. Fn3 (Canceler e) ((b -> a -> b) -> b -> f a -> b) (f (Aff e a)) (Aff e Unit)
220+
foreign import _forkAll :: forall f e a b. Fn3 (Canceler e) ((b -> a -> b) -> b -> f a -> b) (f (Aff e a)) (Aff e (Canceler e))
219221

220222
foreign import _makeAff :: forall e a. ((Error -> Eff e Unit) -> (a -> Eff e Unit) -> Eff e (Canceler e)) -> Aff e a
221223

test/Test/Main.purs

+11-4
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ module Test.Main where
22

33
import Prelude
44

5-
import Control.Monad.Aff (Aff(), runAff, later', forkAll)
5+
import Control.Monad.Aff (Aff(), runAff, later', forkAll, cancel)
66
import Control.Monad.Aff.AVar (AVAR(), makeVar', modifyVar, takeVar)
77
import Control.Monad.Cont.Class (callCC)
88
import Control.Monad.Eff (Eff())
99
import Control.Monad.Eff.Class (liftEff)
1010
import Control.Monad.Eff.Console (CONSOLE(), log, print)
11-
import Control.Monad.Eff.Exception (EXCEPTION(), throwException)
11+
import Control.Monad.Eff.Exception (EXCEPTION(), throwException, error)
1212
import Control.Monad.Rec.Class (tailRecM)
1313

14-
import Data.Array ((..))
14+
import Data.Array (replicate)
1515
import Data.Either (Either(..))
1616

1717
loop :: forall eff. Int -> Aff (console :: CONSOLE | eff) Unit
@@ -25,10 +25,16 @@ loop n = tailRecM go n
2525
all :: forall eff. Int -> Aff (console :: CONSOLE, avar :: AVAR | eff) Unit
2626
all n = do
2727
var <- makeVar' 0
28-
forkAll $ map (\_ -> modifyVar (+ 1) var) (1 .. n)
28+
forkAll $ replicate n (modifyVar (+ 1) var)
2929
count <- takeVar var
3030
liftEff $ log ("Forked " <> show count)
3131

32+
cancelAll :: forall eff. Int -> Aff (console :: CONSOLE, avar :: AVAR | eff) Unit
33+
cancelAll n = do
34+
canceler <- forkAll $ replicate n (later' 100000 (liftEff $ log "oops"))
35+
canceled <- cancel canceler (error "bye")
36+
liftEff $ log ("Cancelled all: " <> show canceled)
37+
3238
delay :: forall eff. Int -> Aff eff Unit
3339
delay n = callCC \cont ->
3440
later' n (cont unit)
@@ -40,3 +46,4 @@ main = runAff throwException (const (pure unit)) $ do
4046
liftEff $ log "post-delay"
4147
loop 1000000
4248
all 100000
49+
cancelAll 100000

0 commit comments

Comments
 (0)