OSDN Git Service

Rename cygWFMO to cygwait throughout and use the magic of polymorphism to "wait
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / exceptions.cc
index 33ba9f0..b16e816 100644 (file)
@@ -1,7 +1,7 @@
 /* exceptions.cc
 
    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-   2005, 2006 Red Hat, Inc.
+   2005, 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
 
 This file is part of Cygwin.
 
@@ -9,43 +9,39 @@ This software is a copyrighted work licensed under the terms of the
 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 details. */
 
+#define CYGTLS_HANDLE
 #include "winsup.h"
+#include "miscfuncs.h"
 #include <wingdi.h>
 #include <winuser.h>
 #include <imagehlp.h>
 #include <stdlib.h>
-#include <setjmp.h>
-#include <assert.h>
 #include <syslog.h>
+#include <wchar.h>
 
-#include "exceptions.h"
-#include "sync.h"
 #include "pinfo.h"
 #include "cygtls.h"
 #include "sigproc.h"
-#include "cygerrno.h"
 #include "shared_info.h"
 #include "perprocess.h"
-#include "security.h"
 #include "path.h"
 #include "fhandler.h"
 #include "dtable.h"
 #include "cygheap.h"
+#include "child_info.h"
+#include "ntdll.h"
+#include "exception.h"
 
-#define CALL_HANDLER_RETRY 20
+#define CALL_HANDLER_RETRY_OUTER 10
+#define CALL_HANDLER_RETRY_INNER 10
 
-char debugger_command[2 * CYG_MAX_PATH + 20];
+char debugger_command[2 * NT_MAX_PATH + 20];
 
 extern "C" {
 extern void sigdelayed ();
 };
 
-extern NO_COPY DWORD dwExeced;
-int NO_COPY sigExeced;
-
 static BOOL WINAPI ctrl_c_handler (DWORD);
-char windows_system_directory[1024];
-static size_t windows_system_directory_length;
 
 /* This is set to indicate that we have already exited.  */
 
@@ -93,24 +89,17 @@ NO_COPY static struct
 
 /* Initialization code.  */
 
-BOOL WINAPI
-dummy_ctrl_c_handler (DWORD dwCtrlType)
-{
-  return TRUE;
-}
-
 void
-init_console_handler (BOOL install_handler)
+init_console_handler (bool install_handler)
 {
   BOOL res;
-  while (SetConsoleCtrlHandler (ctrl_c_handler, FALSE))
-    continue;
+
+  SetConsoleCtrlHandler (ctrl_c_handler, FALSE);
+  SetConsoleCtrlHandler (NULL, FALSE);
   if (install_handler)
     res = SetConsoleCtrlHandler (ctrl_c_handler, TRUE);
-  else if (wincap.has_null_console_handler_routine ())
-    res = SetConsoleCtrlHandler (NULL, TRUE);
   else
-    res = SetConsoleCtrlHandler (dummy_ctrl_c_handler, TRUE);
+    res = SetConsoleCtrlHandler (NULL, TRUE);
   if (!res)
     system_printf ("SetConsoleCtrlHandler failed, %E");
 }
@@ -124,8 +113,8 @@ error_start_init (const char *buf)
       return;
     }
 
-  char pgm[CYG_MAX_PATH];
-  if (!GetModuleFileName (NULL, pgm, CYG_MAX_PATH))
+  char pgm[NT_MAX_PATH];
+  if (!GetModuleFileName (NULL, pgm, NT_MAX_PATH))
     strcpy (pgm, "cygwin1.dll");
   for (char *p = strchr (pgm, '\\'); p; p = strchr (p, '\\'))
     *p = '/';
@@ -136,26 +125,44 @@ error_start_init (const char *buf)
 static void
 open_stackdumpfile ()
 {
-  if (myself->progname[0])
+  /* If we have no executable name, or if the CWD handle is NULL,
+     which means, the CWD is a virtual path, don't even try to open
+     a stackdump file. */
+  if (myself->progname[0] && cygheap->cwd.get_handle ())
     {
-      const char *p;
+      const WCHAR *p;
       /* write to progname.stackdump if possible */
       if (!myself->progname[0])
-       p = "unknown";
-      else if ((p = strrchr (myself->progname, '\\')))
+       p = L"unknown";
+      else if ((p = wcsrchr (myself->progname, L'\\')))
        p++;
       else
        p = myself->progname;
-      char corefile[strlen (p) + sizeof (".stackdump")];
-      __small_sprintf (corefile, "%s.stackdump", p);
-      HANDLE h = CreateFile (corefile, GENERIC_WRITE, 0, &sec_none_nih,
-                            CREATE_ALWAYS, 0, 0);
-      if (h != INVALID_HANDLE_VALUE)
+
+      WCHAR corefile[wcslen (p) + sizeof (".stackdump")];
+      wcpcpy (wcpcpy(corefile, p), L".stackdump");
+      UNICODE_STRING ucore;
+      OBJECT_ATTRIBUTES attr;
+      /* Create the UNICODE variation of <progname>.stackdump. */
+      RtlInitUnicodeString (&ucore, corefile);
+      /* Create an object attribute which refers to <progname>.stackdump
+        in Cygwin's cwd.  Stick to caseinsensitivity. */
+      InitializeObjectAttributes (&attr, &ucore, OBJ_CASE_INSENSITIVE,
+                                 cygheap->cwd.get_handle (), NULL);
+      HANDLE h;
+      IO_STATUS_BLOCK io;
+      NTSTATUS status;
+      /* Try to open it to dump the stack in it. */
+      status = NtCreateFile (&h, GENERIC_WRITE | SYNCHRONIZE, &attr, &io,
+                            NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
+                            FILE_SYNCHRONOUS_IO_NONALERT
+                            | FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0);
+      if (NT_SUCCESS (status))
        {
          if (!myself->cygstarted)
-           system_printf ("Dumping stack trace to %s", corefile);
+           system_printf ("Dumping stack trace to %S", &ucore);
          else
-           debug_printf ("Dumping stack trace to %s", corefile);
+           debug_printf ("Dumping stack trace to %S", &ucore);
          SetStdHandle (STD_ERROR_HANDLE, h);
        }
     }
@@ -164,7 +171,7 @@ open_stackdumpfile ()
 /* Utilities for dumping the stack, etc.  */
 
 static void
-exception (EXCEPTION_RECORD *e,  CONTEXT *in)
+dump_exception (EXCEPTION_RECORD *e,  CONTEXT *in)
 {
   const char *exception_name = NULL;
 
@@ -186,7 +193,7 @@ exception (EXCEPTION_RECORD *e,  CONTEXT *in)
     small_printf ("Signal %d at eip=%08x\r\n", e->ExceptionCode, in->Eip);
   small_printf ("eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\r\n",
                in->Eax, in->Ebx, in->Ecx, in->Edx, in->Esi, in->Edi);
-  small_printf ("ebp=%08x esp=%08x program=%s, pid %u, thread %s\r\n",
+  small_printf ("ebp=%08x esp=%08x program=%W, pid %u, thread %s\r\n",
                in->Ebp, in->Esp, myself->progname, myself->pid, cygthread::name ());
   small_printf ("cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\r\n",
                in->SegCs, in->SegDs, in->SegEs, in->SegFs, in->SegGs, in->SegSs);
@@ -232,39 +239,52 @@ stack_info::init (DWORD ebp, bool wantargs, bool goodframe)
 # undef debp
 }
 
+extern "C" void _cygwin_exit_return ();
+
 /* Walk the stack by looking at successive stored 'bp' frames.
    This is not foolproof. */
 int
 stack_info::walk ()
 {
   char **ebp;
-  if ((ebp = (char **) next_offset ()) == NULL)
+
+  if ((void (*) ()) sf.AddrPC.Offset == _cygwin_exit_return)
+    return 0;          /* stack frames are exhausted */
+
+  if (((ebp = (char **) next_offset ()) == NULL) || (ebp >= (char **) cygwin_hmodule))
     return 0;
 
   sf.AddrFrame.Offset = (DWORD) ebp;
   sf.AddrPC.Offset = sf.AddrReturn.Offset;
 
-  if (!sf.AddrPC.Offset)
-    return 0;          /* stack frames are exhausted */
-
   /* The return address always follows the stack pointer */
   sf.AddrReturn.Offset = (DWORD) *++ebp;
 
   if (needargs)
-    /* The arguments follow the return address */
-    for (unsigned i = 0; i < NPARAMS; i++)
-      sf.Params[i] = (DWORD) *++ebp;
+    {
+      unsigned nparams = NPARAMS;
+
+      /* The arguments follow the return address */
+      sf.Params[0] = (DWORD) *++ebp;
+      /* Hack for XP/2K3 WOW64.  If the first stack param points to the
+        application entry point, we can only fetch one additional
+        parameter.  Accessing anything beyond this address results in
+        a SEGV.  This is fixed in Vista/2K8 WOW64. */
+      if (wincap.has_restricted_stack_args () && sf.Params[0] == 0x401000)
+       nparams = 2;
+      for (unsigned i = 1; i < nparams; i++)
+       sf.Params[i] = (DWORD) *++ebp;
+    }
 
   return 1;
 }
 
-static void
+void
 stackdump (DWORD ebp, int open_file, bool isexception)
 {
-  extern unsigned long rlim_core;
   static bool already_dumped;
 
-  if (rlim_core == 0UL || (open_file && already_dumped))
+  if (cygheap->rlim_core == 0UL || (open_file && already_dumped))
     return;
 
   if (open_file)
@@ -288,33 +308,42 @@ stackdump (DWORD ebp, int open_file, bool isexception)
              i == 16 ? " (more stack frames may be present)" : "");
 }
 
-static bool
-inside_kernel (CONTEXT *cx)
+bool
+_cygtls::inside_kernel (CONTEXT *cx)
 {
   int res;
   MEMORY_BASIC_INFORMATION m;
 
+  if (!isinitialized ())
+    return true;
+
   memset (&m, 0, sizeof m);
   if (!VirtualQuery ((LPCVOID) cx->Eip, &m, sizeof m))
     sigproc_printf ("couldn't get memory info, pc %p, %E", cx->Eip);
 
-  char *checkdir = (char *) alloca (windows_system_directory_length + 4);
-  memset (checkdir, 0, sizeof (checkdir));
+  size_t size = (windows_system_directory_length + 6) * sizeof (WCHAR);
+  PWCHAR checkdir = (PWCHAR) alloca (size);
+  memset (checkdir, 0, size);
 
 # define h ((HMODULE) m.AllocationBase)
   /* Apparently Windows 95 can sometimes return bogus addresses from
      GetThreadContext.  These resolve to a strange allocation base.
      These should *never* be treated as interruptible. */
   if (!h || m.State != MEM_COMMIT)
-    res = false;
-  else if (h == user_data->hmodule)
-    res = true;
-  else if (!GetModuleFileName (h, checkdir, windows_system_directory_length + 2))
     res = true;
+  else if (h == user_data->hmodule)
+    res = false;
+  else if (!GetModuleFileNameW (h, checkdir, windows_system_directory_length + 6))
+    res = false;
   else
-    res = !strncasematch (windows_system_directory, checkdir,
+    {
+      /* Skip potential long path prefix. */
+      if (!wcsncmp (checkdir, L"\\\\?\\", 4))
+       checkdir += 4;
+      res = !wcsncasecmp (windows_system_directory, checkdir,
                          windows_system_directory_length);
-  sigproc_printf ("pc %p, h %p, interruptible %d", cx->Eip, h, res);
+    }
+  sigproc_printf ("pc %p, h %p, inside_kernel %d", cx->Eip, h, res);
 # undef h
   return res;
 }
@@ -339,7 +368,8 @@ try_to_debug (bool waitloop)
     return 0;
   if (being_debugged ())
     {
-      DebugBreak ();
+      extern void break_here ();
+      break_here ();
       return 0;
     }
 
@@ -349,7 +379,7 @@ try_to_debug (bool waitloop)
   SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
   PROCESS_INFORMATION pi = {NULL, 0, 0, 0};
 
-  STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
+  STARTUPINFOW si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
   si.lpReserved = NULL;
   si.lpDesktop = NULL;
   si.dwFlags = 0;
@@ -359,23 +389,24 @@ try_to_debug (bool waitloop)
      suspend_all_threads_except (current_thread_id);
   */
 
-  /* if any of these mutexes is owned, we will fail to start any cygwin app
-     until trapped app exits */
+  /* If the tty mutex is owned, we will fail to start any cygwin app
+     until the trapped app exits.  However, this will only release any
+     the mutex if it is owned by this thread so that may be problematic. */
 
-  ReleaseMutex (tty_mutex);
+  lock_ttys::release ();
 
   /* prevent recursive exception handling */
-  char* rawenv = GetEnvironmentStrings () ;
-  for (char* p = rawenv; *p != '\0'; p = strchr (p, '\0') + 1)
+  PWCHAR rawenv = GetEnvironmentStringsW () ;
+  for (PWCHAR p = rawenv; *p != L'\0'; p = wcschr (p, L'\0') + 1)
     {
-      if (strncmp (p, "CYGWIN=", strlen ("CYGWIN=")) == 0)
+      if (wcsncmp (p, L"CYGWIN=", wcslen (L"CYGWIN=")) == 0)
        {
-         char* q = strstr (p, "error_start") ;
+         PWCHAR q = wcsstr (p, L"error_start") ;
          /* replace 'error_start=...' with '_rror_start=...' */
          if (q)
            {
-             *q = '_' ;
-             SetEnvironmentVariable ("CYGWIN", p + strlen ("CYGWIN=")) ;
+             *q = L'_' ;
+             SetEnvironmentVariableW (L"CYGWIN", p + wcslen (L"CYGWIN=")) ;
            }
          break ;
        }
@@ -384,16 +415,18 @@ try_to_debug (bool waitloop)
   console_printf ("*** starting debugger for pid %u, tid %u\n",
                  cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ());
   BOOL dbg;
-  dbg = CreateProcess (NULL,
-                      debugger_command,
-                      NULL,
-                      NULL,
-                      FALSE,
-                      CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
-                      NULL,
-                      NULL,
-                      &si,
-                      &pi);
+  WCHAR dbg_cmd[strlen(debugger_command)];
+  sys_mbstowcs (dbg_cmd, strlen(debugger_command) + 1, debugger_command);
+  dbg = CreateProcessW (NULL,
+                       dbg_cmd,
+                       NULL,
+                       NULL,
+                       FALSE,
+                       CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
+                       NULL,
+                       NULL,
+                       &si,
+                       &pi);
 
   if (!dbg)
     system_printf ("Failed to start debugger, %E");
@@ -403,7 +436,7 @@ try_to_debug (bool waitloop)
        return dbg;
       SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE);
       while (!being_debugged ())
-       low_priority_sleep (0);
+       yield ();
       Sleep (2000);
     }
 
@@ -437,9 +470,10 @@ rtl_unwind (exception_list *frame, PEXCEPTION_RECORD e)
 
 /* Main exception handler. */
 
-extern "C" char *__progname;
+extern exception_list *_except_list asm ("%fs:0");
+
 int
-_cygtls::handle_exceptions (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, void *)
+exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, void *)
 {
   static bool NO_COPY debugging;
   static int NO_COPY recursed;
@@ -519,17 +553,22 @@ _cygtls::handle_exceptions (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT
       break;
 
     case STATUS_ACCESS_VIOLATION:
-      if (mmap_is_attached_page (e->ExceptionInformation[1]))
+      switch (mmap_is_attached_or_noreserve ((void *)e->ExceptionInformation[1],
+                                            1))
        {
+       case MMAP_NORESERVE_COMMITED:
+         return 0;
+       case MMAP_RAISE_SIGBUS: /* MAP_NORESERVE page, commit failed, or
+                                  access to mmap page beyond EOF. */
          si.si_signo = SIGBUS;
          si.si_code = BUS_OBJERR;
-       }
-      else
-        {
+         break;
+       default:
          MEMORY_BASIC_INFORMATION m;
          VirtualQuery ((PVOID) e->ExceptionInformation[1], &m, sizeof m);
          si.si_signo = SIGSEGV;
          si.si_code = m.State == MEM_FREE ? SEGV_MAPERR : SEGV_ACCERR;
+         break;
        }
       break;
 
@@ -561,12 +600,11 @@ _cygtls::handle_exceptions (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT
       return 1;
     }
 
-  rtl_unwind (frame, e);
+  debug_printf ("In cygwin_except_handler exception %p at %p sp %p", e->ExceptionCode, in->Eip, in->Esp);
+  debug_printf ("In cygwin_except_handler signal %d at %p", si.si_signo, in->Eip);
 
-  debug_printf ("In cygwin_except_handler exc %p at %p sp %p", e->ExceptionCode, in->Eip, in->Esp);
-  debug_printf ("In cygwin_except_handler sig %d at %p", si.si_signo, in->Eip);
-
-  if (global_sigs[si.si_signo].sa_mask & SIGTOMASK (si.si_signo))
+  bool masked = !!(me.sigmask & SIGTOMASK (si.si_signo));
+  if (masked)
     syscall_printf ("signal %d, masked %p", si.si_signo,
                    global_sigs[si.si_signo].sa_mask);
 
@@ -581,12 +619,19 @@ _cygtls::handle_exceptions (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT
        break;
       }
 
-  if (me.fault_guarded ())
-    me.return_from_fault ();
+  if (me.andreas)
+    me.andreas->leave ();      /* Return from a "san" caught fault */
 
   me.copy_context (in);
-  if (!cygwin_finished_initializing
+
+  /* Temporarily replace windows top level SEH with our own handler.
+     We don't want any Windows magic kicking in.  This top level frame
+     will be removed automatically after our exception handler returns. */
+  _except_list->handler = handle;
+
+  if (masked
       || &me == _sig_tls
+      || !cygwin_finished_initializing
       || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_DFL
       || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_IGN
       || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_ERR)
@@ -612,9 +657,13 @@ _cygtls::handle_exceptions (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT
              return 0;
            }
 
-         open_stackdumpfile ();
-         exception (e, in);
-         stackdump ((DWORD) ebp, 0, 1);
+         rtl_unwind (frame, e);
+         if (cygheap->rlim_core > 0UL)
+           {
+             open_stackdumpfile ();
+             dump_exception (e, in);
+             stackdump ((DWORD) ebp, 0, 1);
+           }
        }
 
       if (e->ExceptionCode == STATUS_ACCESS_VIOLATION)
@@ -624,7 +673,7 @@ _cygtls::handle_exceptions (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT
            error_code |= 1;
          if (e->ExceptionInformation[0])       /* Write access */
            error_code |= 2;
-         if (!inside_kernel (in))              /* User space */
+         if (!me.inside_kernel (in))           /* User space */
            error_code |= 4;
          klog (LOG_INFO, "%s[%d]: segfault at %08x rip %08x rsp %08x error %d",
                          __progname, myself->pid,
@@ -633,10 +682,13 @@ _cygtls::handle_exceptions (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT
                           ? 0 : 4) | (e->ExceptionInformation[0] << 1));
        }
 
-      me.signal_exit (0x80 | si.si_signo);     // Flag signal + core dump
+      /* Flag signal + core dump */
+      me.signal_exit ((cygheap->rlim_core > 0UL ? 0x80 : 0) | si.si_signo);
     }
 
-  si.si_addr = (void *) in->Eip;
+  si.si_addr =  (si.si_signo == SIGSEGV || si.si_signo == SIGBUS
+                ? (void *) e->ExceptionInformation[1]
+                : (void *) in->Eip);
   si.si_errno = si.si_pid = si.si_uid = 0;
   me.incyg++;
   sig_send (NULL, si, &me);    // Signal myself
@@ -659,19 +711,13 @@ _cygtls::handle_exceptions (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT
 int __stdcall
 handle_sigsuspend (sigset_t tempmask)
 {
-  if (&_my_tls != _main_tls)
-    {
-      cancelable_wait (signal_arrived, INFINITE, cw_cancel_self);
-      return -1;
-    }
+  sigset_t oldmask = _my_tls.sigmask;  // Remember for restoration
 
-  sigset_t oldmask = myself->getsigmask ();    // Remember for restoration
-
-  set_signal_mask (tempmask, myself->getsigmask ());
+  set_signal_mask (tempmask, _my_tls.sigmask);
   sigproc_printf ("oldmask %p, newmask %p", oldmask, tempmask);
 
   pthread_testcancel ();
-  cancelable_wait (signal_arrived, INFINITE);
+  cancelable_wait (signal_arrived);
 
   set_sig_errno (EINTR);       // Per POSIX
 
@@ -709,7 +755,7 @@ sig_handle_tty_stop (int sig)
     {
     case WAIT_OBJECT_0:
     case WAIT_OBJECT_0 + 1:
-      reset_signal_arrived ();
+      myself->stopsig = SIGCONT;
       myself->alert_parent (SIGCONT);
       break;
     default:
@@ -726,7 +772,11 @@ _cygtls::interrupt_now (CONTEXT *cx, int sig, void *handler,
 {
   bool interrupted;
 
-  if (incyg || spinning || locked () || !inside_kernel (cx))
+  /* Delay the interrupt if we are
+     1) somehow inside the DLL
+     2) in _sigfe (spinning is true) and about to enter cygwin DLL
+     3) in a Windows DLL.  */
+  if (incyg || spinning || inside_kernel (cx))
     interrupted = false;
   else
     {
@@ -769,7 +819,7 @@ _cygtls::interrupt_setup (int sig, void *handler, struct sigaction& siga)
   /* Clear any waiting threads prior to dispatching to handler function */
   int res = SetEvent (signal_arrived); // For an EINTR case
   proc_subproc (PROC_CLEARWAIT, 1);
-  sigproc_printf ("armed signal_arrived %p, sig %d, res %d", signal_arrived,
+  sigproc_printf ("armed signal_arrived %p, signal %d, res %d", signal_arrived,
                  sig, res);
 }
 
@@ -791,64 +841,59 @@ setup_handler (int sig, void *handler, struct sigaction& siga, _cygtls *tls)
 
   if (tls->sig)
     {
-      sigproc_printf ("trying to send sig %d but signal %d already armed",
+      sigproc_printf ("trying to send signal %d but signal %d already armed",
                      sig, tls->sig);
       goto out;
     }
 
-  for (int i = 0; i < CALL_HANDLER_RETRY; i++)
+  for (int n = 0; n < CALL_HANDLER_RETRY_OUTER; n++)
     {
-      tls->lock ();
-      if (tls->incyg)
+      for (int i = 0; i < CALL_HANDLER_RETRY_INNER; i++)
        {
-         sigproc_printf ("controlled interrupt. stackptr %p, stack %p, stackptr[-1] %p",
-                         tls->stackptr, tls->stack, tls->stackptr[-1]);
-         tls->interrupt_setup (sig, handler, siga);
-         interrupted = true;
-         tls->unlock ();
-         break;
-       }
+         tls->lock ();
+         if (tls->incyg)
+           {
+             sigproc_printf ("controlled interrupt. stackptr %p, stack %p, stackptr[-1] %p",
+                             tls->stackptr, tls->stack, tls->stackptr[-1]);
+             tls->interrupt_setup (sig, handler, siga);
+             interrupted = true;
+             tls->unlock ();
+             goto out;
+           }
 
-      tls->unlock ();
-      DWORD res;
-      HANDLE hth = (HANDLE) *tls;
-
-      /* Suspend the thread which will receive the signal.
-        For Windows 95, we also have to ensure that the addresses returned by
-        GetThreadContext are valid.
-        If one of these conditions is not true we loop for a fixed number of times
-        since we don't want to stall the signal handler.  FIXME: Will this result in
-        noticeable delays?
-        If the thread is already suspended (which can occur when a program has called
-        SuspendThread on itself) then just queue the signal. */
-
-#ifndef DEBUGGING
-      sigproc_printf ("suspending mainthread");
-#else
-      cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
-      if (!GetThreadContext (hth, &cx))
-       memset (&cx, 0, sizeof cx);
-      sigproc_printf ("suspending mainthread PC %p", cx.Eip);
-#endif
-      res = SuspendThread (hth);
-      /* Just set pending if thread is already suspended */
-      if (res)
-       {
-         ResumeThread (hth);
-         break;
-       }
-      cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
-      if (!GetThreadContext (hth, &cx))
-       system_printf ("couldn't get context of main thread, %E");
-      else
-       interrupted = tls->interrupt_now (&cx, sig, handler, siga);
+         DWORD res;
+         HANDLE hth = (HANDLE) *tls;
 
-      res = ResumeThread (hth);
-      if (interrupted)
-       break;
+         /* Suspend the thread which will receive the signal.
+            If one of these conditions is not true we loop.
+            If the thread is already suspended (which can occur when a program
+            has called SuspendThread on itself) then just queue the signal. */
 
-      sigproc_printf ("couldn't interrupt.  trying again.");
-      low_priority_sleep (0);
+         sigproc_printf ("suspending thread");
+         res = SuspendThread (hth);
+         /* Just set pending if thread is already suspended */
+         if (res)
+           {
+             ResumeThread (hth);
+             goto out;
+           }
+         cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
+         if (!GetThreadContext (hth, &cx))
+           system_printf ("couldn't get context of thread, %E");
+         else
+           interrupted = tls->interrupt_now (&cx, sig, handler, siga);
+
+         tls->unlock ();
+         res = ResumeThread (hth);
+         if (interrupted)
+           goto out;
+
+         sigproc_printf ("couldn't interrupt.  trying again.");
+         yield ();
+       }
+      /* Hit here if we couldn't deliver the signal.  Take a more drastic
+        action before trying again. */
+      Sleep (1);
     }
 
 out:
@@ -870,7 +915,7 @@ has_visible_window_station ()
      with the desktop (using the "Allow service to interact with desktop"
      property) are running in an invisible window station. */
   if ((station_hdl = GetProcessWindowStation ())
-      && GetUserObjectInformationA (station_hdl, UOI_FLAGS, &uof,
+      && GetUserObjectInformationW (station_hdl, UOI_FLAGS, &uof,
                                    sizeof uof, &len)
       && (uof.dwFlags & WSF_VISIBLE))
     return true;
@@ -891,8 +936,6 @@ ctrl_c_handler (DWORD type)
       ExitProcess (STATUS_CONTROL_C_EXIT);
     }
 
-  _my_tls.remove (INFINITE);
-
 #if 0
   if (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT)
     proc_subproc (PROC_KILLFORKED, 0);
@@ -924,32 +967,22 @@ ctrl_c_handler (DWORD type)
        }
       if (!saw_close && type == CTRL_LOGOFF_EVENT)
        {
-#if 0
-         /* CV, 2005-09-08: The CTRL_LOGOFF_EVENT is only send to services.
-            It's send when *any* user logs off.  Services generally have
-            a modified console handler which allows services to survive
-            also after a user logged out, even if the service has a console
-            window attached to the visible window station of the user
-            ("Interact with desktop").  The below code contradicts this
-            standard behaviour, so for now, we disable it and just return
-            FALSE to get the default behaviour or the one the application's
-            own console handler (if any) requires.
-            In other words: We never send SIGHUP to services and their
-            child processes on a LOGOFF event. */
-
-         /* Check if the process is actually associated with a visible
-            window station, one which actually represents a visible desktop.
-            If not, the CTRL_LOGOFF_EVENT doesn't concern this process. */
-         if (has_visible_window_station ())
-           sig_send (myself_nowait, SIGHUP);
-#endif
+         /* The CTRL_LOGOFF_EVENT is sent when *any* user logs off.
+            The below code sends a SIGHUP only if it is not performing the
+            default activity for SIGHUP.  Note that it is possible for two
+            SIGHUP signals to arrive if a process group leader is exiting
+            too.  Getting this 100% right is saved for a future cygwin mailing
+            list goad.  */
+         if (global_sigs[SIGHUP].sa_handler != SIG_DFL)
+           {
+             sig_send (myself_nowait, SIGHUP);
+             return TRUE;
+           }
          return FALSE;
        }
     }
 
-  /* If we are a stub and the new process has a pinfo structure, let it
-     handle this signal. */
-  if (dwExeced && pinfo (dwExeced))
+  if (ch_spawn.set_saw_ctrl_c ())
     return TRUE;
 
   /* We're only the process group leader when we have a valid pinfo structure.
@@ -957,10 +990,10 @@ ctrl_c_handler (DWORD type)
   if (!pinfo (cygwin_pid (GetCurrentProcessId ())))
     return TRUE;
 
-  tty_min *t = cygwin_shared->tty.get_tty (myself->ctty);
+  tty_min *t = cygwin_shared->tty.get_cttyp ();
   /* Ignore this if we're not the process group leader since it should be handled
      *by* the process group leader. */
-  if (myself->ctty != -1 && t->getpgid () == myself->pid &&
+  if (t && t->getpgid () == myself->pid &&
        (GetTickCount () - t->last_ctrl_c) >= MIN_CTRL_C_SLOP)
     /* Otherwise we just send a SIGINT to the process group and return TRUE (to indicate
        that we have handled the signal).  At this point, type should be
@@ -984,8 +1017,7 @@ ctrl_c_handler (DWORD type)
 extern "C" void __stdcall
 set_process_mask (sigset_t newmask)
 {
-  set_signal_mask (newmask, myself->getsigmask ());
-sigproc_printf ("mask now %p\n", myself->getsigmask ());
+  set_signal_mask (newmask, _my_tls.sigmask);
 }
 
 extern "C" int
@@ -999,9 +1031,9 @@ sighold (int sig)
       return -1;
     }
   mask_sync.acquire (INFINITE);
-  sigset_t mask = myself->getsigmask ();
+  sigset_t mask = _my_tls.sigmask;
   sigaddset (&mask, sig);
-  set_signal_mask (mask, myself->getsigmask ());
+  set_signal_mask (mask, _my_tls.sigmask);
   mask_sync.release ();
   return 0;
 }
@@ -1017,9 +1049,9 @@ sigrelse (int sig)
       return -1;
     }
   mask_sync.acquire (INFINITE);
-  sigset_t mask = myself->getsigmask ();
+  sigset_t mask = _my_tls.sigmask;
   sigdelset (&mask, sig);
-  set_signal_mask (mask, myself->getsigmask ());
+  set_signal_mask (mask, _my_tls.sigmask);
   mask_sync.release ();
   return 0;
 }
@@ -1039,7 +1071,7 @@ sigset (int sig, _sig_func_ptr func)
     }
 
   mask_sync.acquire (INFINITE);
-  sigset_t mask = myself->getsigmask ();
+  sigset_t mask = _my_tls.sigmask;
   /* If sig was in the signal mask return SIG_HOLD, otherwise return the
      previous disposition. */
   if (sigismember (&mask, sig))
@@ -1053,11 +1085,11 @@ sigset (int sig, _sig_func_ptr func)
   else
     {
       /* No error checking.  The test which could return SIG_ERR has already
-         been made above. */
+        been made above. */
       signal (sig, func);
       sigdelset (&mask, sig);
     }
-  set_signal_mask (mask, myself->getsigmask ());
+  set_signal_mask (mask, _my_tls.sigmask);
   mask_sync.release ();
   return prev;
 }
@@ -1079,11 +1111,11 @@ set_process_mask_delta ()
   if (_my_tls.deltamask & SIG_NONMASKABLE)
     oldmask = _my_tls.oldmask; /* from handle_sigsuspend */
   else
-    oldmask = myself->getsigmask ();
+    oldmask = _my_tls.sigmask;
   newmask = (oldmask | _my_tls.deltamask) & ~SIG_NONMASKABLE;
   sigproc_printf ("oldmask %p, newmask %p, deltamask %p", oldmask, newmask,
                  _my_tls.deltamask);
-  myself->setsigmask (newmask);
+  _my_tls.sigmask = newmask;
   mask_sync.release ();
   return oldmask;
 }
@@ -1114,6 +1146,8 @@ int __stdcall
 sigpacket::process ()
 {
   DWORD continue_now;
+  struct sigaction dummy = global_sigs[SIGSTOP];
+
   if (si.si_signo != SIGCONT)
     continue_now = false;
   else
@@ -1128,6 +1162,19 @@ sigpacket::process ()
       sig_clear (SIGTTOU);
     }
 
+  switch (si.si_signo)
+    {
+    case SIGINT:
+    case SIGQUIT:
+    case SIGSTOP:
+    case SIGTSTP:
+      if (cygheap->ctty)
+       cygheap->ctty->sigflush ();
+      break;
+    default:
+      break;
+    }
+
   int rc = 1;
 
   sigproc_printf ("signal %d processing", si.si_signo);
@@ -1137,30 +1184,35 @@ sigpacket::process ()
 
   bool masked;
   void *handler;
-  if (!hExeced || (void *) thissig.sa_handler == (void *) SIG_IGN)
+  if (!have_execed || (void *) thissig.sa_handler == (void *) SIG_IGN)
     handler = (void *) thissig.sa_handler;
   else if (tls)
     return 1;
   else
     handler = NULL;
 
+  _cygtls *use_tls = tls ?: _main_tls;
+
   if (si.si_signo == SIGKILL)
     goto exit_sig;
   if (si.si_signo == SIGSTOP)
     {
       sig_clear (SIGCONT);
-      if (!tls)
-       tls = _main_tls;
       goto stop;
     }
 
   bool insigwait_mask;
   if ((masked = ISSTATE (myself, PID_STOPPED)))
     insigwait_mask = false;
-  else if (!tls)
-    insigwait_mask = !handler && (tls = _cygtls::find_tls (si.si_signo));
-  else
+  else if (tls)
     insigwait_mask = sigismember (&tls->sigwait_mask, si.si_signo);
+  else if (!(tls = _cygtls::find_tls (si.si_signo)))
+    insigwait_mask = false;
+  else
+    {
+      use_tls = tls;
+      insigwait_mask = true;
+    }
 
   if (insigwait_mask)
     goto thread_specific;
@@ -1172,9 +1224,6 @@ sigpacket::process ()
   else if (tls)
     masked  = sigismember (&tls->sigmask, si.si_signo);
 
-  if (!tls)
-    tls = _main_tls;
-
   if (masked)
     {
       sigproc_printf ("signal %d blocked", si.si_signo);
@@ -1223,7 +1272,7 @@ sigpacket::process ()
   if (handler == (void *) SIG_ERR)
     goto exit_sig;
 
-  tls->set_siginfo (this);
+  use_tls->set_siginfo (this);
   goto dosig;
 
 stop:
@@ -1231,102 +1280,36 @@ stop:
   if (ISSTATE (myself, PID_STOPPED))
     goto done;
   handler = (void *) sig_handle_tty_stop;
-  struct sigaction dummy = global_sigs[SIGSTOP];
   thissig = dummy;
 
 dosig:
   /* Dispatch to the appropriate function. */
   sigproc_printf ("signal %d, about to call %p", si.si_signo, handler);
-  rc = setup_handler (si.si_signo, handler, thissig, tls);
+  rc = setup_handler (si.si_signo, handler, thissig, use_tls);
 
 done:
+  tls = use_tls;
   if (continue_now)
     SetEvent (sigCONT);
   sigproc_printf ("returning %d", rc);
   return rc;
 
 thread_specific:
-  tls->sig = si.si_signo;
-  tls->set_siginfo (this);
+  use_tls->sig = si.si_signo;
+  use_tls->set_siginfo (this);
+  use_tls->func = NULL;
   sigproc_printf ("releasing sigwait for thread");
-  SetEvent (tls->event);
+  SetEvent (use_tls->event);
   goto done;
 
 exit_sig:
-  if (si.si_signo == SIGQUIT || si.si_signo == SIGABRT)
-    {
-      CONTEXT c;
-      c.ContextFlags = CONTEXT_FULL;
-      GetThreadContext (hMainThread, &c);
-      tls->copy_context (&c);
-      si.si_signo |= 0x80;
-    }
-  sigproc_printf ("signal %d, about to call do_exit", si.si_signo);
-  tls->signal_exit (si.si_signo);      /* never returns */
+  use_tls->signal_exit (si.si_signo);  /* never returns */
 }
 
-/* Cover function to `do_exit' to handle exiting even in presence of more
-   exceptions.  We used to call exit, but a SIGSEGV shouldn't cause atexit
-   routines to run.  */
-void
-_cygtls::signal_exit (int rc)
-{
-  if (hExeced)
-    {
-      sigproc_printf ("terminating captive process");
-      TerminateProcess (hExeced, sigExeced = rc);
-    }
-
-  signal_debugger (rc & 0x7f);
-  if ((rc & 0x80) && !try_to_debug ())
-    stackdump (thread_context.ebp, 1, 1);
-
-  lock_process until_exit (true);
-  if (hExeced || exit_state)
-    myself.exit (rc);
-
-  /* Starve other threads in a vain attempt to stop them from doing something
-     stupid. */
-  SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
-
-  user_data->resourcelocks->Delete ();
-  user_data->resourcelocks->Init ();
-
-  sigproc_printf ("about to call do_exit (%x)", rc);
-  SetEvent (signal_arrived);
-  do_exit (rc);
-}
-
-HANDLE NO_COPY tty_mutex = NULL;
-
 void
 events_init ()
 {
-  char *name;
-  char mutex_name[CYG_MAX_PATH];
-  /* tty_mutex is on while searching for a tty slot. It's necessary
-     while finding console window handle */
-
-  if (!(tty_mutex = CreateMutex (&sec_all_nih, FALSE,
-                                  name = shared_name (mutex_name,
-                                                      "tty_mutex", 0))))
-    api_fatal ("can't create title mutex '%s', %E", name);
-
-  ProtectHandle (tty_mutex);
   mask_sync.init ("mask_sync");
-  windows_system_directory[0] = '\0';
-  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;
-  debug_printf ("windows_system_directory '%s', windows_system_directory_length %d",
-               windows_system_directory, windows_system_directory_length);
 }
 
 void
@@ -1338,52 +1321,62 @@ events_terminate ()
 int
 _cygtls::call_signal_handler ()
 {
-  int this_sa_flags = 0;
-  /* Call signal handler.  */
-  while (sig)
+  int this_sa_flags = SA_RESTART;
+  while (1)
     {
       lock ();
+      if (sig)
+       pop ();
+      else if (this != _main_tls)
+       {
+         _main_tls->lock ();
+         if (_main_tls->sig)
+           {
+             sig = _main_tls->sig;
+             sa_flags = _main_tls->sa_flags;
+             func = _main_tls->func;
+             infodata = _main_tls->infodata;
+             _main_tls->pop ();
+             _main_tls->sig = 0;
+
+           }
+         _main_tls->unlock ();
+       }
+      if (!sig)
+       break;
+
       this_sa_flags = sa_flags;
       int thissig = sig;
+      void (*thisfunc) (int) = func;
 
-      pop ();
-      reset_signal_arrived ();
       sigset_t this_oldmask = set_process_mask_delta ();
       int this_errno = saved_errno;
       sig = 0;
       unlock ();       // make sure synchronized
-      incyg = 0;
       if (!(this_sa_flags & SA_SIGINFO))
        {
-         void (*sigfunc) (int) = func;
+         void (*sigfunc) (int) = thisfunc;
+         incyg = false;
          sigfunc (thissig);
        }
       else
        {
          siginfo_t thissi = infodata;
-         void (*sigact) (int, siginfo_t *, void *) = (void (*) (int, siginfo_t *, void *)) func;
+         void (*sigact) (int, siginfo_t *, void *) = (void (*) (int, siginfo_t *, void *)) thisfunc;
          /* no ucontext_t information provided yet */
+         incyg = false;
          sigact (thissig, &thissi, NULL);
        }
-      incyg = 1;
-      set_signal_mask (this_oldmask, myself->getsigmask ());
+      incyg = true;
+      set_signal_mask (this_oldmask, _my_tls.sigmask);
       if (this_errno >= 0)
        set_errno (this_errno);
     }
 
+  unlock ();
   return this_sa_flags & SA_RESTART;
 }
 
-extern "C" void __stdcall
-reset_signal_arrived ()
-{
-  // NEEDED? WaitForSingleObject (signal_arrived, 10);
-  ResetEvent (signal_arrived);
-  sigproc_printf ("reset signal_arrived");
-  if (_my_tls.stackptr > _my_tls.stack)
-    debug_printf ("stackptr[-1] %p", _my_tls.stackptr[-1]);
-}
-
 void
 _cygtls::copy_context (CONTEXT *c)
 {
@@ -1393,7 +1386,7 @@ _cygtls::copy_context (CONTEXT *c)
 void
 _cygtls::signal_debugger (int sig)
 {
-  if (being_debugged ())
+  if (isinitialized () && being_debugged ())
     {
       char sigmsg[2 * sizeof (_CYGWIN_SIGNAL_STRING " ffffffff ffffffff")];
       __small_sprintf (sigmsg, _CYGWIN_SIGNAL_STRING " %d %p %p", sig, thread_id, &thread_context);