about summary refs log tree commit diff
path: root/linuxthreads/signals.c
diff options
context:
space:
mode:
Diffstat (limited to 'linuxthreads/signals.c')
-rw-r--r--linuxthreads/signals.c89
1 files changed, 47 insertions, 42 deletions
diff --git a/linuxthreads/signals.c b/linuxthreads/signals.c
index 905e11e5fe..4a83b14d83 100644
--- a/linuxthreads/signals.c
+++ b/linuxthreads/signals.c
@@ -53,83 +53,88 @@ int pthread_kill(pthread_t thread, int signo)
   pthread_handle handle = thread_handle(thread);
   int pid;
 
-  acquire(&handle->h_spinlock);
+  __pthread_lock(&handle->h_lock);
   if (invalid_handle(handle, thread)) {
-    release(&handle->h_spinlock);
+    __pthread_unlock(&handle->h_lock);
     return ESRCH;
   }
   pid = handle->h_descr->p_pid;
-  release(&handle->h_spinlock);
+  __pthread_unlock(&handle->h_lock);
   if (kill(pid, signo) == -1)
     return errno;
   else
     return 0;
 }
 
-/* The set of signals on which some thread is doing a sigwait */
-static sigset_t sigwaited;
-static pthread_mutex_t sigwaited_mut = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t sigwaited_changed = PTHREAD_COND_INITIALIZER;
+/* User-provided signal handlers */
+static __sighandler_t sighandler[NSIG];
+
+/* The wrapper around user-provided signal handlers */
+static void pthread_sighandler(int signo)
+{
+  pthread_descr self = thread_self();
+  char * in_sighandler;
+  /* If we're in a sigwait operation, just record the signal received
+     and return without calling the user's handler */
+  if (self->p_sigwaiting) {
+    self->p_sigwaiting = 0;
+    self->p_signal = signo;
+    return;
+  }
+  /* Record that we're in a signal handler and call the user's
+     handler function */
+  in_sighandler = self->p_in_sighandler;
+  if (in_sighandler == NULL) self->p_in_sighandler = CURRENT_STACK_FRAME;
+  sighandler[signo](signo);
+  if (in_sighandler == NULL) self->p_in_sighandler = NULL;
+}
+
+int sigaction(int sig, const struct sigaction * act,
+              struct sigaction * oact)
+{
+  struct sigaction newact;
+
+  if (sig == PTHREAD_SIG_RESTART || sig == PTHREAD_SIG_CANCEL)
+    return EINVAL;
+  newact = *act;
+  if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL)
+    newact.sa_handler = pthread_sighandler;
+  if (__sigaction(sig, &newact, oact) == -1)
+    return -1;
+  if (oact != NULL) oact->sa_handler = sighandler[sig];
+  sighandler[sig] = act->sa_handler;
+  return 0;
+}
 
 int sigwait(const sigset_t * set, int * sig)
 {
   volatile pthread_descr self = thread_self();
   sigset_t mask;
   int s;
-  struct sigaction action, saved_signals[NSIG];
   sigjmp_buf jmpbuf;
 
-  pthread_mutex_lock(&sigwaited_mut);
-  /* Make sure no other thread is waiting on our signals */
-test_again:
-  for (s = 1; s < NSIG; s++) {
-    if (sigismember(set, s) && sigismember(&sigwaited, s)) {
-      pthread_cond_wait(&sigwaited_changed, &sigwaited_mut);
-      goto test_again;
-    }
-  }
   /* Get ready to block all signals except those in set
      and the cancellation signal */
   sigfillset(&mask);
   sigdelset(&mask, PTHREAD_SIG_CANCEL);
-  /* Signals in set are assumed blocked on entrance */
-  /* Install our signal handler on all signals in set,
-     and unblock them in mask.
-     Also mark those signals as being sigwaited on */
-  for (s = 1; s < NSIG; s++) {
-    if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) {
+  for (s = 1; s <= NSIG; s++) {
+    if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL)
       sigdelset(&mask, s);
-      action.sa_handler = __pthread_sighandler;
-      sigemptyset(&action.sa_mask);
-      action.sa_flags = 0;
-      sigaction(s, &action, &(saved_signals[s]));
-      sigaddset(&sigwaited, s);
-    }
   }
-  pthread_mutex_unlock(&sigwaited_mut);
-
   /* Test for cancellation */
   if (sigsetjmp(jmpbuf, 1) == 0) {
     self->p_cancel_jmp = &jmpbuf;
     if (! (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)) {
       /* Reset the signal count */
       self->p_signal = 0;
+      /* Say we're in sigwait */
+      self->p_sigwaiting = 1;
       /* Unblock the signals and wait for them */
       sigsuspend(&mask);
     }
   }
   self->p_cancel_jmp = NULL;
-  /* The signals are now reblocked. Restore the sighandlers. */
-  pthread_mutex_lock(&sigwaited_mut);
-  for (s = 1; s < NSIG; s++) {
-    if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) {
-      sigaction(s, &(saved_signals[s]), NULL);
-      sigdelset(&sigwaited, s);
-    }
-  }
-  pthread_cond_broadcast(&sigwaited_changed);
-  pthread_mutex_unlock(&sigwaited_mut);
-  /* Check for cancellation */
+  /* The signals are now reblocked.  Check for cancellation */
   pthread_testcancel();
   /* We should have self->p_signal != 0 and equal to the signal received */
   *sig = self->p_signal;