-
Notifications
You must be signed in to change notification settings - Fork 529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rewrote Dispatcher
#3923
Rewrote Dispatcher
#3923
Conversation
Hard for me to tell if this is a legit failure or not, but there's at least one legit bug here somewhere. Got this locally just now:
JVM, 2.12.18, ARM64 (parallel, await = false) |
43a91a1
to
8f0d60c
Compare
Hold on this until we can track down the uncaught supervisor state exceptions. |
I believe I've fixed one of the sources of the uncaught exceptions: #3991 (feel free to merge that branch into this one). The test affected was There remain other sources of the same exception:
|
For the sequential, non-cancelable dispatcher, is Because currently (i.e., in this PR) it nondeterministically behaves either way, based on whether "foobar" in real {
D.use { dispatcher =>
IO.fromFuture(IO {
val (_, cancel) = dispatcher.unsafeToFutureCancelable(IO.never[Unit])
val cancelFut = cancel()
cancelFut
})
}.replicateA_(6).as(ok)
} |
I think the latter is the behavior I would expect. Also I'm chewing on your branch right now. Will merge and then get to work on some of the other bits. |
I fixed the weird parallel IO test to avoid dependence on await/non-await semantics. I still see unhandled exceptions though. It's really difficult for me to reproduce those exceptions on this machine. Also because of parallel specs execution it's very possible the exceptions are coming from somewhere other than those tests, but I'm not sure. Edit: Actually now that I think about it, the nondeterminism in sequential IO.never.uncancelable.start.flatMap(_.cancel) This will nondeterministically hang depending on whether the |
Except in the case of With this dispatcher (note, we're still talking about sequential, cancelable = false, await = true) I think there is a (3): the task starts executing, but This test fails: "foobar2" in real {
D.use { dispatcher =>
IO.ref(0).flatMap { ctr1 =>
IO.ref(0).flatMap { ctr2 =>
IO.fromFuture(IO {
val (_, cancel) = dispatcher.unsafeToFutureCancelable(IO.uncancelable { _ =>
ctr1.update(_ + 1) *> IO.sleep(0.1.second) *> ctr2.update(_ + 1)
})
val cancelFut = cancel()
cancelFut
}).flatMap { _ =>
// if we're here, `cancel()` finished, so
// either the task didn't run at all (i.e.,
// it was cancelled before starting), or
// it ran and already finished completely:
(ctr1.get, ctr2.get).flatMapN { (v1, v2) =>
IO(v1 mustEqual v2)
}
}
}
}
}.replicateA_(10000).as(ok)
} |
Ah, okay I buy that this is a problem then. |
Minor update: I've seen this test fail once, but it's very difficult to get it to fail at all on my laptop. Something about the timing I suspect. I may need some help tracking this one down. |
I think I've fixed it. See #4012 (feel free to merge my branch directly into this PR). The problem is with this comment: // we can use unit as a cancel action here since it must always sequence *after* the task
// thus, the task must complete before the cancel action will be picked up This is not (always) true:
My fix (in 6a9cfa6) is to instead of the synthetic |
|
Magnificent fix @durban! Sorry I had been too busy to get to this. Snagged it now. Thank you! |
Let's call this good. I got the 👍 from @armanbilge just now to merge without waiting for him, since @durban in particular has been all up in this PR's business. If nothing else, this should make the build a lot more stable! |
I have fixed many things. I possibly also made new problems. I think it's more maintainable now, but who am I to say?
I wrote a long comment in the middle which kind of explains the implementation, but the tldr is we're now very explicit about the impure submission queue and its associated race conditions. Those race conditions are fully resolved out by
Worker
, including the cancelation state machine. On the "actually do stuff" side isExecutor
, which is quite simple for the existing modes and quite complex for the new mode:sequentialCancelable
.Speaking of which, I added a third mode because I didn't realize it wasn't already the semantics of the second mode (
sequential
). Whoops. Anyway, we might not want to expose this one since I'm not sure anyone wants it, but I did all the work so here you go. Mostly I wanted to prove that it was possible. And it is… just hard.Oh, I also removed and rewrote a bunch of tests which either weren't right, weren't reliable, or weren't interesting. I added a few new tests covering cases we probably should have always had. I don't think I missed anything, but who can say? Please enjoy.