]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add support for pthread_sigmask() and sigwait(). All absolutely
authorJulian Seward <jseward@acm.org>
Mon, 13 May 2002 00:16:03 +0000 (00:16 +0000)
committerJulian Seward <jseward@acm.org>
Mon, 13 May 2002 00:16:03 +0000 (00:16 +0000)
horrible, especially the latter.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@266

20 files changed:
coregrind/arch/x86-linux/vg_libpthread.c
coregrind/arch/x86-linux/vg_libpthread_unimp.c
coregrind/vg_errcontext.c
coregrind/vg_include.h
coregrind/vg_kerneliface.h
coregrind/vg_libpthread.c
coregrind/vg_libpthread_unimp.c
coregrind/vg_mylibc.c
coregrind/vg_scheduler.c
coregrind/vg_signals.c
tests/pth_signal1.c [new file with mode: 0644]
tests/signal2.c
vg_errcontext.c
vg_include.h
vg_kerneliface.h
vg_libpthread.c
vg_libpthread_unimp.c
vg_mylibc.c
vg_scheduler.c
vg_signals.c

index 5e6e14c61ca9232265676fc86a5070e556a6d9ec..a7dfb70259cf6e3c9e04e1d456f288bb04d3aad1 100644 (file)
@@ -616,6 +616,61 @@ void __pthread_kill_other_threads_np ( void )
 }
 
 
+/* ---------------------------------------------------
+   SIGNALS
+   ------------------------------------------------ */
+
+#include <signal.h>
+
+int pthread_sigmask(int how, const sigset_t *newmask, 
+                             sigset_t *oldmask)
+{
+   int res;
+
+   /* A bit subtle, because the scheduler expects newmask and oldmask
+      to be vki_sigset_t* rather than sigset_t*, and the two are
+      different.  Fortunately the first 64 bits of a sigset_t are
+      exactly a vki_sigset_t, so we just pass the pointers through
+      unmodified.  Haaaack! 
+
+      Also mash the how value so that the SIG_ constants from glibc
+      do not have to be included into vg_scheduler.c. */
+
+   ensure_valgrind("pthread_sigmask");
+
+   switch (how) {
+      case SIG_SETMASK: how = 1; break;
+      case SIG_BLOCK:   how = 2; break;
+      case SIG_UNBLOCK: how = 3; break;
+      default:          return EINVAL;
+   }
+
+   /* Crude check */
+   if (newmask == NULL)
+      return EFAULT;
+
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__PTHREAD_SIGMASK,
+                           how, newmask, oldmask, 0);
+
+   /* The scheduler tells us of any memory violations. */
+   return res == 0 ? 0 : EFAULT;
+}
+
+
+int sigwait ( const sigset_t* set, int* sig )
+{
+   int res;
+   ensure_valgrind("sigwait");
+   /* As with pthread_sigmask we deliberately confuse sigset_t with
+      vki_ksigset_t. */
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__SIGWAIT,
+                           set, sig, 0, 0);
+   return res;
+}
+
+
 /* ---------------------------------------------------
    THREAD-SPECIFICs
    ------------------------------------------------ */
@@ -872,7 +927,6 @@ void* (*__libc_internal_tsd_get)
    ------------------------------------------------------------------ */
 
 #include <stdlib.h>
-#include <signal.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
index c592e3215f436c5eb702b92f3c1f3a32b1bc5678..4157af6c667722949af104077f7b82fef6862fae 100644 (file)
@@ -150,7 +150,7 @@ void pthread_rwlockattr_setpshared ( void )  { unimp("pthread_rwlockattr_setpsha
 //void pthread_setcanceltype ( void )  { unimp("pthread_setcanceltype"); }
 //void pthread_setschedparam ( void )  { unimp("pthread_setschedparam"); }
 //void pthread_setspecific ( void )  { unimp("pthread_setspecific"); }
-void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
+//void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
 //void pthread_testcancel ( void )  { unimp("pthread_testcancel"); }
 void raise ( void )  { unimp("raise"); }
 void sem_close ( void )  { unimp("sem_close"); }
@@ -159,7 +159,7 @@ void sem_timedwait ( void )  { unimp("sem_timedwait"); }
 void sem_unlink ( void )  { unimp("sem_unlink"); }
 //void sigaction ( void )  { unimp("sigaction"); }
 void siglongjmp ( void )  { unimp("siglongjmp"); }
-void sigwait ( void )  { unimp("sigwait"); }
+//void sigwait ( void )  { unimp("sigwait"); }
 
 #if 0
 void pthread_create@@GLIBC_2.1 ( void )  { unimp("pthread_create@@GLIBC_2.1"); }
index aabbe430985ab98faa05ae405fcf074bb42c566d..61753391c00971b07510998e3f5b3431e63aac5d 100644 (file)
@@ -288,7 +288,7 @@ static void pp_AddrInfo ( Addr a, AddrInfo* ai )
       case Stack: 
          VG_(message)(Vg_UserMsg, 
                       "   Address 0x%x is on thread %d's stack", 
-                      ai->stack_tid, a);
+                      a, ai->stack_tid);
          break;
       case Unknown:
          if (ai->maybe_gcc) {
index 765d719f4f922535fd628f35b7e53a3bfa149449..d1cfca053b3e84d89fe7ac82bf31f560412a75a3 100644 (file)
@@ -433,6 +433,8 @@ extern Bool  VG_(is_empty_arena) ( ArenaId aid );
 #define VG_USERREQ__PTHREAD_SETSPECIFIC     0x300F
 #define VG_USERREQ__PTHREAD_GETSPECIFIC     0x3010
 #define VG_USERREQ__READ_MILLISECOND_TIMER  0x3011
+#define VG_USERREQ__PTHREAD_SIGMASK         0x3012
+#define VG_USERREQ__SIGWAIT                 0x3013
 
 /* Cosmetic ... */
 #define VG_USERREQ__GET_PTHREAD_TRACE_LEVEL 0x3101
@@ -478,6 +480,7 @@ typedef
       VgTs_WaitFD,     /* waiting for I/O completion on a fd */
       VgTs_WaitMX,     /* waiting on a mutex */
       VgTs_WaitCV,     /* waiting on a condition variable */
+      VgTs_WaitSIG,    /* waiting due to sigwait() */
       VgTs_Sleeping    /* sleeping for a while */
    }
    ThreadStatus;
@@ -530,6 +533,19 @@ typedef
       /* thread-specific data */
       void* specifics[VG_N_THREAD_KEYS];
 
+      /* This thread's blocked-signals mask.  Semantics is that for a
+         signal to be delivered to this thread, the signal must not be
+         blocked by either the process-wide signal mask nor by this
+         one.  So, if this thread is prepared to handle any signal that
+         the process as a whole is prepared to handle, this mask should
+         be made empty -- and that it is its default, starting
+         state. */
+      vki_ksigset_t sig_mask;
+
+      /* When not VgTs_WaitSIG, has no meaning.  When VgTs_WaitSIG,
+         is the set of signals for which we are sigwait()ing. */
+      vki_ksigset_t sigs_waited_for;
+
       /* Stacks.  When a thread slot is freed, we don't deallocate its
          stack; we just leave it lying around for the next use of the
          slot.  If the next use of the slot requires a larger stack,
@@ -581,6 +597,9 @@ typedef
    ThreadState;
 
 
+/* Trivial range check on tid. */
+extern Bool VG_(is_valid_tid) ( ThreadId tid );
+
 /* Copy the specified thread's state into VG_(baseBlock) in
    preparation for running it. */
 extern void VG_(load_thread_state)( ThreadId );
@@ -591,6 +610,7 @@ extern void VG_(save_thread_state)( ThreadId );
 
 /* Get the thread state block for the specified thread. */
 extern ThreadState* VG_(get_thread_state)( ThreadId );
+extern ThreadState* VG_(get_thread_state_UNCHECKED)( ThreadId );
 
 /* And for the currently running one, if valid. */
 extern ThreadState* VG_(get_current_thread_state) ( void );
@@ -663,9 +683,10 @@ extern Int     VG_(longjmpd_on_signal);
 
 extern void VG_(sigstartup_actions) ( void );
 
-extern Bool VG_(deliver_signals) ( ThreadId );
+extern Bool VG_(deliver_signals) ( void );
 extern void VG_(unblock_host_signal) ( Int sigNo );
-
+extern void VG_(notify_signal_machinery_of_thread_exit) ( ThreadId tid );
+extern void VG_(update_sigstate_following_WaitSIG_change) ( void );
 
 /* Fake system calls for signal handling. */
 extern void VG_(do__NR_sigaction)     ( ThreadId tid );
@@ -797,9 +818,10 @@ extern Int VG_(system) ( Char* cmd );
    definitions, which are different in places from those that glibc
    defines.  Since we're operating right at the kernel interface,
    glibc's view of the world is entirely irrelevant. */
-extern Int VG_(ksigfillset)( vki_ksigset_t* set );
-extern Int VG_(ksigemptyset)( vki_ksigset_t* set );
-extern Int VG_(ksigaddset)( vki_ksigset_t* set, Int signum );
+extern Int  VG_(ksigfillset)( vki_ksigset_t* set );
+extern Int  VG_(ksigemptyset)( vki_ksigset_t* set );
+extern Bool VG_(kisemptysigset)( vki_ksigset_t* set );
+extern Int  VG_(ksigaddset)( vki_ksigset_t* set, Int signum );
 
 extern Int VG_(ksigprocmask)( Int how, const vki_ksigset_t* set, 
                                        vki_ksigset_t* oldset );
@@ -807,6 +829,11 @@ extern Int VG_(ksigaction) ( Int signum,
                              const vki_ksigaction* act,  
                              vki_ksigaction* oldact );
 extern Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum );
+extern void VG_(ksigaddset_from_set)( vki_ksigset_t* dst, 
+                                      vki_ksigset_t* src );
+extern void VG_(ksigdelset_from_set)( vki_ksigset_t* dst, 
+                                      vki_ksigset_t* src );
+
 
 extern Int VG_(ksignal)(Int signum, void (*sighandler)(Int));
 
index a7690dd732218e0134ba133b562058888cdce838..b42ab44a80ed5592733212f6e7c6a400e6b1636b 100644 (file)
@@ -71,6 +71,7 @@ typedef
    }
    vki_ksigset_t;
 
+
 typedef
    struct {
       void*         ksa_handler;
@@ -89,6 +90,8 @@ typedef
    vki_kstack_t;
 
 
+
+
 #define VKI_SIG_BLOCK          0    /* for blocking signals */
 #define VKI_SIG_UNBLOCK        1    /* for unblocking signals */
 #define VKI_SIG_SETMASK        2    /* for setting the signal mask */
@@ -297,14 +300,13 @@ struct vki_stat {
 };
 
 
-/* To do with the ELF constructed by the kernel on a process' stack
-   just before it transfers control to the program's interpreter
-   (to use the ELF parlance).  
+/* To do with the ELF frame constructed by the kernel on a process'
+   stack just before it transfers control to the program's interpreter
+   (to use the ELF parlance).
    Constants from /usr/src/linux-2.4.9-31/include/linux/elf.h
    Logic from     /usr/src/linux-2.4.9-31/fs/binfmt_elf.c
                   and its counterpart in the 2.2.14 kernel sources 
-                  in Red Hat 6.2.
-*/
+                  in Red Hat 6.2.  */
 #define VKI_AT_CLKTCK 17    /* frequency at which times() increments */
 #define VKI_AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
 #define VKI_AT_BASE   7     /* base address of interpreter */
index 5e6e14c61ca9232265676fc86a5070e556a6d9ec..a7dfb70259cf6e3c9e04e1d456f288bb04d3aad1 100644 (file)
@@ -616,6 +616,61 @@ void __pthread_kill_other_threads_np ( void )
 }
 
 
+/* ---------------------------------------------------
+   SIGNALS
+   ------------------------------------------------ */
+
+#include <signal.h>
+
+int pthread_sigmask(int how, const sigset_t *newmask, 
+                             sigset_t *oldmask)
+{
+   int res;
+
+   /* A bit subtle, because the scheduler expects newmask and oldmask
+      to be vki_sigset_t* rather than sigset_t*, and the two are
+      different.  Fortunately the first 64 bits of a sigset_t are
+      exactly a vki_sigset_t, so we just pass the pointers through
+      unmodified.  Haaaack! 
+
+      Also mash the how value so that the SIG_ constants from glibc
+      do not have to be included into vg_scheduler.c. */
+
+   ensure_valgrind("pthread_sigmask");
+
+   switch (how) {
+      case SIG_SETMASK: how = 1; break;
+      case SIG_BLOCK:   how = 2; break;
+      case SIG_UNBLOCK: how = 3; break;
+      default:          return EINVAL;
+   }
+
+   /* Crude check */
+   if (newmask == NULL)
+      return EFAULT;
+
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__PTHREAD_SIGMASK,
+                           how, newmask, oldmask, 0);
+
+   /* The scheduler tells us of any memory violations. */
+   return res == 0 ? 0 : EFAULT;
+}
+
+
+int sigwait ( const sigset_t* set, int* sig )
+{
+   int res;
+   ensure_valgrind("sigwait");
+   /* As with pthread_sigmask we deliberately confuse sigset_t with
+      vki_ksigset_t. */
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__SIGWAIT,
+                           set, sig, 0, 0);
+   return res;
+}
+
+
 /* ---------------------------------------------------
    THREAD-SPECIFICs
    ------------------------------------------------ */
@@ -872,7 +927,6 @@ void* (*__libc_internal_tsd_get)
    ------------------------------------------------------------------ */
 
 #include <stdlib.h>
-#include <signal.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
index c592e3215f436c5eb702b92f3c1f3a32b1bc5678..4157af6c667722949af104077f7b82fef6862fae 100644 (file)
@@ -150,7 +150,7 @@ void pthread_rwlockattr_setpshared ( void )  { unimp("pthread_rwlockattr_setpsha
 //void pthread_setcanceltype ( void )  { unimp("pthread_setcanceltype"); }
 //void pthread_setschedparam ( void )  { unimp("pthread_setschedparam"); }
 //void pthread_setspecific ( void )  { unimp("pthread_setspecific"); }
-void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
+//void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
 //void pthread_testcancel ( void )  { unimp("pthread_testcancel"); }
 void raise ( void )  { unimp("raise"); }
 void sem_close ( void )  { unimp("sem_close"); }
@@ -159,7 +159,7 @@ void sem_timedwait ( void )  { unimp("sem_timedwait"); }
 void sem_unlink ( void )  { unimp("sem_unlink"); }
 //void sigaction ( void )  { unimp("sigaction"); }
 void siglongjmp ( void )  { unimp("siglongjmp"); }
-void sigwait ( void )  { unimp("sigwait"); }
+//void sigwait ( void )  { unimp("sigwait"); }
 
 #if 0
 void pthread_create@@GLIBC_2.1 ( void )  { unimp("pthread_create@@GLIBC_2.1"); }
index f42ba9400b606679f7dfb696a26f9aae449739a4..4b4c8f310fdb4e4eda26ad8520cef1dbb8a499e3 100644 (file)
@@ -154,6 +154,15 @@ Int VG_(ksigemptyset)( vki_ksigset_t* set )
    return 0;
 }
 
+Bool VG_(kisemptysigset)( vki_ksigset_t* set )
+{
+   Int i;
+   vg_assert(set != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      if (set->ws[i] != 0x0) return False;
+   return True;
+}
+
 Int VG_(ksigaddset)( vki_ksigset_t* set, Int signum )
 {
    if (set == NULL)
@@ -168,9 +177,9 @@ Int VG_(ksigaddset)( vki_ksigset_t* set, Int signum )
 Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum )
 {
    if (set == NULL)
-      return -1;
+      return 0;
    if (signum < 1 && signum > VKI_KNSIG)
-      return -1;
+      return 0;
    signum--;
    if (1 & ((set->ws[signum / VKI_KNSIG_BPW]) >> (signum % VKI_KNSIG_BPW)))
       return 1;
@@ -179,6 +188,25 @@ Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum )
 }
 
 
+/* Add all signals in src to dst. */
+void VG_(ksigaddset_from_set)( vki_ksigset_t* dst, vki_ksigset_t* src )
+{
+   Int i;
+   vg_assert(dst != NULL && src != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      dst->ws[i] |= src->ws[i];
+}
+
+/* Remove all signals in src from dst. */
+void VG_(ksigdelset_from_set)( vki_ksigset_t* dst, vki_ksigset_t* src )
+{
+   Int i;
+   vg_assert(dst != NULL && src != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      dst->ws[i] &= ~(src->ws[i]);
+}
+
+
 /* The functions sigaction, sigprocmask, sigpending and sigsuspend
    return 0 on success and -1 on error.  
 */
index 53edabb28d841a65efac2f106c37b45dd09024c6..d50fe93e0deeb22f083acbff6d997125a9be2379 100644 (file)
@@ -30,7 +30,6 @@
 
 #include "vg_include.h"
 #include "vg_constants.h"
-
 #include "valgrind.h" /* for VG_USERREQ__MAKE_NOACCESS and
                          VG_USERREQ__DO_LEAK_CHECK */
 
@@ -156,8 +155,8 @@ static void do_pthread_getspecific ( ThreadId,
    Helper functions for the scheduler.
    ------------------------------------------------------------------ */
 
-static __inline__
-Bool is_valid_tid ( ThreadId tid )
+__inline__
+Bool VG_(is_valid_tid) ( ThreadId tid )
 {
    /* tid is unsigned, hence no < 0 test. */
    if (tid == 0) return False;
@@ -216,6 +215,7 @@ void VG_(pp_sched_status) ( void )
          case VgTs_Sleeping:   VG_(printf)("Sleeping"); break;
          case VgTs_WaitMX:     VG_(printf)("WaitMX"); break;
          case VgTs_WaitCV:     VG_(printf)("WaitCV"); break;
+         case VgTs_WaitSIG:    VG_(printf)("WaitSIG"); break;
          default: VG_(printf)("???"); break;
       }
       VG_(printf)(", associated_mx = %p, associated_cv = %p\n", 
@@ -340,9 +340,16 @@ ThreadId vg_alloc_ThreadState ( void )
 }
 
 
+ThreadState* VG_(get_thread_state_UNCHECKED) ( ThreadId tid )
+{
+   vg_assert(VG_(is_valid_tid)(tid));
+   return & vg_threads[tid];
+}
+
+
 ThreadState* VG_(get_thread_state) ( ThreadId tid )
 {
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
    return & vg_threads[tid];
 }
@@ -461,7 +468,7 @@ static
 UInt run_thread_for_a_while ( ThreadId tid )
 {
    volatile UInt trc = 0;
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
    vg_assert(VG_(bbs_to_go) > 0);
 
@@ -541,6 +548,8 @@ void VG_(scheduler_init) ( void )
       vg_threads[i].stack_size = 0;
       vg_threads[i].stack_base = (Addr)NULL;
       vg_threads[i].tid        = i;
+      VG_(ksigemptyset)(&vg_threads[i].sig_mask);
+      VG_(ksigemptyset)(&vg_threads[i].sigs_waited_for);
    }
 
    for (i = 0; i < VG_N_WAITING_FDS; i++)
@@ -714,7 +723,7 @@ void cleanup_waiting_fd_table ( ThreadId tid )
 {
    Int  i, waiters;
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_WaitFD);
    vg_assert(vg_threads[tid].m_eax == __NR_read 
              || vg_threads[tid].m_eax == __NR_write);
@@ -744,7 +753,7 @@ void handle_signal_return ( ThreadId tid )
    Char msg_buf[100];
    Bool restart_blocked_syscalls;
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
 
    restart_blocked_syscalls = VG_(signal_returns)(tid);
 
@@ -793,7 +802,7 @@ void sched_do_syscall ( ThreadId tid )
    Bool orig_fd_blockness;
    Char msg_buf[100];
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    syscall_no = vg_threads[tid].m_eax; /* syscall number */
@@ -969,7 +978,7 @@ void poll_for_ready_fds ( void )
       if (fd > fd_max) 
          fd_max = fd;
       tid = vg_waiting_fds[i].tid;
-      vg_assert(is_valid_tid(tid));
+      vg_assert(VG_(is_valid_tid)(tid));
       syscall_no = vg_waiting_fds[i].syscall_no;
       switch (syscall_no) {
          case __NR_read:
@@ -1075,7 +1084,7 @@ void complete_blocked_syscalls ( void )
 
       fd  = vg_waiting_fds[i].fd;
       tid = vg_waiting_fds[i].tid;
-      vg_assert(is_valid_tid(tid));
+      vg_assert(VG_(is_valid_tid)(tid));
 
       /* The thread actually has to be waiting for the I/O event it
          requested before we can deliver the result! */
@@ -1214,7 +1223,7 @@ VgSchedReturnCode VG_(scheduler) ( void )
             even if, as a result, it has missed the unlocking of it.
             Potential deadlock.  This sounds all very strange, but the
             POSIX standard appears to require this behaviour.  */
-         sigs_delivered = VG_(deliver_signals)( 1 /*HACK*/ );
+         sigs_delivered = VG_(deliver_signals)();
         if (sigs_delivered)
             VG_(do_sanity_checks)( False );
 
@@ -1226,6 +1235,7 @@ VgSchedReturnCode VG_(scheduler) ( void )
             if (tid_next >= VG_N_THREADS) tid_next = 1;
             if (vg_threads[tid_next].status == VgTs_WaitFD
                 || vg_threads[tid_next].status == VgTs_Sleeping
+                || vg_threads[tid_next].status == VgTs_WaitSIG
                 || (vg_threads[tid_next].status == VgTs_WaitCV 
                     && vg_threads[tid_next].awaken_at != 0xFFFFFFFF))
                n_in_bounded_wait ++;
@@ -1257,7 +1267,7 @@ VgSchedReturnCode VG_(scheduler) ( void )
             thread becomes runnable. */
          nanosleep_for_a_while();
         /* pp_sched_status(); */
-        /* VG_(printf)(".\n"); */
+        /* VG_(printf)("."); */
       }
 
 
@@ -1524,16 +1534,37 @@ VgSchedReturnCode VG_(scheduler) ( void )
    Thread CREATION, JOINAGE and CANCELLATION.
    -------------------------------------------------------- */
 
+/* Release resources and generally clean up once a thread has finally
+   disappeared. */
+static
+void cleanup_after_thread_exited ( ThreadId tid )
+{
+   vg_assert(VG_(is_valid_tid)(tid));
+   vg_assert(vg_threads[tid].status == VgTs_Empty);
+   /* Mark its stack no-access */
+   if (VG_(clo_instrument) && tid != 1)
+      VGM_(make_noaccess)( vg_threads[tid].stack_base,
+                           vg_threads[tid].stack_size );
+   /* Forget about any pending signals directed specifically at this
+      thread. */
+   VG_(notify_signal_machinery_of_thread_exit)( tid );
+
+   /* Get rid of signal handlers specifically arranged for this
+      thread. */
+   VG_(update_sigstate_following_WaitSIG_change)();
+}
+
+
 static
 void do_pthread_cancel ( ThreadId  tid,
                          pthread_t tid_cancellee )
 {
    Char msg_buf[100];
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
 
-   if (!is_valid_tid(tid_cancellee)
+   if (!VG_(is_valid_tid)(tid_cancellee)
        || vg_threads[tid_cancellee].status == VgTs_Empty) {
       SET_EDX(tid, ESRCH);
       return;
@@ -1588,7 +1619,7 @@ void handle_pthread_return ( ThreadId tid, void* retval )
 
    /* Mark it as not in use.  Leave the stack in place so the next
       user of this slot doesn't reallocate it. */
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
 
    vg_threads[tid].retval = retval;
@@ -1611,7 +1642,7 @@ void handle_pthread_return ( ThreadId tid, void* retval )
          call.  TODO: free properly the slot (also below). 
       */
       jnr = vg_threads[tid].joiner;
-      vg_assert(is_valid_tid(jnr));
+      vg_assert(VG_(is_valid_tid)(jnr));
       vg_assert(vg_threads[jnr].status == VgTs_WaitJoinee);
       jnr_args = (UInt*)vg_threads[jnr].m_eax;
       jnr_thread_return = (void**)(jnr_args[2]);
@@ -1620,9 +1651,7 @@ void handle_pthread_return ( ThreadId tid, void* retval )
       SET_EDX(jnr, 0); /* success */
       vg_threads[jnr].status = VgTs_Runnable;
       vg_threads[tid].status = VgTs_Empty; /* bye! */
-      if (VG_(clo_instrument) && tid != 0)
-         VGM_(make_noaccess)( vg_threads[tid].stack_base,
-                              vg_threads[tid].stack_size );
+      cleanup_after_thread_exited ( tid );
       if (VG_(clo_trace_sched)) {
          VG_(sprintf)(msg_buf, 
             "root fn returns, to find a waiting pthread_join(%d)", tid);
@@ -1645,7 +1674,7 @@ void do_pthread_join ( ThreadId tid, ThreadId jee, void** thread_return )
 
    /* jee, the joinee, is the thread specified as an arg in thread
       tid's call to pthread_join.  So tid is the join-er. */
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    if (jee == tid) {
@@ -1687,9 +1716,7 @@ void do_pthread_join ( ThreadId tid, ThreadId jee, void** thread_return )
       }
       vg_threads[tid].status = VgTs_Runnable;
       vg_threads[jee].status = VgTs_Empty; /* bye! */
-      if (VG_(clo_instrument) && jee != 0)
-         VGM_(make_noaccess)( vg_threads[jee].stack_base,
-                              vg_threads[jee].stack_size );
+      cleanup_after_thread_exited ( jee );
       if (VG_(clo_trace_sched)) {
         VG_(sprintf)(msg_buf,
                      "someone called pthread_join() on me; bye!");
@@ -1736,7 +1763,7 @@ void do_pthread_create ( ThreadId parent_tid,
 
    /* If we've created the main thread's tid, we're in deep trouble :) */
    vg_assert(tid != 1);
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
 
    /* Copy the parent's CPU state into the child's, in a roundabout
       way (via baseBlock). */
@@ -1807,6 +1834,10 @@ void do_pthread_create ( ThreadId parent_tid,
    for (i = 0; i < VG_N_THREAD_KEYS; i++)
       vg_threads[tid].specifics[i] = NULL;
 
+   /* We inherit our parent's signal mask. (?!) */
+   vg_threads[tid].sig_mask = vg_threads[parent_tid].sig_mask;
+   VG_(ksigemptyset)(&vg_threads[i].sigs_waited_for);
+
    /* return zero */
    SET_EDX(parent_tid, 0); /* success */
 }
@@ -1920,7 +1951,7 @@ void do_pthread_mutex_lock( ThreadId tid,
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    /* POSIX doesn't mandate this, but for sanity ... */
@@ -1951,7 +1982,7 @@ void do_pthread_mutex_lock( ThreadId tid,
 
    if (mutex->__m_count > 0) {
 
-      vg_assert(is_valid_tid((ThreadId)mutex->__m_owner));
+      vg_assert(VG_(is_valid_tid)((ThreadId)mutex->__m_owner));
 
       /* Someone has it already. */
       if ((ThreadId)mutex->__m_owner == tid) {
@@ -2018,7 +2049,7 @@ void do_pthread_mutex_unlock ( ThreadId tid,
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (mutex == NULL) {
@@ -2109,7 +2140,7 @@ void do_pthread_cond_timedwait_TIMEOUT ( ThreadId tid )
    pthread_mutex_t* mx;
    pthread_cond_t*  cv;
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_WaitCV
              && vg_threads[tid].awaken_at != 0xFFFFFFFF);
    mx = vg_threads[tid].associated_mx;
@@ -2238,7 +2269,7 @@ void do_pthread_cond_wait ( ThreadId tid,
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (mutex == NULL || cond == NULL) {
@@ -2306,7 +2337,7 @@ void do_pthread_cond_signal_or_broadcast ( ThreadId tid,
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (cond == NULL) {
@@ -2352,7 +2383,7 @@ void do_pthread_key_create ( ThreadId tid,
    }
 
    vg_assert(sizeof(pthread_key_t) == sizeof(ThreadKey));
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    for (i = 0; i < VG_N_THREAD_KEYS; i++)
@@ -2388,7 +2419,7 @@ void do_pthread_key_delete ( ThreadId tid, pthread_key_t key )
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
    
    if (!is_valid_key(key)) {
@@ -2420,7 +2451,7 @@ void do_pthread_getspecific ( ThreadId tid, pthread_key_t key )
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (!is_valid_key(key)) {
@@ -2444,7 +2475,7 @@ void do_pthread_setspecific ( ThreadId tid,
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (!is_valid_key(key)) {
@@ -2457,6 +2488,85 @@ void do_pthread_setspecific ( ThreadId tid,
 }
 
 
+/* ---------------------------------------------------
+   SIGNALS
+   ------------------------------------------------ */
+
+/* See comment in vg_libthread.c:pthread_sigmask() regarding
+   deliberate confusion of types sigset_t and vki_sigset_t.  Also re
+   meaning of the mashed_how value.  Return 0 for OK and 1 for some
+   kind of addressing error, which the vg_libpthread.c routine turns
+   into return values 0 and EFAULT respectively. */
+static
+void do_pthread_sigmask ( ThreadId tid,
+                          Int mashed_how,
+                          vki_ksigset_t* newmask, 
+                          vki_ksigset_t* oldmask )
+{
+   Char msg_buf[100];
+   if (VG_(clo_trace_pthread_level) >= 1) {
+      VG_(sprintf)(msg_buf, 
+         "pthread_sigmask          m_how %d, newmask %p, oldmask %p",
+         mashed_how, newmask, oldmask );
+      print_pthread_event(tid, msg_buf);
+   }
+
+   vg_assert(VG_(is_valid_tid)(tid) 
+             && vg_threads[tid].status == VgTs_Runnable);
+
+   if (VG_(clo_instrument)) {
+      /* TODO check newmask/oldmask are addressible/defined */
+   }
+
+   if (oldmask != NULL) {
+      *oldmask = vg_threads[tid].sig_mask;
+      if (VG_(clo_instrument)) {
+         VGM_(make_readable)( (Addr)oldmask, sizeof(vki_ksigset_t) );
+      }
+   }
+
+   switch (mashed_how) {
+      case 1: /* SIG_SETMASK */
+         vg_threads[tid].sig_mask = *newmask;
+         break;
+      case 2: /* SIG_BLOCK */
+         VG_(ksigaddset_from_set)( & vg_threads[tid].sig_mask, newmask);
+         break;
+      case 3: /* SIG_UNBLOCK */
+         VG_(ksigdelset_from_set)( & vg_threads[tid].sig_mask, newmask);
+         break;
+      default: 
+        VG_(panic)("do_pthread_sigmask: invalid mashed_how");
+        /*NOTREACHED*/
+        break;
+   }
+
+   SET_EDX(tid, 0);
+}
+
+
+static
+void do_sigwait ( ThreadId tid,
+                  vki_ksigset_t* set, 
+                  Int* sig )
+{
+   Char msg_buf[100];
+   if (VG_(clo_trace_signals) || VG_(clo_trace_sched)) {
+      VG_(sprintf)(msg_buf, 
+         "suspend due to sigwait(): set %p, sig %p",
+         set, sig );
+      print_pthread_event(tid, msg_buf);
+   }
+
+   vg_assert(VG_(is_valid_tid)(tid) 
+             && vg_threads[tid].status == VgTs_Runnable);
+
+   vg_threads[tid].sigs_waited_for = *set;
+   vg_threads[tid].status = VgTs_WaitSIG;
+   VG_(update_sigstate_following_WaitSIG_change)();
+}
+
+
 /* ---------------------------------------------------------------------
    Handle non-trivial client requests.
    ------------------------------------------------------------------ */
@@ -2537,6 +2647,19 @@ void do_nontrivial_clientreq ( ThreadId tid )
                                  (void*)(arg[2]) );
         break;
 
+      case VG_USERREQ__PTHREAD_SIGMASK:
+         do_pthread_sigmask ( tid,
+                              arg[1],
+                              (vki_ksigset_t*)(arg[2]),
+                              (vki_ksigset_t*)(arg[3]) );
+        break;
+
+      case VG_USERREQ__SIGWAIT:
+         do_sigwait ( tid,
+                      (vki_ksigset_t*)(arg[1]),
+                      (Int*)(arg[2]) );
+        break;
+
       case VG_USERREQ__MAKE_NOACCESS:
       case VG_USERREQ__MAKE_WRITABLE:
       case VG_USERREQ__MAKE_READABLE:
@@ -2595,7 +2718,7 @@ void scheduler_sanity ( void )
          vg_assert(cv == NULL);
          /* 1 */ vg_assert(mx != NULL);
         /* 2 */ vg_assert(mx->__m_count > 0);
-         /* 3 */ vg_assert(is_valid_tid((ThreadId)mx->__m_owner));
+         /* 3 */ vg_assert(VG_(is_valid_tid)((ThreadId)mx->__m_owner));
          /* 4 */ vg_assert(i != (ThreadId)mx->__m_owner); 
       } else 
       if (vg_threads[i].status == VgTs_WaitCV) {
@@ -2626,6 +2749,15 @@ void scheduler_sanity ( void )
                "VG_PTHREAD_STACK_SIZE in vg_include.h and recompile.");
             VG_(exit)(1);
         }
+
+         if (vg_threads[i].status == VgTs_WaitSIG) {
+            vg_assert( ! VG_(kisemptysigset)(
+                            & vg_threads[i].sigs_waited_for) );
+        } else {
+            vg_assert( VG_(kisemptysigset)(
+                          & vg_threads[i].sigs_waited_for) );
+        }
+
       }
    }
 
index cbc72d35791994462ecdeeb1d6f0ae85be86be13..3edff21b97e8ed07a47fda9041a8711f51e5aeba 100644 (file)
    Signal state for this process.
    ------------------------------------------------------------------ */
 
-/* For each signal, the current action.  Is NULL if the client hasn't
-   asked to handle the signal.  Consequently, we expect never to
-   receive a signal for which the corresponding handler is NULL. */
-void* VG_(sighandler)[VKI_KNSIG];
+/* Base-ment of these arrays[VKI_KNSIG].
+
+   Valid signal numbers are 1 .. VKI_KNSIG inclusive.
+   Rather than subtracting 1 for indexing these arrays, which
+   is tedious and error-prone, they are simply dimensioned 1 larger,
+   and entry [0] is not used. 
+ */
+
+/* For each signal, the current action.  Either:
+
+   -- VG_SH_NOHANDLER if the client hasn't asked to handle the signal, 
+      and we havent surreptitiously installed any handler ourselves.
+
+   -- VG_SH_FAKEHANDLER if the client hasn't asked to handle the signal
+      directly, but has so indirectly via a sigwait() request.  In this
+      case we may need to install our own handler to catch signals which
+      the sigwait-mask for some thread will accept, but for which the
+      client hasn't actually installed a handler.  These "fake" handlers
+      are invisible to the client, so we need to be able to distinguish
+      this case so that we can fake a suitable response if the client
+      should enquire about the state of this signal using sigaction.
+
+   -- Otherwise, the client has installed a signal handler, and this
+      is the pointer to it.
+
+   Invariant: we never expect to receive a signal for which the 
+   vg_sighandler[] entry is VG_SH_NOHANDLER.  If it is VG_SH_FAKEHANDLER
+   we know that we should look for a thread in VgTs_WaitSIG state to
+   release.  Otherwise, we find a thread capable of handling this
+   signal and run the specified handler on it.
+*/
+#define VG_SH_NOHANDLER    ((void*)0)
+#define VG_SH_FAKEHANDLER  ((void*)1)
+
+void* vg_sighandler[1+VKI_KNSIG];
 
 
 /* For each signal, either:
-   -- VG_SIGIDLE if not pending and not running
-   -- Handler address if pending
-   -- VG_SIGRUNNING if the handler is running and hasn't (returned or 
+   -- VG_SP_SIGIDLE if not pending and not running
+   -- Handler address if pending AND real handler
+   -- VG_SH_FAKEHANDLER if pending for sigwait
+   -- VG_SP_SIGRUNNING if the handler is running and hasn't (returned or 
       unblocked the signal using sigprocmask following a longjmp out 
       of the handler).
  */
-#define VG_SIGIDLE    ((void*)0)
-#define VG_SIGRUNNING ((void*)1)
+#define VG_SP_SIGIDLE    ((void*)0)
+#define VG_SP_SIGRUNNING ((void*)2)
+
+static
+void* vg_sigpending[1+VKI_KNSIG];
+
 
-void* VG_(sigpending)[VKI_KNSIG];
+/* For each signal, the thread id to which the signal should be
+   delivered.  This is only meaningful if the corresponding
+   vg_sigpending entry actually points to a handler, ie, the signal
+   is pending.
 
+   In this case, the value VG_INVALID_THREADID indicates the signal is
+   not directed at a specific thread and so should be delivered to any
+   thread whose signal mask (ThreadState.sig_mask) field allows it.
 
-/* For each signal that we have a handler for (ie, for those for which
-   the VG_(sighandler) entry is non-NULL), record whether or not the
-   client asked for syscalls to be restartable (SA_RESTART) if
-   interrupted by this signal.  We need to consult this when a signal
-   returns, if it should happen that the signal which we delivered has
-   interrupted a system call. */
+   Any other value indicates that the signal should be delivered only
+   to that specific thread, as some point in time when the thread has
+   not blocked the signal.  It remains pending until then. */
+static
+ThreadId vg_sig_threadid[1+VKI_KNSIG];
+
+
+/* For each signal that the client installed a handler for (ie, for
+   those for which the vg_sighandler entry is non-VG_SH_NOHANDLER and
+   non-VG_SH_FAKEHANDLER), record whether or not the client asked for
+   syscalls to be restartable (SA_RESTART) if interrupted by this
+   signal.  We need to consult this when a signal returns, if it
+   should happen that the signal which we delivered has interrupted a
+   system call. */
 static 
-Bool vg_sig_sarestart[VKI_KNSIG];
+Bool vg_sig_sarestart[1+VKI_KNSIG];
 
 
 /* ---------------------------------------------------------------------
@@ -179,7 +229,7 @@ void vg_push_signal_frame ( ThreadId tid, int sigNo )
 
    /* Set the thread so it will next run the handler. */
    tst->m_esp  = esp;
-   tst->m_eip  = (Addr)VG_(sigpending)[sigNo];
+   tst->m_eip  = (Addr)vg_sigpending[sigNo];
    /* This thread needs to be marked runnable, but we leave that the
       caller to do. */
 
@@ -218,7 +268,8 @@ Int vg_pop_signal_frame ( ThreadId tid )
    vg_assert(frame->magicPI == 0x31415927);
    vg_assert(frame->magicE  == 0x27182818);
    if (VG_(clo_trace_signals))
-      VG_(message)(Vg_DebugMsg, "vg_pop_signal_frame: valid magic");
+      VG_(message)(Vg_DebugMsg, 
+         "vg_pop_signal_frame (thread %d): valid magic", tid);
 
    /* restore machine state */
    for (i = 0; i < VG_SIZE_OF_FPUSTATE_W; i++)
@@ -276,7 +327,7 @@ Bool VG_(signal_returns) ( ThreadId tid )
    /* You would have thought that the following assertion made sense
       here:
 
-         vg_assert(vg_sigpending[sigNo] == VG_SIGRUNNING);
+         vg_assert(vg_sigpending[sigNo] == VG_SP_SIGRUNNING);
 
       Alas, you would be wrong.  If a sigprocmask has been intercepted
       and it unblocks this signal, then vg_sigpending[sigNo] will
@@ -291,8 +342,8 @@ Bool VG_(signal_returns) ( ThreadId tid )
       Ho Hum.  This seems like a race condition which surely isn't
       handled correctly.  */
 
-   vg_assert(sigNo >= 1 && sigNo < VKI_KNSIG);
-   VG_(sigpending)[sigNo] = VG_SIGIDLE;
+   vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
+   vg_sigpending[sigNo] = VG_SP_SIGIDLE;
 
    /* Unlock and return. */
    VG_(restore_host_signals)( &saved_procmask );
@@ -306,12 +357,14 @@ Bool VG_(signal_returns) ( ThreadId tid )
 
 /* Deliver all pending signals, by building stack frames for their
    handlers.  Return True if any signals were delivered. */
-Bool VG_(deliver_signals) ( ThreadId tid )
+Bool VG_(deliver_signals) ( void )
 {
    vki_ksigset_t  saved_procmask;
    Int            sigNo;
    Bool           found;
+   ThreadState*   tst;
+   ThreadId       tid;
+
    /* A cheap check.  We don't need to have exclusive access
       to the queue, because in the worst case, vg_oursignalhandler
       will add signals, causing us to return, thinking there
@@ -319,9 +372,10 @@ Bool VG_(deliver_signals) ( ThreadId tid )
       A subsequent call here will handle the signal(s) we missed.
    */
    found = False;
-   for (sigNo = 1; sigNo < VKI_KNSIG; sigNo++)
-      if (VG_(sigpending)[sigNo] != VG_SIGIDLE &&
-          VG_(sigpending)[sigNo] != VG_SIGRUNNING) found = True;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++)
+      if (vg_sigpending[sigNo] != VG_SP_SIGIDLE 
+          && vg_sigpending[sigNo] != VG_SP_SIGRUNNING) 
+         found = True;
 
    if (!found) return False;
 
@@ -332,22 +386,98 @@ Bool VG_(deliver_signals) ( ThreadId tid )
    VG_(block_all_host_signals)( &saved_procmask );
 
    /* Look for signals to deliver ... */
-   for (sigNo = 1; sigNo < VKI_KNSIG; sigNo++) {
-      if (VG_(sigpending)[sigNo] == VG_SIGIDLE ||
-          VG_(sigpending)[sigNo] == VG_SIGRUNNING) continue;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++) {
+      if (vg_sigpending[sigNo] == VG_SP_SIGIDLE
+          || vg_sigpending[sigNo] == VG_SP_SIGRUNNING) continue;
+      /* sigNo is pending.  Try to find a suitable thread to deliver
+         it to. */
+
+      /* First off, are any threads in sigwait() for the signal? 
+         If so just give to one of them and have done. */
+      for (tid = 1; tid < VG_N_THREADS; tid++) {
+         tst = VG_(get_thread_state_UNCHECKED)(tid);
+         if (tst->status != VgTs_WaitSIG)
+            continue;
+         if (VG_(ksigismember)(&(tst->sigs_waited_for), sigNo))
+            break;
+      }
+      if (tid < VG_N_THREADS) {
+         UInt* sigwait_args;
+         tst = VG_(get_thread_state)(tid);
+         if (VG_(clo_trace_signals) || VG_(clo_trace_sched))
+            VG_(message)(Vg_DebugMsg,
+               "releasing thread %d from sigwait() due to signal %d",
+               tid, sigNo );
+         sigwait_args = (UInt*)(tst->m_eax);
+         if (NULL != (UInt*)(sigwait_args[2])) {
+            *(Int*)(sigwait_args[2]) = sigNo;
+            if (VG_(clo_instrument))
+               VGM_(make_readable)( (Addr)(sigwait_args[2]), sizeof(UInt));
+         }
+        tst->m_edx = 0;
+         tst->sh_edx = VGM_WORD_VALID;
+         tst->status = VgTs_Runnable;
+         VG_(update_sigstate_following_WaitSIG_change)();
+         vg_sigpending[sigNo] = VG_SP_SIGIDLE;
+         continue; /* for (sigNo = 1; ...) loop */
+      }
+
+      /* Well, nobody appears to be sigwaiting for it.  So we really
+         are delivering the signal in the usual way, and so the
+         handler better be valid. */
+      vg_assert(vg_sigpending[sigNo] != VG_SP_SIGIDLE);
+      vg_assert(vg_sigpending[sigNo] != VG_SH_FAKEHANDLER);
+      vg_assert(vg_sigpending[sigNo] != VG_SP_SIGRUNNING);
+
+      tid = vg_sig_threadid[sigNo];
+      vg_assert(tid == VG_INVALID_THREADID 
+                || VG_(is_valid_tid)(tid));
+
+      if (tid != VG_INVALID_THREADID) {
+         /* directed to a specific thread; ensure it actually still
+            exists ... */
+         tst = VG_(get_thread_state_UNCHECKED)(tid);
+         if (tst->status == VgTs_Empty) {
+            /* dead, for whatever reason; ignore this signal */
+            if (VG_(clo_trace_signals))
+               VG_(message)(Vg_DebugMsg,
+                  "discarding signal %d for nonexistent thread %d",
+                  sigNo, tid );
+            vg_sigpending[sigNo] = VG_SP_SIGIDLE;
+            continue; /* for (sigNo = 1; ...) loop */
+        }
+      } else {
+         /* not directed to a specific thread, so search for a
+            suitable candidate */
+         for (tid = 1; tid < VG_N_THREADS; tid++) {
+            tst = VG_(get_thread_state_UNCHECKED)(tid);
+            if (tst->status != VgTs_Empty
+                && !VG_(ksigismember)(&(tst->sig_mask), sigNo))
+               break;
+         }
+         if (tid == VG_N_THREADS) 
+            /* All threads have this signal blocked, so we can't
+               deliver it just now */
+            continue; /* for (sigNo = 1; ...) loop */
+      }
+
+      /* Ok, we can deliver signal sigNo to thread tid. */
 
       if (VG_(clo_trace_signals))
-         VG_(message)(Vg_DebugMsg,"delivering signal %d", sigNo );
+         VG_(message)(Vg_DebugMsg,"delivering signal %d to thread %d", 
+                                  sigNo, tid );
 
       /* Create a signal delivery frame, and set the client's %ESP and
          %EIP so that when execution continues, we will enter the
          signal handler with the frame on top of the client's stack,
          as it expects. */
+      vg_assert(VG_(is_valid_tid)(tid));
+      vg_assert(VG_(get_thread_state)(tid)->status != VgTs_Empty);
       vg_push_signal_frame ( tid, sigNo );
       VG_(get_thread_state)(tid)->status = VgTs_Runnable;
       
       /* Signify that the signal has been delivered. */
-      VG_(sigpending)[sigNo] = VG_SIGRUNNING;
+      vg_sigpending[sigNo] = VG_SP_SIGRUNNING;
    }
 
    /* Unlock and return. */
@@ -356,12 +486,36 @@ Bool VG_(deliver_signals) ( ThreadId tid )
 }
 
 
+/* A thread is about to exit.  Forget about any signals which are
+   still pending for it. */
+void VG_(notify_signal_machinery_of_thread_exit) ( ThreadId tid )
+{
+   Int sigNo;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++) {
+      if (vg_sigpending[sigNo] == VG_SP_SIGIDLE
+          || vg_sigpending[sigNo] == VG_SP_SIGRUNNING)
+         continue;
+      if (vg_sig_threadid[sigNo] == tid) {
+         /* sigNo is pending for tid, which is just about to disappear.
+            So forget about the pending signal. */
+         vg_sig_threadid[sigNo] = VG_INVALID_THREADID;
+         vg_sigpending[sigNo] = VG_SP_SIGIDLE;
+         if (VG_(clo_trace_signals)) 
+            VG_(message)(Vg_DebugMsg, 
+                "discarding pending signal %d due to thread %d exiting",
+                sigNo, tid );
+      }   
+   }
+}
+
+
 /* Receive a signal from the host, and either discard it or park it in
    the queue of pending signals.  All other signals will be blocked
    when this handler runs.  Runs with all host signals blocked, so as
    to have mutual exclusion when adding stuff to the queue. */
 
-static void VG_(oursignalhandler) ( Int sigNo )
+static 
+void VG_(oursignalhandler) ( Int sigNo )
 {
    Int           dummy_local;
    vki_ksigset_t saved_procmask;
@@ -381,7 +535,7 @@ static void VG_(oursignalhandler) ( Int sigNo )
       VG_(start_msg)(Vg_DebugMsg);
       VG_(add_to_msg)("signal %d arrived ... ", sigNo );
    }
-   vg_assert(sigNo >= 1 && sigNo < VKI_KNSIG);
+   vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
 
    /* Sanity check.  Ensure we're really running on the signal stack
       we asked for. */
@@ -407,7 +561,7 @@ static void VG_(oursignalhandler) ( Int sigNo )
 
    VG_(block_all_host_signals)( &saved_procmask );
 
-   if (VG_(sighandler)[sigNo] == NULL) {
+   if (vg_sighandler[sigNo] == VG_SH_NOHANDLER) {
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("unexpected!");
          VG_(end_msg)();
@@ -418,7 +572,7 @@ static void VG_(oursignalhandler) ( Int sigNo )
    }
 
    /* Decide what to do with it. */
-   if (VG_(sigpending)[sigNo] == VG_SIGRUNNING) {
+   if (vg_sigpending[sigNo] == VG_SP_SIGRUNNING) {
        /* Already running; ignore it. */
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("already running; discarded" );
@@ -426,8 +580,8 @@ static void VG_(oursignalhandler) ( Int sigNo )
       }
    }
    else
-   if (VG_(sigpending)[sigNo] != VG_SIGRUNNING && 
-       VG_(sigpending)[sigNo] != VG_SIGIDLE) {
+   if (vg_sigpending[sigNo] != VG_SP_SIGRUNNING 
+       && vg_sigpending[sigNo] != VG_SP_SIGIDLE) {
       /* Not running and not idle == pending; ignore it. */
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("already pending; discarded" );
@@ -436,9 +590,11 @@ static void VG_(oursignalhandler) ( Int sigNo )
    } 
    else {
       /* Ok, we'd better deliver it to the client. */
-      vg_assert(VG_(sigpending)[sigNo] == VG_SIGIDLE);
+      vg_assert(vg_sigpending[sigNo] == VG_SP_SIGIDLE);
       /* Queue it up for delivery at some point in the future. */
-      VG_(sigpending)[sigNo] = VG_(sighandler)[sigNo];
+      vg_assert(vg_sighandler[sigNo] != VG_SH_NOHANDLER);
+      vg_sigpending[sigNo] = vg_sighandler[sigNo];
+      vg_sig_threadid[sigNo] = VG_INVALID_THREADID;
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("queued" );
          VG_(end_msg)();
@@ -481,7 +637,7 @@ void pp_vg_ksigaction ( vki_ksigaction* sa )
    VG_(printf)("vg_ksigaction: handler %p, flags 0x%x, restorer %p\n", 
                sa->ksa_handler, (UInt)sa->ksa_flags, sa->ksa_restorer);
    VG_(printf)("vg_ksigaction: { ");
-   for (i = 1; i < VKI_KNSIG; i++)
+   for (i = 1; i <= VKI_KNSIG; i++)
       if (VG_(ksigismember(&(sa->ksa_mask),i)))
          VG_(printf)("%d ", i);
    VG_(printf)("}\n");
@@ -520,13 +676,14 @@ void VG_(sigstartup_actions) ( void )
    }
 
    /* Set initial state for the signal simulation. */
-   for (i = 1; i < VKI_KNSIG; i++) {
-      VG_(sighandler)[i] = NULL;
-      VG_(sigpending)[i] = NULL;
+   for (i = 1; i <= VKI_KNSIG; i++) {
+      vg_sighandler[i] = VG_SH_NOHANDLER;
+      vg_sigpending[i] = VG_SP_SIGIDLE;
       vg_sig_sarestart[i] = True; /* An easy default */
+      vg_sig_threadid[i] = VG_INVALID_THREADID;
    }
 
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
 
       /* Get the old host action */
       ret = VG_(ksigaction)(i, NULL, &sa);
@@ -534,7 +691,8 @@ void VG_(sigstartup_actions) ( void )
 
       /* If there's already a handler set, record it, then route the
          signal through to our handler. */
-      if (sa.ksa_handler != VKI_SIG_IGN && sa.ksa_handler != VKI_SIG_DFL) {
+      if (sa.ksa_handler != VKI_SIG_IGN 
+          && sa.ksa_handler != VKI_SIG_DFL) {
          if (VG_(clo_trace_signals))
             VG_(printf)("snaffling handler 0x%x for signal %d\n", 
                         (Addr)(sa.ksa_handler), i );
@@ -542,7 +700,7 @@ void VG_(sigstartup_actions) ( void )
             VG_(unimplemented)
                ("signals on an alternative stack (SA_ONSTACK)");
 
-         VG_(sighandler)[i] = sa.ksa_handler;
+         vg_sighandler[i] = sa.ksa_handler;
          sa.ksa_handler = &VG_(oursignalhandler);
         /* Save the restart status, then set it to restartable. */
         vg_sig_sarestart[i] 
@@ -585,12 +743,13 @@ void VG_(sigshutdown_actions) ( void )
    /* copy the sim signal actions to the real ones. */
    /* Hmm, this isn't accurate.  Doesn't properly restore the
       SA_RESTART flag nor SA_ONSTACK. */
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
       if (i == VKI_SIGKILL || i == VKI_SIGSTOP) continue;
-      if (VG_(sighandler)[i] == NULL) continue;
+      if (vg_sighandler[i] == VG_SH_NOHANDLER 
+          || vg_sighandler[i] == VG_SH_FAKEHANDLER) continue;
       ret = VG_(ksigaction)(i, NULL, &sa);
       vg_assert(ret == 0);
-      sa.ksa_handler = VG_(sighandler)[i];
+      sa.ksa_handler = vg_sighandler[i];
       ret = VG_(ksigaction)(i, &sa, NULL);      
    }
 
@@ -598,6 +757,89 @@ void VG_(sigshutdown_actions) ( void )
 }
 
 
+void VG_(update_sigstate_following_WaitSIG_change) ( void )
+{
+   ThreadId      tid;
+   Int           sig;
+   vki_ksigset_t global_waitsigs;
+   ThreadState*  tst;
+
+   VG_(ksigemptyset)( &global_waitsigs );
+
+   /* Calculate the new set of signals which are being sigwait()d for
+      by at least one thread. */
+   for (tid = 1; tid < VG_N_THREADS; tid++) {
+      tst = VG_(get_thread_state_UNCHECKED)(tid);
+      if (tst->status != VgTs_WaitSIG)
+         continue;
+      vg_assert(! VG_(kisemptysigset)(
+                     & tst->sigs_waited_for ));
+      VG_(ksigaddset_from_set)( & global_waitsigs, 
+                                & tst->sigs_waited_for );
+   }
+
+   /* Now adjust vg_sighandler accordingly.
+
+      For each signal s: (lapses into pseudo-Haskell ...)
+
+      if s `elem` global_waitsigs[s]
+       -- at least one thread is sigwait()ing for s.  That means that at 
+          least _some_ kind of handler is needed.
+       case vg_sighandler[s] of
+          VG_SH_NOHANDLER -> install our own handler and set waitsigs[s] 
+             to VG_SH_FAKEHANDLER 
+          VG_SH_FAKEHANDLER -> there's already a handler.  Do nothing.
+          real_handler -> the client had a handler here anyway, so
+             just leave it alone, ie, do nothing.
+
+      if s `notElem` global_waitsigs[s]
+       -- we're not sigwait()ing for s (any longer).  
+       case vg_sighandler[s] of
+          VG_SH_FAKEHANDLER -> there is a handler installed, but ONLY for 
+             the purposes of handling sigwait().  So set it back to 
+             VG_SH_NOHANDLER and tell the kernel that we want to do the 
+             default action for s from now on, ie, we wish to deregister 
+             OUR handle.
+          VG_SH_NOHANDLER -> there was no handler anyway.  Do nothing.
+          real_handler -> the client had a handler here anyway, so
+             just leave it alone, ie, do nothing.
+   */
+
+   for (sig = 1; sig <= VKI_KNSIG; sig++) {
+      if (VG_(ksigismember)( & global_waitsigs, sig )) {
+         if (vg_sighandler[sig] == VG_SH_NOHANDLER
+             /* && existing kernel handler is SIG_DFL */) {
+            /* add handler */
+            /* We really only ought to do this if the existing kernel
+               handler is SIG_DFL.  That's because when queried by the
+               client's sigaction, that's what we claim it is if a fake
+               handler has been installed.  Or (perhaps better) 
+               remember the kernel's setting. 
+            */
+            VG_(ksignal)( sig, &VG_(oursignalhandler) );
+            vg_sighandler[sig] = VG_SH_FAKEHANDLER;
+            if (VG_(clo_trace_signals)) {
+               VG_(message)(Vg_DebugMsg,
+                  "adding fake handler for signal %d "
+                  "following WaitSIG change", sig );
+            }
+         }
+      } else {
+         if (vg_sighandler[sig] == VG_SH_FAKEHANDLER) {
+            /* remove handler */
+            VG_(ksignal)( sig, VKI_SIG_DFL);
+            vg_sighandler[sig] = VG_SH_NOHANDLER;
+            if (VG_(clo_trace_signals)) {
+               VG_(message)(Vg_DebugMsg,
+                  "removing fake handler for signal %d "
+                  "following WaitSIG change", sig );
+            }
+         }
+      }
+   }
+}
+
 /* ---------------------------------------------------------------------
    Handle signal-related syscalls from the simulatee.
    ------------------------------------------------------------------ */
@@ -628,7 +870,7 @@ void VG_(do__NR_sigaction) ( ThreadId tid )
       the call is passed to the kernel it will definitely succeed. */
 
    /* Reject out-of-range signal numbers. */
-   if (param1 < 1 || param1 >= VKI_KNSIG) goto bad_signo;
+   if (param1 < 1 || param1 > VKI_KNSIG) goto bad_signo;
 
    /* Reject attempts to set a handler (or set ignore) for SIGKILL. */
    if ( (param1 == VKI_SIGKILL || param1 == VKI_SIGSTOP)
@@ -636,14 +878,14 @@ void VG_(do__NR_sigaction) ( ThreadId tid )
        && new_action->ksa_handler != VKI_SIG_DFL)
       goto bad_sigkill_or_sigstop;
 
-   our_old_handler = VG_(sighandler)[param1];
+   our_old_handler = vg_sighandler[param1];
    /* VG_(printf)("old handler = 0x%x\n", our_old_handler); */
    /* If a new handler has been specified, mess with its handler. */
    if (new_action) {
       if (new_action->ksa_handler == VKI_SIG_IGN ||
           new_action->ksa_handler == VKI_SIG_DFL) {
-         VG_(sighandler)[param1] = NULL; 
-         VG_(sigpending)[param1] = NULL;
+         vg_sighandler[param1] = VG_SH_NOHANDLER;
+         vg_sigpending[param1] = VG_SP_SIGIDLE;
          /* Dangerous!  Could lose signals like this. */
       } else {
          /* VG_(printf)("new handler = 0x%x\n", new_action->ksa_handler); */
@@ -653,7 +895,7 @@ void VG_(do__NR_sigaction) ( ThreadId tid )
             VG_(unimplemented)
                ("signals on an alternative stack (SA_ONSTACK)");
          new_action->ksa_flags |= VKI_SA_ONSTACK;
-         VG_(sighandler)[param1] = new_action->ksa_handler;
+         vg_sighandler[param1] = new_action->ksa_handler;
         vg_sig_sarestart[param1] 
             = (new_action->ksa_flags & VKI_SA_RESTART) ? True : False;
          new_action->ksa_flags |= VKI_SA_RESTART;
@@ -670,14 +912,21 @@ void VG_(do__NR_sigaction) ( ThreadId tid )
       if (old_action->ksa_handler == VKI_SIG_IGN ||
           old_action->ksa_handler == VKI_SIG_DFL) {
          /* No old action; we should have a NULL handler. */
-         vg_assert(our_old_handler == NULL);
+         vg_assert(our_old_handler == VG_SH_NOHANDLER);
       } else {
          /* There's a handler. */
          if (param1 != VKI_SIGKILL && param1 != VKI_SIGSTOP) {
             vg_assert(old_action->ksa_handler == &VG_(oursignalhandler));
            vg_assert((old_action->ksa_flags & VKI_SA_ONSTACK) != 0);
          }
-         old_action->ksa_handler = our_old_handler;
+        /* Is the handler a fake one which the client doesn't know
+            about? */
+         if (vg_sighandler[param1] == VG_SH_FAKEHANDLER) {
+            /* Yes.  Pretend it was in a SIG_DFL state before. */
+            old_action->ksa_handler = VKI_SIG_DFL;
+         } else {
+            old_action->ksa_handler = our_old_handler;
+         }
          /* Since the client is not allowed to ask for an alternative
             sig stack, unset the bit for anything we pass back to
             it. */
@@ -758,7 +1007,7 @@ void VG_(do__NR_sigprocmask) ( Int how, vki_ksigset_t* set )
       return;
    }
 
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
       Bool unblock_me = False;
       if (how == VKI_SIG_SETMASK) {
          if (!VG_(ksigismember)(set,i))
@@ -767,8 +1016,8 @@ void VG_(do__NR_sigprocmask) ( Int how, vki_ksigset_t* set )
          if (VG_(ksigismember)(set,i))
             unblock_me = True;
       }
-      if (unblock_me && VG_(sigpending)[i] == VG_SIGRUNNING) {
-         VG_(sigpending)[i] = VG_SIGIDLE;
+      if (unblock_me && vg_sigpending[i] == VG_SP_SIGRUNNING) {
+         vg_sigpending[i] = VG_SP_SIGIDLE;
         if (VG_(clo_verbosity) > 1)
             VG_(message)(Vg_UserMsg, 
                          "Warning: unblocking signal %d "
diff --git a/tests/pth_signal1.c b/tests/pth_signal1.c
new file mode 100644 (file)
index 0000000..4063412
--- /dev/null
@@ -0,0 +1,141 @@
+/********************************************************
+ * An example source module to accompany...
+ *
+ * "Using POSIX Threads: Programming with Pthreads"
+ *     by Brad nichols, Dick Buttlar, Jackie Farrell
+ *     O'Reilly & Associates, Inc.
+ *
+ ********************************************************
+ * sig.c
+ *
+ * Simple example of pthreads and signals.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+
+#include <pthread.h>
+
+#define MAX_NUM_THREADS  10
+
+
+void *catch_usr1(void *p)
+{
+  int signo=SIGUSR1;
+  /* struct sigaction action; */
+  int caught;
+  sigset_t  sigs_to_catch;
+
+  /* Identify our thread */
+  printf("\ncatchit() signal %d processing running as thread 0x%x \n", 
+        signo, (int)pthread_self());
+  printf("Someone please send pid %d a SIGUSR1\n", getpid());
+
+  /*
+   * We inherited a thread sigmask with all the signals 
+   * blocked.  So, we can wait on whatever signals we're
+   * interested in and (as long as no other thread waits
+   * for them) we'll be sure return from sigwait() to
+   * handle it.
+   */ 
+
+  /* set this thread's signal mask to block out all other signals */
+  sigemptyset(&sigs_to_catch);
+  sigaddset(&sigs_to_catch, signo);
+
+  sigwait(&sigs_to_catch, &caught);
+
+  printf("\ncatchit() signal %d processing thread caught signal %d\n", 
+         signo, caught);
+
+  return(NULL);
+}
+
+void bugcatcher(int sig) 
+{
+  printf("The BUGCATCHER caught signal %d in thread 0x%x\n", 
+        sig, (int)pthread_self());
+  pthread_exit(0);
+}
+
+void *cause_sig_sync(void *p)
+{
+  int i, id;
+  sigset_t sigs_to_catch;
+
+  /* Identify our thread */
+  printf("cause_sig_sync() running in thread 0x%x\n", (int)pthread_self()); 
+
+  /* set this thread's signal mask to block out all other signals */
+  sigemptyset(&sigs_to_catch);
+  sigaddset(&sigs_to_catch, SIGSEGV);
+  sigaddset(&sigs_to_catch, SIGBUS);
+  pthread_sigmask(SIG_UNBLOCK, &sigs_to_catch, NULL);
+
+  /* Loop simulating useful processing in this thread */
+  for(i=1;i==i;i++) {
+    printf("printing count: %4d\r",i);
+    if (i%100 == 0) {
+      id = *(int *)p; /* Guaranteed bad address */
+    }
+  }
+
+  return(NULL); 
+}
+
+extern int
+main(void)
+{
+  int       i;
+  pthread_t threads[MAX_NUM_THREADS];
+  int       num_threads = 0;
+  sigset_t  sigs_to_block;
+  struct    sigaction action;
+
+
+  /* Identify our thread */
+  printf("main() running in thread 0x%x\n", (int)pthread_self()); 
+
+  /* 
+   * Set this thread's signal mask to block out all other signals
+   * Other threads will inherit the mask
+   */
+  sigfillset(&sigs_to_block);
+  pthread_sigmask(SIG_BLOCK, &sigs_to_block, NULL);
+
+  /* Set signal handler for catching SIGSEGV and SIGBUS */
+  action.sa_handler=bugcatcher;
+  sigaction(SIGSEGV, &action, NULL);
+  sigaction(SIGBUS, &action, NULL);
+
+  /* spawn the threads */
+   
+  /* Make sure we can catch synchronous signals as exceptions */
+  pthread_create(&threads[num_threads++],
+                NULL,
+                 cause_sig_sync,
+                NULL);
+  
+  /* Rather than install the action/handler for the process,
+     we create a thread to wait for the signal */
+  pthread_create(&threads[num_threads++],
+                NULL,
+                 catch_usr1,
+                NULL);
+
+  printf("main()\t\t\t\t%d threads created\n",num_threads);
+  
+  /* wait until all threads have finished */
+  for (i = 0; i < num_threads; i++) {
+    pthread_join(threads[i], NULL);
+    printf("main()\t\tjoined to thread %d \n", i);
+  }
+  
+  printf("main()\t\tall %d threads have finished. \n", num_threads);
+
+  return 0;
+}
+
index 5797537698d6104d385902744ece3805e07c5d71..f04b1b40c3f324636a66ca2335b7859a124c5e1a 100644 (file)
@@ -13,7 +13,7 @@ int main ( void )
    printf ( "installing sig handler\n" );
    signal(SIGSEGV, sig_hdlr);
    printf ( "doing bad thing\n" );
-   * (int*) 0 = 0;
+   * (int*) 65536 = 0;
    printf ( "exited normally ?!\n" );
    return 0;
 }
index aabbe430985ab98faa05ae405fcf074bb42c566d..61753391c00971b07510998e3f5b3431e63aac5d 100644 (file)
@@ -288,7 +288,7 @@ static void pp_AddrInfo ( Addr a, AddrInfo* ai )
       case Stack: 
          VG_(message)(Vg_UserMsg, 
                       "   Address 0x%x is on thread %d's stack", 
-                      ai->stack_tid, a);
+                      a, ai->stack_tid);
          break;
       case Unknown:
          if (ai->maybe_gcc) {
index 765d719f4f922535fd628f35b7e53a3bfa149449..d1cfca053b3e84d89fe7ac82bf31f560412a75a3 100644 (file)
@@ -433,6 +433,8 @@ extern Bool  VG_(is_empty_arena) ( ArenaId aid );
 #define VG_USERREQ__PTHREAD_SETSPECIFIC     0x300F
 #define VG_USERREQ__PTHREAD_GETSPECIFIC     0x3010
 #define VG_USERREQ__READ_MILLISECOND_TIMER  0x3011
+#define VG_USERREQ__PTHREAD_SIGMASK         0x3012
+#define VG_USERREQ__SIGWAIT                 0x3013
 
 /* Cosmetic ... */
 #define VG_USERREQ__GET_PTHREAD_TRACE_LEVEL 0x3101
@@ -478,6 +480,7 @@ typedef
       VgTs_WaitFD,     /* waiting for I/O completion on a fd */
       VgTs_WaitMX,     /* waiting on a mutex */
       VgTs_WaitCV,     /* waiting on a condition variable */
+      VgTs_WaitSIG,    /* waiting due to sigwait() */
       VgTs_Sleeping    /* sleeping for a while */
    }
    ThreadStatus;
@@ -530,6 +533,19 @@ typedef
       /* thread-specific data */
       void* specifics[VG_N_THREAD_KEYS];
 
+      /* This thread's blocked-signals mask.  Semantics is that for a
+         signal to be delivered to this thread, the signal must not be
+         blocked by either the process-wide signal mask nor by this
+         one.  So, if this thread is prepared to handle any signal that
+         the process as a whole is prepared to handle, this mask should
+         be made empty -- and that it is its default, starting
+         state. */
+      vki_ksigset_t sig_mask;
+
+      /* When not VgTs_WaitSIG, has no meaning.  When VgTs_WaitSIG,
+         is the set of signals for which we are sigwait()ing. */
+      vki_ksigset_t sigs_waited_for;
+
       /* Stacks.  When a thread slot is freed, we don't deallocate its
          stack; we just leave it lying around for the next use of the
          slot.  If the next use of the slot requires a larger stack,
@@ -581,6 +597,9 @@ typedef
    ThreadState;
 
 
+/* Trivial range check on tid. */
+extern Bool VG_(is_valid_tid) ( ThreadId tid );
+
 /* Copy the specified thread's state into VG_(baseBlock) in
    preparation for running it. */
 extern void VG_(load_thread_state)( ThreadId );
@@ -591,6 +610,7 @@ extern void VG_(save_thread_state)( ThreadId );
 
 /* Get the thread state block for the specified thread. */
 extern ThreadState* VG_(get_thread_state)( ThreadId );
+extern ThreadState* VG_(get_thread_state_UNCHECKED)( ThreadId );
 
 /* And for the currently running one, if valid. */
 extern ThreadState* VG_(get_current_thread_state) ( void );
@@ -663,9 +683,10 @@ extern Int     VG_(longjmpd_on_signal);
 
 extern void VG_(sigstartup_actions) ( void );
 
-extern Bool VG_(deliver_signals) ( ThreadId );
+extern Bool VG_(deliver_signals) ( void );
 extern void VG_(unblock_host_signal) ( Int sigNo );
-
+extern void VG_(notify_signal_machinery_of_thread_exit) ( ThreadId tid );
+extern void VG_(update_sigstate_following_WaitSIG_change) ( void );
 
 /* Fake system calls for signal handling. */
 extern void VG_(do__NR_sigaction)     ( ThreadId tid );
@@ -797,9 +818,10 @@ extern Int VG_(system) ( Char* cmd );
    definitions, which are different in places from those that glibc
    defines.  Since we're operating right at the kernel interface,
    glibc's view of the world is entirely irrelevant. */
-extern Int VG_(ksigfillset)( vki_ksigset_t* set );
-extern Int VG_(ksigemptyset)( vki_ksigset_t* set );
-extern Int VG_(ksigaddset)( vki_ksigset_t* set, Int signum );
+extern Int  VG_(ksigfillset)( vki_ksigset_t* set );
+extern Int  VG_(ksigemptyset)( vki_ksigset_t* set );
+extern Bool VG_(kisemptysigset)( vki_ksigset_t* set );
+extern Int  VG_(ksigaddset)( vki_ksigset_t* set, Int signum );
 
 extern Int VG_(ksigprocmask)( Int how, const vki_ksigset_t* set, 
                                        vki_ksigset_t* oldset );
@@ -807,6 +829,11 @@ extern Int VG_(ksigaction) ( Int signum,
                              const vki_ksigaction* act,  
                              vki_ksigaction* oldact );
 extern Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum );
+extern void VG_(ksigaddset_from_set)( vki_ksigset_t* dst, 
+                                      vki_ksigset_t* src );
+extern void VG_(ksigdelset_from_set)( vki_ksigset_t* dst, 
+                                      vki_ksigset_t* src );
+
 
 extern Int VG_(ksignal)(Int signum, void (*sighandler)(Int));
 
index a7690dd732218e0134ba133b562058888cdce838..b42ab44a80ed5592733212f6e7c6a400e6b1636b 100644 (file)
@@ -71,6 +71,7 @@ typedef
    }
    vki_ksigset_t;
 
+
 typedef
    struct {
       void*         ksa_handler;
@@ -89,6 +90,8 @@ typedef
    vki_kstack_t;
 
 
+
+
 #define VKI_SIG_BLOCK          0    /* for blocking signals */
 #define VKI_SIG_UNBLOCK        1    /* for unblocking signals */
 #define VKI_SIG_SETMASK        2    /* for setting the signal mask */
@@ -297,14 +300,13 @@ struct vki_stat {
 };
 
 
-/* To do with the ELF constructed by the kernel on a process' stack
-   just before it transfers control to the program's interpreter
-   (to use the ELF parlance).  
+/* To do with the ELF frame constructed by the kernel on a process'
+   stack just before it transfers control to the program's interpreter
+   (to use the ELF parlance).
    Constants from /usr/src/linux-2.4.9-31/include/linux/elf.h
    Logic from     /usr/src/linux-2.4.9-31/fs/binfmt_elf.c
                   and its counterpart in the 2.2.14 kernel sources 
-                  in Red Hat 6.2.
-*/
+                  in Red Hat 6.2.  */
 #define VKI_AT_CLKTCK 17    /* frequency at which times() increments */
 #define VKI_AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
 #define VKI_AT_BASE   7     /* base address of interpreter */
index 5e6e14c61ca9232265676fc86a5070e556a6d9ec..a7dfb70259cf6e3c9e04e1d456f288bb04d3aad1 100644 (file)
@@ -616,6 +616,61 @@ void __pthread_kill_other_threads_np ( void )
 }
 
 
+/* ---------------------------------------------------
+   SIGNALS
+   ------------------------------------------------ */
+
+#include <signal.h>
+
+int pthread_sigmask(int how, const sigset_t *newmask, 
+                             sigset_t *oldmask)
+{
+   int res;
+
+   /* A bit subtle, because the scheduler expects newmask and oldmask
+      to be vki_sigset_t* rather than sigset_t*, and the two are
+      different.  Fortunately the first 64 bits of a sigset_t are
+      exactly a vki_sigset_t, so we just pass the pointers through
+      unmodified.  Haaaack! 
+
+      Also mash the how value so that the SIG_ constants from glibc
+      do not have to be included into vg_scheduler.c. */
+
+   ensure_valgrind("pthread_sigmask");
+
+   switch (how) {
+      case SIG_SETMASK: how = 1; break;
+      case SIG_BLOCK:   how = 2; break;
+      case SIG_UNBLOCK: how = 3; break;
+      default:          return EINVAL;
+   }
+
+   /* Crude check */
+   if (newmask == NULL)
+      return EFAULT;
+
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__PTHREAD_SIGMASK,
+                           how, newmask, oldmask, 0);
+
+   /* The scheduler tells us of any memory violations. */
+   return res == 0 ? 0 : EFAULT;
+}
+
+
+int sigwait ( const sigset_t* set, int* sig )
+{
+   int res;
+   ensure_valgrind("sigwait");
+   /* As with pthread_sigmask we deliberately confuse sigset_t with
+      vki_ksigset_t. */
+   VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */,
+                           VG_USERREQ__SIGWAIT,
+                           set, sig, 0, 0);
+   return res;
+}
+
+
 /* ---------------------------------------------------
    THREAD-SPECIFICs
    ------------------------------------------------ */
@@ -872,7 +927,6 @@ void* (*__libc_internal_tsd_get)
    ------------------------------------------------------------------ */
 
 #include <stdlib.h>
-#include <signal.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
index c592e3215f436c5eb702b92f3c1f3a32b1bc5678..4157af6c667722949af104077f7b82fef6862fae 100644 (file)
@@ -150,7 +150,7 @@ void pthread_rwlockattr_setpshared ( void )  { unimp("pthread_rwlockattr_setpsha
 //void pthread_setcanceltype ( void )  { unimp("pthread_setcanceltype"); }
 //void pthread_setschedparam ( void )  { unimp("pthread_setschedparam"); }
 //void pthread_setspecific ( void )  { unimp("pthread_setspecific"); }
-void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
+//void pthread_sigmask ( void )  { unimp("pthread_sigmask"); }
 //void pthread_testcancel ( void )  { unimp("pthread_testcancel"); }
 void raise ( void )  { unimp("raise"); }
 void sem_close ( void )  { unimp("sem_close"); }
@@ -159,7 +159,7 @@ void sem_timedwait ( void )  { unimp("sem_timedwait"); }
 void sem_unlink ( void )  { unimp("sem_unlink"); }
 //void sigaction ( void )  { unimp("sigaction"); }
 void siglongjmp ( void )  { unimp("siglongjmp"); }
-void sigwait ( void )  { unimp("sigwait"); }
+//void sigwait ( void )  { unimp("sigwait"); }
 
 #if 0
 void pthread_create@@GLIBC_2.1 ( void )  { unimp("pthread_create@@GLIBC_2.1"); }
index f42ba9400b606679f7dfb696a26f9aae449739a4..4b4c8f310fdb4e4eda26ad8520cef1dbb8a499e3 100644 (file)
@@ -154,6 +154,15 @@ Int VG_(ksigemptyset)( vki_ksigset_t* set )
    return 0;
 }
 
+Bool VG_(kisemptysigset)( vki_ksigset_t* set )
+{
+   Int i;
+   vg_assert(set != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      if (set->ws[i] != 0x0) return False;
+   return True;
+}
+
 Int VG_(ksigaddset)( vki_ksigset_t* set, Int signum )
 {
    if (set == NULL)
@@ -168,9 +177,9 @@ Int VG_(ksigaddset)( vki_ksigset_t* set, Int signum )
 Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum )
 {
    if (set == NULL)
-      return -1;
+      return 0;
    if (signum < 1 && signum > VKI_KNSIG)
-      return -1;
+      return 0;
    signum--;
    if (1 & ((set->ws[signum / VKI_KNSIG_BPW]) >> (signum % VKI_KNSIG_BPW)))
       return 1;
@@ -179,6 +188,25 @@ Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum )
 }
 
 
+/* Add all signals in src to dst. */
+void VG_(ksigaddset_from_set)( vki_ksigset_t* dst, vki_ksigset_t* src )
+{
+   Int i;
+   vg_assert(dst != NULL && src != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      dst->ws[i] |= src->ws[i];
+}
+
+/* Remove all signals in src from dst. */
+void VG_(ksigdelset_from_set)( vki_ksigset_t* dst, vki_ksigset_t* src )
+{
+   Int i;
+   vg_assert(dst != NULL && src != NULL);
+   for (i = 0; i < VKI_KNSIG_WORDS; i++)
+      dst->ws[i] &= ~(src->ws[i]);
+}
+
+
 /* The functions sigaction, sigprocmask, sigpending and sigsuspend
    return 0 on success and -1 on error.  
 */
index 53edabb28d841a65efac2f106c37b45dd09024c6..d50fe93e0deeb22f083acbff6d997125a9be2379 100644 (file)
@@ -30,7 +30,6 @@
 
 #include "vg_include.h"
 #include "vg_constants.h"
-
 #include "valgrind.h" /* for VG_USERREQ__MAKE_NOACCESS and
                          VG_USERREQ__DO_LEAK_CHECK */
 
@@ -156,8 +155,8 @@ static void do_pthread_getspecific ( ThreadId,
    Helper functions for the scheduler.
    ------------------------------------------------------------------ */
 
-static __inline__
-Bool is_valid_tid ( ThreadId tid )
+__inline__
+Bool VG_(is_valid_tid) ( ThreadId tid )
 {
    /* tid is unsigned, hence no < 0 test. */
    if (tid == 0) return False;
@@ -216,6 +215,7 @@ void VG_(pp_sched_status) ( void )
          case VgTs_Sleeping:   VG_(printf)("Sleeping"); break;
          case VgTs_WaitMX:     VG_(printf)("WaitMX"); break;
          case VgTs_WaitCV:     VG_(printf)("WaitCV"); break;
+         case VgTs_WaitSIG:    VG_(printf)("WaitSIG"); break;
          default: VG_(printf)("???"); break;
       }
       VG_(printf)(", associated_mx = %p, associated_cv = %p\n", 
@@ -340,9 +340,16 @@ ThreadId vg_alloc_ThreadState ( void )
 }
 
 
+ThreadState* VG_(get_thread_state_UNCHECKED) ( ThreadId tid )
+{
+   vg_assert(VG_(is_valid_tid)(tid));
+   return & vg_threads[tid];
+}
+
+
 ThreadState* VG_(get_thread_state) ( ThreadId tid )
 {
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
    return & vg_threads[tid];
 }
@@ -461,7 +468,7 @@ static
 UInt run_thread_for_a_while ( ThreadId tid )
 {
    volatile UInt trc = 0;
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
    vg_assert(VG_(bbs_to_go) > 0);
 
@@ -541,6 +548,8 @@ void VG_(scheduler_init) ( void )
       vg_threads[i].stack_size = 0;
       vg_threads[i].stack_base = (Addr)NULL;
       vg_threads[i].tid        = i;
+      VG_(ksigemptyset)(&vg_threads[i].sig_mask);
+      VG_(ksigemptyset)(&vg_threads[i].sigs_waited_for);
    }
 
    for (i = 0; i < VG_N_WAITING_FDS; i++)
@@ -714,7 +723,7 @@ void cleanup_waiting_fd_table ( ThreadId tid )
 {
    Int  i, waiters;
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_WaitFD);
    vg_assert(vg_threads[tid].m_eax == __NR_read 
              || vg_threads[tid].m_eax == __NR_write);
@@ -744,7 +753,7 @@ void handle_signal_return ( ThreadId tid )
    Char msg_buf[100];
    Bool restart_blocked_syscalls;
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
 
    restart_blocked_syscalls = VG_(signal_returns)(tid);
 
@@ -793,7 +802,7 @@ void sched_do_syscall ( ThreadId tid )
    Bool orig_fd_blockness;
    Char msg_buf[100];
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    syscall_no = vg_threads[tid].m_eax; /* syscall number */
@@ -969,7 +978,7 @@ void poll_for_ready_fds ( void )
       if (fd > fd_max) 
          fd_max = fd;
       tid = vg_waiting_fds[i].tid;
-      vg_assert(is_valid_tid(tid));
+      vg_assert(VG_(is_valid_tid)(tid));
       syscall_no = vg_waiting_fds[i].syscall_no;
       switch (syscall_no) {
          case __NR_read:
@@ -1075,7 +1084,7 @@ void complete_blocked_syscalls ( void )
 
       fd  = vg_waiting_fds[i].fd;
       tid = vg_waiting_fds[i].tid;
-      vg_assert(is_valid_tid(tid));
+      vg_assert(VG_(is_valid_tid)(tid));
 
       /* The thread actually has to be waiting for the I/O event it
          requested before we can deliver the result! */
@@ -1214,7 +1223,7 @@ VgSchedReturnCode VG_(scheduler) ( void )
             even if, as a result, it has missed the unlocking of it.
             Potential deadlock.  This sounds all very strange, but the
             POSIX standard appears to require this behaviour.  */
-         sigs_delivered = VG_(deliver_signals)( 1 /*HACK*/ );
+         sigs_delivered = VG_(deliver_signals)();
         if (sigs_delivered)
             VG_(do_sanity_checks)( False );
 
@@ -1226,6 +1235,7 @@ VgSchedReturnCode VG_(scheduler) ( void )
             if (tid_next >= VG_N_THREADS) tid_next = 1;
             if (vg_threads[tid_next].status == VgTs_WaitFD
                 || vg_threads[tid_next].status == VgTs_Sleeping
+                || vg_threads[tid_next].status == VgTs_WaitSIG
                 || (vg_threads[tid_next].status == VgTs_WaitCV 
                     && vg_threads[tid_next].awaken_at != 0xFFFFFFFF))
                n_in_bounded_wait ++;
@@ -1257,7 +1267,7 @@ VgSchedReturnCode VG_(scheduler) ( void )
             thread becomes runnable. */
          nanosleep_for_a_while();
         /* pp_sched_status(); */
-        /* VG_(printf)(".\n"); */
+        /* VG_(printf)("."); */
       }
 
 
@@ -1524,16 +1534,37 @@ VgSchedReturnCode VG_(scheduler) ( void )
    Thread CREATION, JOINAGE and CANCELLATION.
    -------------------------------------------------------- */
 
+/* Release resources and generally clean up once a thread has finally
+   disappeared. */
+static
+void cleanup_after_thread_exited ( ThreadId tid )
+{
+   vg_assert(VG_(is_valid_tid)(tid));
+   vg_assert(vg_threads[tid].status == VgTs_Empty);
+   /* Mark its stack no-access */
+   if (VG_(clo_instrument) && tid != 1)
+      VGM_(make_noaccess)( vg_threads[tid].stack_base,
+                           vg_threads[tid].stack_size );
+   /* Forget about any pending signals directed specifically at this
+      thread. */
+   VG_(notify_signal_machinery_of_thread_exit)( tid );
+
+   /* Get rid of signal handlers specifically arranged for this
+      thread. */
+   VG_(update_sigstate_following_WaitSIG_change)();
+}
+
+
 static
 void do_pthread_cancel ( ThreadId  tid,
                          pthread_t tid_cancellee )
 {
    Char msg_buf[100];
 
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
 
-   if (!is_valid_tid(tid_cancellee)
+   if (!VG_(is_valid_tid)(tid_cancellee)
        || vg_threads[tid_cancellee].status == VgTs_Empty) {
       SET_EDX(tid, ESRCH);
       return;
@@ -1588,7 +1619,7 @@ void handle_pthread_return ( ThreadId tid, void* retval )
 
    /* Mark it as not in use.  Leave the stack in place so the next
       user of this slot doesn't reallocate it. */
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status != VgTs_Empty);
 
    vg_threads[tid].retval = retval;
@@ -1611,7 +1642,7 @@ void handle_pthread_return ( ThreadId tid, void* retval )
          call.  TODO: free properly the slot (also below). 
       */
       jnr = vg_threads[tid].joiner;
-      vg_assert(is_valid_tid(jnr));
+      vg_assert(VG_(is_valid_tid)(jnr));
       vg_assert(vg_threads[jnr].status == VgTs_WaitJoinee);
       jnr_args = (UInt*)vg_threads[jnr].m_eax;
       jnr_thread_return = (void**)(jnr_args[2]);
@@ -1620,9 +1651,7 @@ void handle_pthread_return ( ThreadId tid, void* retval )
       SET_EDX(jnr, 0); /* success */
       vg_threads[jnr].status = VgTs_Runnable;
       vg_threads[tid].status = VgTs_Empty; /* bye! */
-      if (VG_(clo_instrument) && tid != 0)
-         VGM_(make_noaccess)( vg_threads[tid].stack_base,
-                              vg_threads[tid].stack_size );
+      cleanup_after_thread_exited ( tid );
       if (VG_(clo_trace_sched)) {
          VG_(sprintf)(msg_buf, 
             "root fn returns, to find a waiting pthread_join(%d)", tid);
@@ -1645,7 +1674,7 @@ void do_pthread_join ( ThreadId tid, ThreadId jee, void** thread_return )
 
    /* jee, the joinee, is the thread specified as an arg in thread
       tid's call to pthread_join.  So tid is the join-er. */
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
    vg_assert(vg_threads[tid].status == VgTs_Runnable);
 
    if (jee == tid) {
@@ -1687,9 +1716,7 @@ void do_pthread_join ( ThreadId tid, ThreadId jee, void** thread_return )
       }
       vg_threads[tid].status = VgTs_Runnable;
       vg_threads[jee].status = VgTs_Empty; /* bye! */
-      if (VG_(clo_instrument) && jee != 0)
-         VGM_(make_noaccess)( vg_threads[jee].stack_base,
-                              vg_threads[jee].stack_size );
+      cleanup_after_thread_exited ( jee );
       if (VG_(clo_trace_sched)) {
         VG_(sprintf)(msg_buf,
                      "someone called pthread_join() on me; bye!");
@@ -1736,7 +1763,7 @@ void do_pthread_create ( ThreadId parent_tid,
 
    /* If we've created the main thread's tid, we're in deep trouble :) */
    vg_assert(tid != 1);
-   vg_assert(is_valid_tid(tid));
+   vg_assert(VG_(is_valid_tid)(tid));
 
    /* Copy the parent's CPU state into the child's, in a roundabout
       way (via baseBlock). */
@@ -1807,6 +1834,10 @@ void do_pthread_create ( ThreadId parent_tid,
    for (i = 0; i < VG_N_THREAD_KEYS; i++)
       vg_threads[tid].specifics[i] = NULL;
 
+   /* We inherit our parent's signal mask. (?!) */
+   vg_threads[tid].sig_mask = vg_threads[parent_tid].sig_mask;
+   VG_(ksigemptyset)(&vg_threads[i].sigs_waited_for);
+
    /* return zero */
    SET_EDX(parent_tid, 0); /* success */
 }
@@ -1920,7 +1951,7 @@ void do_pthread_mutex_lock( ThreadId tid,
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    /* POSIX doesn't mandate this, but for sanity ... */
@@ -1951,7 +1982,7 @@ void do_pthread_mutex_lock( ThreadId tid,
 
    if (mutex->__m_count > 0) {
 
-      vg_assert(is_valid_tid((ThreadId)mutex->__m_owner));
+      vg_assert(VG_(is_valid_tid)((ThreadId)mutex->__m_owner));
 
       /* Someone has it already. */
       if ((ThreadId)mutex->__m_owner == tid) {
@@ -2018,7 +2049,7 @@ void do_pthread_mutex_unlock ( ThreadId tid,
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (mutex == NULL) {
@@ -2109,7 +2140,7 @@ void do_pthread_cond_timedwait_TIMEOUT ( ThreadId tid )
    pthread_mutex_t* mx;
    pthread_cond_t*  cv;
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_WaitCV
              && vg_threads[tid].awaken_at != 0xFFFFFFFF);
    mx = vg_threads[tid].associated_mx;
@@ -2238,7 +2269,7 @@ void do_pthread_cond_wait ( ThreadId tid,
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (mutex == NULL || cond == NULL) {
@@ -2306,7 +2337,7 @@ void do_pthread_cond_signal_or_broadcast ( ThreadId tid,
    }
 
    /* Paranoia ... */
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (cond == NULL) {
@@ -2352,7 +2383,7 @@ void do_pthread_key_create ( ThreadId tid,
    }
 
    vg_assert(sizeof(pthread_key_t) == sizeof(ThreadKey));
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    for (i = 0; i < VG_N_THREAD_KEYS; i++)
@@ -2388,7 +2419,7 @@ void do_pthread_key_delete ( ThreadId tid, pthread_key_t key )
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
    
    if (!is_valid_key(key)) {
@@ -2420,7 +2451,7 @@ void do_pthread_getspecific ( ThreadId tid, pthread_key_t key )
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (!is_valid_key(key)) {
@@ -2444,7 +2475,7 @@ void do_pthread_setspecific ( ThreadId tid,
       print_pthread_event(tid, msg_buf);
    }
 
-   vg_assert(is_valid_tid(tid) 
+   vg_assert(VG_(is_valid_tid)(tid) 
              && vg_threads[tid].status == VgTs_Runnable);
 
    if (!is_valid_key(key)) {
@@ -2457,6 +2488,85 @@ void do_pthread_setspecific ( ThreadId tid,
 }
 
 
+/* ---------------------------------------------------
+   SIGNALS
+   ------------------------------------------------ */
+
+/* See comment in vg_libthread.c:pthread_sigmask() regarding
+   deliberate confusion of types sigset_t and vki_sigset_t.  Also re
+   meaning of the mashed_how value.  Return 0 for OK and 1 for some
+   kind of addressing error, which the vg_libpthread.c routine turns
+   into return values 0 and EFAULT respectively. */
+static
+void do_pthread_sigmask ( ThreadId tid,
+                          Int mashed_how,
+                          vki_ksigset_t* newmask, 
+                          vki_ksigset_t* oldmask )
+{
+   Char msg_buf[100];
+   if (VG_(clo_trace_pthread_level) >= 1) {
+      VG_(sprintf)(msg_buf, 
+         "pthread_sigmask          m_how %d, newmask %p, oldmask %p",
+         mashed_how, newmask, oldmask );
+      print_pthread_event(tid, msg_buf);
+   }
+
+   vg_assert(VG_(is_valid_tid)(tid) 
+             && vg_threads[tid].status == VgTs_Runnable);
+
+   if (VG_(clo_instrument)) {
+      /* TODO check newmask/oldmask are addressible/defined */
+   }
+
+   if (oldmask != NULL) {
+      *oldmask = vg_threads[tid].sig_mask;
+      if (VG_(clo_instrument)) {
+         VGM_(make_readable)( (Addr)oldmask, sizeof(vki_ksigset_t) );
+      }
+   }
+
+   switch (mashed_how) {
+      case 1: /* SIG_SETMASK */
+         vg_threads[tid].sig_mask = *newmask;
+         break;
+      case 2: /* SIG_BLOCK */
+         VG_(ksigaddset_from_set)( & vg_threads[tid].sig_mask, newmask);
+         break;
+      case 3: /* SIG_UNBLOCK */
+         VG_(ksigdelset_from_set)( & vg_threads[tid].sig_mask, newmask);
+         break;
+      default: 
+        VG_(panic)("do_pthread_sigmask: invalid mashed_how");
+        /*NOTREACHED*/
+        break;
+   }
+
+   SET_EDX(tid, 0);
+}
+
+
+static
+void do_sigwait ( ThreadId tid,
+                  vki_ksigset_t* set, 
+                  Int* sig )
+{
+   Char msg_buf[100];
+   if (VG_(clo_trace_signals) || VG_(clo_trace_sched)) {
+      VG_(sprintf)(msg_buf, 
+         "suspend due to sigwait(): set %p, sig %p",
+         set, sig );
+      print_pthread_event(tid, msg_buf);
+   }
+
+   vg_assert(VG_(is_valid_tid)(tid) 
+             && vg_threads[tid].status == VgTs_Runnable);
+
+   vg_threads[tid].sigs_waited_for = *set;
+   vg_threads[tid].status = VgTs_WaitSIG;
+   VG_(update_sigstate_following_WaitSIG_change)();
+}
+
+
 /* ---------------------------------------------------------------------
    Handle non-trivial client requests.
    ------------------------------------------------------------------ */
@@ -2537,6 +2647,19 @@ void do_nontrivial_clientreq ( ThreadId tid )
                                  (void*)(arg[2]) );
         break;
 
+      case VG_USERREQ__PTHREAD_SIGMASK:
+         do_pthread_sigmask ( tid,
+                              arg[1],
+                              (vki_ksigset_t*)(arg[2]),
+                              (vki_ksigset_t*)(arg[3]) );
+        break;
+
+      case VG_USERREQ__SIGWAIT:
+         do_sigwait ( tid,
+                      (vki_ksigset_t*)(arg[1]),
+                      (Int*)(arg[2]) );
+        break;
+
       case VG_USERREQ__MAKE_NOACCESS:
       case VG_USERREQ__MAKE_WRITABLE:
       case VG_USERREQ__MAKE_READABLE:
@@ -2595,7 +2718,7 @@ void scheduler_sanity ( void )
          vg_assert(cv == NULL);
          /* 1 */ vg_assert(mx != NULL);
         /* 2 */ vg_assert(mx->__m_count > 0);
-         /* 3 */ vg_assert(is_valid_tid((ThreadId)mx->__m_owner));
+         /* 3 */ vg_assert(VG_(is_valid_tid)((ThreadId)mx->__m_owner));
          /* 4 */ vg_assert(i != (ThreadId)mx->__m_owner); 
       } else 
       if (vg_threads[i].status == VgTs_WaitCV) {
@@ -2626,6 +2749,15 @@ void scheduler_sanity ( void )
                "VG_PTHREAD_STACK_SIZE in vg_include.h and recompile.");
             VG_(exit)(1);
         }
+
+         if (vg_threads[i].status == VgTs_WaitSIG) {
+            vg_assert( ! VG_(kisemptysigset)(
+                            & vg_threads[i].sigs_waited_for) );
+        } else {
+            vg_assert( VG_(kisemptysigset)(
+                          & vg_threads[i].sigs_waited_for) );
+        }
+
       }
    }
 
index cbc72d35791994462ecdeeb1d6f0ae85be86be13..3edff21b97e8ed07a47fda9041a8711f51e5aeba 100644 (file)
    Signal state for this process.
    ------------------------------------------------------------------ */
 
-/* For each signal, the current action.  Is NULL if the client hasn't
-   asked to handle the signal.  Consequently, we expect never to
-   receive a signal for which the corresponding handler is NULL. */
-void* VG_(sighandler)[VKI_KNSIG];
+/* Base-ment of these arrays[VKI_KNSIG].
+
+   Valid signal numbers are 1 .. VKI_KNSIG inclusive.
+   Rather than subtracting 1 for indexing these arrays, which
+   is tedious and error-prone, they are simply dimensioned 1 larger,
+   and entry [0] is not used. 
+ */
+
+/* For each signal, the current action.  Either:
+
+   -- VG_SH_NOHANDLER if the client hasn't asked to handle the signal, 
+      and we havent surreptitiously installed any handler ourselves.
+
+   -- VG_SH_FAKEHANDLER if the client hasn't asked to handle the signal
+      directly, but has so indirectly via a sigwait() request.  In this
+      case we may need to install our own handler to catch signals which
+      the sigwait-mask for some thread will accept, but for which the
+      client hasn't actually installed a handler.  These "fake" handlers
+      are invisible to the client, so we need to be able to distinguish
+      this case so that we can fake a suitable response if the client
+      should enquire about the state of this signal using sigaction.
+
+   -- Otherwise, the client has installed a signal handler, and this
+      is the pointer to it.
+
+   Invariant: we never expect to receive a signal for which the 
+   vg_sighandler[] entry is VG_SH_NOHANDLER.  If it is VG_SH_FAKEHANDLER
+   we know that we should look for a thread in VgTs_WaitSIG state to
+   release.  Otherwise, we find a thread capable of handling this
+   signal and run the specified handler on it.
+*/
+#define VG_SH_NOHANDLER    ((void*)0)
+#define VG_SH_FAKEHANDLER  ((void*)1)
+
+void* vg_sighandler[1+VKI_KNSIG];
 
 
 /* For each signal, either:
-   -- VG_SIGIDLE if not pending and not running
-   -- Handler address if pending
-   -- VG_SIGRUNNING if the handler is running and hasn't (returned or 
+   -- VG_SP_SIGIDLE if not pending and not running
+   -- Handler address if pending AND real handler
+   -- VG_SH_FAKEHANDLER if pending for sigwait
+   -- VG_SP_SIGRUNNING if the handler is running and hasn't (returned or 
       unblocked the signal using sigprocmask following a longjmp out 
       of the handler).
  */
-#define VG_SIGIDLE    ((void*)0)
-#define VG_SIGRUNNING ((void*)1)
+#define VG_SP_SIGIDLE    ((void*)0)
+#define VG_SP_SIGRUNNING ((void*)2)
+
+static
+void* vg_sigpending[1+VKI_KNSIG];
+
 
-void* VG_(sigpending)[VKI_KNSIG];
+/* For each signal, the thread id to which the signal should be
+   delivered.  This is only meaningful if the corresponding
+   vg_sigpending entry actually points to a handler, ie, the signal
+   is pending.
 
+   In this case, the value VG_INVALID_THREADID indicates the signal is
+   not directed at a specific thread and so should be delivered to any
+   thread whose signal mask (ThreadState.sig_mask) field allows it.
 
-/* For each signal that we have a handler for (ie, for those for which
-   the VG_(sighandler) entry is non-NULL), record whether or not the
-   client asked for syscalls to be restartable (SA_RESTART) if
-   interrupted by this signal.  We need to consult this when a signal
-   returns, if it should happen that the signal which we delivered has
-   interrupted a system call. */
+   Any other value indicates that the signal should be delivered only
+   to that specific thread, as some point in time when the thread has
+   not blocked the signal.  It remains pending until then. */
+static
+ThreadId vg_sig_threadid[1+VKI_KNSIG];
+
+
+/* For each signal that the client installed a handler for (ie, for
+   those for which the vg_sighandler entry is non-VG_SH_NOHANDLER and
+   non-VG_SH_FAKEHANDLER), record whether or not the client asked for
+   syscalls to be restartable (SA_RESTART) if interrupted by this
+   signal.  We need to consult this when a signal returns, if it
+   should happen that the signal which we delivered has interrupted a
+   system call. */
 static 
-Bool vg_sig_sarestart[VKI_KNSIG];
+Bool vg_sig_sarestart[1+VKI_KNSIG];
 
 
 /* ---------------------------------------------------------------------
@@ -179,7 +229,7 @@ void vg_push_signal_frame ( ThreadId tid, int sigNo )
 
    /* Set the thread so it will next run the handler. */
    tst->m_esp  = esp;
-   tst->m_eip  = (Addr)VG_(sigpending)[sigNo];
+   tst->m_eip  = (Addr)vg_sigpending[sigNo];
    /* This thread needs to be marked runnable, but we leave that the
       caller to do. */
 
@@ -218,7 +268,8 @@ Int vg_pop_signal_frame ( ThreadId tid )
    vg_assert(frame->magicPI == 0x31415927);
    vg_assert(frame->magicE  == 0x27182818);
    if (VG_(clo_trace_signals))
-      VG_(message)(Vg_DebugMsg, "vg_pop_signal_frame: valid magic");
+      VG_(message)(Vg_DebugMsg, 
+         "vg_pop_signal_frame (thread %d): valid magic", tid);
 
    /* restore machine state */
    for (i = 0; i < VG_SIZE_OF_FPUSTATE_W; i++)
@@ -276,7 +327,7 @@ Bool VG_(signal_returns) ( ThreadId tid )
    /* You would have thought that the following assertion made sense
       here:
 
-         vg_assert(vg_sigpending[sigNo] == VG_SIGRUNNING);
+         vg_assert(vg_sigpending[sigNo] == VG_SP_SIGRUNNING);
 
       Alas, you would be wrong.  If a sigprocmask has been intercepted
       and it unblocks this signal, then vg_sigpending[sigNo] will
@@ -291,8 +342,8 @@ Bool VG_(signal_returns) ( ThreadId tid )
       Ho Hum.  This seems like a race condition which surely isn't
       handled correctly.  */
 
-   vg_assert(sigNo >= 1 && sigNo < VKI_KNSIG);
-   VG_(sigpending)[sigNo] = VG_SIGIDLE;
+   vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
+   vg_sigpending[sigNo] = VG_SP_SIGIDLE;
 
    /* Unlock and return. */
    VG_(restore_host_signals)( &saved_procmask );
@@ -306,12 +357,14 @@ Bool VG_(signal_returns) ( ThreadId tid )
 
 /* Deliver all pending signals, by building stack frames for their
    handlers.  Return True if any signals were delivered. */
-Bool VG_(deliver_signals) ( ThreadId tid )
+Bool VG_(deliver_signals) ( void )
 {
    vki_ksigset_t  saved_procmask;
    Int            sigNo;
    Bool           found;
+   ThreadState*   tst;
+   ThreadId       tid;
+
    /* A cheap check.  We don't need to have exclusive access
       to the queue, because in the worst case, vg_oursignalhandler
       will add signals, causing us to return, thinking there
@@ -319,9 +372,10 @@ Bool VG_(deliver_signals) ( ThreadId tid )
       A subsequent call here will handle the signal(s) we missed.
    */
    found = False;
-   for (sigNo = 1; sigNo < VKI_KNSIG; sigNo++)
-      if (VG_(sigpending)[sigNo] != VG_SIGIDLE &&
-          VG_(sigpending)[sigNo] != VG_SIGRUNNING) found = True;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++)
+      if (vg_sigpending[sigNo] != VG_SP_SIGIDLE 
+          && vg_sigpending[sigNo] != VG_SP_SIGRUNNING) 
+         found = True;
 
    if (!found) return False;
 
@@ -332,22 +386,98 @@ Bool VG_(deliver_signals) ( ThreadId tid )
    VG_(block_all_host_signals)( &saved_procmask );
 
    /* Look for signals to deliver ... */
-   for (sigNo = 1; sigNo < VKI_KNSIG; sigNo++) {
-      if (VG_(sigpending)[sigNo] == VG_SIGIDLE ||
-          VG_(sigpending)[sigNo] == VG_SIGRUNNING) continue;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++) {
+      if (vg_sigpending[sigNo] == VG_SP_SIGIDLE
+          || vg_sigpending[sigNo] == VG_SP_SIGRUNNING) continue;
+      /* sigNo is pending.  Try to find a suitable thread to deliver
+         it to. */
+
+      /* First off, are any threads in sigwait() for the signal? 
+         If so just give to one of them and have done. */
+      for (tid = 1; tid < VG_N_THREADS; tid++) {
+         tst = VG_(get_thread_state_UNCHECKED)(tid);
+         if (tst->status != VgTs_WaitSIG)
+            continue;
+         if (VG_(ksigismember)(&(tst->sigs_waited_for), sigNo))
+            break;
+      }
+      if (tid < VG_N_THREADS) {
+         UInt* sigwait_args;
+         tst = VG_(get_thread_state)(tid);
+         if (VG_(clo_trace_signals) || VG_(clo_trace_sched))
+            VG_(message)(Vg_DebugMsg,
+               "releasing thread %d from sigwait() due to signal %d",
+               tid, sigNo );
+         sigwait_args = (UInt*)(tst->m_eax);
+         if (NULL != (UInt*)(sigwait_args[2])) {
+            *(Int*)(sigwait_args[2]) = sigNo;
+            if (VG_(clo_instrument))
+               VGM_(make_readable)( (Addr)(sigwait_args[2]), sizeof(UInt));
+         }
+        tst->m_edx = 0;
+         tst->sh_edx = VGM_WORD_VALID;
+         tst->status = VgTs_Runnable;
+         VG_(update_sigstate_following_WaitSIG_change)();
+         vg_sigpending[sigNo] = VG_SP_SIGIDLE;
+         continue; /* for (sigNo = 1; ...) loop */
+      }
+
+      /* Well, nobody appears to be sigwaiting for it.  So we really
+         are delivering the signal in the usual way, and so the
+         handler better be valid. */
+      vg_assert(vg_sigpending[sigNo] != VG_SP_SIGIDLE);
+      vg_assert(vg_sigpending[sigNo] != VG_SH_FAKEHANDLER);
+      vg_assert(vg_sigpending[sigNo] != VG_SP_SIGRUNNING);
+
+      tid = vg_sig_threadid[sigNo];
+      vg_assert(tid == VG_INVALID_THREADID 
+                || VG_(is_valid_tid)(tid));
+
+      if (tid != VG_INVALID_THREADID) {
+         /* directed to a specific thread; ensure it actually still
+            exists ... */
+         tst = VG_(get_thread_state_UNCHECKED)(tid);
+         if (tst->status == VgTs_Empty) {
+            /* dead, for whatever reason; ignore this signal */
+            if (VG_(clo_trace_signals))
+               VG_(message)(Vg_DebugMsg,
+                  "discarding signal %d for nonexistent thread %d",
+                  sigNo, tid );
+            vg_sigpending[sigNo] = VG_SP_SIGIDLE;
+            continue; /* for (sigNo = 1; ...) loop */
+        }
+      } else {
+         /* not directed to a specific thread, so search for a
+            suitable candidate */
+         for (tid = 1; tid < VG_N_THREADS; tid++) {
+            tst = VG_(get_thread_state_UNCHECKED)(tid);
+            if (tst->status != VgTs_Empty
+                && !VG_(ksigismember)(&(tst->sig_mask), sigNo))
+               break;
+         }
+         if (tid == VG_N_THREADS) 
+            /* All threads have this signal blocked, so we can't
+               deliver it just now */
+            continue; /* for (sigNo = 1; ...) loop */
+      }
+
+      /* Ok, we can deliver signal sigNo to thread tid. */
 
       if (VG_(clo_trace_signals))
-         VG_(message)(Vg_DebugMsg,"delivering signal %d", sigNo );
+         VG_(message)(Vg_DebugMsg,"delivering signal %d to thread %d", 
+                                  sigNo, tid );
 
       /* Create a signal delivery frame, and set the client's %ESP and
          %EIP so that when execution continues, we will enter the
          signal handler with the frame on top of the client's stack,
          as it expects. */
+      vg_assert(VG_(is_valid_tid)(tid));
+      vg_assert(VG_(get_thread_state)(tid)->status != VgTs_Empty);
       vg_push_signal_frame ( tid, sigNo );
       VG_(get_thread_state)(tid)->status = VgTs_Runnable;
       
       /* Signify that the signal has been delivered. */
-      VG_(sigpending)[sigNo] = VG_SIGRUNNING;
+      vg_sigpending[sigNo] = VG_SP_SIGRUNNING;
    }
 
    /* Unlock and return. */
@@ -356,12 +486,36 @@ Bool VG_(deliver_signals) ( ThreadId tid )
 }
 
 
+/* A thread is about to exit.  Forget about any signals which are
+   still pending for it. */
+void VG_(notify_signal_machinery_of_thread_exit) ( ThreadId tid )
+{
+   Int sigNo;
+   for (sigNo = 1; sigNo <= VKI_KNSIG; sigNo++) {
+      if (vg_sigpending[sigNo] == VG_SP_SIGIDLE
+          || vg_sigpending[sigNo] == VG_SP_SIGRUNNING)
+         continue;
+      if (vg_sig_threadid[sigNo] == tid) {
+         /* sigNo is pending for tid, which is just about to disappear.
+            So forget about the pending signal. */
+         vg_sig_threadid[sigNo] = VG_INVALID_THREADID;
+         vg_sigpending[sigNo] = VG_SP_SIGIDLE;
+         if (VG_(clo_trace_signals)) 
+            VG_(message)(Vg_DebugMsg, 
+                "discarding pending signal %d due to thread %d exiting",
+                sigNo, tid );
+      }   
+   }
+}
+
+
 /* Receive a signal from the host, and either discard it or park it in
    the queue of pending signals.  All other signals will be blocked
    when this handler runs.  Runs with all host signals blocked, so as
    to have mutual exclusion when adding stuff to the queue. */
 
-static void VG_(oursignalhandler) ( Int sigNo )
+static 
+void VG_(oursignalhandler) ( Int sigNo )
 {
    Int           dummy_local;
    vki_ksigset_t saved_procmask;
@@ -381,7 +535,7 @@ static void VG_(oursignalhandler) ( Int sigNo )
       VG_(start_msg)(Vg_DebugMsg);
       VG_(add_to_msg)("signal %d arrived ... ", sigNo );
    }
-   vg_assert(sigNo >= 1 && sigNo < VKI_KNSIG);
+   vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
 
    /* Sanity check.  Ensure we're really running on the signal stack
       we asked for. */
@@ -407,7 +561,7 @@ static void VG_(oursignalhandler) ( Int sigNo )
 
    VG_(block_all_host_signals)( &saved_procmask );
 
-   if (VG_(sighandler)[sigNo] == NULL) {
+   if (vg_sighandler[sigNo] == VG_SH_NOHANDLER) {
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("unexpected!");
          VG_(end_msg)();
@@ -418,7 +572,7 @@ static void VG_(oursignalhandler) ( Int sigNo )
    }
 
    /* Decide what to do with it. */
-   if (VG_(sigpending)[sigNo] == VG_SIGRUNNING) {
+   if (vg_sigpending[sigNo] == VG_SP_SIGRUNNING) {
        /* Already running; ignore it. */
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("already running; discarded" );
@@ -426,8 +580,8 @@ static void VG_(oursignalhandler) ( Int sigNo )
       }
    }
    else
-   if (VG_(sigpending)[sigNo] != VG_SIGRUNNING && 
-       VG_(sigpending)[sigNo] != VG_SIGIDLE) {
+   if (vg_sigpending[sigNo] != VG_SP_SIGRUNNING 
+       && vg_sigpending[sigNo] != VG_SP_SIGIDLE) {
       /* Not running and not idle == pending; ignore it. */
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("already pending; discarded" );
@@ -436,9 +590,11 @@ static void VG_(oursignalhandler) ( Int sigNo )
    } 
    else {
       /* Ok, we'd better deliver it to the client. */
-      vg_assert(VG_(sigpending)[sigNo] == VG_SIGIDLE);
+      vg_assert(vg_sigpending[sigNo] == VG_SP_SIGIDLE);
       /* Queue it up for delivery at some point in the future. */
-      VG_(sigpending)[sigNo] = VG_(sighandler)[sigNo];
+      vg_assert(vg_sighandler[sigNo] != VG_SH_NOHANDLER);
+      vg_sigpending[sigNo] = vg_sighandler[sigNo];
+      vg_sig_threadid[sigNo] = VG_INVALID_THREADID;
       if (VG_(clo_trace_signals)) {
          VG_(add_to_msg)("queued" );
          VG_(end_msg)();
@@ -481,7 +637,7 @@ void pp_vg_ksigaction ( vki_ksigaction* sa )
    VG_(printf)("vg_ksigaction: handler %p, flags 0x%x, restorer %p\n", 
                sa->ksa_handler, (UInt)sa->ksa_flags, sa->ksa_restorer);
    VG_(printf)("vg_ksigaction: { ");
-   for (i = 1; i < VKI_KNSIG; i++)
+   for (i = 1; i <= VKI_KNSIG; i++)
       if (VG_(ksigismember(&(sa->ksa_mask),i)))
          VG_(printf)("%d ", i);
    VG_(printf)("}\n");
@@ -520,13 +676,14 @@ void VG_(sigstartup_actions) ( void )
    }
 
    /* Set initial state for the signal simulation. */
-   for (i = 1; i < VKI_KNSIG; i++) {
-      VG_(sighandler)[i] = NULL;
-      VG_(sigpending)[i] = NULL;
+   for (i = 1; i <= VKI_KNSIG; i++) {
+      vg_sighandler[i] = VG_SH_NOHANDLER;
+      vg_sigpending[i] = VG_SP_SIGIDLE;
       vg_sig_sarestart[i] = True; /* An easy default */
+      vg_sig_threadid[i] = VG_INVALID_THREADID;
    }
 
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
 
       /* Get the old host action */
       ret = VG_(ksigaction)(i, NULL, &sa);
@@ -534,7 +691,8 @@ void VG_(sigstartup_actions) ( void )
 
       /* If there's already a handler set, record it, then route the
          signal through to our handler. */
-      if (sa.ksa_handler != VKI_SIG_IGN && sa.ksa_handler != VKI_SIG_DFL) {
+      if (sa.ksa_handler != VKI_SIG_IGN 
+          && sa.ksa_handler != VKI_SIG_DFL) {
          if (VG_(clo_trace_signals))
             VG_(printf)("snaffling handler 0x%x for signal %d\n", 
                         (Addr)(sa.ksa_handler), i );
@@ -542,7 +700,7 @@ void VG_(sigstartup_actions) ( void )
             VG_(unimplemented)
                ("signals on an alternative stack (SA_ONSTACK)");
 
-         VG_(sighandler)[i] = sa.ksa_handler;
+         vg_sighandler[i] = sa.ksa_handler;
          sa.ksa_handler = &VG_(oursignalhandler);
         /* Save the restart status, then set it to restartable. */
         vg_sig_sarestart[i] 
@@ -585,12 +743,13 @@ void VG_(sigshutdown_actions) ( void )
    /* copy the sim signal actions to the real ones. */
    /* Hmm, this isn't accurate.  Doesn't properly restore the
       SA_RESTART flag nor SA_ONSTACK. */
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
       if (i == VKI_SIGKILL || i == VKI_SIGSTOP) continue;
-      if (VG_(sighandler)[i] == NULL) continue;
+      if (vg_sighandler[i] == VG_SH_NOHANDLER 
+          || vg_sighandler[i] == VG_SH_FAKEHANDLER) continue;
       ret = VG_(ksigaction)(i, NULL, &sa);
       vg_assert(ret == 0);
-      sa.ksa_handler = VG_(sighandler)[i];
+      sa.ksa_handler = vg_sighandler[i];
       ret = VG_(ksigaction)(i, &sa, NULL);      
    }
 
@@ -598,6 +757,89 @@ void VG_(sigshutdown_actions) ( void )
 }
 
 
+void VG_(update_sigstate_following_WaitSIG_change) ( void )
+{
+   ThreadId      tid;
+   Int           sig;
+   vki_ksigset_t global_waitsigs;
+   ThreadState*  tst;
+
+   VG_(ksigemptyset)( &global_waitsigs );
+
+   /* Calculate the new set of signals which are being sigwait()d for
+      by at least one thread. */
+   for (tid = 1; tid < VG_N_THREADS; tid++) {
+      tst = VG_(get_thread_state_UNCHECKED)(tid);
+      if (tst->status != VgTs_WaitSIG)
+         continue;
+      vg_assert(! VG_(kisemptysigset)(
+                     & tst->sigs_waited_for ));
+      VG_(ksigaddset_from_set)( & global_waitsigs, 
+                                & tst->sigs_waited_for );
+   }
+
+   /* Now adjust vg_sighandler accordingly.
+
+      For each signal s: (lapses into pseudo-Haskell ...)
+
+      if s `elem` global_waitsigs[s]
+       -- at least one thread is sigwait()ing for s.  That means that at 
+          least _some_ kind of handler is needed.
+       case vg_sighandler[s] of
+          VG_SH_NOHANDLER -> install our own handler and set waitsigs[s] 
+             to VG_SH_FAKEHANDLER 
+          VG_SH_FAKEHANDLER -> there's already a handler.  Do nothing.
+          real_handler -> the client had a handler here anyway, so
+             just leave it alone, ie, do nothing.
+
+      if s `notElem` global_waitsigs[s]
+       -- we're not sigwait()ing for s (any longer).  
+       case vg_sighandler[s] of
+          VG_SH_FAKEHANDLER -> there is a handler installed, but ONLY for 
+             the purposes of handling sigwait().  So set it back to 
+             VG_SH_NOHANDLER and tell the kernel that we want to do the 
+             default action for s from now on, ie, we wish to deregister 
+             OUR handle.
+          VG_SH_NOHANDLER -> there was no handler anyway.  Do nothing.
+          real_handler -> the client had a handler here anyway, so
+             just leave it alone, ie, do nothing.
+   */
+
+   for (sig = 1; sig <= VKI_KNSIG; sig++) {
+      if (VG_(ksigismember)( & global_waitsigs, sig )) {
+         if (vg_sighandler[sig] == VG_SH_NOHANDLER
+             /* && existing kernel handler is SIG_DFL */) {
+            /* add handler */
+            /* We really only ought to do this if the existing kernel
+               handler is SIG_DFL.  That's because when queried by the
+               client's sigaction, that's what we claim it is if a fake
+               handler has been installed.  Or (perhaps better) 
+               remember the kernel's setting. 
+            */
+            VG_(ksignal)( sig, &VG_(oursignalhandler) );
+            vg_sighandler[sig] = VG_SH_FAKEHANDLER;
+            if (VG_(clo_trace_signals)) {
+               VG_(message)(Vg_DebugMsg,
+                  "adding fake handler for signal %d "
+                  "following WaitSIG change", sig );
+            }
+         }
+      } else {
+         if (vg_sighandler[sig] == VG_SH_FAKEHANDLER) {
+            /* remove handler */
+            VG_(ksignal)( sig, VKI_SIG_DFL);
+            vg_sighandler[sig] = VG_SH_NOHANDLER;
+            if (VG_(clo_trace_signals)) {
+               VG_(message)(Vg_DebugMsg,
+                  "removing fake handler for signal %d "
+                  "following WaitSIG change", sig );
+            }
+         }
+      }
+   }
+}
+
 /* ---------------------------------------------------------------------
    Handle signal-related syscalls from the simulatee.
    ------------------------------------------------------------------ */
@@ -628,7 +870,7 @@ void VG_(do__NR_sigaction) ( ThreadId tid )
       the call is passed to the kernel it will definitely succeed. */
 
    /* Reject out-of-range signal numbers. */
-   if (param1 < 1 || param1 >= VKI_KNSIG) goto bad_signo;
+   if (param1 < 1 || param1 > VKI_KNSIG) goto bad_signo;
 
    /* Reject attempts to set a handler (or set ignore) for SIGKILL. */
    if ( (param1 == VKI_SIGKILL || param1 == VKI_SIGSTOP)
@@ -636,14 +878,14 @@ void VG_(do__NR_sigaction) ( ThreadId tid )
        && new_action->ksa_handler != VKI_SIG_DFL)
       goto bad_sigkill_or_sigstop;
 
-   our_old_handler = VG_(sighandler)[param1];
+   our_old_handler = vg_sighandler[param1];
    /* VG_(printf)("old handler = 0x%x\n", our_old_handler); */
    /* If a new handler has been specified, mess with its handler. */
    if (new_action) {
       if (new_action->ksa_handler == VKI_SIG_IGN ||
           new_action->ksa_handler == VKI_SIG_DFL) {
-         VG_(sighandler)[param1] = NULL; 
-         VG_(sigpending)[param1] = NULL;
+         vg_sighandler[param1] = VG_SH_NOHANDLER;
+         vg_sigpending[param1] = VG_SP_SIGIDLE;
          /* Dangerous!  Could lose signals like this. */
       } else {
          /* VG_(printf)("new handler = 0x%x\n", new_action->ksa_handler); */
@@ -653,7 +895,7 @@ void VG_(do__NR_sigaction) ( ThreadId tid )
             VG_(unimplemented)
                ("signals on an alternative stack (SA_ONSTACK)");
          new_action->ksa_flags |= VKI_SA_ONSTACK;
-         VG_(sighandler)[param1] = new_action->ksa_handler;
+         vg_sighandler[param1] = new_action->ksa_handler;
         vg_sig_sarestart[param1] 
             = (new_action->ksa_flags & VKI_SA_RESTART) ? True : False;
          new_action->ksa_flags |= VKI_SA_RESTART;
@@ -670,14 +912,21 @@ void VG_(do__NR_sigaction) ( ThreadId tid )
       if (old_action->ksa_handler == VKI_SIG_IGN ||
           old_action->ksa_handler == VKI_SIG_DFL) {
          /* No old action; we should have a NULL handler. */
-         vg_assert(our_old_handler == NULL);
+         vg_assert(our_old_handler == VG_SH_NOHANDLER);
       } else {
          /* There's a handler. */
          if (param1 != VKI_SIGKILL && param1 != VKI_SIGSTOP) {
             vg_assert(old_action->ksa_handler == &VG_(oursignalhandler));
            vg_assert((old_action->ksa_flags & VKI_SA_ONSTACK) != 0);
          }
-         old_action->ksa_handler = our_old_handler;
+        /* Is the handler a fake one which the client doesn't know
+            about? */
+         if (vg_sighandler[param1] == VG_SH_FAKEHANDLER) {
+            /* Yes.  Pretend it was in a SIG_DFL state before. */
+            old_action->ksa_handler = VKI_SIG_DFL;
+         } else {
+            old_action->ksa_handler = our_old_handler;
+         }
          /* Since the client is not allowed to ask for an alternative
             sig stack, unset the bit for anything we pass back to
             it. */
@@ -758,7 +1007,7 @@ void VG_(do__NR_sigprocmask) ( Int how, vki_ksigset_t* set )
       return;
    }
 
-   for (i = 1; i < VKI_KNSIG; i++) {
+   for (i = 1; i <= VKI_KNSIG; i++) {
       Bool unblock_me = False;
       if (how == VKI_SIG_SETMASK) {
          if (!VG_(ksigismember)(set,i))
@@ -767,8 +1016,8 @@ void VG_(do__NR_sigprocmask) ( Int how, vki_ksigset_t* set )
          if (VG_(ksigismember)(set,i))
             unblock_me = True;
       }
-      if (unblock_me && VG_(sigpending)[i] == VG_SIGRUNNING) {
-         VG_(sigpending)[i] = VG_SIGIDLE;
+      if (unblock_me && vg_sigpending[i] == VG_SP_SIGRUNNING) {
+         vg_sigpending[i] = VG_SP_SIGIDLE;
         if (VG_(clo_verbosity) > 1)
             VG_(message)(Vg_UserMsg, 
                          "Warning: unblocking signal %d "