diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 71a7a5c25..13c22d82e 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,20 @@ +Wed Feb 23 21:34:58 2000 Christopher Faylor + + * exceptions.cc (interruptible): Change method for determining if + something is interruptible. + (call_handler): Avoid suspending a thread if it owns a mutex. Only set + signal_arrived if the thread was actually interrupted. + (events_init): Initialize module information needed by interruptible(). + (sigdelayed): Don't call sig_dispatch_pending since it could screw up + * init.cc (dll_entry): Record module handle of main for use by + interruptible(). + (proc_subproc): Reorganize handling of terminated child so that the + bulk of the processing comes from the signal thread. + (wait_sig): Force processing of waiting threads if SIGCHLD is not + processed. + * sync.cc (muto::release): Set tid == 0 after lock is released or + signal processor will be confused. + Tue Feb 22 23:06:01 2000 Christopher Faylor Respond to more g++ warnings relating to initializing structures. diff --git a/winsup/cygwin/autoload.h b/winsup/cygwin/autoload.h index 262eaa0b0..71a51c28b 100644 --- a/winsup/cygwin/autoload.h +++ b/winsup/cygwin/autoload.h @@ -1,6 +1,6 @@ /* autoload.h: Define functions for auto-loading symbols from a DLL. - Copyright 1999 Cygnus Solutions. + Copyright 1999, 2000 Cygnus Solutions. Written by Christopher Faylor diff --git a/winsup/cygwin/errno.cc b/winsup/cygwin/errno.cc index 40e161654..02c86cb7a 100644 --- a/winsup/cygwin/errno.cc +++ b/winsup/cygwin/errno.cc @@ -1,6 +1,6 @@ /* errno.cc: errno-related functions - Copyright 1996, 1997, 1998, 1999 Cygnus Solutions. + Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions. This file is part of Cygwin. diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index a1ab2678f..4f2aa15c3 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -31,12 +31,15 @@ extern DWORD __sigfirst, __siglast; static BOOL WINAPI ctrl_c_handler (DWORD); static void really_exit (int); +static char windows_system_directory[1024]; +static size_t windows_system_directory_length; /* This is set to indicate that we have already exited. */ static NO_COPY int exit_already = 0; static NO_COPY muto *mask_sync = NULL; +HMODULE cygwin_hmodule; HANDLE NO_COPY console_handler_thread_waiter = NULL; static const struct @@ -611,12 +614,31 @@ handle_sigsuspend (sigset_t tempmask) extern DWORD exec_exit; // Possible exit value for exec extern int pending_signals; -extern __inline int +int interruptible (DWORD pc) { +#if 0 DWORD pchigh = pc & 0xf0000000; return ((pc >= (DWORD) &__sigfirst) && (pc <= (DWORD) &__siglast)) || !(pchigh == 0xb0000000 || pchigh == 0x70000000 || pchigh == 0x60000000); +#else + if ((pc >= (DWORD) &__sigfirst) && (pc <= (DWORD) &__siglast)) + return 1; + + MEMORY_BASIC_INFORMATION m; + memset (&m, 0, sizeof m); + if (!VirtualQuery ((LPCVOID) pc, &m, sizeof m)) + sigproc_printf ("couldn't get memory info, %E"); + +# define h ((HMODULE) m.AllocationBase) + if (h == cygwin_hmodule) + return 0; + char *checkdir = (char *) alloca (windows_system_directory_length); + if (!GetModuleFileName (h, checkdir, windows_system_directory_length)) + return 0; + return !strncasematch (windows_system_directory, checkdir, windows_system_directory_length); +# undef h +#endif } void @@ -689,7 +711,9 @@ static int call_handler (int sig, struct sigaction& siga, void *handler) { CONTEXT *cx, orig; + int interrupted = 1; int res; + extern muto *sync_proc_subproc; if (hExeced != NULL && hExeced != INVALID_HANDLE_VALUE) { @@ -706,7 +730,24 @@ call_handler (int sig, struct sigaction& siga, void *handler) sigproc_printf ("Suspending %p (mainthread)", myself->getthread2signal()); HANDLE hth = myself->getthread2signal (); - res = SuspendThread (hth); + /* Suspend the thread which will receive the signal. But first ensure that + this thread doesn't have the sync_proc_subproc and mask_sync mutos, since + we need those (hack alert). If the thread-to-be-suspended has either of + these mutos, enter a busy loop until it is released. If the thread is + already suspended (which should never occur) then just queue the signal. */ + for (;;) + { + res = SuspendThread (hth); + /* FIXME: Make multi-thread aware */ + if (sync_proc_subproc->owner () != maintid && mask_sync->owner () != maintid) + break; + + if (res) + goto set_pending; + ResumeThread (hth); + Sleep (0); + } + sigproc_printf ("suspend said %d, %E", res); /* Clear any waiting threads prior to dispatching to handler function */ @@ -734,17 +775,20 @@ call_handler (int sig, struct sigaction& siga, void *handler) interrupt_now (cx, sig, siga, handler); else if (!interrupt_on_return (cx, sig, siga, handler)) { + set_pending: pending_signals = 1; /* FIXME: Probably need to be more tricky here */ sig_set_pending (sig); + interrupted = 0; } (void) ResumeThread (hth); - (void) SetEvent (signal_arrived); // For an EINTR case + if (interrupted) + (void) SetEvent (signal_arrived); // For an EINTR case sigproc_printf ("armed signal_arrived %p, res %d", signal_arrived, res); out: sigproc_printf ("returning"); - return 1; + return interrupted; } #endif /* i386 */ @@ -983,6 +1027,17 @@ events_init (void) ProtectHandle (title_mutex); mask_sync = new_muto (FALSE, NULL); + windows_system_directory[0] = '\0'; + (void) GetSystemDirectory (windows_system_directory, sizeof (windows_system_directory) - 2); + char *end = strchr (windows_system_directory, '\0'); + if (end == windows_system_directory) + api_fatal ("can't find windows system directory"); + if (end[-1] != '\\') + { + *end++ = '\\'; + *end = '\0'; + } + windows_system_directory_length = end - windows_system_directory; } void @@ -1035,12 +1090,7 @@ _sigreturn: ret _sigdelayed: - # addl 4,%%esp - cmpl $0,_pending_signals - je 2f - pushl $0 - call _sig_dispatch_pending@4 -2: pushl %2 # original return address + pushl %2 # original return address pushf pushl %%esi pushl %%edi @@ -1053,9 +1103,16 @@ _sigdelayed: pushl %4 # signal argument pushl $_sigreturn movl $0,%0 - pushl $_signal_arrived - call _ResetEvent@4 - jmp *%5 + + pushl _signal_arrived # Everybody waiting for this should + call _ResetEvent@4 # have woken up by now. + + cmpl $0,_pending_signals + je 2f + pushl $0 + call _sig_dispatch_pending@4 + +2: jmp *%5 ___siglast: " : "=m" (sigsave.sig) : "m" (&_impure_ptr->_errno), diff --git a/winsup/cygwin/external.cc b/winsup/cygwin/external.cc index 539e0ba7e..ccbcb7782 100644 --- a/winsup/cygwin/external.cc +++ b/winsup/cygwin/external.cc @@ -1,6 +1,6 @@ /* external.cc: Interface to Cygwin internals from external programs. - Copyright 1997, 1998, 1999 Cygnus Solutions. + Copyright 1997, 1998, 1999, 2000 Cygnus Solutions. Written by Christopher Faylor diff --git a/winsup/cygwin/fhandler_floppy.cc b/winsup/cygwin/fhandler_floppy.cc index ed1c7b6e5..93e8435b8 100644 --- a/winsup/cygwin/fhandler_floppy.cc +++ b/winsup/cygwin/fhandler_floppy.cc @@ -1,7 +1,7 @@ /* fhandler_floppy.cc. See fhandler.h for a description of the fhandler classes. - Copyright 1999 Cygnus Solutions. + Copyright 1999, 2000 Cygnus Solutions. This file is part of Cygwin. diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 9f50f08d1..44fc49dc2 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -1,6 +1,6 @@ /* fork.cc - Copyright 1996, 1997, 1998, 1999 Cygnus Solutions. + Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions. This file is part of Cygwin. diff --git a/winsup/cygwin/heap.cc b/winsup/cygwin/heap.cc index 6c8ddae8f..ce1cab5c4 100644 --- a/winsup/cygwin/heap.cc +++ b/winsup/cygwin/heap.cc @@ -1,6 +1,6 @@ /* heap.cc: Cygwin heap manager. - Copyright 1996, 1997, 1998, 1999 Cygnus Solutions. + Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions. This file is part of Cygwin. diff --git a/winsup/cygwin/init.cc b/winsup/cygwin/init.cc index 3a33bd15b..2df50676d 100644 --- a/winsup/cygwin/init.cc +++ b/winsup/cygwin/init.cc @@ -1,6 +1,6 @@ /* init.cc for WIN32. - Copyright 1996, 1997, 1998, 1999 Cygnus Solutions. + Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions. This file is part of Cygwin. @@ -12,26 +12,17 @@ details. */ #include #include "winsup.h" -extern "C" -{ - int WINAPI dll_entry (HANDLE h, DWORD reason, void *ptr); -}; - -extern "C" void *export_malloc (unsigned int); -extern "C" void *export_realloc (void *,unsigned int); -extern "C" void *export_calloc (unsigned int,unsigned int); -extern "C" void export_free (void *); - -extern void do_global_ctors (void (**in_pfunc)(), int force); +extern HMODULE cygwin_hmodule; int NO_COPY dynamically_loaded; -int -WINAPI dll_entry (HANDLE, DWORD reason, void *static_load) +extern "C" int +WINAPI dll_entry (HANDLE h, DWORD reason, void *static_load) { switch (reason) { case DLL_PROCESS_ATTACH: + cygwin_hmodule = (HMODULE) h; dynamically_loaded = (static_load == NULL); break; case DLL_THREAD_ATTACH: diff --git a/winsup/cygwin/pipe.cc b/winsup/cygwin/pipe.cc index 2ba99cd10..fc1db5e3a 100644 --- a/winsup/cygwin/pipe.cc +++ b/winsup/cygwin/pipe.cc @@ -1,6 +1,6 @@ -/* pipe.cc: pipe for WIN32. +/* pipe.cc: pipe for Cygwin. - Copyright 1996, 1998, 1999 Cygnus Solutions. + Copyright 1996, 1998, 1999, 2000 Cygnus Solutions. This file is part of Cygwin. diff --git a/winsup/cygwin/registry.cc b/winsup/cygwin/registry.cc index 321b13fc9..f0c11ca25 100644 --- a/winsup/cygwin/registry.cc +++ b/winsup/cygwin/registry.cc @@ -1,6 +1,6 @@ /* registry.cc: registry interface - Copyright 1996, 1997, 1998, 1999 Cygnus Solutions. + Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions. This file is part of Cygwin. diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index f924a4c59..5efd067d6 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -85,8 +85,6 @@ Static HANDLE hwait_subproc = NULL; // Handle of sig_subproc thread Static HANDLE wait_sig_inited = NULL; // Control synchronization of // message queue startup -Static muto *sync_proc_subproc = NULL; // Control access to - // subproc stuff /* Used by WaitForMultipleObjects. These are handles to child processes. */ @@ -100,6 +98,9 @@ Static int nzombies = 0; // Number of deceased children Static waitq waitq_head = {0, 0, 0, 0, 0, 0, 0};// Start of queue for wait'ing threads Static waitq waitq_main; // Storage for main thread +muto NO_COPY *sync_proc_subproc = NULL; // Control access to + // subproc stuff + DWORD NO_COPY maintid = 0; // ID of the main thread Static DWORD sigtid = 0; // ID of the signal thread @@ -244,7 +245,7 @@ proc_subproc (DWORD what, DWORD val) int potential_match; DWORD exitcode; pinfo *child; - int send_sigchld = 0; + int clearing = 0; waitq *w; #define wval ((waitq *) val) @@ -284,14 +285,6 @@ proc_subproc (DWORD what, DWORD val) wake_wait_subproc (); break; - /* A child is in the stopped state. Scan wait() queue to see if anyone - * should be notified. (Called from wait_sig thread) - */ - case PROC_CHILDSTOPPED: - child = myself; // Just to avoid accidental NULL dereference - sip_printf ("Received stopped notification"); - goto scan_wait; - /* A child process had terminated. * Possibly this is just due to an exec(). Cygwin implements an exec() * as a "handoff" from one windows process to another. If child->hProcess @@ -310,7 +303,6 @@ proc_subproc (DWORD what, DWORD val) ForceCloseHandle1 (hchildren[val], childhProc); hchildren[val] = child->hProcess; /* Filled out by child */ ProtectHandle1 (child->hProcess, childhProc); - wake_wait_subproc (); break; // This was an exec() } @@ -318,46 +310,21 @@ proc_subproc (DWORD what, DWORD val) child->pid, val, hchildren[val], nchildren, nzombies); remove_child (val); // Remove from children arrays zombies[nzombies++] = child; // Add to zombie array - wake_wait_subproc (); // Notify wait_subproc thread that - // nchildren has changed. child->process_state = PID_ZOMBIE;// Walking dead if (!proc_loop_wait) // Don't bother if wait_subproc is break; // exiting - send_sigchld = 1; - - scan_wait: - /* Scan the linked list of wait()ing threads. If a wait's parameters - * match this pid, then activate it. - */ - for (w = &waitq_head; w->next != NULL; w = w->next) - { - if ((potential_match = checkstate (w)) > 0) - sip_printf ("released waiting thread"); - else if (potential_match < 0) - sip_printf ("only found non-terminated children"); - else if (potential_match == 0) // nothing matched - { - sip_printf ("waiting thread found no children"); - HANDLE oldw = w->next->ev; - w->next->ev = NULL; - if (!SetEvent (oldw)) - system_printf ("couldn't wake up wait event %p, %E", oldw); - w->next = w->next->next; - } - if (w->next == NULL) - break; - } - - sip_printf ("finished processing terminated/stopped child"); - if (!send_sigchld) - break; // No need to send a SIGCHLD - /* Send a SIGCHLD to myself. */ - sync_proc_subproc->release (); // Avoid a potential deadlock - rc = sig_send (NULL, SIGCHLD); // Send a SIGCHLD - goto out1; // Don't try to unlock. We don't have a lock. + rc = sig_send (myself_nowait, SIGCHLD); // Send a SIGCHLD + break; // Don't try to unlock. We don't have a lock. + /* A child is in the stopped state. Scan wait() queue to see if anyone + * should be notified. (Called from wait_sig thread) + */ + case PROC_CHILDSTOPPED: + child = myself; // Just to avoid accidental NULL dereference + sip_printf ("Received stopped notification"); + goto scan_wait; /* Clear all waiting threads. Called from exceptions.cc prior to * the main thread's dispatch to a signal handler function. @@ -366,15 +333,41 @@ proc_subproc (DWORD what, DWORD val) case PROC_CLEARWAIT: /* Clear all "wait"ing threads. */ sip_printf ("clear waiting threads"); + clearing = 1; + + case PROC_SIGCHLD: + scan_wait: + /* Scan the linked list of wait()ing threads. If a wait's parameters + * match this pid, then activate it. + */ for (w = &waitq_head; w->next != NULL; w = w->next) { - sip_printf ("clearing waiting thread, pid %d", w->next->pid); - w->next->status = -1; /* flag that a signal was received */ - if (!SetEvent (w->next->ev)) - system_printf ("Couldn't wake up wait event, %E"); + if ((potential_match = checkstate (w)) > 0) + sip_printf ("released waiting thread"); + else if (!clearing && potential_match < 0) + sip_printf ("only found non-terminated children"); + else if (potential_match <= 0) // nothing matched + { + sip_printf ("waiting thread found no children"); + HANDLE oldw = w->next->ev; + w->next->ev = NULL; + if (clearing) + w->next->status = -1; /* flag that a signal was received */ + if (!SetEvent (oldw)) + system_printf ("couldn't wake up wait event %p, %E", oldw); + w->next = w->next->next; + } + if (w->next == NULL) + break; + } + + if (!clearing) + sip_printf ("finished processing terminated/stopped child"); + else + { + waitq_head.next = NULL; + sip_printf ("finished clearing"); } - waitq_head.next = NULL; - sip_printf ("finished clearing"); break; /* Handle a wait4() operation. Allocates an event for the calling @@ -1252,6 +1245,10 @@ wait_sig (VOID *) /* A normal UNIX signal */ default: sip_printf ("Got signal %d", sig); + int wasdispatched = sig_handle (sig); + dispatched |= wasdispatched; + if (sig == SIGCHLD && !wasdispatched) + proc_subproc (PROC_SIGCHLD, 0); dispatched |= sig_handle (sig); goto nextsig; } diff --git a/winsup/cygwin/sigproc.h b/winsup/cygwin/sigproc.h index b1b4eafc0..7f237a925 100644 --- a/winsup/cygwin/sigproc.h +++ b/winsup/cygwin/sigproc.h @@ -18,7 +18,8 @@ enum procstuff PROC_CHILDSTOPPED = 2, // a child stopped PROC_CHILDTERMINATED = 3, // a child died PROC_CLEARWAIT = 4, // clear all waits - signal arrived - PROC_WAIT = 5 // setup for wait() for subproc + PROC_WAIT = 5, // setup for wait() for subproc + PROC_SIGCHLD = 6 // saw a non-trapped SIGCHLD }; typedef struct struct_waitq diff --git a/winsup/cygwin/sync.cc b/winsup/cygwin/sync.cc index 89d92ce06..96102828b 100644 --- a/winsup/cygwin/sync.cc +++ b/winsup/cygwin/sync.cc @@ -4,7 +4,7 @@ which is intended to operate similarly to a mutex but attempts to avoid making expensive calls to the kernel. - Copyright 1999 Cygnus Solutions. + Copyright 2000 Cygnus Solutions. Written by Christopher Faylor @@ -43,7 +43,11 @@ muto::~muto () /* Acquire the lock. Argument is the number of milliseconds to wait for the lock. Multiple visits from the same thread are allowed and should - be handled correctly. */ + be handled correctly. + + Note: The goal here is to minimize, as much as possible, calls to the + OS. Hence the use of InterlockedIncrement, etc., rather than (much) more + expensive OS mutexes. */ int muto::acquire (DWORD ms) { @@ -59,7 +63,7 @@ muto::acquire (DWORD ms) lock the same muto to succeed without attempting to manipulate sync. If the muto is already locked then this thread will wait for ms until it is signalled by muto::release. Then it will attempt to grab the - sync field. If it succeeds, then this thread owns the mutex. + sync field. If it succeeds, then this thread owns the muto. There is a pathological condition where a thread times out waiting for bruteforce but the release code triggers the bruteforce event. In this @@ -99,11 +103,11 @@ muto::release () /* FIXME: Need to check that other thread has not exited, too. */ if (!--visits) { - tid = 0; /* We were the last unlocker. */ InterlockedExchange (&sync, 0); /* Reset trigger. */ /* This thread had incremented waiters but had never decremented it. Decrement it now. If it is >= 0 then there are possibly other threads waiting for the lock, so trigger bruteforce. */ + tid = 0; /* We were the last unlocker. */ if (InterlockedDecrement (&waiters) >= 0) (void) SetEvent (bruteforce); /* Wake up one of the waiting threads */ } diff --git a/winsup/cygwin/sync.h b/winsup/cygwin/sync.h index 37efbe54f..7189ae936 100644 --- a/winsup/cygwin/sync.h +++ b/winsup/cygwin/sync.h @@ -1,6 +1,6 @@ /* sync.h: Header file for cygwin synchronization primitives. - Copyright 1999 Cygnus Solutions. + Copyright 1999, 2000 Cygnus Solutions. Written by Christopher Faylor @@ -36,6 +36,7 @@ public: /* Return true if caller thread owns the lock. */ int ismine () {return tid == GetCurrentThreadId ();} + DWORD owner () {return tid;} }; /* Use a statically allocated buffer as the storage for a muto */ diff --git a/winsup/cygwin/thread.h b/winsup/cygwin/thread.h index 9aca0c61a..e9e0c82b7 100644 --- a/winsup/cygwin/thread.h +++ b/winsup/cygwin/thread.h @@ -1,6 +1,6 @@ /* thread.h: Locking and threading module definitions - Copyright 1998, 1999 Cygnus Solutions. + Copyright 1998, 1999, 2000 Cygnus Solutions. Written by Marco Fuykschot diff --git a/winsup/cygwin/wait.cc b/winsup/cygwin/wait.cc index 182240022..30b8ef885 100644 --- a/winsup/cygwin/wait.cc +++ b/winsup/cygwin/wait.cc @@ -1,6 +1,6 @@ /* wait.cc: Posix wait routines. - Copyright 1996, 1997, 1998, 1999 Cygnus Solutions. + Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions. This file is part of Cygwin.