Fix #4627 resource early cancelation#4630
Conversation
Adds uncancelable wrapper to evaluation of resource so that cancelation can only occur at reasonable points.
7fa5573 to
8da0407
Compare
|
I now realize my suggested "obvious fix" is completely incorrect. But I don't think this one's correct either. For example with this branch, this test fails: real("eval - uncancelable continuation") {
def res(d: Deferred[IO, Unit]) = Resource.make(IO.pure(42))(_ => IO.unit).flatMap { _ =>
Resource.eval(IO.uncancelable { _ => d.complete(()).as(99) })
}
val t = for {
d <- IO.deferred[Unit]
ctr <- IO.ref(0)
fib <- IO.uncancelable { poll =>
poll(res(d).allocatedCase).flatMap { _ =>
ctr.update(_ + 1)
}
}.start
_ <- d.get
_ <- fib.cancel
c <- ctr.get
_ <- IO { assertEquals(c, 1) }
} yield ()
t.replicateA_(1000)
}Unless I'm missing something, this is just the original problem with some extra steps, so it probably shouldn't fail |
- polling the entire brackFull unmasks the rest of the evaluation. Instead we have to just mask the acquire and use - in allocatedCase, insert cancelation boundary before checking the next frame instead of unmasking the rest of the evaluation
|
I was suspicious of the I golfed your failing test down to a deterministic one that fails consistently, then narrowed the uses of poll more. I had to do an odd thing ( tickedProperty("combineK - behave like orElse when underlying effect does") {
implicit ticker =>
forAll { (r1: Resource[IO, Int], r2: Resource[IO, Int]) =>
val lhs = r1.orElse(r2)
val rhs = r1 <+> r2
assertEqv(lhs, rhs)
}
}It does not consistently fail, but fails with this seed at least: override def scalaCheckInitialSeed = "EpTk-jCEjNXrCuelFnCg7QRFmJK5gqhF6DEW9EUaFtF="
|
|
Poking at this a bit more, in Resource, Should Poking @armanbilge in case you have any insight, since you were modifying the propagation behavior a few years ago to propagate the same exit case to both sides for |
|
Hmm, |
in Resource, combineK has a bespoke implementation which propagates the final exit case to both inner resource finalizers, while orElse is derived from handleErrorWith, which does not propagate the final exit case, so it's not surprising that the test that it is equivalent to combineK would fail. Removing this test.
Adds
uncancelablewrapper to evaluation of resource so that cancelation can only occur at reasonable points.This fix does a slightly more involved poll wrapping of the calls @durban identified as fix targets in #4627 . Passes both the law tests and a new test for the bug.
I'm a little suspicious of thepollwrapping the allocate case as it aught to break the tailrec, butallocatedCasealreadypollsits continues inside it'sAllocatedcase, so I guess it isn't any worse than existing code.EDIT: oh, I see now, the Allocate case was just suspending the whole thing in
bracketFulland returning anyways. Tail recursion only happens in Bind and Pure.