]> git.pond.sub.org Git - empserver/blob - src/lib/lwp/lwp.c
(lwpReschedule): Fix the previous revision.
[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 size, int flags, char *name, char *desc, int argc, char **argv, void *ud)
146 {
147     struct lwpProc *newp;
148     char *s, *sp;
149     int redsize, x;
150 #ifdef UCONTEXT
151     stack_t usp;
152 #endif /* UCONTEXT */
153
154     if (!(newp = malloc(sizeof(struct lwpProc))))
155         return 0;
156     /* Add a 1K buffer on each side of the stack */
157     redsize = flags & LWP_STACKCHECK ? LWP_REDZONE : 0;
158     size += 2 * redsize;
159     size += LWP_EXTRASTACK;
160     size += sizeof(stkalign_t);
161     if (!(s = malloc(size)))
162         return 0;
163     newp->flags = flags;
164     newp->name = strdup(name);
165     newp->desc = strdup(desc);
166     newp->entry = entry;
167     newp->argc = argc;
168     newp->argv = argv;
169     newp->ud = ud;
170     if (growsdown(&x)) {
171         sp = s + size - sizeof(stkalign_t) - LWP_EXTRASTACK - redsize;
172         sp = (char *)0 + ((sp - (char *)0) & -sizeof(stkalign_t));
173         newp->lowmark = sp + LWP_EXTRASTACK;
174         newp->himark = s;
175     } else {
176         sp = s + LWP_EXTRASTACK + redsize;
177         sp = (char *)0 + ((sp - (char *)0) & -sizeof(stkalign_t));
178         newp->lowmark = s;
179         newp->himark = s + size - LWP_REDZONE;
180     }
181     if (LWP_MAX_PRIO <= priority)
182         priority = LWP_MAX_PRIO - 1;
183     if (LwpMaxpri < (newp->pri = priority))
184         LwpMaxpri = priority;
185     newp->sbtm = s;
186     newp->size = size;
187     newp->dead = 0;
188     if (flags & LWP_STACKCHECK)
189         lwpStackCheckInit(newp);
190     lwpStatus(newp, "creating process structure sbtm: %p",
191               newp->sbtm);
192     lwpReady(newp);
193     lwpReady(LwpCurrent);
194 #ifdef UCONTEXT
195     usp.ss_sp = sp;
196     usp.ss_size = size;
197     usp.ss_flags = 0;
198     lwpInitContext(newp, &usp); /* architecture-dependent: from arch.c */
199 #else  /* UCONTEXT */
200     lwpInitContext(newp, sp);   /* architecture-dependent: from arch.c */
201 #endif /* UCONTEXT */
202     lwpReschedule();
203     return newp;
204 }
205
206 void
207 lwpDestroy(struct lwpProc *proc)
208 {
209     if (proc->flags & LWP_STACKCHECK) {
210         lwpStackCheckUsed(proc);
211         lwpStackCheck(proc);
212     }
213     lwpStatus(proc, "destroying sbtm: %p", proc->sbtm);
214     proc->entry = 0;
215     proc->ud = 0;
216     proc->argv = 0;
217     free(proc->sbtm);
218     free(proc->name);
219     free(proc->desc);
220     proc->name = 0;
221     proc->desc = 0;
222     proc->sbtm = 0;
223     proc->lowmark = 0;
224     proc->himark = 0;
225     free(proc);
226 }
227
228 /*
229  * lwpReady -- put process on ready queue.  if null, assume current.
230  */
231 void
232 lwpReady(struct lwpProc *p)
233 {
234     if (!p)
235         p = LwpCurrent;
236     lwpStatus(p, "added to run queue");
237     lwpAddTail(&LwpSchedQ[p->pri], p);
238 }
239
240 /*
241  * return user's data
242  */
243 void *
244 lwpGetUD(struct lwpProc *p)
245 {
246     if (!p)
247         p = LwpCurrent;
248     return p->ud;
249 }
250
251 /*
252  * set user's data
253  */
254 void
255 lwpSetUD(struct lwpProc *p, char *ud)
256 {
257     if (!p)
258         p = LwpCurrent;
259     p->ud = ud;
260 }
261
262 /*
263  * set name & desc
264  */
265 void
266 lwpSetDesc(struct lwpProc *p, char *name, char *desc)
267 {
268     if (!p)
269         p = LwpCurrent;
270     free(p->name);
271     free(p->desc);
272     p->name = strdup(name);
273     p->desc = strdup(desc);
274 }
275
276 /*
277  * lwpYield -- yield the processor to another thread.
278  */
279 void
280 lwpYield(void)
281 {
282     lwpStatus(LwpCurrent, "yielding control");
283     lwpReady(LwpCurrent);
284     lwpReschedule();
285 }
286
287 /*
288  * cause the current process to be scheduled for deletion.
289  */
290 void
291 lwpExit(void)
292 {
293     lwpStatus(LwpCurrent, "marking self as dead");
294     LwpCurrent->dead = 1;
295     lwpYield();
296 }
297
298 /*
299  * mark another process as dead, so it will never be rescheduled.
300  * remove any lingering FD action
301  */
302 void
303 lwpTerminate(struct lwpProc *p)
304 {
305     lwpStatus(p, "terminating process");
306     p->dead = 1;
307     if (p->fd >= 0)
308         lwpWakeupFd(p);
309 }
310
311 /*
312  * set the thread's priority, returning the old.
313  * if the new priority is lower than the old, we reschedule.
314  */
315 int
316 lwpSetPriority(int new)
317 {
318     int old = LwpCurrent->pri;
319
320     if (LWP_MAX_PRIO <= new)
321         new = LWP_MAX_PRIO - 1;
322     if (LwpMaxpri < new)
323         LwpMaxpri = new;
324     LwpCurrent->pri = new;
325     lwpStatus(LwpCurrent, "resetting priority (%d -> %d)", old, new);
326     if (new < old)
327         lwpYield();
328     return old;
329 }
330
331 /*
332  * initialise the coroutine structures
333  */
334 struct lwpProc *
335 lwpInitSystem(int pri, char **ctxptr, int flags)
336 {
337     struct lwpQueue *q;
338     int i, *stack;
339     struct lwpProc *sel;
340
341     LwpContextPtr = ctxptr;
342     if (pri < 1)
343         pri = 1;
344     /* *LwpContextPtr = 0; */
345     if (!(LwpCurrent = calloc(1, sizeof(struct lwpProc))))
346         return 0;
347     if (!(stack = malloc(64)))
348         return 0;
349     if (LWP_MAX_PRIO <= pri)
350         pri = LWP_MAX_PRIO - 1;
351     if (LwpMaxpri < pri)
352         LwpMaxpri = pri;
353     LwpCurrent->next = 0;
354     LwpCurrent->sbtm = stack;   /* dummy stack for "main" */
355     LwpCurrent->pri = pri;
356     LwpCurrent->dead = 0;
357     LwpCurrent->flags = flags & ~LWP_STACKCHECK;
358     LwpCurrent->name = "Main";
359     for (i = LWP_MAX_PRIO, q = LwpSchedQ; i--; q++)
360         q->head = q->tail = 0;
361     LwpDeadQ.head = LwpDeadQ.tail = 0;
362     /* must be lower in priority than us for this to work right */
363     sel = lwpCreate(0, lwpSelect, 16384, flags, "EventHandler",
364                     "Select (main loop) Event Handler", 0, 0, 0);
365     lwpInitSelect(sel);
366     return LwpCurrent;
367 }
368
369 /* lwpStackCheckInit
370  *
371  * Initialize the entire stack (including both redzones) with the stack
372  * check mark.  Thus, we can get some indication of how much stack was
373  * used.
374  */
375 static void
376 lwpStackCheckInit(struct lwpProc *newp)
377 {
378     register int i;
379     register long *lp;
380
381     int lim = newp->size / sizeof(long);
382     if (!newp || !newp->sbtm)
383         return;
384     for (lp = newp->sbtm, i = 0; i < lim; i++, lp++) {
385         *lp = LWP_CHECKMARK;
386     }
387 }
388
389 /* lwpStackCheck
390  *
391  * Check if the thread has overflowed/underflowed its stack.
392  * Should that happen, abort the process, as we cannot recover.
393  */
394 static void
395 lwpStackCheck(struct lwpProc *newp)
396 {
397     register int end, amt;
398     register unsigned int i;
399     register long *lp;
400     register int growsDown;
401     int marker;
402
403     if (CANT_HAPPEN(!newp || !newp->himark || !newp->lowmark))
404         return;
405     growsDown = growsdown(&marker);
406     for (lp = newp->himark, i = 0; i < LWP_REDZONE / sizeof(long);
407          i++, lp++) {
408         if (*lp == LWP_CHECKMARK)
409             continue;
410         /* Stack overflow. */
411         if (growsDown) {
412             end = i;
413             while (i < LWP_REDZONE / sizeof(long)) {
414                 if (*lp++ != LWP_CHECKMARK)
415                     end = i;
416                 i++;
417             }
418             amt = (end + 1) * sizeof(long);
419         } else {
420             amt = (i + 1) * sizeof(long);
421         }
422         logerror("Thread %s stack overflow %d bytes (of %u)",
423                  newp->name, amt,
424                  newp->size - 2 * LWP_REDZONE - (int)sizeof(stkalign_t));
425         abort();
426     }
427     for (lp = newp->lowmark, i = 0; i < LWP_REDZONE / sizeof(long);
428          i++, lp++) {
429         if (*lp == LWP_CHECKMARK)
430             continue;
431         /* Stack underflow. */
432         if (growsDown) {
433             end = i;
434             while (i < LWP_REDZONE / sizeof(long)) {
435                 if (*lp++ != LWP_CHECKMARK)
436                     end = i;
437                 i++;
438             }
439             amt = (end + 1) * sizeof(long);
440         } else {
441             amt = (LWP_REDZONE - i + 1) * sizeof(long);
442         }
443         logerror("Thread %s stack underflow %d bytes (of %u)",
444                   newp->name, amt,
445                  newp->size - 2 * LWP_REDZONE - (int)sizeof(stkalign_t));
446         abort();
447     }
448 }
449
450 /* lwpStackCheckUsed
451  *
452  * Figure out how much stack was used by this thread.
453  */
454 static void
455 lwpStackCheckUsed(struct lwpProc *newp)
456 {
457     register int i;
458     register long *lp;
459     register int lim;
460     int marker;
461
462     if (!newp || !newp->sbtm)
463         return;
464     lim = newp->size / sizeof(long);
465     if (growsdown(&marker)) {
466         /* Start at the bottom and find first non checkmark. */
467         for (lp = newp->sbtm, i = 0; i < lim; i++, lp++) {
468             if (*lp != LWP_CHECKMARK) {
469                 break;
470             }
471         }
472     } else {
473         /* Start at the top and find first non checkmark. */
474         lp = newp->sbtm;
475         lp += newp->size / sizeof(long);
476         lp--;
477         for (i = 0; i < lim; i++, lp--) {
478             if (*lp != LWP_CHECKMARK) {
479                 break;
480             }
481         }
482     }
483     lwpStatus(newp, "Thread stack %lu used %lu left %lu total",
484               labs((char *)lp - (char *)newp->lowmark) - LWP_REDZONE,
485               labs((char *)newp->himark - (char *)lp) - LWP_REDZONE,
486               labs((char *)newp->himark - (char *)newp->lowmark) - LWP_REDZONE);
487 }
488
489 #endif