/* * lwp.c -- lightweight process creation, destruction and manipulation. * Copyright (C) 1991-3 Stephen Crane. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * author: Stephen Crane, (jsc@doc.ic.ac.uk), Department of Computing, * Imperial College of Science, Technology and Medicine, 180 Queen's * Gate, London SW7 2BZ, England. */ #include #include #include #include #include "lwp.h" #include "lwpint.h" #include "prototypes.h" #if defined(_EMPTH_LWP) struct lwpQueue LwpSchedQ[LWP_MAX_PRIO], LwpDeadQ; struct lwpProc *LwpCurrent = NULL; char **LwpContextPtr; int LwpMaxpri = 0; /* maximum priority so far */ static sigset_t oldmask; static void lwpStackCheckInit(struct lwpProc *newp); static void lwpStackCheck(struct lwpProc *newp); static void lwpStackCheckUsed(struct lwpProc *newp); /* check stack direction */ static int growsdown(void *x) { int y; y = (x > (void *)&y); return y; } /* * lwpReschedule -- schedule another process. we also check for dead * processes here and free them. */ void lwpReschedule(void) { static int lcount = LCOUNT; static struct lwpProc *nextp; static int i; static sigset_t tmask; if (LwpCurrent && (LwpCurrent->flags & LWP_STACKCHECK)) { lwpStackCheck(LwpCurrent); } if (!--lcount) { int p = lwpSetPriority(LWP_MAX_PRIO - 1); lcount = LCOUNT; sigprocmask(SIG_SETMASK, &oldmask, &tmask); sigprocmask(SIG_SETMASK, &tmask, &oldmask); LwpCurrent->pri = p; } /* destroy dead threads */ lwpStatus(LwpCurrent, "Cleaning dead queue"); while (NULL != (nextp = lwpGetFirst(&LwpDeadQ))) { if (nextp == LwpCurrent) { lwpStatus(nextp, "OOOPS, we are running already dead thread"); exit(1); } lwpDestroy(nextp); lwpStatus(LwpCurrent, "Destroying done"); } for (i = LwpMaxpri + 1; i--;) { while (NULL != (nextp = lwpGetFirst(&LwpSchedQ[i]))) { if (!nextp->dead) break; /* clean up after dead bodies */ lwpStatus(nextp, "got a dead body"); if (nextp == LwpCurrent) { lwpStatus(nextp, "we are in it -- will bury later"); lwpAddTail(&LwpDeadQ, nextp); } else { lwpDestroy(nextp); } nextp = 0; } if (nextp) break; } if (LwpCurrent == 0 && nextp == 0) { fprintf(stderr, "No processes to run!\n"); exit(1); } if (LwpCurrent) lwpStatus(LwpCurrent, "switch out"); /* do context switch */ i = LwpCurrent && lwpSave(LwpCurrent->context); if (LwpCurrent != nextp && !i) { /* restore previous context */ lwpStatus(nextp, "switch in %d", nextp->pri); LwpCurrent = nextp; *LwpContextPtr = LwpCurrent->ud; lwpRestore(LwpCurrent->context); } } /* * lwpEntryPoint -- process entry point. */ void lwpEntryPoint(void) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGALRM); sigprocmask(SIG_SETMASK, &set, &oldmask); *LwpContextPtr = LwpCurrent->ud; lwpStatus(LwpCurrent, "starting at entry point"); (*LwpCurrent->entry)(LwpCurrent->ud); lwpExit(); } /* * lwpCreate -- create a process. */ struct lwpProc * lwpCreate(int priority, void (*entry)(void *), int stacksz, int flags, char *name, char *desc, int argc, char **argv, void *ud) { struct lwpProc *newp; char *s, *sp; int size, redsize, x; #ifdef UCONTEXT stack_t usp; #endif /* UCONTEXT */ if (CANT_HAPPEN(STKALIGN == 0|| (STKALIGN & (STKALIGN - 1)))) return NULL; /* STKALIGN not power of 2 */ if (!(newp = malloc(sizeof(struct lwpProc)))) return 0; /* Make size a multiple of sizeof(long) to things aligned */ stacksz = (stacksz + sizeof(long) - 1) & -sizeof(long); /* Add a red zone on each side of the stack for LWP_STACKCHECK */ redsize = flags & LWP_STACKCHECK ? LWP_REDZONE : 0; size = stacksz + 2 * redsize + LWP_EXTRASTACK + STKALIGN - 1; if (!(s = malloc(size))) return 0; newp->flags = flags; newp->name = strdup(name); newp->desc = strdup(desc); newp->entry = entry; newp->argc = argc; newp->argv = argv; newp->ud = ud; if (growsdown(&x)) { /* * Stack layout for stack growing downward: * ptr block size * -------------------------------------- * waste x * lowmark -> red zone LWP_REDZONE * sp -> extra LWP_EXTRASTACK * stack stacksz * himark -> red zone LWP_EXTRASTACK * waste STKALIGN - 1 - x * sp is aligned to a multiple of STKALIGN. */ sp = s + redsize + stacksz; sp = (char *)0 + (((sp + STKALIGN - 1) - (char *)0) & -STKALIGN); newp->lowmark = sp + LWP_EXTRASTACK; newp->himark = sp - stacksz - redsize; } else { /* * Stack layout for stack growing upward: * ptr block size * -------------------------------------- * waste x * himark -> red zone LWP_REDZONE * extra LWP_EXTRASTACK * sp -> stack stacksz * lowmark -> red zone LWP_EXTRASTACK * waste STKALIGN - 1 - x * sp is aligned to a multiple of STKALIGN. */ sp = s + redsize + LWP_EXTRASTACK; sp = (char *)0 + (((sp + STKALIGN - 1) - (char *)0) & -STKALIGN); newp->lowmark = sp - LWP_EXTRASTACK - redsize; newp->himark = sp + size; } if (LWP_MAX_PRIO <= priority) priority = LWP_MAX_PRIO - 1; if (LwpMaxpri < (newp->pri = priority)) LwpMaxpri = priority; newp->sbtm = s; newp->size = size; newp->dead = 0; if (flags & LWP_STACKCHECK) lwpStackCheckInit(newp); lwpStatus(newp, "creating process structure sbtm: %p", newp->sbtm); lwpReady(newp); lwpReady(LwpCurrent); #ifdef UCONTEXT usp.ss_sp = s + redsize; usp.ss_size = stacksz; usp.ss_flags = 0; lwpInitContext(newp, &usp); /* architecture-dependent: from arch.c */ #else /* UCONTEXT */ lwpInitContext(newp, sp); /* architecture-dependent: from arch.c */ #endif /* UCONTEXT */ lwpReschedule(); return newp; } void lwpDestroy(struct lwpProc *proc) { if (proc->flags & LWP_STACKCHECK) { lwpStackCheckUsed(proc); lwpStackCheck(proc); } lwpStatus(proc, "destroying sbtm: %p", proc->sbtm); proc->entry = 0; proc->ud = 0; proc->argv = 0; free(proc->sbtm); free(proc->name); free(proc->desc); proc->name = 0; proc->desc = 0; proc->sbtm = 0; proc->lowmark = 0; proc->himark = 0; free(proc); } /* * lwpReady -- put process on ready queue. if null, assume current. */ void lwpReady(struct lwpProc *p) { if (!p) p = LwpCurrent; lwpStatus(p, "added to run queue"); lwpAddTail(&LwpSchedQ[p->pri], p); } /* * return user's data */ void * lwpGetUD(struct lwpProc *p) { if (!p) p = LwpCurrent; return p->ud; } /* * set user's data */ void lwpSetUD(struct lwpProc *p, char *ud) { if (!p) p = LwpCurrent; p->ud = ud; } /* * set name & desc */ void lwpSetDesc(struct lwpProc *p, char *name, char *desc) { if (!p) p = LwpCurrent; free(p->name); free(p->desc); p->name = strdup(name); p->desc = strdup(desc); } /* * lwpYield -- yield the processor to another thread. */ void lwpYield(void) { lwpStatus(LwpCurrent, "yielding control"); lwpReady(LwpCurrent); lwpReschedule(); } /* * cause the current process to be scheduled for deletion. */ void lwpExit(void) { lwpStatus(LwpCurrent, "marking self as dead"); LwpCurrent->dead = 1; lwpYield(); } /* * mark another process as dead, so it will never be rescheduled. * remove any lingering FD action */ void lwpTerminate(struct lwpProc *p) { lwpStatus(p, "terminating process"); p->dead = 1; if (p->fd >= 0) lwpWakeupFd(p); } /* * set the thread's priority, returning the old. * if the new priority is lower than the old, we reschedule. */ int lwpSetPriority(int new) { int old = LwpCurrent->pri; if (LWP_MAX_PRIO <= new) new = LWP_MAX_PRIO - 1; if (LwpMaxpri < new) LwpMaxpri = new; LwpCurrent->pri = new; lwpStatus(LwpCurrent, "resetting priority (%d -> %d)", old, new); if (new < old) lwpYield(); return old; } /* * initialise the coroutine structures */ struct lwpProc * lwpInitSystem(int pri, char **ctxptr, int flags) { struct lwpQueue *q; int i, *stack; struct lwpProc *sel; LwpContextPtr = ctxptr; if (pri < 1) pri = 1; /* *LwpContextPtr = 0; */ if (!(LwpCurrent = calloc(1, sizeof(struct lwpProc)))) return 0; if (!(stack = malloc(64))) return 0; if (LWP_MAX_PRIO <= pri) pri = LWP_MAX_PRIO - 1; if (LwpMaxpri < pri) LwpMaxpri = pri; LwpCurrent->next = 0; LwpCurrent->sbtm = stack; /* dummy stack for "main" */ LwpCurrent->pri = pri; LwpCurrent->dead = 0; LwpCurrent->flags = flags & ~LWP_STACKCHECK; LwpCurrent->name = "Main"; for (i = LWP_MAX_PRIO, q = LwpSchedQ; i--; q++) q->head = q->tail = 0; LwpDeadQ.head = LwpDeadQ.tail = 0; /* must be lower in priority than us for this to work right */ sel = lwpCreate(0, lwpSelect, 16384, flags, "EventHandler", "Select (main loop) Event Handler", 0, 0, 0); lwpInitSelect(sel); return LwpCurrent; } /* lwpStackCheckInit * * Initialize the entire stack (including both redzones) with the stack * check mark. Thus, we can get some indication of how much stack was * used. */ static void lwpStackCheckInit(struct lwpProc *newp) { register int i; register long *lp; int lim = newp->size / sizeof(long); if (!newp || !newp->sbtm) return; for (lp = newp->sbtm, i = 0; i < lim; i++, lp++) { *lp = LWP_CHECKMARK; } } /* lwpStackCheck * * Check if the thread has overflowed/underflowed its stack. * Should that happen, abort the process, as we cannot recover. */ static void lwpStackCheck(struct lwpProc *newp) { register int end, amt; register unsigned int i; register long *lp; register int growsDown; int marker; if (CANT_HAPPEN(!newp || !newp->himark || !newp->lowmark)) return; growsDown = growsdown(&marker); for (lp = newp->himark, i = 0; i < LWP_REDZONE / sizeof(long); i++, lp++) { if (*lp == LWP_CHECKMARK) continue; /* Stack overflow. */ if (growsDown) { end = i; while (i < LWP_REDZONE / sizeof(long)) { if (*lp++ != LWP_CHECKMARK) end = i; i++; } amt = (end + 1) * sizeof(long); } else { amt = (i + 1) * sizeof(long); } logerror("Thread %s stack overflow %d bytes (of %u)", newp->name, amt, newp->size - 2 * LWP_REDZONE - (int)STKALIGN); abort(); } for (lp = newp->lowmark, i = 0; i < LWP_REDZONE / sizeof(long); i++, lp++) { if (*lp == LWP_CHECKMARK) continue; /* Stack underflow. */ if (growsDown) { end = i; while (i < LWP_REDZONE / sizeof(long)) { if (*lp++ != LWP_CHECKMARK) end = i; i++; } amt = (end + 1) * sizeof(long); } else { amt = (LWP_REDZONE - i + 1) * sizeof(long); } logerror("Thread %s stack underflow %d bytes (of %u)", newp->name, amt, newp->size - 2 * LWP_REDZONE - (int)STKALIGN); abort(); } } /* lwpStackCheckUsed * * Figure out how much stack was used by this thread. */ static void lwpStackCheckUsed(struct lwpProc *newp) { register int i; register long *lp; register int lim; int marker; if (!newp || !newp->sbtm) return; lim = newp->size / sizeof(long); if (growsdown(&marker)) { /* Start at the bottom and find first non checkmark. */ for (lp = newp->sbtm, i = 0; i < lim; i++, lp++) { if (*lp != LWP_CHECKMARK) { break; } } } else { /* Start at the top and find first non checkmark. */ lp = newp->sbtm; lp += newp->size / sizeof(long); lp--; for (i = 0; i < lim; i++, lp--) { if (*lp != LWP_CHECKMARK) { break; } } } lwpStatus(newp, "Thread stack %lu used %lu left %lu total", labs((char *)lp - (char *)newp->lowmark) - LWP_REDZONE, labs((char *)newp->himark - (char *)lp) - LWP_REDZONE, labs((char *)newp->himark - (char *)newp->lowmark) - LWP_REDZONE); } #endif