e22e3484d01961484a82377585b86e9bc9f96e02
[npth.git] / src / npth-sigev.c
1 /* npth.h - a lightweight implementation of pth over pthread.
2
3    This file is part of NPTH.
4
5    NPTH is free software; you can redistribute it and/or modify it
6    under the terms of either
7
8    - the GNU Lesser General Public License as published by the Free
9    Software Foundation; either version 3 of the License, or (at
10    your option) any later version.
11
12    or
13    
14    - the GNU General Public License as published by the Free
15    Software Foundation; either version 2 of the License, or (at
16    your option) any later version.
17     
18    or both in parallel, as here.
19  
20    NPTH is distributed in the hope that it will be useful, but WITHOUT
21    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
23    License for more details.
24
25    You should have received a copies of the GNU General Public License
26    and the GNU Lesser General Public License along with this program;
27    if not, see <http://www.gnu.org/licenses/>.  */
28
29 /* This is a support interface to make it easier to handle signals.
30
31    The interfaces here support one (and only one) thread (here called
32    "main thread") in the application to monitor several signals while
33    selecting on filedescriptors.
34
35    First, the main thread should call npth_sigev_init.  This
36    initializes some global data structures used to record interesting
37    and pending signals.
38
39    Then, the main thread should call npth_sigev_add for every signal
40    it is interested in observing, and finally npth_sigev_fini.  This
41    will block the signal in the main threads sigmask.  Note that these
42    signals should also be blocked in all other threads.  Since they
43    are blocked in the main thread after calling npth_sigev_add, it is
44    recommended to call npth_sigev_add in the main thread before
45    creating any threads.
46
47    The function npth_sigev_sigmask is a convenient function that
48    returns the sigmask of the thread at time of npth_sigev_init, but
49    with all registered signals unblocked.  It is recommended to do all
50    other changes to the main thread's sigmask before calling
51    npth_sigev_init, so that the return value of npth_sigev_sigmask can
52    be used in the npth_pselect invocation.
53
54    In any case, the main thread should invoke npth_pselect with a
55    sigmask that has all signals that should be monitored unblocked.
56
57    After npth_pselect returns, npth_sigev_get_pending can be called in
58    a loop until it returns 0 to iterate over the list of pending
59    signals.  Each time a signal is returned by that function, its
60    status is reset to non-pending.  */
61
62 #ifdef HAVE_CONFIG_H
63 #include <config.h>
64 #endif
65
66 #include <signal.h>
67 #include <assert.h>
68
69 #include "npth.h"
70
71 /* Record events that have been noticed.  */
72 static sigset_t sigev_pending;
73
74 /* The signal mask during normal operation.  */
75 static sigset_t sigev_block;
76
77 /* The signal mask during pselect.  */
78 static sigset_t sigev_unblock;
79
80 /* Registered signal numbers.  Needed to iterate over sigset_t.
81    Bah.  */
82 #define SIGEV_MAX 32
83 static int sigev_signum[SIGEV_MAX];
84 static int sigev_signum_cnt;
85
86 /* The internal handler which just sets a global flag.  */
87 static void
88 _sigev_handler (int signum)
89 {
90   sigaddset (&sigev_pending, signum);
91 }
92
93
94 /* Start setting up signal event handling.  */
95 void
96 npth_sigev_init (void)
97 {
98   sigemptyset (&sigev_pending);
99   pthread_sigmask (SIG_SETMASK, NULL, &sigev_block);
100   pthread_sigmask (SIG_SETMASK, NULL, &sigev_unblock);
101 }
102
103
104 /* Add signal SIGNUM to the list of watched signals.  */
105 void
106 npth_sigev_add (int signum)
107 {
108   struct sigaction sa;
109   sigset_t ss;
110
111   sigemptyset(&ss);
112
113   assert (sigev_signum_cnt < SIGEV_MAX);
114   sigev_signum[sigev_signum_cnt++] = signum;
115
116   /* Make sure we can receive it.  */
117   sigdelset (&sigev_unblock, signum);
118   sigaddset (&sigev_block, signum);
119
120   sa.sa_handler = _sigev_handler;
121   sa.sa_mask = ss;
122   sa.sa_flags = 0; /* NOT setting SA_RESTART! */
123
124   sigaction (signum, &sa, NULL);
125 }
126
127
128 /* Finish the list of watched signals.  This starts to block them,
129    too.  */
130 void
131 npth_sigev_fini (void)
132 {
133   /* Block the interesting signals.  */
134   pthread_sigmask (SIG_SETMASK, &sigev_block, NULL);
135 }
136
137
138 /* Get the sigmask as needed for pselect.  */
139 sigset_t *
140 npth_sigev_sigmask (void)
141 {
142   return &sigev_unblock;
143 }
144
145
146 /* Return the next signal event that occured.  Returns if none are
147    left, 1 on success.  */
148 int
149 npth_sigev_get_pending (int *r_signum)
150 {
151   int i;
152   for (i = 0; i < sigev_signum_cnt; i++)
153     {
154       int signum = sigev_signum[i];
155       if (sigismember (&sigev_pending, signum))
156         {
157           sigdelset (&sigev_pending, signum);
158           *r_signum = signum;
159           return 1;
160         }
161     }
162   return 0;
163 }
164