Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this .NET 6.0 async code not returning

The following code never returns for me and I don't understand why. It gets stuck on var s = await and never continues. What exactly am I doing wrong? I am running this from VS2022 and it is compiled for .NET 6.0.

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
cts.Token.Register(() => Console.WriteLine("cancelled"));
Console.WriteLine(DateTime.Now);
var s = await Console.In.ReadLineAsync().WaitAsync(cts.Token);
Console.WriteLine(DateTime.Now);
Environment.Exit(0);
like image 263
user17983115 Avatar asked Oct 16 '25 10:10

user17983115


2 Answers

Reading from the console with a timeout is surprisingly hard and async in Console.In is all pretend :)

You may notice that Console.In on Windows is System.IO.SyncTextReader and

ReadLineAsync, and even .NET7's ReadLineAsync(CancellationToken cancellationToken), are not that cancellable (from source):

public override Task<string?> ReadLineAsync()
{
   return Task.FromResult(ReadLine());
}

public override ValueTask<string?> ReadLineAsync(CancellationToken cancellationToken)
{
   return cancellationToken.IsCancellationRequested ?
      ValueTask.FromCanceled<string?>(cancellationToken) :
      new ValueTask<string?>(ReadLine());
}
like image 177
tymtam Avatar answered Oct 19 '25 11:10

tymtam


I see that there are solid answers for the questions of Why is this .NET 6.0 async code not returning and what am I doing wrong (nothing). That being the case, I had to wonder what it would take to fulfill on the original outcome. In the process of reproducing the original issue I came up with a method that conceivably might be useful even though it doesn't directly answer the question as asked.

I would also point out that even if the original code had succeeded, the user only has 2 seconds to complete the entire line. There's no recognition of typing effort and if they miss the deadline to ENTER by even 1 tick then it's all gone. So this method offers the added benefit of restarting the timeout interval every time a key is pressed.

async Task<string> ReadLineWithTimeout(TimeSpan timeout)
{
    List<char> currentLine = new List<char>();
    var wdtStartOrRestart = DateTime.Now;

    while (true)
    {
        while (Console.KeyAvailable)
        {
            wdtStartOrRestart = DateTime.Now;
            ConsoleKeyInfo keyInfo = Console.ReadKey(intercept: true);
            switch (keyInfo.Key)
            {
                case ConsoleKey.Enter:
                    Console.WriteLine();
                    goto breakFromSwitch;
                case ConsoleKey.Backspace:
                    Console.Write("\b \b");
                    var removeIndex = currentLine.Count - 1;
                    if(removeIndex != -1)
                    {
                        currentLine.RemoveAt(removeIndex);
                    }
                    break;
                case ConsoleKey.LeftArrow:
                case ConsoleKey.RightArrow:
                    // Handling these is more than we're taking on right now.
                    break;
                default:
                    Console.Write(keyInfo.KeyChar);
                    currentLine.Add(keyInfo.KeyChar);
                    break;
            }
        }
        await Task.Delay(1);
        if(DateTime.Now.Subtract(wdtStartOrRestart) > timeout)
        {
            throw new TaskCanceledException();
        }
    }
    breakFromSwitch:
        return string.Join(String.Empty, currentLine);
} 

Net Core 6 Test

var timeout = TimeSpan.FromSeconds(5);

while (true)
{
    Console.WriteLine();
    Console.WriteLine(DateTime.Now);
    try
    {
        Console.WriteLine(await ReadLineWithTimeout(timeout) + "(ECHO)");
    }
    catch
    {
        Console.WriteLine("Cancelled");
    }
    Console.WriteLine(DateTime.Now);
}

console output

like image 27
IVSoftware Avatar answered Oct 19 '25 12:10

IVSoftware



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!