Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's causing a deadlock? [duplicate]

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.

like image 207
Mario Vernari Avatar asked Oct 17 '22 01:10

Mario Vernari


1 Answers

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:

  1. Thread executing Worker() is blocked by tcs.SetResult(null) call.

  2. Thread executing Stop() is blocked by _thread.Join() call.

like image 67
CodeFuller Avatar answered Oct 21 '22 07:10

CodeFuller