]> git.pond.sub.org Git - empserver/blob - src/lib/lwp/sig.c
lwp: Simplify lwpSigWait() interface
[empserver] / src / lib / lwp / sig.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1994-2020, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  sig.c: Wait for signals
28  *
29  *  Known contributors to this file:
30  *     Markus Armbruster, 2006-2020
31  */
32
33 #include <config.h>
34
35 #include <errno.h>
36 #include <stddef.h>
37 #include <stdlib.h>
38 #include <signal.h>
39 #include "lwp.h"
40 #include "lwpint.h"
41
42 /*
43  * Awaited signal numbers, terminated with 0.
44  */
45 static int *LwpAwaitedSig;
46
47 /*
48  * Pending awaited signals.
49  * Access only with signals blocked!
50  */
51 static volatile int *LwpPendingSig;
52
53 /*
54  * Is there anything in LwpPendingSig[]?
55  */
56 static volatile sig_atomic_t LwpSigCheck;
57
58 /* The thread waiting for signals in lwpSigWait() */
59 static struct lwpProc *LwpSigWaiter;
60
61 static void lwpHandleAwaitedSig(int);
62
63 /*
64  * Initialize waiting for signals in @sig[].
65  * @sig[] contains signal numbers, terminated with 0.  It must have
66  * static storage duration.
67  */
68 void
69 lwpInitSigWait(int sig[])
70 {
71     struct sigaction act;
72     int i;
73
74     LwpAwaitedSig = sig;
75
76     act.sa_flags = 0;
77     act.sa_handler = lwpHandleAwaitedSig;
78     sigemptyset(&act.sa_mask);
79     for (i = 0; sig[i]; i++)
80         sigaddset(&act.sa_mask, sig[i]);
81
82     LwpPendingSig = calloc(i, sizeof(*LwpPendingSig));
83
84     for (i = 0; sig[i]; i++)
85         sigaction(sig[i], &act, NULL);
86 }
87
88 /*
89  * Signal handler for awaited signals.
90  * Set @LwpPendingSig[] for @sig, and set @LwpSigCheck.
91  * Not reentrant; lwpInitSigWait() guards.
92  */
93 static void
94 lwpHandleAwaitedSig(int sig)
95 {
96     int i;
97
98     for (i = 0; LwpAwaitedSig[i]; i++) {
99         if (sig == LwpAwaitedSig[i])
100             LwpPendingSig[i] = 1;
101     }
102     LwpSigCheck = 1;
103 }
104
105 /*
106  * Test whether an awaited signal is pending.
107  * If yes, remove that signal from the set of pending signals, and
108  * return its number.
109  * Else return 0.
110  */
111 static int
112 lwpGetSig(void)
113 {
114     int ret = 0;
115     sigset_t set, save;
116     int i;
117
118     sigemptyset(&set);
119     for (i = 0; LwpAwaitedSig[i]; i++)
120         sigaddset(&set, LwpAwaitedSig[i]);
121     sigprocmask(SIG_BLOCK, &set, &save);
122
123     for (i = 0; LwpAwaitedSig[i]; i++) {
124         if (LwpPendingSig[i]) {
125             lwpStatus(LwpCurrent, "Got awaited signal %d", LwpPendingSig[i]);
126             ret = LwpAwaitedSig[i];
127             LwpPendingSig[i] = 0;
128         }
129     }
130
131     for (; LwpAwaitedSig[i] && LwpPendingSig[i]; i++) ;
132     if (!LwpPendingSig[i])
133         LwpSigCheck = 0;
134
135     sigprocmask(SIG_SETMASK, &save, NULL);
136     return ret;
137 }
138
139 /*
140  * Wait until one of the signals passed to lwpInitSigWait() arrives.
141  * Return its signal number.
142  * If another thread is already waiting for signals, return -1
143  * without waiting.
144  */
145 int
146 lwpSigWait(void)
147 {
148     int res;
149
150     if (LwpSigWaiter)
151         return -1;
152     for (;;) {
153         res = lwpGetSig();
154         if (res > 0)
155             break;
156         lwpStatus(LwpCurrent, "Waiting for signals");
157         LwpSigWaiter = LwpCurrent;
158         lwpReschedule();
159     }
160     return res;
161 }
162
163 /*
164  * Wake up the thread awaiting signals if one arrived.
165  * To be called from lwpReschedule().
166  */
167 void
168 lwpSigWakeup(void)
169 {
170     if (LwpSigWaiter && LwpSigCheck) {
171         lwpReady(LwpSigWaiter);
172         LwpSigWaiter = NULL;
173     }
174 }