Skip to content

Commit 1f28d40

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 1f28d40

File tree

1 file changed

+19
-6
lines changed

1 file changed

+19
-6
lines changed

src/Effect/Aff.js

+19-6
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,14 @@ var Aff = function () {
494496

495497
case FINALIZED:
496498
bracketCount--;
499+
500+
if (pendingInterrupt != null && bracketCount === 0) {
501+
if (attempts == null || attempts._1.tag !== RELEASE) {
502+
interrupt = pendingInterrupt;
503+
pendingInterrupt = null;
504+
}
505+
}
506+
497507
status = RETURN;
498508
step = attempt._1;
499509
fail = attempt._2;
@@ -578,10 +588,13 @@ var Aff = function () {
578588
run(runTick);
579589
break;
580590
case PENDING:
581-
if (interrupt === null) {
582-
interrupt = util.left(error);
591+
if (pendingInterrupt === null) {
592+
pendingInterrupt = util.left(error);
583593
}
584594
if (bracketCount === 0) {
595+
if (interrupt === null) {
596+
interrupt = util.left(error);
597+
}
585598
if (status === PENDING) {
586599
attempts = new Aff(CONS, new Aff(FINALIZER, step(error)), attempts, interrupt);
587600
}

0 commit comments

Comments
 (0)