]> git.pond.sub.org Git - empserver/blob - src/lib/lwp/lwp.c
(lwpCreate) [UCONTEXT]: ss_sp is the (base) address of the stack area,
[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 struct lwpQueue LwpSchedQ[LWP_MAX_PRIO], LwpDeadQ;
35
36 struct lwpProc *LwpCurrent = NULL;
37 char **LwpContextPtr;
38 int LwpMaxpri = 0;              /* maximum priority so far */
39
40 static sigset_t oldmask;
41
42 static void lwpStackCheckInit(struct lwpProc *newp);
43 static void lwpStackCheck(struct lwpProc *newp);
44 static void lwpStackCheckUsed(struct lwpProc *newp);
45
46 /* check stack direction */
47 static int
48 growsdown(void *x)
49 {
50     int y;
51     y = (x > (void *)&y);
52     return y;
53 }
54
55 /*
56  * lwpReschedule -- schedule another process.  we also check for dead
57  * processes here and free them.
58  */
59 void
60 lwpReschedule(void)
61 {
62     static int lcount = LCOUNT;
63     static struct lwpProc *nextp;
64     static int i;
65     static sigset_t tmask;
66
67     if (LwpCurrent && (LwpCurrent->flags & LWP_STACKCHECK)) {
68         lwpStackCheck(LwpCurrent);
69     }
70     if (!--lcount) {
71         int p = lwpSetPriority(LWP_MAX_PRIO - 1);
72         lcount = LCOUNT;
73         sigprocmask(SIG_SETMASK, &oldmask, &tmask);
74         sigprocmask(SIG_SETMASK, &tmask, &oldmask);
75         LwpCurrent->pri = p;
76     }
77
78     /* destroy dead threads */
79     lwpStatus(LwpCurrent, "Cleaning dead queue");
80     while (NULL != (nextp = lwpGetFirst(&LwpDeadQ))) {
81         if (nextp == LwpCurrent) {
82             lwpStatus(nextp, "OOOPS, we are running already dead thread");
83             exit(1);
84         }
85         lwpDestroy(nextp);
86         lwpStatus(LwpCurrent, "Destroying done");
87     }
88
89     for (i = LwpMaxpri + 1; i--;) {
90         while (NULL != (nextp = lwpGetFirst(&LwpSchedQ[i]))) {
91             if (!nextp->dead)
92                 break;
93             /* clean up after dead bodies */
94             lwpStatus(nextp, "got a dead body");
95             if (nextp == LwpCurrent) {
96                 lwpStatus(nextp, "we are in it -- will bury later");
97                 lwpAddTail(&LwpDeadQ, nextp);
98             } else {
99                 lwpDestroy(nextp);
100             }
101             nextp = 0;
102         }
103         if (nextp)
104             break;
105     }
106     if (LwpCurrent == 0 && nextp == 0) {
107         fprintf(stderr, "No processes to run!\n");
108         exit(1);
109     }
110     if (LwpCurrent)
111         lwpStatus(LwpCurrent, "switch out");
112     /* do context switch */
113     i = LwpCurrent && lwpSave(LwpCurrent->context);
114     if (LwpCurrent != nextp && !i) {
115         /* restore previous context */
116         lwpStatus(nextp, "switch in %d", nextp->pri);
117         LwpCurrent = nextp;
118         *LwpContextPtr = LwpCurrent->ud;
119         lwpRestore(LwpCurrent->context);
120     }
121 }
122
123 /*
124  * lwpEntryPoint -- process entry point.
125  */
126 void
127 lwpEntryPoint(void)
128 {
129     sigset_t set;
130
131     sigemptyset(&set);
132     sigaddset(&set, SIGALRM);
133     sigprocmask(SIG_SETMASK, &set, &oldmask);
134     *LwpContextPtr = LwpCurrent->ud;
135
136     lwpStatus(LwpCurrent, "starting at entry point");
137     (*LwpCurrent->entry)(LwpCurrent->ud);
138     lwpExit();
139 }
140
141 /*
142  * lwpCreate -- create a process.
143  */
144 struct lwpProc *
145 lwpCreate(int priority, void (*entry)(void *), int stacksz, int flags, char *name, char *desc, int argc, char **argv, void *ud)
146 {
147     struct lwpProc *newp;
148     char *s, *sp;
149     int size, redsize, x;
150 #ifdef UCONTEXT
151     stack_t usp;
152 #endif /* UCONTEXT */
153
154     if (CANT_HAPPEN(STKALIGN == 0|| (STKALIGN & (STKALIGN - 1))))
155         return NULL;            /* STKALIGN not power of 2 */
156     if (!(newp = malloc(sizeof(struct lwpProc))))
157         return 0;
158     /* Make size a multiple of sizeof(long) to things aligned */
159     stacksz = (stacksz + sizeof(long) - 1) & -sizeof(long);
160     /* Add a red zone on each side of the stack for LWP_STACKCHECK */
161     redsize = flags & LWP_STACKCHECK ? LWP_REDZONE : 0;
162     size = stacksz + 2 * redsize + LWP_EXTRASTACK + STKALIGN - 1;
163     if (!(s = malloc(size)))
164         return 0;
165     newp->flags = flags;
166     newp->name = strdup(name);
167     newp->desc = strdup(desc);
168     newp->entry = entry;
169     newp->argc = argc;
170     newp->argv = argv;
171     newp->ud = ud;
172     if (growsdown(&x)) {
173         /*
174          * Stack layout for stack growing downward:
175          *     ptr        block      size
176          *     --------------------------------------
177          *                waste      x
178          *     lowmark -> red zone   LWP_REDZONE
179          *     sp      -> extra      LWP_EXTRASTACK
180          *                stack      stacksz
181          *     himark  -> red zone   LWP_EXTRASTACK
182          *                waste      STKALIGN - 1 - x
183          * sp is aligned to a multiple of STKALIGN.
184          */
185         sp = s + redsize + stacksz;
186         sp = (char *)0 + (((sp + STKALIGN - 1) - (char *)0) & -STKALIGN);
187         newp->lowmark = sp + LWP_EXTRASTACK;
188         newp->himark = sp - stacksz - redsize;
189     } else {
190         /*
191          * Stack layout for stack growing upward:
192          *     ptr        block      size
193          *     --------------------------------------
194          *                waste      x
195          *     himark  -> red zone   LWP_REDZONE
196          *                extra      LWP_EXTRASTACK
197          *     sp      -> stack      stacksz
198          *     lowmark -> red zone   LWP_EXTRASTACK
199          *                waste      STKALIGN - 1 - x
200          * sp is aligned to a multiple of STKALIGN.
201          */
202         sp = s + redsize + LWP_EXTRASTACK;
203         sp = (char *)0 + (((sp + STKALIGN - 1) - (char *)0) & -STKALIGN);
204         newp->lowmark = sp - LWP_EXTRASTACK - redsize;
205         newp->himark = sp + size;
206     }
207     if (LWP_MAX_PRIO <= priority)
208         priority = LWP_MAX_PRIO - 1;
209     if (LwpMaxpri < (newp->pri = priority))
210         LwpMaxpri = priority;
211     newp->sbtm = s;
212     newp->size = size;
213     newp->dead = 0;
214     if (flags & LWP_STACKCHECK)
215         lwpStackCheckInit(newp);
216     lwpStatus(newp, "creating process structure sbtm: %p",
217               newp->sbtm);
218     lwpReady(newp);
219     lwpReady(LwpCurrent);
220 #ifdef UCONTEXT
221     usp.ss_sp = s + redsize;
222     usp.ss_size = stacksz;
223     usp.ss_flags = 0;
224     lwpInitContext(newp, &usp); /* architecture-dependent: from arch.c */
225 #else  /* UCONTEXT */
226     lwpInitContext(newp, sp);   /* architecture-dependent: from arch.c */
227 #endif /* UCONTEXT */
228     lwpReschedule();
229     return newp;
230 }
231
232 void
233 lwpDestroy(struct lwpProc *proc)
234 {
235     if (proc->flags & LWP_STACKCHECK) {
236         lwpStackCheckUsed(proc);
237         lwpStackCheck(proc);
238     }
239     lwpStatus(proc, "destroying sbtm: %p", proc->sbtm);
240     proc->entry = 0;
241     proc->ud = 0;
242     proc->argv = 0;
243     free(proc->sbtm);
244     free(proc->name);
245     free(proc->desc);
246     proc->name = 0;
247     proc->desc = 0;
248     proc->sbtm = 0;
249     proc->lowmark = 0;
250     proc->himark = 0;
251     free(proc);
252 }
253
254 /*
255  * lwpReady -- put process on ready queue.  if null, assume current.
256  */
257 void
258 lwpReady(struct lwpProc *p)
259 {
260     if (!p)
261         p = LwpCurrent;
262     lwpStatus(p, "added to run queue");
263     lwpAddTail(&LwpSchedQ[p->pri], p);
264 }
265
266 /*
267  * return user's data
268  */
269 void *
270 lwpGetUD(struct lwpProc *p)
271 {
272     if (!p)
273         p = LwpCurrent;
274     return p->ud;
275 }
276
277 /*
278  * set user's data
279  */
280 void
281 lwpSetUD(struct lwpProc *p, char *ud)
282 {
283     if (!p)
284         p = LwpCurrent;
285     p->ud = ud;
286 }
287
288 /*
289  * set name & desc
290  */
291 void
292 lwpSetDesc(struct lwpProc *p, char *name, char *desc)
293 {
294     if (!p)
295         p = LwpCurrent;
296     free(p->name);
297     free(p->desc);
298     p->name = strdup(name);
299     p->desc = strdup(desc);
300 }
301
302 /*
303  * lwpYield -- yield the processor to another thread.
304  */
305 void
306 lwpYield(void)
307 {
308     lwpStatus(LwpCurrent, "yielding control");
309     lwpReady(LwpCurrent);
310     lwpReschedule();
311 }
312
313 /*
314  * cause the current process to be scheduled for deletion.
315  */
316 void
317 lwpExit(void)
318 {
319     lwpStatus(LwpCurrent, "marking self as dead");
320     LwpCurrent->dead = 1;
321     lwpYield();
322 }
323
324 /*
325  * mark another process as dead, so it will never be rescheduled.
326  * remove any lingering FD action
327  */
328 void
329 lwpTerminate(struct lwpProc *p)
330 {
331     lwpStatus(p, "terminating process");
332     p->dead = 1;
333     if (p->fd >= 0)
334         lwpWakeupFd(p);
335 }
336
337 /*
338  * set the thread's priority, returning the old.
339  * if the new priority is lower than the old, we reschedule.
340  */
341 int
342 lwpSetPriority(int new)
343 {
344     int old = LwpCurrent->pri;
345
346     if (LWP_MAX_PRIO <= new)
347         new = LWP_MAX_PRIO - 1;
348     if (LwpMaxpri < new)
349         LwpMaxpri = new;
350     LwpCurrent->pri = new;
351     lwpStatus(LwpCurrent, "resetting priority (%d -> %d)", old, new);
352     if (new < old)
353         lwpYield();
354     return old;
355 }
356
357 /*
358  * initialise the coroutine structures
359  */
360 struct lwpProc *
361 lwpInitSystem(int pri, char **ctxptr, int flags)
362 {
363     struct lwpQueue *q;
364     int i, *stack;
365     struct lwpProc *sel;
366
367     LwpContextPtr = ctxptr;
368     if (pri < 1)
369         pri = 1;
370     /* *LwpContextPtr = 0; */
371     if (!(LwpCurrent = calloc(1, sizeof(struct lwpProc))))
372         return 0;
373     if (!(stack = malloc(64)))
374         return 0;
375     if (LWP_MAX_PRIO <= pri)
376         pri = LWP_MAX_PRIO - 1;
377     if (LwpMaxpri < pri)
378         LwpMaxpri = pri;
379     LwpCurrent->next = 0;
380     LwpCurrent->sbtm = stack;   /* dummy stack for "main" */
381     LwpCurrent->pri = pri;
382     LwpCurrent->dead = 0;
383     LwpCurrent->flags = flags & ~LWP_STACKCHECK;
384     LwpCurrent->name = "Main";
385     for (i = LWP_MAX_PRIO, q = LwpSchedQ; i--; q++)
386         q->head = q->tail = 0;
387     LwpDeadQ.head = LwpDeadQ.tail = 0;
388     /* must be lower in priority than us for this to work right */
389     sel = lwpCreate(0, lwpSelect, 16384, flags, "EventHandler",
390                     "Select (main loop) Event Handler", 0, 0, 0);
391     lwpInitSelect(sel);
392     return LwpCurrent;
393 }
394
395 /* lwpStackCheckInit
396  *
397  * Initialize the entire stack (including both redzones) with the stack
398  * check mark.  Thus, we can get some indication of how much stack was
399  * used.
400  */
401 static void
402 lwpStackCheckInit(struct lwpProc *newp)
403 {
404     register int i;
405     register long *lp;
406
407     int lim = newp->size / sizeof(long);
408     if (!newp || !newp->sbtm)
409         return;
410     for (lp = newp->sbtm, i = 0; i < lim; i++, lp++) {
411         *lp = LWP_CHECKMARK;
412     }
413 }
414
415 /* lwpStackCheck
416  *
417  * Check if the thread has overflowed/underflowed its stack.
418  * Should that happen, abort the process, as we cannot recover.
419  */
420 static void
421 lwpStackCheck(struct lwpProc *newp)
422 {
423     register int end, amt;
424     register unsigned int i;
425     register long *lp;
426     register int growsDown;
427     int marker;
428
429     if (CANT_HAPPEN(!newp || !newp->himark || !newp->lowmark))
430         return;
431     growsDown = growsdown(&marker);
432     for (lp = newp->himark, i = 0; i < LWP_REDZONE / sizeof(long);
433          i++, lp++) {
434         if (*lp == LWP_CHECKMARK)
435             continue;
436         /* Stack overflow. */
437         if (growsDown) {
438             end = i;
439             while (i < LWP_REDZONE / sizeof(long)) {
440                 if (*lp++ != LWP_CHECKMARK)
441                     end = i;
442                 i++;
443             }
444             amt = (end + 1) * sizeof(long);
445         } else {
446             amt = (i + 1) * sizeof(long);
447         }
448         logerror("Thread %s stack overflow %d bytes (of %u)",
449                  newp->name, amt,
450                  newp->size - 2 * LWP_REDZONE - (int)STKALIGN);
451         abort();
452     }
453     for (lp = newp->lowmark, i = 0; i < LWP_REDZONE / sizeof(long);
454          i++, lp++) {
455         if (*lp == LWP_CHECKMARK)
456             continue;
457         /* Stack underflow. */
458         if (growsDown) {
459             end = i;
460             while (i < LWP_REDZONE / sizeof(long)) {
461                 if (*lp++ != LWP_CHECKMARK)
462                     end = i;
463                 i++;
464             }
465             amt = (end + 1) * sizeof(long);
466         } else {
467             amt = (LWP_REDZONE - i + 1) * sizeof(long);
468         }
469         logerror("Thread %s stack underflow %d bytes (of %u)",
470                   newp->name, amt,
471                  newp->size - 2 * LWP_REDZONE - (int)STKALIGN);
472         abort();
473     }
474 }
475
476 /* lwpStackCheckUsed
477  *
478  * Figure out how much stack was used by this thread.
479  */
480 static void
481 lwpStackCheckUsed(struct lwpProc *newp)
482 {
483     register int i;
484     register long *lp;
485     register int lim;
486     int marker;
487
488     if (!newp || !newp->sbtm)
489         return;
490     lim = newp->size / sizeof(long);
491     if (growsdown(&marker)) {
492         /* Start at the bottom and find first non checkmark. */
493         for (lp = newp->sbtm, i = 0; i < lim; i++, lp++) {
494             if (*lp != LWP_CHECKMARK) {
495                 break;
496             }
497         }
498     } else {
499         /* Start at the top and find first non checkmark. */
500         lp = newp->sbtm;
501         lp += newp->size / sizeof(long);
502         lp--;
503         for (i = 0; i < lim; i++, lp--) {
504             if (*lp != LWP_CHECKMARK) {
505                 break;
506             }
507         }
508     }
509     lwpStatus(newp, "Thread stack %lu used %lu left %lu total",
510               labs((char *)lp - (char *)newp->lowmark) - LWP_REDZONE,
511               labs((char *)newp->himark - (char *)lp) - LWP_REDZONE,
512               labs((char *)newp->himark - (char *)newp->lowmark) - LWP_REDZONE);
513 }
514
515 #endif