Skip to content

Commit 5450756

Browse files
authored
Fix finalization after failure (#161)
* Fix finalization after failure It appears to have only worked coincidentally before. When the interpreter enters into a finalization block, it puts a `Finalized` node onto the attempt stack with the current `step`. However if there was a failure, then step would be null. It "worked" before because there was stale state, and `fail` could be preserved through finalization. This bug was exposed by using `catchError` within the finalization block, which resets the failure state to null. Thus when it resumes after finalization, both `step` and `fail` are null, which is an invalid state. The solution is to preserve `fail` in addition to `step` in a `Finalized` node. * Cleanup potentially stale state
1 parent c02ae17 commit 5450756

File tree

2 files changed

+15
-4
lines changed

2 files changed

+15
-4
lines changed

src/Effect/Aff.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,11 @@ var Aff = function () {
261261
// the provided callback in `makeAff` more than once, but it may also be an
262262
// async effect resuming after the fiber was already cancelled.
263263
function run(localRunTick) {
264-
var tmp, result, attempt, canceler;
264+
var tmp, result, attempt;
265265
while (true) {
266266
tmp = null;
267267
result = null;
268268
attempt = null;
269-
canceler = null;
270269

271270
switch (status) {
272271
case STEP_BIND:
@@ -460,7 +459,7 @@ var Aff = function () {
460459
// because it should not be cancelled.
461460
case RELEASE:
462461
bracketCount++;
463-
attempts = new Aff(CONS, new Aff(FINALIZED, step), attempts, interrupt);
462+
attempts = new Aff(CONS, new Aff(FINALIZED, step, fail), attempts, interrupt);
464463
status = CONTINUE;
465464
// It has only been killed if the interrupt status has changed
466465
// since we enqueued the item.
@@ -471,11 +470,12 @@ var Aff = function () {
471470
} else {
472471
step = attempt._1.completed(util.fromRight(step))(attempt._2);
473472
}
473+
fail = null;
474474
break;
475475

476476
case FINALIZER:
477477
bracketCount++;
478-
attempts = new Aff(CONS, new Aff(FINALIZED, step), attempts, interrupt);
478+
attempts = new Aff(CONS, new Aff(FINALIZED, step, fail), attempts, interrupt);
479479
status = CONTINUE;
480480
step = attempt._1;
481481
break;
@@ -484,6 +484,7 @@ var Aff = function () {
484484
bracketCount--;
485485
status = RETURN;
486486
step = attempt._1;
487+
fail = attempt._2;
487488
break;
488489
}
489490
}

test/Test/Main.purs

+10
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,15 @@ test_regression_par_apply_async_canceler = assert "regression/par-apply-async-ca
651651
val <- readRef ref
652652
pure (val == "throwdone" && message err == "Nope.")
653653

654+
test_regression_bracket_catch_cleanup Aff Unit
655+
test_regression_bracket_catch_cleanup = assert "regression/bracket-catch-cleanup" do
656+
res :: Either Error Unit
657+
try $ bracket
658+
(pure unit)
659+
(\_ catchError (pure unit) (const (pure unit)))
660+
(\_ throwError (error "Nope."))
661+
pure $ lmap message res == Left "Nope."
662+
654663
main Effect Unit
655664
main = do
656665
test_pure
@@ -697,3 +706,4 @@ main = do
697706
test_parallel_stack
698707
test_regression_return_fork
699708
test_regression_par_apply_async_canceler
709+
test_regression_bracket_catch_cleanup

0 commit comments

Comments
 (0)