]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Merge r6120:
authorJulian Seward <jseward@acm.org>
Tue, 17 Oct 2006 01:30:47 +0000 (01:30 +0000)
committerJulian Seward <jseward@acm.org>
Tue, 17 Oct 2006 01:30:47 +0000 (01:30 +0000)
Get rid of VG_(sigtimedwait) and replace it a simpler version,
VG_(sigtimedwait_zero), which polls for signals and returns
immediately.  AIX doesn't have a sigtimedwait syscall, so in that case
try and implement VG_(sigtimedwait_zero) using various other signal
syscalls.

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

coregrind/m_libcsignal.c
coregrind/pub_core_libcsignal.h

index 9e11ed04573b7b1e8fc5c51d66eb3a88afce7571..7dd14267cc543740a5197cab157f9cd292deb4e2 100644 (file)
 */
 
 #include "pub_core_basics.h"
+#include "pub_core_debuglog.h"
 #include "pub_core_vki.h"
+#include "pub_core_vkiscnums.h"
 #include "pub_core_libcbase.h"
 #include "pub_core_libcassert.h"
-#include "pub_core_libcsignal.h"
 #include "pub_core_syscall.h"
-#include "pub_core_vkiscnums.h"
+#include "pub_core_libcsignal.h"    /* self */
 
 /* sigemptyset, sigfullset, sigaddset and sigdelset return 0 on
    success and -1 on error.  */
+/* I believe the indexing scheme in ->sig[] is also correct for
+   32- and 64-bit AIX (verified 27 July 06). */
 
 Int VG_(sigfillset)( vki_sigset_t* set )
 {
@@ -122,7 +125,6 @@ Int VG_(sigismember) ( const vki_sigset_t* set, Int signum )
       return 0;
 }
 
-
 /* Add all signals in src to dst. */
 void VG_(sigaddset_from_set)( vki_sigset_t* dst, vki_sigset_t* src )
 {
@@ -164,30 +166,6 @@ Int VG_(sigaction) ( Int signum, const struct vki_sigaction* act,
 }
 
 
-Int VG_(sigtimedwait)( const vki_sigset_t *set, vki_siginfo_t *info, 
-                       const struct vki_timespec *timeout )
-{
-   SysRes res = VG_(do_syscall4)(__NR_rt_sigtimedwait, (UWord)set, (UWord)info, 
-                                 (UWord)timeout, sizeof(*set));
-   return res.isError ? -1 : res.val;
-}
-Int VG_(signal)(Int signum, void (*sighandler)(Int))
-{
-   SysRes res;
-   Int    n;
-   struct vki_sigaction sa;
-   sa.ksa_handler = sighandler;
-   sa.sa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
-   sa.sa_restorer = NULL;
-   n = VG_(sigemptyset)( &sa.sa_mask );
-   vg_assert(n == 0);
-   res = VG_(do_syscall4)(__NR_rt_sigaction, signum, (UWord)&sa, (UWord)NULL,
-                           _VKI_NSIG_WORDS * sizeof(UWord));
-   return res.isError ? -1 : 0;
-}
-
-
 Int VG_(kill)( Int pid, Int signo )
 {
    SysRes res = VG_(do_syscall2)(__NR_kill, pid, signo);
@@ -198,21 +176,134 @@ Int VG_(kill)( Int pid, Int signo )
 Int VG_(tkill)( ThreadId tid, Int signo )
 {
    SysRes res = VG_(mk_SysRes_Error)(VKI_ENOSYS);
+   res = VG_(do_syscall2)(__NR_tkill, tid, signo);
+   if (res.isError && res.err == VKI_ENOSYS)
+      res = VG_(do_syscall2)(__NR_kill, tid, signo);
+   return res.isError ? -1 : 0;
+}
 
-#if 0
-   /* This isn't right because the client may create a process
-      structure with multiple thread groups */
-   res = VG_(do_syscall3)(__NR_tgkill, VG_(getpid)(), tid, signo);
-#endif
 
-   res = VG_(do_syscall2)(__NR_tkill, tid, signo);
+/* A cut-down version of POSIX sigtimedwait: poll for pending signals
+   mentioned in the sigset_t, and if any are present, select one
+   arbitrarily, return its number (which must be > 0), and put
+   auxiliary info about it in the siginfo_t, and make it
+   not-pending-any-more.  If none are pending, return zero.  The _zero
+   refers to the fact that there is zero timeout, so if no signals are
+   pending it returns immediately.  Perhaps a better name would be
+   'sigpoll'.  Returns -1 on error, 0 if no signals pending, and n > 0
+   if signal n was selected. 
 
-   if (res.isError && res.val == VKI_ENOSYS)
-      res = VG_(do_syscall2)(__NR_kill, tid, signo);
+   The Linux implementation is trivial: do the corresponding syscall.
 
-   return res.isError ? -1 : 0;
+   The AIX implementation is horrible and probably broken in a dozen
+   obscure ways.  I suspect it's only thread-safe because V forces
+   single-threadedness. */
+
+#if defined(VGO_linux)
+Int VG_(sigtimedwait_zero)( const vki_sigset_t *set, 
+                            vki_siginfo_t *info )
+{
+   static const struct vki_timespec zero = { 0, 0 };
+   SysRes res = VG_(do_syscall4)(__NR_rt_sigtimedwait, (UWord)set, (UWord)info, 
+                                 (UWord)&zero, sizeof(*set));
+   return res.isError ? -1 : res.res;
 }
 
+#elif defined(VGO_aix5)
+/* The general idea is:
+   - use sigpending to find out which signals are pending
+   - choose one
+   - temporarily set its handler to sigtimedwait_zero_handler
+   - use sigsuspend atomically unblock it and wait for the signal.
+     Upon return, sigsuspend restores the signal mask to what it
+     was to start with.
+   - Restore the handler for the signal to whatever it was before.
+*/
+
+/* A signal handler which does nothing (it doesn't need to).  It does
+   however check that it's not handing a sync signal for which
+   returning is meaningless. */
+static void sigtimedwait_zero_handler ( Int sig ) 
+{ 
+   vg_assert(sig != VKI_SIGILL);
+   vg_assert(sig != VKI_SIGSEGV);
+   vg_assert(sig != VKI_SIGBUS);
+   vg_assert(sig != VKI_SIGTRAP);
+   /* do nothing */ 
+}
+
+Int VG_(sigtimedwait_zero)( const vki_sigset_t *set, 
+                            vki_siginfo_t *info )
+{
+  Int    i, ir;
+  SysRes sr;
+  vki_sigset_t pending, blocked, allbutone;
+  struct vki_sigaction sa, saved_sa;
+
+  /* Find out what's pending: AIX _sigpending */
+  sr = VG_(do_syscall1)(__NR__sigpending, (UWord)&pending);
+  vg_assert(!sr.isError);
+
+  /* don't try for signals not in 'set' */
+  /* pending = pending `intersect` set */
+  for (i = 0; i < _VKI_NSIG_WORDS; i++)
+     pending.sig[i] &= set->sig[i];
+
+  /* don't try for signals not blocked at the moment */
+  ir = VG_(sigprocmask)(VKI_SIG_SETMASK, NULL, &blocked);
+  vg_assert(ir == 0);
+
+  /* pending = pending `intersect` blocked */
+  for (i = 0; i < _VKI_NSIG_WORDS; i++)
+     pending.sig[i] &= blocked.sig[i];
+
+  /* decide which signal we're going to snarf */
+  for (i = 1; i < _VKI_NSIG; i++)
+     if (VG_(sigismember)(&pending,i))
+        break;
+
+  if (i == _VKI_NSIG)
+     return 0;
+
+  /* fetch signal i.
+     pre: i is blocked and pending
+     pre: we are the only thread running 
+  */
+  /* Set up alternative signal handler */
+  VG_(sigfillset)(&allbutone);
+  VG_(sigdelset)(&allbutone, i);
+  sa.sa_mask     = allbutone;
+  sa.ksa_handler = &sigtimedwait_zero_handler;
+  sa.sa_flags    = 0;
+  ir = VG_(sigaction)(i, &sa, &saved_sa);
+  vg_assert(ir == 0);
+
+  /* Switch signal masks and wait for the signal.  This should happen
+     immediately, since we've already established it is pending and
+     blocked. */
+  sr = VG_(do_syscall1)(__NR__sigsuspend, (UWord)&allbutone);
+  vg_assert(sr.isError);
+  if (0)
+     VG_(debugLog)(0, "libcsignal",
+                      "sigtimedwait_zero: sigsuspend got res %ld err %ld\n", 
+                      sr.res, sr.err);
+  vg_assert(sr.res == (UWord)-1);
+
+  /* Restore signal's handler to whatever it was before */
+  ir = VG_(sigaction)(i, &saved_sa, NULL);
+  vg_assert(ir == 0);
+
+  /* This is bogus - we could get more info from the sighandler. */
+  VG_(memset)( info, 0, sizeof(*info) );
+  info->si_signo = i;
+
+  return i;
+}
+
+#else
+#  error Unknown OS
+#endif
+
 /*--------------------------------------------------------------------*/
 /*--- end                                                          ---*/
 /*--------------------------------------------------------------------*/
index 32bd8497809d7279e7b1ce972ad7464a3e4b8ac5..db364d387da9e8a0dedfb356a62a53d56396b99a 100644 (file)
@@ -65,14 +65,19 @@ extern Int VG_(sigaction)   ( Int signum,
                               const struct vki_sigaction* act,
                               struct vki_sigaction* oldact );
 
-extern Int VG_(sigtimedwait)( const vki_sigset_t *, vki_siginfo_t *, 
-                             const struct vki_timespec * );
-
-extern Int VG_(signal)      ( Int signum, void (*sighandler)(Int) );
-
 extern Int VG_(kill)        ( Int pid, Int signo );
 extern Int VG_(tkill)       ( ThreadId tid, Int signo );
 
+/* A cut-down version of POSIX sigtimedwait: poll for pending signals
+   mentioned in the sigset_t, and if any are present, select one
+   arbitrarily, return its number (which must be > 0), and put
+   auxiliary info about it in the siginfo_t, and make it
+   not-pending-any-more.  If none are pending, return zero.  The _zero
+   refers to the fact that there is zero timeout, so if no signals are
+   pending it returns immediately.  Perhaps a better name would be
+   'sigpoll'.  Returns -1 on error, 0 if no signals pending, and n > 0
+   if signal n was selected. */
+extern Int VG_(sigtimedwait_zero)( const vki_sigset_t *, vki_siginfo_t * );
 
 #endif   // __PUB_CORE_LIBCSIGNAL_H