Pull in some of the lpt_port_test fixes from lpt.c.
[unix-history] / lib / libpthread / pthreads / signal.c
CommitLineData
90269d47 1/* ==== signal.c ============================================================
83dfe606 2 * Copyright (c) 1993, 1994 by Chris Provenzano, proven@mit.edu
30b08b75
AM
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
56a62443
CP
15 * This product includes software developed by Chris Provenzano.
16 * 4. The name of Chris Provenzano may not be used to endorse or promote
17 * products derived from this software without specific prior written
18 * permission.
30b08b75 19 *
56a62443 20 * THIS SOFTWARE IS PROVIDED BY CHRIS PROVENZANO ``AS IS'' AND
30b08b75
AM
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56a62443
CP
23 * ARE DISCLAIMED. IN NO EVENT SHALL CHRIS PROVENZANO BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * Description : Queue functions.
33 *
34 * 1.00 93/07/21 proven
35 * -Started coding this file.
30b08b75 36 */
56a62443 37
83dfe606 38#include <pthread.h>
90269d47
AM
39#include <signal.h>
40
83dfe606
CP
41/*
42 * Time which select in fd_kern_wait() will sleep.
43 * If there are no threads to run we sleep for an hour or until
44 * we get an interrupt or an fd thats awakens. To make sure we
45 * don't miss an interrupt this variable gets reset too zero in
46 * sig_handler_real().
47 */
48struct timeval __fd_kern_wait_timeout = { 0, 0 };
49
90269d47
AM
50/*
51 * Global for user-kernel lock, and blocked signals
52 */
83dfe606 53static volatile sigset_t sig_to_tryagain;
90269d47
AM
54static volatile sigset_t sig_to_process;
55static volatile int kernel_lock = 0;
56static volatile int sig_count = 0;
57
83dfe606 58static void sig_handler(int signal);
90269d47
AM
59static void set_thread_timer();
60void sig_prevent(void);
61void sig_resume(void);
62
63/* ==========================================================================
64 * context_switch()
65 *
66 * This routine saves the current state of the running thread gets
67 * the next thread to run and restores it's state. To allow different
68 * processors to work with this routine, I allow the machdep_restore_state()
69 * to either return or have it return from machdep_save_state with a value
70 * other than 0, this is for implementations which use setjmp/longjmp.
71 */
90269d47
AM
72static void context_switch()
73{
83dfe606
CP
74 struct pthread **current, *next, *last;
75 semaphore *lock;
76 int count;
90269d47
AM
77
78 /* save state of current thread */
79 if (machdep_save_state()) {
80 return;
81 }
82
83dfe606 83 last = pthread_run;
90269d47
AM
84 if (pthread_run = pthread_queue_deq(&pthread_current_queue)) {
85 /* restore state of new current thread */
86 machdep_restore_state();
87 return;
88 }
83dfe606 89
90269d47
AM
90 /* Poll all the kernel fds */
91 fd_kern_poll();
92
93context_switch_reschedule:;
94 /*
95 * Go through the reschedule list once, this is the only place
96 * that goes through the queue without using the queue routines.
97 *
98 * But first delete the current queue.
99 */
100 pthread_queue_init(&pthread_current_queue);
101 current = &(pthread_link_list);
83dfe606
CP
102 count = 0;
103
90269d47
AM
104 while (*current) {
105 switch((*current)->state) {
106 case PS_RUNNING:
107 pthread_queue_enq(&pthread_current_queue, *current);
108 current = &((*current)->pll);
83dfe606 109 count++;
90269d47
AM
110 break;
111 case PS_DEAD:
83dfe606
CP
112 /* Cleanup thread, unless we're using the stack */
113 if (((*current)->flags & PF_DETACHED) && (*current != last)) {
114 next = (*current)->pll;
115 lock = &((*current)->lock);
116 if (SEMAPHORE_TEST_AND_SET(lock)) {
117 /* Couldn't cleanup this time, try again later */
118 current = &((*current)->pll);
119 } else {
120 if (!((*current)->attr.stackaddr_attr)) {
121 free (machdep_pthread_cleanup(&((*current)->machdep_data)));
122 }
123 free (*current);
124 *current = next;
125 }
126 } else {
127 current = &((*current)->pll);
128 }
90269d47
AM
129 break;
130 default:
131 /* Should be on a different queue. Ignore. */
132 current = &((*current)->pll);
83dfe606 133 count++;
90269d47
AM
134 break;
135 }
136 }
137
83dfe606 138 /* Are there any threads to run */
90269d47
AM
139 if (pthread_run = pthread_queue_deq(&pthread_current_queue)) {
140 /* restore state of new current thread */
141 machdep_restore_state();
142 return;
143 }
144
83dfe606
CP
145 /* Are there any threads at all */
146 if (count) {
147 /*
148 * Do a wait, timeout is set to a hour unless we get an interrupt
149 * before the select in wich case it polls and returns.
150 */
151 fd_kern_wait();
90269d47 152
83dfe606
CP
153 /* Check for interrupts, but ignore SIGVTALR */
154 sigdelset(&sig_to_process, SIGVTALRM);
155
156 if (sig_to_process) {
157 /* Process interrupts */
158 sig_handler(0);
159 }
160
161 goto context_switch_reschedule;
162
163 }
164 exit(0);
165}
166
167/* ==========================================================================
168 * sig_handler_pause()
169 *
170 * Wait until a signal is sent to the process.
171 */
172void sig_handler_pause()
173{
174 sigset_t sig_to_block, sig_to_pause;
175
176 sigfillset(&sig_to_block);
177 sigemptyset(&sig_to_pause);
178 sigprocmask(SIG_BLOCK, &sig_to_block, NULL);
179 if (!sig_to_process) {
180 sigsuspend(&sig_to_pause);
181 }
182 sigprocmask(SIG_UNBLOCK, &sig_to_block, NULL);
90269d47
AM
183}
184
185/* ==========================================================================
186 * context_switch_done()
187 *
188 * This routine does all the things that are necessary after a context_switch()
189 * calls the machdep_restore_state(). DO NOT put this in the context_switch()
190 * routine because sometimes the machdep_restore_state() doesn't return
191 * to context_switch() but instead ends up in machdep_thread_start() or
192 * some such routine, which will need to call this routine and
193 * sig_check_and_resume().
194 */
195void context_switch_done()
196{
197 sigdelset(&sig_to_process, SIGVTALRM);
198 set_thread_timer();
199}
200
201/* ==========================================================================
202 * set_thread_timer()
203 *
204 * Assums kernel is locked.
205 */
206static void set_thread_timer()
207{
208 static int last_sched_attr = SCHED_RR;
209
210 switch (pthread_run->attr.sched_attr) {
211 case SCHED_RR:
212 machdep_set_thread_timer(&(pthread_run->machdep_data));
213 break;
214 case SCHED_FIFO:
215 if (last_sched_attr != SCHED_FIFO) {
216 machdep_unset_thread_timer();
217 }
218 break;
219 case SCHED_IO:
83dfe606 220 if ((last_sched_attr != SCHED_IO) && (!sig_count)) {
90269d47
AM
221 machdep_set_thread_timer(&(pthread_run->machdep_data));
222 }
223 break;
224 default:
225 machdep_set_thread_timer(&(pthread_run->machdep_data));
226 break;
227 }
83dfe606 228 last_sched_attr = pthread_run->attr.sched_attr;
90269d47
AM
229}
230
231/* ==========================================================================
232 * sig_handler()
233 *
234 * Assumes the kernel is locked.
235 */
236static void sig_handler(int sig)
237{
83dfe606
CP
238
239 /*
240 * First check for old signals, do one pass through and don't
241 * check any twice.
242 */
243 if (sig_to_tryagain) {
244 if (sigismember(&sig_to_tryagain, SIGALRM)) {
245 switch (sleep_wakeup()) {
246 case 1:
247 /* Do the default action, no threads were sleeping */
248 case OK:
249 /* Woke up a sleeping thread */
250 sigdelset(&sig_to_tryagain, SIGALRM);
251 break;
252 case NOTOK:
253 /* Couldn't get appropriate locks, try again later */
254 break;
255 }
256 } else {
257 PANIC();
258 }
259 }
260
261 /*
262 * NOW, process signal that just came in, plus any pending on the
263 * signal mask. All of these must be resolved.
264 */
265
266sig_handler_top:;
90269d47
AM
267
268 switch(sig) {
269 case 0:
270 break;
271 case SIGVTALRM:
272 if (sig_count) {
273 sigset_t sigall;
274
275 sig_count = 0;
276
277 /* Unblock all signals */
278 sigemptyset(&sigall);
279 sigprocmask(SIG_SETMASK, &sigall, NULL);
280 }
281 context_switch();
282 context_switch_done();
283 break;
284 case SIGALRM:
83dfe606
CP
285 sigdelset(&sig_to_process, SIGALRM);
286 switch (sleep_wakeup()) {
287 case 1:
288 /* Do the default action, no threads were sleeping */
289 case OK:
290 /* Woke up a sleeping thread */
90269d47 291 break;
83dfe606
CP
292 case NOTOK:
293 /* Couldn't get appropriate locks, try again later */
294 sigaddset(&sig_to_tryagain, SIGALRM);
295 break;
296 }
297 break;
90269d47
AM
298 default:
299 PANIC();
300 }
301
302 /* Determine if there are any other signals */
303 if (sig_to_process) {
304 for (sig = 1; sig <= SIGMAX; sig++) {
305 if (sigismember(&sig_to_process, sig)) {
306
307 /* goto sig_handler_top */
308 goto sig_handler_top;
309 }
310 }
311 }
312}
313
314/* ==========================================================================
315 * sig_handler_real()
316 *
317 * On a multi-processor this would need to use the test and set instruction
318 * otherwise the following will work.
319 */
320void sig_handler_real(int sig)
321{
322 if (kernel_lock) {
83dfe606 323 __fd_kern_wait_timeout.tv_sec = 0;
90269d47
AM
324 sigaddset(&sig_to_process, sig);
325 return;
326 }
327 sig_prevent();
328 sig_count++;
329 sig_handler(sig);
330 sig_resume();
331}
332
333/* ==========================================================================
334 * sig_handler_fake()
335 */
336void sig_handler_fake(int sig)
337{
338 if (kernel_lock) {
339 /* Currently this should be impossible */
340 PANIC();
341 }
342 sig_prevent();
343 sig_handler(sig);
344 sig_resume();
345}
346
347/* ==========================================================================
348 * reschedule()
349 *
350 * This routine assumes that the caller is the current pthread, pthread_run
351 * and that it has a lock on itself and that it wants to reschedule itself.
352 */
353void reschedule(enum pthread_state state)
354{
355 semaphore *plock;
356
357 if (kernel_lock) {
358 /* Currently this should be impossible */
359 PANIC();
360 }
361 sig_prevent();
362 pthread_run->state = state;
363 SEMAPHORE_RESET((plock = &(pthread_run->lock)));
364 sig_handler(SIGVTALRM);
365 sig_resume();
366}
367
368/* ==========================================================================
369 * sig_prevent()
370 */
371void sig_prevent(void)
372{
373 kernel_lock++;
374}
375
376/* ==========================================================================
377 * sig_resume()
378 */
379void sig_resume()
380{
381 kernel_lock--;
382}
383
384/* ==========================================================================
385 * sig_check_and_resume()
386 */
387void sig_check_and_resume()
388{
389 /* Some routine name that is yet to be determined. */
390
391 /* Only bother if we are truely unlocking the kernel */
392 while (!(--kernel_lock)) {
393
394 /* Assume sigset_t is not a struct or union */
395 if (sig_to_process) {
396 kernel_lock++;
397 sig_handler(0);
398 } else {
399 break;
400 }
401 }
402}
403
404/* ==========================================================================
405 * sig_init()
406 *
407 * SIGVTALRM (NOT POSIX) needed for thread timeslice timeouts.
408 * Since it's not POSIX I will replace it with a
409 * virtual timer for threads.
410 * SIGALRM (IS POSIX) so some special handling will be
411 * necessary to fake SIGALRM signals
412 */
413void sig_init(void)
414{
415 int sig_to_init[] = { SIGVTALRM, SIGALRM, 0 };
416 int i;
417
418 /* Initialize only the necessary signals */
419
420 for (i = 0; sig_to_init[i]; i++) {
421 if (signal(sig_to_init[i], sig_handler_real)) {
422 PANIC();
423 }
424 }
425}
426