]> git.pond.sub.org Git - empserver/blob - src/lib/lwp/lwp.c
The fix in the previous rev. wasn't complete.
[empserver] / src / lib / lwp / lwp.c
1 /*
2  * lwp.c -- lightweight process creation, destruction and manipulation.
3  * Copyright (C) 1991-3 Stephen Crane.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  * 
10  * This library 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 GNU
13  * Library General Public License for more details.
14  * 
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * author: Stephen Crane, (jsc@doc.ic.ac.uk), Department of Computing,
20  * Imperial College of Science, Technology and Medicine, 180 Queen's
21  * Gate, London SW7 2BZ, England.
22  */
23
24 #include <stdio.h>
25 #include <signal.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "lwp.h"
29 #include "lwpint.h"
30 #include "prototypes.h"
31
32 #if defined(_EMPTH_LWP)
33
34 #ifdef BOUNDS_CHECK
35 #include <bounds/fix-args.h>
36 #include <bounds/unchecked.h>
37 #endif
38
39 struct lwpQueue LwpSchedQ[LWP_MAX_PRIO], LwpDeadQ;
40
41 struct lwpProc *LwpCurrent = NULL;
42 char **LwpContextPtr;
43 int LwpMaxpri = 0;              /* maximum priority so far */
44
45 #ifdef POSIXSIGNALS
46 static sigset_t oldmask;
47 #else  /* POSIXSIGNALS */
48 static int oldmask;
49 #endif /* POSIXSIGNALS */
50
51 /* for systems without strdup  */
52 #ifdef NOSTRDUP
53 extern char *strdup();
54 #endif /* NOSTRDUP */
55
56 static void lwpStackCheckInit(struct lwpProc *newp);
57 static int lwpStackCheck(struct lwpProc *newp);
58 static void lwpStackCheckUsed(struct lwpProc *newp);
59
60 /* check stack direction */
61 static int
62 growsdown(void *x)
63 {
64     int y;
65
66 #ifdef BOUNDS_CHECK
67     BOUNDS_CHECKING_OFF;
68 #endif
69     y = (x > (void *)&y);
70
71 #ifdef BOUNDS_CHECK
72     BOUNDS_CHECKING_ON;
73 #endif
74
75     return y;
76 }
77
78 /*
79  * lwpReschedule -- schedule another process.  we also check for dead
80  * processes here and free them.
81  */
82 void
83 lwpReschedule(void)
84 {
85     static int lcount = LCOUNT;
86     static struct lwpProc *nextp;
87     static int i;
88 #ifdef POSIXSIGNALS
89     static sigset_t tmask;
90 #endif /* POSIXSIGNALS */
91
92     if (LwpCurrent && (LwpCurrent->flags & LWP_STACKCHECK)) {
93         lwpStackCheck(LwpCurrent);
94     }
95     if (!--lcount) {
96         int p = lwpSetPriority(LWP_MAX_PRIO - 1);
97         lcount = LCOUNT;
98 #ifdef POSIXSIGNALS
99         sigprocmask(SIG_SETMASK, &oldmask, &tmask);
100         sigprocmask(SIG_SETMASK, &tmask, &oldmask);
101 #else  /* POSIXSIGNALS */
102         sigsetmask(sigsetmask(oldmask));
103 #endif /* POSIXSIGNALS */
104         LwpCurrent->pri = p;
105     }
106
107     /* destroy dead threads */
108     lwpStatus(LwpCurrent, "Cleaning dead queue");
109     while (NULL != (nextp = lwpGetFirst(&LwpDeadQ))) {
110         if (nextp == LwpCurrent) {
111             lwpStatus(nextp, "OOOPS, we are running already dead thread");
112             exit(1);
113         }
114         lwpDestroy(nextp);
115         lwpStatus(LwpCurrent, "Destroying done");
116     }
117
118     for (i = LwpMaxpri + 1; i--;) {
119         while (NULL != (nextp = lwpGetFirst(&LwpSchedQ[i]))) {
120             if (!nextp->dead)
121                 break;
122             /* clean up after dead bodies */
123             lwpStatus(nextp, "got a dead body");
124             if (nextp == LwpCurrent) {
125                 lwpStatus(nextp, "we are in it -- will bury later");
126                 lwpAddTail(&LwpDeadQ, nextp);
127             } else {
128                 lwpDestroy(nextp);
129 /*                              fprintf(stderr,  "Destroying done\n"); */
130             }
131             nextp = 0;
132         }
133         if (nextp)
134             break;
135     }
136     if (LwpCurrent == 0 && nextp == 0) {
137         fprintf(stderr, "No processes to run!\n");
138         exit(1);
139     }
140     if (LwpCurrent)
141         lwpStatus(LwpCurrent, "switch out");
142     /* do context switch */
143 #ifdef BOUNDS_CHECK
144     BOUNDS_CHECKING_OFF;
145 #endif
146
147 #if defined(hpc)
148     {
149         int endpoint;
150
151         endpoint = &endpoint;
152         if (initcontext == NULL || endpoint < startpoint) {
153             i = lwpSave(LwpCurrent->context);
154         } else {
155             LwpCurrent->size = endpoint - startpoint;
156             LwpCurrent->sbtm = realloc(LwpCurrent->sbtm, LwpCurrent->size);
157             memcpy(LwpCurrent->sbtm, startpoint, LwpCurrent->size);
158             if (i = lwpSave(LwpCurrent->context)) {
159                 memcpy(startpoint, LwpCurrent->sbtm, LwpCurrent->size);
160                 i = 1;
161             }
162         }
163     }
164 #else
165     i = lwpSave(LwpCurrent->context);
166 #endif
167 #ifdef BOUNDS_CHECK
168     BOUNDS_CHECKING_ON;
169 #endif
170
171     if (LwpCurrent != nextp && !(LwpCurrent && i)) {
172         /* restore previous context */
173         lwpStatus(nextp, "switch in %d", nextp->pri);
174         LwpCurrent = nextp;
175         *LwpContextPtr = LwpCurrent->ud;
176 #ifdef BOUNDS_CHECK
177         BOUNDS_CHECKING_OFF;
178 #endif
179         lwpRestore(LwpCurrent->context);
180
181 #ifdef BOUNDS_CHECK
182         BOUNDS_CHECKING_ON;
183 #endif
184     }
185 }
186
187 /*
188  * lwpEntryPoint -- process entry point.
189  */
190 void
191 lwpEntryPoint(void)
192 {
193 #ifdef POSIXSIGNALS
194     sigset_t set;
195 #endif /* POSIXSIGNALS */
196
197 #ifdef BOUNDS_CHECK
198     BOUNDS_CHECKING_OFF;
199 #endif
200 #ifdef POSIXSIGNALS
201     sigemptyset(&set);
202     sigaddset(&set, SIGALRM);
203     sigprocmask(SIG_SETMASK, &set, &oldmask);
204 #else  /*  POSIXSIGNALS */
205     sigsetmask(SIGNALS);
206 #endif /* POSIXSIGNALS */
207     *LwpContextPtr = LwpCurrent->ud;
208
209     lwpStatus(LwpCurrent, "starting at entry point");
210     (*LwpCurrent->entry)(LwpCurrent->ud);
211     lwpExit();
212 #ifdef BOUNDS_CHECK
213     BOUNDS_CHECKING_ON;
214 #endif
215
216
217 }
218
219 /*
220  * lwpCreate -- create a process.
221  */
222 struct lwpProc *
223 lwpCreate(int priority, void (*entry)(void *), int size, int flags, char *name, char *desc, int argc, char **argv, void *ud)
224 {
225     struct lwpProc *newp;
226     int *s, x;
227 #ifdef UCONTEXT
228     stack_t sp;
229 #else  /* UCONTEXT */
230     void *sp;
231 #endif /* UCONTEXT */
232     unsigned long stackp;
233
234     if (!(newp = (struct lwpProc *)malloc(sizeof(struct lwpProc))))
235         return (0);
236     if (flags & LWP_STACKCHECK) {
237         /* Add a 1K buffer on each side of the stack */
238         size += 2 * LWP_REDZONE;
239     }
240     size += LWP_EXTRASTACK;
241     size += sizeof(stkalign_t);
242     if (!(s = (int *)malloc(size)))
243         return (0);
244     newp->flags = flags;
245     newp->name = strdup(name);
246     newp->desc = strdup(desc);
247     newp->entry = entry;
248     newp->argc = argc;
249     newp->argv = argv;
250     newp->ud = ud;
251     if ((newp->flags & LWP_STACKCHECK) == 0) {
252         stackp = growsdown((void *)&x) ?
253             (((long)s) + size - sizeof(stkalign_t) - LWP_EXTRASTACK) :
254             (long)s + LWP_EXTRASTACK;
255 #ifdef UCONTEXT
256         sp.ss_sp = (void *)(stackp & -sizeof(stkalign_t));
257         sp.ss_size = size;
258         sp.ss_flags = 0;
259 #else  /* UCONTEXT */
260         sp = (void *)(stackp & -sizeof(stkalign_t));
261 #endif /* UCONTEXT */
262     } else {
263         if (growsdown(&x)) {
264             /* round address off to stkalign_t */
265             stackp = ((long)s) + size - LWP_REDZONE -
266                 LWP_EXTRASTACK - sizeof(stkalign_t);
267 #ifdef UCONTEXT
268             sp.ss_sp = (void *)(stackp & -sizeof(stkalign_t));
269             sp.ss_size = size;
270             sp.ss_flags = 0;
271             newp->lowmark = (void *)(((long)sp.ss_sp) + LWP_EXTRASTACK);
272 #else  /* UCONTEXT */
273             sp = (void *)(stackp & -sizeof(stkalign_t));
274             newp->lowmark = (void *)(((long)sp) + LWP_EXTRASTACK);
275 #endif /* UCONTEXT */
276             newp->himark = s;
277         } else {
278             stackp = ((long)s) + LWP_REDZONE + LWP_EXTRASTACK;
279 #ifdef UCONTEXT
280             sp.ss_sp = (void *)(((long)stackp) & -sizeof(stkalign_t));
281             sp.ss_size = size;
282             sp.ss_flags = 0;
283 #else  /* UCONTEXT */
284             sp = (void *)(((long)stackp) & -sizeof(stkalign_t));
285 #endif /* UCONTEXT */
286             newp->lowmark = (void *)s;
287             newp->himark = (void *)(((long)s) + size - LWP_REDZONE);
288         }
289     }
290     if (LWP_MAX_PRIO <= priority)
291         priority = LWP_MAX_PRIO - 1;
292     if (LwpMaxpri < (newp->pri = priority))
293         LwpMaxpri = priority;
294     newp->sbtm = (void *)s;
295     newp->size = size;
296     newp->dead = 0;
297     if (flags & LWP_STACKCHECK)
298         lwpStackCheckInit(newp);
299     lwpStatus(newp, "creating process structure sbtm: %p",
300               newp->sbtm);
301     lwpReady(newp);
302     lwpReady(LwpCurrent);
303 #ifdef UCONTEXT
304     lwpInitContext(newp, &sp);  /* architecture-dependent: from arch.c */
305 #else  /* UCONTEXT */
306     lwpInitContext(newp, sp);   /* architecture-dependent: from arch.c */
307 #endif /* UCONTEXT */
308     lwpReschedule();
309     return (newp);
310 }
311
312 void
313 lwpDestroy(struct lwpProc *proc)
314 {
315     if (proc->flags & LWP_STACKCHECK) {
316         lwpStackCheckUsed(proc);
317         lwpStackCheck(proc);
318     }
319     lwpStatus(proc, "destroying sbtm: %p", proc->sbtm);
320     proc->entry = 0;
321     proc->ud = 0;
322     proc->argv = 0;
323     free((char *)proc->sbtm);
324     free(proc->name);
325     free(proc->desc);
326     proc->name = 0;
327     proc->desc = 0;
328     proc->sbtm = 0;
329     proc->lowmark = 0;
330     proc->himark = 0;
331     free((char *)proc);
332 }
333
334 /*
335  * lwpReady -- put process on ready queue.  if null, assume current.
336  */
337 void
338 lwpReady(struct lwpProc *p)
339 {
340     if (!p)
341         p = LwpCurrent;
342     lwpStatus(p, "added to run queue");
343     lwpAddTail(&LwpSchedQ[p->pri], p);
344 }
345
346 /*
347  * return user's data
348  */
349 void *
350 lwpGetUD(struct lwpProc *p)
351 {
352     if (!p)
353         p = LwpCurrent;
354     return (p->ud);
355 }
356
357 /*
358  * set user's data
359  */
360 void
361 lwpSetUD(struct lwpProc *p, char *ud)
362 {
363     if (!p)
364         p = LwpCurrent;
365     p->ud = ud;
366 }
367
368 /*
369  * set name & desc
370  */
371 void
372 lwpSetDesc(struct lwpProc *p, char *name, char *desc)
373 {
374     if (!p)
375         p = LwpCurrent;
376     free(p->name);
377     free(p->desc);
378     p->name = strdup(name);
379     p->desc = strdup(desc);
380 }
381
382 /*
383  * lwpYield -- yield the processor to another thread.
384  */
385 void
386 lwpYield(void)
387 {
388     lwpStatus(LwpCurrent, "yielding control");
389     lwpReady(LwpCurrent);
390     lwpReschedule();
391 }
392
393 /*
394  * cause the current process to be scheduled for deletion.
395  */
396 void
397 lwpExit(void)
398 {
399     lwpStatus(LwpCurrent, "marking self as dead");
400     LwpCurrent->dead = 1;
401     lwpYield();
402 }
403
404 /*
405  * mark another process as dead, so it will never be rescheduled.
406  * remove any lingering FD action
407  */
408 void
409 lwpTerminate(struct lwpProc *p)
410 {
411     lwpStatus(p, "terminating process");
412     p->dead = 1;
413     if (p->fd >= 0)
414         lwpWakeupFd(p);
415 }
416
417 /*
418  * set the thread's priority, returning the old.
419  * if the new priority is lower than the old, we reschedule.
420  */
421 int
422 lwpSetPriority(int new)
423 {
424     int old = LwpCurrent->pri;
425
426     if (LWP_MAX_PRIO <= new)
427         new = LWP_MAX_PRIO - 1;
428     if (LwpMaxpri < new)
429         LwpMaxpri = new;
430     LwpCurrent->pri = new;
431     lwpStatus(LwpCurrent, "resetting priority (%d -> %d)", old, new);
432     if (new < old)
433         lwpYield();
434     return (old);
435 }
436
437 /*
438  * initialise the coroutine structures
439  */
440 struct lwpProc *
441 lwpInitSystem(int pri, char **ctxptr, int flags)
442 {
443     struct lwpQueue *q;
444     int i, *stack;
445     struct lwpProc *sel;
446
447     LwpContextPtr = ctxptr;
448     if (pri < 1)
449         pri = 1;
450     /* *LwpContextPtr = 0; */
451     if (!(LwpCurrent = calloc(1, sizeof(struct lwpProc))))
452         return (0);
453     if (!(stack = (int *)malloc(64)))
454         return (0);
455     if (LWP_MAX_PRIO <= pri)
456         pri = LWP_MAX_PRIO - 1;
457     if (LwpMaxpri < pri)
458         LwpMaxpri = pri;
459     LwpCurrent->next = 0;
460     LwpCurrent->sbtm = stack;   /* dummy stack for "main" */
461     LwpCurrent->pri = pri;
462     LwpCurrent->dead = 0;
463     LwpCurrent->flags = flags;
464     LwpCurrent->name = "Main";
465     for (i = LWP_MAX_PRIO, q = LwpSchedQ; i--; q++)
466         q->head = q->tail = 0;
467     LwpDeadQ.head = LwpDeadQ.tail = 0;
468     /* must be lower in priority than us for this to work right */
469     sel = lwpCreate(0, lwpSelect, 16384, flags, "EventHandler",
470                     "Select (main loop) Event Handler", 0, 0, 0);
471     lwpInitSelect(sel);
472     return (LwpCurrent);
473 }
474
475 /* lwpStackCheckInit
476  *
477  * Initialize the entire stack (including both redzones) with the stack
478  * check mark.  Thus, we can get some indication of how much stack was
479  * used.
480  */
481 static void
482 lwpStackCheckInit(struct lwpProc *newp)
483 {
484     register int i;
485     register long *lp;
486
487     int lim = newp->size / sizeof(long);
488     if (!newp || !newp->sbtm)
489         return;
490     for (lp = newp->sbtm, i = 0; i < lim; i++, lp++) {
491         *lp = LWP_CHECKMARK;
492     }
493 }
494
495 /* lwpStackCheck
496  *
497  * Check if the thread has overflowed/underflowed its stack.
498  * NOTE:
499  *   If an problem occurs, it is not corrected.
500  *   The buffer is not cleaned up, nor is the thread terminated.
501  *   Cleaning up the buffer would be a mistake, and terminating
502  *   the thread, well, could be done.   Should more like take
503  *   down the entire process.
504  */
505 static int
506 lwpStackCheck(struct lwpProc *newp)
507 {
508     register int end, amt;
509     register unsigned int i;
510     register long *lp;
511     register int growsDown;
512     int marker;
513
514     if (!newp || !newp->himark || !newp->lowmark)
515         return (1);
516     growsDown = growsdown(&marker);
517     for (lp = newp->himark, i = 0; i < LWP_REDZONE / sizeof(long);
518          i++, lp++) {
519         if (*lp == LWP_CHECKMARK)
520             continue;
521         /* Stack overflow. */
522         if (growsDown) {
523             end = i;
524             while (i < LWP_REDZONE / sizeof(long)) {
525                 if (*lp++ != LWP_CHECKMARK)
526                     end = i;
527                 i++;
528             }
529             amt = (end + 1) * sizeof(long);
530         } else {
531             amt = (i + 1) * sizeof(long);
532         }
533         lwpStatus(newp, "Thread stack overflowed %d bytes (of %u)",
534                   amt, newp->size - 2 * LWP_REDZONE - (int)sizeof(stkalign_t));
535         return (0);
536     }
537     for (lp = newp->lowmark, i = 0; i < LWP_REDZONE / sizeof(long);
538          i++, lp++) {
539         if (*lp == LWP_CHECKMARK)
540             continue;
541         /* Stack underflow. */
542         if (growsDown) {
543             end = i;
544             while (i < LWP_REDZONE / sizeof(long)) {
545                 if (*lp++ != LWP_CHECKMARK)
546                     end = i;
547                 i++;
548             }
549             amt = (end + 1) * sizeof(long);
550         } else {
551             amt = (LWP_REDZONE - i + 1) * sizeof(long);
552         }
553         lwpStatus(newp, "Thread stack underflow %d bytes (of %u)",
554                   amt, newp->size - 2 * LWP_REDZONE - (int)sizeof(stkalign_t));
555         return (0);
556     }
557     return (1);
558 }
559
560 /* lwpStackCheckUsed
561  *
562  * Figure out how much stack was used by this thread.
563  */
564 static void
565 lwpStackCheckUsed(struct lwpProc *newp)
566 {
567     register int i;
568     register long *lp;
569     register int lim;
570     int marker;
571
572     if (!newp || !newp->sbtm)
573         return;
574     lim = newp->size / sizeof(long);
575     if (growsdown(&marker)) {
576         /* Start at the bottom and find first non checkmark. */
577         for (lp = newp->sbtm, i = 0; i < lim; i++, lp++) {
578             if (*lp != LWP_CHECKMARK) {
579                 break;
580             }
581         }
582     } else {
583         /* Start at the top and find first non checkmark. */
584         lp = newp->sbtm;
585         lp += newp->size / sizeof(long);
586         lp--;
587         for (i = 0; i < lim; i++, lp--) {
588             if (*lp != LWP_CHECKMARK) {
589                 break;
590             }
591         }
592     }
593     lwpStatus(newp, "stack use: %u bytes (of %u total)",
594               i * (int)sizeof(long) - LWP_REDZONE,
595               newp->size - 2 * LWP_REDZONE - (int)sizeof(stkalign_t));
596 }
597
598 #endif