After a call to SetCurrentDirectory(), I have seen occasional (< 5%) failures 
of CreatePipe() with code ERROR_INVALID_HANDLE.  I have also seen failure of 
Cygwin signal handling because the signal handling pipe has mysteriously closed.

I believe that both symptoms are due to this code in winsup/cygwin/path.cc, 
which as the comments state, is not thread-safe:

      /* Workaround a problem in Vista/Longhorn which fails in subsequent
         calls to CreateFile with ERROR_INVALID_HANDLE if the handle in
         CurrentDirectoryHandle changes without calling SetCurrentDirectory,
         and the filename given to CreateFile is a relative path.  It looks
         like Vista stores a copy of the CWD handle in some other undocumented
         place.  The NtClose/DuplicateHandle reuses the original handle for
         the copy of the new handle and the next CreateFile works.
         Note that this is not thread-safe (yet?) */
      NtClose (*phdl);
      if (DuplicateHandle (GetCurrentProcess (), h, GetCurrentProcess (), phdl,
                           0, TRUE, DUPLICATE_SAME_ACCESS))
        NtClose (h);

If another thread allocates a handle between the NtClose and the Duplicate, 
then the handle value is not preserved, and strange effects may result.  
Presumably the issues mentioned in the source comment may also occur, but what 
I have seen myself is a subsequent call to SetCurrentDirectory() closing, not 
the current directory handle, but the handle that was allocated by the other 
thread.

Specifically, dll_crt0_1() calls sigproc_init(), then cwdstuff::set(), and 
finally wait_for_sigthread().  The function sigproc_init() creates the signal 
handling thread, which creates the signal handling pipe as
one of its very first actions, then sets the event for which 
wait_for_sigthread() waits.  I think this scenario happens:

  1. main thread: cwdstuff::set(): NtClose().

  2. signal handling thread: CreatePipe() opens a handle to 
'\Device\NamedPipe\' and stores it in a global variable (because this is the 
first call to CreatePipe()).

  3. main thread: cwdstuff::set(): DuplicateHandle().

In this case, the current directory handle value has changed, which is not the 
intend of the NtClose-Duplicate sequence.  Perhaps it causes CreateFile to fail 
with ERROR_INVALID_HANDLE as mentioned in the source comments, but I have not 
seen that.  I think that the CreatePipe() failures I have seen are triggered 
when SetCurrentDirectory() closes the handle to '\Device\NamedPipe\', thinking 
that it is the current directory handle.  After that, CreatePipe() will fail 
with ERROR_INVALID_HANDLE.

I think this other scenario also happens:

  1. signal handling thread: CreatePipe() opens a handle to 
'\Device\NamedPipe\' (because it is the first call to CreatePipe()).

  2. main thread: cwdstuff::set(): NtClose().

  3. signal handling thread: CreatePipe() opens pipe handles.

  4. main thread: cwdstuff::set(): DuplicateHandle().

In this case it is Cygwin signal handling that is sabotaged by subsequent calls 
to SetCurrentDirectory(), because they close one of the pipe handles used for 
Cygwin signal handling.

Note that replacing calls to SetCurrentDirectory() with chdir() could actually 
make the problem worse, because it would trigger the thread-unsafe code more 
frequently.  The call to SetCurrentDirectory() is merely making it possible to 
detect the problem sooner.

Though this would not eliminate the problem entirely, would it be possible to 
better synchronize the signal handling thread during Cygwin initialization, 
either by delaying creation of that thread until the first cwdstuff::set(), or 
by calling wait_for_sigthread() before cwdstuff::set()?

-- John

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

Reply via email to