command-line-api icon indicating copy to clipboard operation
command-line-api copied to clipboard

Termination handling in .net framework 4.7.2 deadlocks always with Ctrl + C

Open toddstedel opened this issue 4 years ago • 2 comments

ConsoleApp1.zip

This program works in .net core and .net 5. Framework, however, never works when you cancel with Ctrl + C

This is the gist of the program:

static async Task<int> Main(string[] args)
        {
            var thing = new Command("thing");
            thing.Handler = CommandHandler.Create(async (CancellationToken token) =>
            {
                await Task.Delay(TimeSpan.FromSeconds(10), token);
            });

            var root = new RootCommand
            {
                thing
            };

            return await root.InvokeAsync(args);
        }

toddstedel avatar Oct 26 '21 07:10 toddstedel

Ran into this issue and can confirm it's easy to reproduce when using the default Invoke or a CommandLineBuilder with CancelOnProcessTermination. The main thread gets stuck in System.Console.ControlCHooker.Unhook:

mscorlib.dll!System.Console.ControlCHooker.Unhook()
mscorlib.dll!System.Console.CancelKeyPress.remove(System.ConsoleCancelEventHandler value)
System.CommandLine.dll!System.CommandLine.Builder.CommandLineBuilderExtensions.CancelOnProcessTermination.AnonymousMethod__1_0(System.CommandLine.Invocation.InvocationContext context, System.Func<System.CommandLine.Invocation.InvocationContext, System.Threading.Tasks.Task> next)

Here's the native call-stack (Windows 11 Version 23H2 Build 22631.3296)

ntdll.dll!ZwWaitForAlertByThreadId()
[Inline Frame] ntdll.dll!RtlpWaitOnAddressWithTimeout(_RTL_WAIT_ON_ADDRESS_HASH_BUCKET *)
[Inline Frame] ntdll.dll!RtlpWaitOnAddress(volatile void *)
ntdll.dll!RtlpWaitOnCriticalSection(_RTL_CRITICAL_SECTION * CriticalSection, unsigned long OldLockCount)
ntdll.dll!RtlpEnterCriticalSectionContended(_RTL_CRITICAL_SECTION * CriticalSection)
ntdll.dll!RtlEnterCriticalSection(_RTL_CRITICAL_SECTION * CriticalSection)
KernelBase.dll!SetConsoleCtrlHandler(int(*)(unsigned long) HandlerRoutine, int Add)
mscorlib.ni.dll!00007fffc8e54252()
[Managed to Native Transition]	
mscorlib.dll!System.Console.ControlCHooker.Unhook()
mscorlib.dll!System.Console.CancelKeyPress.remove(System.ConsoleCancelEventHandler value)
[Async] System.CommandLine.dll!System.CommandLine.Builder.CommandLineBuilderExtensions.CancelOnProcessTermination.AnonymousMethod__1_0(System.CommandLine.Invocation.InvocationContext context, System.Func<System.CommandLine.Invocation.InvocationContext, System.Threading.Tasks.Task> next)

The CriticalSection being entered is the ConsoleStateLock which is held by the thread that's handling the Ctrl+C, calling the ConsoleCancelEventHandler, calling cts.Cancel(). It's not clear why that thread isn't making progress.

pharring avatar Apr 05 '24 20:04 pharring

Dupe of #832

pharring avatar Apr 05 '24 21:04 pharring