-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Fix race condition with cancellation tokens in Read/Flush operations #59090
Conversation
- There's a tight race where UnsafeRegister can fire inline and then ReadAsync never completes. This is because the cancellation token property has not been assigned yet so the callback noops. This change checks the result of the UnsafeRegister operation to see if ran synchronously and throws if it did. We also move any state transitions to after these checks to make sure the PipeAwaitable state doesn't change before throwing.
@davidfowl wouldn't it be a problem that the completion data isn't being zeroed out? Also the Pipe seem to use the completion data to schedule the next read / write. Here runtime/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeAwaitable.cs Lines 139 to 150 in 57bfe47
And here the next runtime/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/Pipe.cs Lines 1114 to 1122 in 57bfe47
And it seems that runtime/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/Pipe.cs Lines 783 to 806 in 57bfe47
This might as well be the desired behavior, I'm just pointing it out :) |
In the cases where the PipeAwaitable returns no CompletionData it's a noop schedule. That's by design. It could be optimized by making these methods return a bool but this is how it has always worked. |
This actually is my point. In case of synchronous cancellation callback, the completion data will be defaulted (but not reset). If it was asynchronous, it could contain actual data. |
That's the case that's being handled by this PR. It will run the callback, result in a noop, then throw synchronously from ReadAsync/FlushAsync (aka BeginOperation) |
src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeAwaitable.cs
Show resolved
Hide resolved
src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeAwaitable.cs
Show resolved
Hide resolved
src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeAwaitable.cs
Show resolved
Hide resolved
src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeAwaitable.cs
Show resolved
Hide resolved
@davidfowl Will you be backporting this to 6 once it's in? |
6, 5, 3 yes |
/backport to release/6.0 |
Started backporting to release/6.0: https://github.com/dotnet/runtime/actions/runs/1239603000 |
/backport to release/6.0 |
Fixes #58909
cc @zlatanov
PS: We conveniently added an overload that takes the cancellation token in .NET 6 but I didn't try to use it here.