diff options
author | Alexander Köplinger <alex.koeplinger@outlook.com> | 2018-06-23 04:56:53 +0300 |
---|---|---|
committer | Alexander Köplinger <alex.koeplinger@outlook.com> | 2018-06-25 13:25:01 +0300 |
commit | 2d4301a0d697ab0178044bbcd37afd6b59b43a6d (patch) | |
tree | 1aec244519832c56aab9d02578c3fa981f9587f8 | |
parent | b9709fa283d6f8ddbe5c566cb328bf0cf7e61bf1 (diff) |
Fix race in TaskCancelWaitTestCases.TaskCancelWait1() (#30615)
When `CancelChildren` is enabled (currently only in the `TaskCancelWait1` test)
we're hitting a race in `TaskCancelWaitTest.CreateTask()`:
1. Let's assume Task A is processing `node`
2. Task A spawns a new Task B for processing `node_1`
3. Task B starts and signals the `_countdownEvent`
4. `RealRun()` on the main thread exits the wait on `_countdownEvent`
5. `RealRun()` calls `Verify()` which loops through all the nodes and calls `VerifyCancel()`
6. We get an assertion because `CancellationToken.IsCancellationRequested` is false
7. Task A continues and sets the CancellationToken, but it is already too late by this point
To fix this race we wait on the task in `RealRun()` before proceeding to
the verification step.
This uncovered another issue:
Since `_countdownEvent` is accessed by multiple threads we have another
race between checking `_countdownEvent.IsSet` and `_countdownEvent.Signal()`.
This caused a `System.InvalidOperationException: Invalid attempt made to decrement the event's count below zero.`
Fixed it by locking access to the `_countdownEvent`.
Fixes https://github.com/dotnet/corefx/issues/20457
Fixes https://github.com/mono/mono/issues/6920
(cherry picked from commit 69cf791e365c2be6f919929c5ae521b2e070aa5d)
-rw-r--r-- | src/System.Threading.Tasks/tests/Task/TaskCancelWaitTest.cs | 18 |
1 files changed, 13 insertions, 5 deletions
diff --git a/src/System.Threading.Tasks/tests/Task/TaskCancelWaitTest.cs b/src/System.Threading.Tasks/tests/Task/TaskCancelWaitTest.cs index 5d178dc3c5..5abffea66f 100644 --- a/src/System.Threading.Tasks/tests/Task/TaskCancelWaitTest.cs +++ b/src/System.Threading.Tasks/tests/Task/TaskCancelWaitTest.cs @@ -70,6 +70,7 @@ namespace System.Threading.Tasks.Tests.CancelWait { case API.Cancel: _taskTree.CancellationTokenSource.Cancel(); + _taskTree.Task.Wait(); break; case API.Wait: @@ -131,8 +132,11 @@ namespace System.Threading.Tasks.Tests.CancelWait if (current.IsLeaf) { - if (!_countdownEvent.IsSet) - _countdownEvent.Signal(); + lock (_countdownEvent) + { + if (!_countdownEvent.IsSet) + _countdownEvent.Signal(); + } } else { @@ -162,10 +166,13 @@ namespace System.Threading.Tasks.Tests.CancelWait } finally { - // stop the tree creation and let the main thread proceed - if (!_countdownEvent.IsSet) + lock (_countdownEvent) { - _countdownEvent.Signal(_countdownEvent.CurrentCount); + // stop the tree creation and let the main thread proceed + if (!_countdownEvent.IsSet) + { + _countdownEvent.Signal(_countdownEvent.CurrentCount); + } } } } @@ -208,6 +215,7 @@ namespace System.Threading.Tasks.Tests.CancelWait VerifyCancel(current); VerifyResult(current); }); + Assert.Null(_caughtException); break; //root task was calling wait |