-
Notifications
You must be signed in to change notification settings - Fork 522
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
unsafeToFuture fails to raise a second InterruptedException if the exception is handled and retried #4055
Comments
It seems there are multiple things going on here. The essential thing is that when a fatal exception is detected (which There are incorrect things I see in the behavior of CE with the code above:
These should be fixed, but fixing these won't make the code example "work", at least I don't think so: the idea is, that if there is a fatal exception, the most we can hope for is a clean shutdown. What would you consider "correct behavior" for this code example? |
So the tricky thing here is that Of course, that's not what you want here since you're trying to run new things after we torpedo the runtime. The answer is probably for you to not use |
Hmm, but maybe we can fix this. We already made some changes so that when the global runtime shuts down, it removes itself so that a new one may be installed. Not entirely sure why that's not working in this instance. |
It's working. The second iteration of the while loop executes the IO on a different IORuntime. But then the fatal failure handling code doesn't entirely run, because |
As per @durban comment creating the runtime on every iteration still causes the second iteration to not throw an exception outside of the IO. while(true){
val (compute,_) = IORuntime.createWorkStealingComputeThreadPool()
val (blocking,_) = IORuntime.createDefaultBlockingExecutionContext()
val (scheduler,_) = IORuntime.createDefaultScheduler()
val ioRuntime = IORuntime.builder().set compute(compute, ()=>()).setBlocking(blocking, ()=>()).setScheduler(scheduler, ()=>()).build
val f = IO{
throw new InterruptedException("Test")
}.attempt.unsafeToFuture()(ioRuntime)
try{
Await.result(f, Duration.Inf)
} catch {
case t:ExecutionException => println(s"Underlyng exception ${t.getCause}")
}
} To give some context to the implementation we are migrating from cats effect 2 to 3. Our current implementation runs the IO in separate threads which are managed via JMX so on an interrupted exception from the IO is caught in the thread, the interrupt reset and then continues. |
The code below catches a ExecutionException which wraps the Interrupted exception on the first iteration. On the second iteration no ExecutionException is thrown and the main thread blocks on the Await.
I think the issue is in
IOFiber.onFatalFailure
specifically the lineif (IORuntime.globalFatalFailureHandled.compareAndSet(false, true))
On the first iteration
globalFatalFailureHandled
is initialised to false on the second iteration its now true so the codeblock is never executed and the exception isn't raised.
If that is the case would adding a function like
resetFatalFailureHandled
be added to IORuntime to reset it if the future returned fromunsafeToFuture
has its ExecutionException handled and retried in a loop?The text was updated successfully, but these errors were encountered: