Skip to content
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

Avoid internal exception on timeout of AsyncSemaphore request that races with disposal #924

Merged
merged 1 commit into from
Sep 28, 2021

Conversation

AArnott
Copy link
Member

@AArnott AArnott commented Sep 28, 2021

The repro is a very particularly timed race between a timer thread and disposal on another thread. As such, no test can reliably repro it except with careful scheduling under a debugger.

The scenario

  1. A contested enter request is made of the AsyncSemaphore that includes a timeout.
  2. The timeout expires and CancellationHandler is invoked. The WaiterInfo.Trigger.TrySetCanceled call succeeds. This thread is now suspended, before it enters the lock on AsyncSemaphore.syncObject.
  3. Another thread calls AsyncSemaphore.Dispose(), which runs to completion.
  4. The timeout thread now resumes, acquires the lock and tries to remove its WaiterInfo.Node from the LinkedList, but the LinkedList was already cleared in Dispose, so an exception is thrown.

The fix is simply to clear the WaiterInfo.Node property when that node no longer belongs to it. This causes the racing CancellationHandler to skip the code to remove and recycle the node.

Fixes #906

…ces with disposal

The repro is a very particularly timed race between a timer thread and disposal on another thread. As such, no test can reliably repro it except with careful scheduling under a debugger.

## The scenario

1. A contested enter request is made of the `AsyncSemaphore` that includes a timeout.
1. The timeout expires and `CancellationHandler` is invoked. The `WaiterInfo.Trigger.TrySetCanceled` call succeeds. This thread is now suspended, before it enters the lock on `AsyncSemaphore.syncObject`.
1. Another thread calls `AsyncSemaphore.Dispose()`, which runs to completion.
1. The timeout thread now resumes, acquires the lock and tries to remove its `WaiterInfo.Node` from the `LinkedList`, but the `LinkedList` was already cleared in `Dispose`, so an exception is thrown.

The fix is simply to clear the `WaiterInfo.Node` property when that node no longer belongs to it. This causes the racing `CancellationHandler` to skip the code to remove and recycle the node.

Fixes microsoft#906
@AArnott AArnott added this to the v17.0 milestone Sep 28, 2021
@AArnott AArnott self-assigned this Sep 28, 2021
Copy link
Member

@lifengl lifengl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good to me.

@AArnott AArnott merged commit 9708b8b into microsoft:v17.0 Sep 28, 2021
@AArnott AArnott deleted the fix906 branch September 28, 2021 18:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants