-
Couldn't load subscription status.
- Fork 1.4k
Prevent Interruption While Closing Scopes #4121
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
Conversation
| */ | ||
| if (!state.compareAndSet(oldState, Executing(oldStatus.toFinishing, observers, interrupted))) done(v) | ||
| else openScope.close(v) *> ZIO.done(v) | ||
| else openScope.close(v).uninterruptible *> ZIO.done(v) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if it is interrupted before the openScope.close(v) begins execution?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Maybe we need to use unsafeClose here?
|
@adamgfraser Let's pair on this since I think it will be quite tricky. |
|
@jdegoes That would be great! |
|
|
||
| oldState match { | ||
| case Executing(Status.Suspended(oldStatus, true, _, _, _), observers, interrupted) => | ||
| case Executing(Status.Suspended(oldStatus, true, _, _, _), observers, interrupted) if !oldState.interrupting => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a race condition in if !oldState.interrupting. I think it's ok.
We may want to add a test for this if one doesn't exist, basically that async finalizers during winddown can't be interrupted. Could be tested with a latch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, can you say more about this?
We're getting the interrupting status from the old state so we're working off of one consistent snapshot of the state at a single point in time.
If the interrupting status gets changed to true while we executing then the compareAndSet will return false and we will loop again, at which point interrupting will be true so we won't do anything here.
So we are relying on whatever other fiber changed the interrupting status to true to actually do the interruption since this fiber won't. Is that right?
Can definitely work on adding a test for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kill is called from other fibers, so what happens if the fiber itself changes the state of setInterrupting to false at the same time this check is happening. In this case, this code here (in kill) will make a different decision about what to do based on the changing boolean value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Then state would not be equal to oldState and we would loop and then on the next loop interrupting would be false and we would interrupt. But isn't that the behavior we want? You're definitely right there can be a race between setting the interrupting status and interrupting but I think with this whatever the interrupting status is when the compareAndSet gets evaluated will control which seems correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a test for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, you're right, because you are looking at oldState.interrupting, which will be consistent with the state.
Good with me. 👍
* close scope uninterruptibly * prevent interruption before close begins execution * add test * revert
This reverts commit 7f2d4da.
Resolves #4116.
It looks like the issue here is that we are getting interrupted when attempting to close the scope of the child fiber. The child fiber tries to close its scope after it finishes forking the grandchild but then is externally interrupted. As a result, the finalizers in its scope are never run and so we do not wait for the interruption of the grandchild to return.