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, |
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.