Consider the following .Net 6 console program:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
var semaphore = new SemaphoreSlim(1, 1);
var cts = new CancellationTokenSource();
var tasks = Enumerable.Range(0, 10).Select(WaitingTask);
var t = Task.Run(async () =>
{
await Task.WhenAll(tasks);
Console.WriteLine("Tasks complete");
});
await Task.Delay(500);
Console.WriteLine("Press any key to Cancel waiting tasks, then immediately release semaphore");
Console.ReadKey();
cts.Cancel();
semaphore.Release();
await t;
async Task WaitingTask(int i)
{
try
{
Console.WriteLine($"{i} Waiting");
await semaphore.WaitAsync(cts.Token);
Console.WriteLine($"{i} Aquired");
await Task.Delay(50);
Console.WriteLine($"{i} Complete");
}
catch (OperationCanceledException)
{
Console.WriteLine($"{i} Cancelled");
}
}
It creates 10 tasks that try to acquire a lock on a semaphore that only allows 1 entry at a time.
After the first task has reported completion, and the other nine tasks report that they are waiting for the semaphore, I wish to cancel the token passed to the waiting tasks, and then immediately release the lock on the semaphore.
Expected: the remaining 9 tasks throw and handle OperationCanceledException, and report "Canceled".
Actual: 8 of the remaining tasks do this, but 1 of them will sucessfully enter the semaphore and complete normally. I.e. you cannot reliably cancel calls to WaitAsync(CancellationToken)
Commenting out the line semaphore.Release(); results in all 9 tasks reporting canceled as expected.
I'm assuming a race condition somewhere but my question is: Am I wrong to expect my stated behaviour, and if so why?
Many thanks.
Example output:
Hello, World!
0 Waiting
0 Aquired
1 Waiting
2 Waiting
3 Waiting
4 Waiting
5 Waiting
6 Waiting
7 Waiting
8 Waiting
9 Waiting
0 Complete
Press any key to Cancel waiting tasks, then immediately release semaphore
1 Aquired
4 Cancelled
8 Cancelled
5 Cancelled
9 Cancelled
6 Cancelled
3 Cancelled
2 Cancelled
7 Cancelled
1 Complete
Tasks complete