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