Hello Cygwin community,

This problem had originally surfaced as hangs during the exit of Make
processes when building the R programming language using Rtools (MSYS)
[1], but a weaker version of the problem can be demonstrated in Cygwin
as well. I think it originates from the incomplete fix in recent commit
57634e7a (Cygwin: console: Use input_mutex in the parent PTY in master
thread).

Open a Cygwin terminal, start cmd in it, start bash from cmd:

User@DESKTOP-PDI66KN ~
$ cmd
Microsoft Windows [Version 10.0.19044.3324]
(c) Microsoft Corporation. All rights reserved.

C:\cygwin64\home\User>bash

User@DESKTOP-PDI66KN ~
$ echo $$
1477

Open another Cygwin terminal and attach a debugger to the child bash
process:

User@DESKTOP-PDI66KN ~
$ gdb --pid=1477

Since this bash process was run by a non-Cygwin process, it shares a
pseudo console with the cmd process, which it will attempt to hand off
on exit. Set a breakpoint on the hand-off function:

(gdb) tb pcon_hand_over_proc
Temporary breakpoint 1 at 0x7fffd9efb05a: pcon_hand_over_proc. (2 locations)
(gdb) c
Continuing.

Tell the child bash process to exit:

Имя@DESKTOP-PDI66KN ~
$ exit
exit

Thread 1 "bash" hit Temporary breakpoint 1.2, 
fhandler_console::pcon_hand_over_proc ()
    at /usr/src/debug/cygwin-3.6.9-1/winsup/cygwin/fhandler/console.cc:1970
1970      if (!inside_pcon)
(gdb) tb CloseHandle
Temporary breakpoint 2 at 0x7ff8002d5060 (2 locations)
(gdb) c
Continuing.

Thread 1 "bash" hit Breakpoint 2.1, 0x00007ff8002d5060 in KERNEL32!CloseHandle 
() from /cygdrive/c/Windows/System32/KERNEL32.DLL
(gdb) bt
#0  0x00007ff8002d5060 in KERNEL32!CloseHandle () from 
/cygdrive/c/Windows/System32/KERNEL32.DLL
#1  0x00007fffd9efb0e1 in fhandler_console::pcon_hand_over_proc ()
    at /usr/src/debug/cygwin-3.6.9-1/winsup/cygwin/fhandler/console.cc:1987
#2  0x00007fffd9fe8709 in __call_exitprocs (code=code@entry=0, d=d@entry=0x0)
    at /usr/src/debug/cygwin-3.6.9-1/newlib/libc/stdlib/__call_atexit.c:130
#3  0x00007fffd9fa7b8e in exit (code=0) at 
/usr/src/debug/cygwin-3.6.9-1/newlib/libc/stdlib/exit.c:60
#4  0x00007fffd9e46a37 in cygwin_exit (n=540) at 
/usr/src/debug/cygwin-3.6.9-1/winsup/cygwin/dcrt0.cc:1238
#5  0x00007fffd9f8dc54 in _sigfe () at sigfe.s:35
#6  0x0000000100401c35 in exit_shell ()
#7  0x00000001004958cf in main ()
(gdb) frame 1
#1  0x00007fffd9efb0e1 in fhandler_console::pcon_hand_over_proc ()
    at /usr/src/debug/cygwin-3.6.9-1/winsup/cygwin/fhandler/console.cc:1987
1987      ForceCloseHandle (parent_pty_input_mutex);

A different thread will try to use this handle after it's closed:

(gdb) rwatch parent_pty_input_mutex
Hardware read watchpoint 3: parent_pty_input_mutex
(gdb) c
Continuing.
[Switching to Thread 3568.0xb68]

Thread 3 "consm" hit Hardware read watchpoint 3: parent_pty_input_mutex

Value = (HANDLE) 0x21c
0x00007fffd9efcdb6 in fhandler_console::cons_master_thread (p=<optimized out>, 
ttyp=0x1a0030000)
    at /usr/src/debug/cygwin-3.6.9-1/winsup/cygwin/fhandler/console.cc:469
469           if (inside_pcon && parent_pty_input_mutex)
(gdb) n
470             WaitForSingleObject (parent_pty_input_mutex, mutex_timeout);
(gdb) bt
#0  fhandler_console::cons_master_thread (p=<optimized out>, ttyp=0x1a0030000)
    at /usr/src/debug/cygwin-3.6.9-1/winsup/cygwin/fhandler/console.cc:470
#1  0x00007fffd9f0221d in cons_master_thread (arg=<optimized out>)
    at /usr/src/debug/cygwin-3.6.9-1/winsup/cygwin/fhandler/console.cc:296
#2  0x00007fffd9e44cd0 in cygthread::callfunc (this=this@entry=0x7fffda035618 
<threads+88>, issimplestub=issimplestub@entry=false)
    at /usr/src/debug/cygwin-3.6.9-1/winsup/cygwin/cygthread.cc:130
#3  0x00007fffd9e451c9 in cygthread::stub (arg=arg@entry=0x7fffda035618 
<threads+88>)
    at /usr/src/debug/cygwin-3.6.9-1/winsup/cygwin/cygthread.cc:173
#4  0x00007fffd9e45cc5 in _cygtls::call2 (this=0x2b7ce00, func=0x7fffd9e45160 
<cygthread::stub(void*)>,
    arg=0x7fffda035618 <threads+88>, buf=buf@entry=0x2b7cd20) at 
/usr/src/debug/cygwin-3.6.9-1/winsup/cygwin/cygtls.cc:41
#5  0x00007fffd9e45d7a in _cygtls::call (func=<optimized out>, arg=<optimized 
out>)
    at /usr/src/debug/cygwin-3.6.9-1/winsup/cygwin/cygtls.cc:28
#6  0x00007ff8002c7614 in KERNEL32!BaseThreadInitThunk () from 
/cygdrive/c/Windows/System32/KERNEL32.DLL
#7  0x00007ff8013e26b1 in ntdll!RtlUserThreadStart () from 
/cygdrive/c/Windows/SYSTEM32/ntdll.dll
#8  0x0000000000000000 in ?? ()

At this point, there is no handle 0x21c:

C:\Users\Имя\Handle>handle64.exe -p 3568

Nthandle v5.0 - Handle viewer
Copyright (C) 1997-2022 Mark Russinovich
Sysinternals - www.sysinternals.com

   94: Section       
\BaseNamedObjects\cygwin1S5-e022582115c10879\e022582115c10879-cons0x160232.0
   F4: Section       
\BaseNamedObjects\cygwin1S5-e022582115c10879\S-1-5-21-3144901607-635644686-4150892844-1001.1
   FC: Section       
\BaseNamedObjects\cygwin1S5-e022582115c10879\S-1-5-21-3144901607-635644686-4150892844-1001.1
  10C: Section       \BaseNamedObjects\cygwin1S5-e022582115c10879\shared.5
  144: File          C:\cygwin64\home\???
  164: Section       \BaseNamedObjects\cygwin1S5-e022582115c10879\cygpid.1477
  2A8: File          C:\cygwin64\home\???
  344: Section       
\Sessions\1\BaseNamedObjects\C:*ProgramData*Microsoft*Windows*Caches*cversions.2.ro
  348: Section       
\Sessions\1\BaseNamedObjects\C:*ProgramData*Microsoft*Windows*Caches*{6AF0698E-D558-4F6E-9B3C-3716689AF493}.2.ver0x0000000000000001.db
  34C: Section       
\Sessions\1\BaseNamedObjects\C:*ProgramData*Microsoft*Windows*Caches*cversions.2.ro
  350: Section       
\Sessions\1\BaseNamedObjects\C:*ProgramData*Microsoft*Windows*Caches*{DDF571F2-BE98-426D-8288-1A9A39C3FDA2}.2.ver0x0000000000000001.db

As described in the MSYS bug report, since R is a non-Cygwin process
but Make is, during package the build process it's possible for them to
form process chains (bash -> R -> make -> R -> possibly more make)
sharing a pseudo console, and sometimes (for me, only on the slow
Windows 10 VM that lives on an HDD) the expired handle number gets
reused for a file handle, which causes WaitForSingleObject() above to
wait forever. I'm not sure how easy this is to reproduce in a pure
Cygwin environment.

The following band-aid patch stops the MSYS hangs for me:

--- a/winsup/cygwin/fhandler/console.cc
+++ b/winsup/cygwin/fhandler/console.cc
@@ -1984,7 +1984,7 @@ fhandler_console::pcon_hand_over_proc (void)
   else
     system_printf("Acquiring pcon_ho_mutex failed.");
   ReleaseMutex (mtx);
-  ForceCloseHandle (parent_pty_input_mutex);
+  ForceCloseHandle (InterlockedExchangePointer (&parent_pty_input_mutex, 
NULL));
 }
 
 bool

I think it doesn't close the race window completely. Maybe setting
InterlockedExchange8(&inside_pcon, false) before closing the
parent_pty_input_mutex will help? I'm afraid that the threads that read
these two variables will need to adjusted as well.

-- 
Best regards,
Ivan

[1]
https://stat.ethz.ch/pipermail/r-devel/2026-May/084541.html
https://github.com/msys2/msys2-runtime/issues/338

Attachment: cygcheck.out
Description: Binary data

-- 
Problem reports:      https://cygwin.com/problems.html
FAQ:                  https://cygwin.com/faq/
Documentation:        https://cygwin.com/docs.html
Unsubscribe info:     https://cygwin.com/ml/#unsubscribe-simple

Reply via email to