Skip to content

Commit f0cf996

Browse files
author
Felix Schlitter
committed
Fix #174
Fix #174 by guaranteeing that iff a bracket body handler completes uninterrupted (e.g. if masked, or simply no interrupts,) it *will* call the 'completed' handler of the surrounding bracket. This effectively allows writing code like this: ```purescript bracket (liftEffect $ Ref.new Nothing) { completed: \a ref -> liftEffect $ Ref.write (Just a) ref , killed: \_ _ -> ... , failed: \_ _ -> ... } \_ -> action ``` We can effectively reason about the fact that, should `action` ever complete, we *will* run the completion handler. Currently, if `action` is masked, and thus guaranteed to succeed, we invoke the 'killed' handler, thus denying us the possibily of obtaining 'a', even if it was produced.
1 parent bb70d19 commit f0cf996

File tree

1 file changed

+25
-7
lines changed

1 file changed

+25
-7
lines changed

src/Effect/Aff.js

+25-7
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ var Aff = function () {
238238
var step = aff; // Successful step
239239
var fail = null; // Failure step
240240
var interrupt = null; // Asynchronous interrupt
241+
var pendingInterrupt = null; // Pending, asynchronous interrupt
241242

242243
// Stack of continuations for the current fiber.
243244
var bhead = null;
@@ -455,10 +456,10 @@ var Aff = function () {
455456
result = util.fromRight(step);
456457
// We need to enqueue the Release with the same interrupt
457458
// status as the Bracket that is initiating it.
458-
attempts = new Aff(CONS, new Aff(RELEASE, attempt._2, result), attempts, tmp);
459+
attempts = new Aff(CONS, new Aff(RELEASE, attempt._2, result, pendingInterrupt), attempts, tmp);
459460
// We should only coninue as long as the interrupt status has not changed or
460461
// we are currently within a non-interruptable finalizer.
461-
if (interrupt === tmp || bracketCount > 0) {
462+
if ((!pendingInterrupt && interrupt === tmp) || bracketCount > 0) {
462463
status = CONTINUE;
463464
step = attempt._3(result);
464465
}
@@ -470,12 +471,13 @@ var Aff = function () {
470471
case RELEASE:
471472
attempts = new Aff(CONS, new Aff(FINALIZED, step, fail), attempts, interrupt);
472473
status = CONTINUE;
474+
473475
// It has only been killed if the interrupt status has changed
474476
// since we enqueued the item, and the bracket count is 0. If the
475477
// bracket count is non-zero then we are in a masked state so it's
476478
// impossible to be killed.
477-
if (interrupt && interrupt !== tmp && bracketCount === 0) {
478-
step = attempt._1.killed(util.fromLeft(interrupt))(attempt._2);
479+
if (((interrupt && interrupt !== tmp) || attempt._3) && bracketCount === 0) {
480+
step = attempt._1.killed(util.fromLeft(interrupt || pendingInterrupt))(attempt._2);
479481
} else if (fail) {
480482
step = attempt._1.failed(util.fromLeft(fail))(attempt._2);
481483
} else {
@@ -494,6 +496,18 @@ var Aff = function () {
494496

495497
case FINALIZED:
496498
bracketCount--;
499+
500+
// If we've got a pending interrupt and are no longer masked,
501+
// check the stack if we are about to run the release handler of a
502+
// containing bracket. If that is the case, postpone setting the
503+
// interrupt until after the release handler ran.
504+
if (pendingInterrupt != null && bracketCount === 0) {
505+
if (attempts == null || attempts._1.tag !== RELEASE) {
506+
interrupt = pendingInterrupt;
507+
pendingInterrupt = null;
508+
}
509+
}
510+
497511
status = RETURN;
498512
step = attempt._1;
499513
fail = attempt._2;
@@ -578,17 +592,21 @@ var Aff = function () {
578592
run(runTick);
579593
break;
580594
case PENDING:
581-
if (interrupt === null) {
582-
interrupt = util.left(error);
583-
}
584595
if (bracketCount === 0) {
596+
if (interrupt === null) {
597+
interrupt = util.left(error);
598+
}
585599
if (status === PENDING) {
586600
attempts = new Aff(CONS, new Aff(FINALIZER, step(error)), attempts, interrupt);
587601
}
588602
status = RETURN;
589603
step = null;
590604
fail = null;
591605
run(++runTick);
606+
} else {
607+
if (pendingInterrupt === null) {
608+
pendingInterrupt = util.left(error);
609+
}
592610
}
593611
break;
594612
default:

0 commit comments

Comments
 (0)