Skip to content

Commit 22a3ad8

Browse files
authored
ParAff applicative should abort on uncaught exception (#135)
* ParAff applicative should abort on uncaught exception * Test parallel/throw with never
1 parent a048d9c commit 22a3ad8

File tree

2 files changed

+49
-21
lines changed

2 files changed

+49
-21
lines changed

src/Control/Monad/Aff.js

+27-20
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ var Aff = function () {
1919
| Async ((Either Error a -> Eff eff Unit) -> Eff eff (Canceler eff))
2020
| forall b. Bind (Aff eff b) (b -> Aff eff a)
2121
| forall b. Bracket (Aff eff b) (BracketConditions eff b) (b -> Aff eff a)
22-
| forall b. Fork Boolean (Aff eff b) ?(Thread eff b -> a)
22+
| forall b. Fork Boolean (Aff eff b) ?(Fiber eff b -> a)
2323
| Sequential (ParAff aff a)
2424
2525
*/
@@ -762,26 +762,33 @@ var Aff = function () {
762762
case APPLY:
763763
lhs = head._1._3;
764764
rhs = head._2._3;
765-
// We can only proceed if both sides have resolved.
766-
if (lhs === EMPTY || rhs === EMPTY) {
767-
return;
768-
}
769-
// If either side resolve with an error, we should continue with
770-
// the first error.
771-
if (util.isLeft(lhs)) {
772-
if (util.isLeft(rhs)) {
773-
if (fail === lhs) {
774-
fail = rhs;
775-
}
776-
} else {
777-
fail = lhs;
778-
}
779-
step = null;
780-
head._3 = fail;
781-
} else if (util.isLeft(rhs)) {
782-
step = null;
783-
fail = rhs;
765+
// If we have a failure we should kill the other side because we
766+
// can't possible yield a result anymore.
767+
if (fail) {
784768
head._3 = fail;
769+
tmp = true;
770+
kid = killId++;
771+
772+
kills[kid] = kill(early, fail === lhs ? head._2 : head._1, function (/* unused */) {
773+
return function () {
774+
delete kills[kid];
775+
if (tmp) {
776+
tmp = false;
777+
} else if (tail === null) {
778+
join(step, null, null);
779+
} else {
780+
join(step, tail._1, tail._2);
781+
}
782+
};
783+
});
784+
785+
if (tmp) {
786+
tmp = false;
787+
return;
788+
}
789+
} else if (lhs === EMPTY || rhs === EMPTY) {
790+
// We can only proceed if both sides have resolved.
791+
return;
785792
} else {
786793
step = util.right(util.fromRight(lhs)(util.fromRight(rhs)));
787794
head._3 = step;

test/Test/Main.purs

+22-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import Control.Monad.Error.Class (throwError, catchError)
1919
import Control.Parallel (parallel, sequential, parTraverse_)
2020
import Data.Array as Array
2121
import Data.Bifunctor (lmap)
22-
import Data.Either (Either(..), isLeft, isRight)
22+
import Data.Either (Either(..), either, isLeft, isRight)
2323
import Data.Foldable (sum)
2424
import Data.Maybe (Maybe(..))
2525
import Data.Monoid (mempty)
@@ -64,6 +64,11 @@ assertEq s a aff = liftEff <<< assertEff s <<< map (eq a) =<< try aff
6464
assert eff. String TestAff eff Boolean TestAff eff Unit
6565
assert s aff = liftEff <<< assertEff s =<< try aff
6666

67+
withTimeout eff a. Milliseconds TestAff eff a TestAff eff a
68+
withTimeout ms aff =
69+
either throwError pure =<< sequential do
70+
parallel (try aff) <|> parallel (delay ms $> Left (error "Timed out"))
71+
6772
test_pure eff. TestEff eff Unit
6873
test_pure = runAssertEq "pure" 42 (pure 42)
6974

@@ -411,6 +416,21 @@ test_parallel = assert "parallel" do
411416
r2 ← joinFiber f1
412417
pure (r1 == "foobar" && r2.a == "foo" && r2.b == "bar")
413418

419+
test_parallel_throw eff. TestAff eff Unit
420+
test_parallel_throw = assert "parallel/throw" $ withTimeout (Milliseconds 100.0) do
421+
ref ← newRef ""
422+
let
423+
action n s = do
424+
delay (Milliseconds n)
425+
modifyRef ref (_ <> s)
426+
pure s
427+
r1 ← try $ sequential $
428+
{ a: _, b: _ }
429+
<$> parallel (action 10.0 "foo" *> throwError (error "Nope"))
430+
<*> parallel never
431+
r2 ← readRef ref
432+
pure (isLeft r1 && r2 == "foo")
433+
414434
test_kill_parallel eff. TestAff eff Unit
415435
test_kill_parallel = assert "kill/parallel" do
416436
ref ← newRef ""
@@ -641,6 +661,7 @@ main = do
641661
test_kill_finalizer_catch
642662
test_kill_finalizer_bracket
643663
test_parallel
664+
test_parallel_throw
644665
test_kill_parallel
645666
test_parallel_alt
646667
test_parallel_alt_throw

0 commit comments

Comments
 (0)