I'm facing a deadlock-issue in a piece of code of mine. Thankfully, I've been able to reproduce the problem in the below example. Run as a normal .Net Core 2.0 Console application.
class Class2
{
static void Main(string[] args)
{
Task.Run(MainAsync);
Console.WriteLine("Press any key...");
Console.ReadKey();
}
static async Task MainAsync()
{
await StartAsync();
//await Task.Delay(1); //a little delay makes it working
Stop();
}
static async Task StartAsync()
{
var tcs = new TaskCompletionSource<object>();
StartCore(tcs);
await tcs.Task;
}
static void StartCore(TaskCompletionSource<object> tcs)
{
_cts = new CancellationTokenSource();
_thread = new Thread(Worker);
_thread.Start(tcs);
}
static Thread _thread;
static CancellationTokenSource _cts;
static void Worker(object state)
{
Console.WriteLine("entering worker");
Thread.Sleep(100); //some work
var tcs = (TaskCompletionSource<object>)state;
tcs.SetResult(null);
Console.WriteLine("entering loop");
while (_cts.IsCancellationRequested == false)
{
Thread.Sleep(100); //some work
}
Console.WriteLine("exiting worker");
}
static void Stop()
{
Console.WriteLine("entering stop");
_cts.Cancel();
_thread.Join();
Console.WriteLine("exiting stop");
}
}
What I'd expect is the complete sequence as follows:
Press any key...
entering worker
entering loop
entering stop
exiting worker
exiting stop
However, the actual sequence stalls on the Thread.Join
call:
Press any key...
entering worker
entering stop
Finally, if I insert a small delay in the MainAsync
body, everything goes fine.
Why (where) the deadlock happens?
NOTE: in the original code I solved using a SemaphoreSlim
instead of a TaskCompletionSource
, and there's no problems at all. I only would like to understand where the problem is.
tcs.SetResult(null);
call in Worker()
will not return until the underlying task is finished (check this question for details). In you case the task status is WaitingForActivation
that's why you get a deadlock:
Thread executing Worker()
is blocked by tcs.SetResult(null)
call.
Thread executing Stop()
is blocked by _thread.Join()
call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With