LinuxThreads library.
[kopensolaris-gnu/glibc.git] / linuxthreads / signals.c
1 /* Linuxthreads - a simple clone()-based implementation of Posix        */
2 /* threads for Linux.                                                   */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
4 /*                                                                      */
5 /* This program is free software; you can redistribute it and/or        */
6 /* modify it under the terms of the GNU Library General Public License  */
7 /* as published by the Free Software Foundation; either version 2       */
8 /* of the License, or (at your option) any later version.               */
9 /*                                                                      */
10 /* This program is distributed in the hope that it will be useful,      */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
13 /* GNU Library General Public License for more details.                 */
14
15 /* Handling of signals */
16
17 #include <errno.h>
18 #include <signal.h>
19 #include "pthread.h"
20 #include "internals.h"
21 #include "spinlock.h"
22
23 int pthread_sigmask(int how, const sigset_t * newmask, sigset_t * oldmask)
24 {
25   sigset_t mask;
26
27   if (newmask != NULL) {
28     mask = *newmask;
29     /* Don't allow PTHREAD_SIG_RESTART to be unmasked.
30        Don't allow PTHREAD_SIG_CANCEL to be masked. */
31     switch(how) {
32     case SIG_SETMASK:
33       sigaddset(&mask, PTHREAD_SIG_RESTART);
34       sigdelset(&mask, PTHREAD_SIG_CANCEL);
35       break;
36     case SIG_BLOCK:
37       sigdelset(&mask, PTHREAD_SIG_CANCEL);
38       break;
39     case SIG_UNBLOCK:
40       sigdelset(&mask, PTHREAD_SIG_RESTART);
41       break;
42     }
43     newmask = &mask;
44   }
45   if (sigprocmask(how, newmask, oldmask) == -1)
46     return errno;
47   else
48     return 0;
49 }
50
51 int pthread_kill(pthread_t thread, int signo)
52 {
53   pthread_handle handle = thread_handle(thread);
54   int pid;
55
56   acquire(&handle->h_spinlock);
57   if (invalid_handle(handle, thread)) {
58     release(&handle->h_spinlock);
59     return ESRCH;
60   }
61   pid = handle->h_descr->p_pid;
62   release(&handle->h_spinlock);
63   if (kill(pid, signo) == -1)
64     return errno;
65   else
66     return 0;
67 }
68
69 /* The set of signals on which some thread is doing a sigwait */
70 static sigset_t sigwaited;
71 static pthread_mutex_t sigwaited_mut = PTHREAD_MUTEX_INITIALIZER;
72 static pthread_cond_t sigwaited_changed = PTHREAD_COND_INITIALIZER;
73
74 int sigwait(const sigset_t * set, int * sig)
75 {
76   volatile pthread_descr self = thread_self();
77   sigset_t mask;
78   int s;
79   struct sigaction action, saved_signals[NSIG];
80   sigjmp_buf jmpbuf;
81
82   pthread_mutex_lock(&sigwaited_mut);
83   /* Make sure no other thread is waiting on our signals */
84 test_again:
85   for (s = 1; s < NSIG; s++) {
86     if (sigismember(set, s) && sigismember(&sigwaited, s)) {
87       pthread_cond_wait(&sigwaited_changed, &sigwaited_mut);
88       goto test_again;
89     }
90   }
91   /* Get ready to block all signals except those in set
92      and the cancellation signal */
93   sigfillset(&mask);
94   sigdelset(&mask, PTHREAD_SIG_CANCEL);
95   /* Signals in set are assumed blocked on entrance */
96   /* Install our signal handler on all signals in set,
97      and unblock them in mask.
98      Also mark those signals as being sigwaited on */
99   for (s = 1; s < NSIG; s++) {
100     if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) {
101       sigdelset(&mask, s);
102       action.sa_handler = __pthread_sighandler;
103       sigemptyset(&action.sa_mask);
104       action.sa_flags = 0;
105       sigaction(s, &action, &(saved_signals[s]));
106       sigaddset(&sigwaited, s);
107     }
108   }
109   pthread_mutex_unlock(&sigwaited_mut);
110
111   /* Test for cancellation */
112   if (sigsetjmp(jmpbuf, 1) == 0) {
113     self->p_cancel_jmp = &jmpbuf;
114     if (! (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)) {
115       /* Reset the signal count */
116       self->p_signal = 0;
117       /* Unblock the signals and wait for them */
118       sigsuspend(&mask);
119     }
120   }
121   self->p_cancel_jmp = NULL;
122   /* The signals are now reblocked. Restore the sighandlers. */
123   pthread_mutex_lock(&sigwaited_mut);
124   for (s = 1; s < NSIG; s++) {
125     if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) {
126       sigaction(s, &(saved_signals[s]), NULL);
127       sigdelset(&sigwaited, s);
128     }
129   }
130   pthread_cond_broadcast(&sigwaited_changed);
131   pthread_mutex_unlock(&sigwaited_mut);
132   /* Check for cancellation */
133   pthread_testcancel();
134   /* We should have self->p_signal != 0 and equal to the signal received */
135   *sig = self->p_signal;
136   return 0;
137 }
138
139 int raise (int sig)
140 {
141   int retcode = pthread_kill(pthread_self(), sig);
142   if (retcode == 0)
143     return 0;
144   else {
145     errno = retcode;
146     return -1;
147   }
148 }