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