Commit | Line | Data |
---|---|---|
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 | */ | |
48 | struct timeval __fd_kern_wait_timeout = { 0, 0 }; | |
49 | ||
90269d47 AM |
50 | /* |
51 | * Global for user-kernel lock, and blocked signals | |
52 | */ | |
83dfe606 | 53 | static volatile sigset_t sig_to_tryagain; |
90269d47 AM |
54 | static volatile sigset_t sig_to_process; |
55 | static volatile int kernel_lock = 0; | |
56 | static volatile int sig_count = 0; | |
57 | ||
83dfe606 | 58 | static void sig_handler(int signal); |
90269d47 AM |
59 | static void set_thread_timer(); |
60 | void sig_prevent(void); | |
61 | void 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 |
72 | static 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 | ||
93 | context_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 | */ | |
172 | void 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 | */ | |
195 | void 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 | */ | |
206 | static 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 | */ | |
236 | static 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 | ||
266 | sig_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 | */ | |
320 | void 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 | */ | |
336 | void 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 | */ | |
353 | void 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 | */ | |
371 | void sig_prevent(void) | |
372 | { | |
373 | kernel_lock++; | |
374 | } | |
375 | ||
376 | /* ========================================================================== | |
377 | * sig_resume() | |
378 | */ | |
379 | void sig_resume() | |
380 | { | |
381 | kernel_lock--; | |
382 | } | |
383 | ||
384 | /* ========================================================================== | |
385 | * sig_check_and_resume() | |
386 | */ | |
387 | void 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 | */ | |
413 | void 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 |