/* Copyright (C) 1986 Sun Microsystems Inc. */ #include #include #include #include #include #include #include #include #include #include #ifndef lint SCCSID(@(#) process.c 1.1 94/10/31 Copyr 1987 Sun Micro); #endif lint extern int runthreads; extern void return_stk(); /* * PRIMITIVES contained herein: * lwp_self() * lwp_create(tid, pc, prio, flags, stack, nargs, arg1, arg2, ..., argn) * lwp_destroy(tid) * lwp_suspend(tid) * lwp_resume(tid) * lwp_geterr() */ extern int __LwpRunCnt; /* total number of lwp's in the system */ STATIC int LwpType; /* cookie for context caches */ STATIC int LwpStatus; /* exit status for pod */ thread_t __ThreadNull = {CHARNULL, 0}; /* the non-existent thread */ int __Nrunnable; /* Number of runnable threads */ qheader_t __SuspendQ; qheader_t __SleepQ; /* * Automatic kill on process exit */ void __lwpkill() { thread_t corpse; /* * If an interrupt causes this process to die * in another way, we never return here. * __Curproc will always be correct when * this process is running so there is no race. */ LWPTRACE(tr_PROCESS, 1, ("process died automatically\n")); corpse.thread_id = (caddr_t)__Curproc; corpse.thread_key = __Curproc->lwp_lock; (void)lwp_destroy(corpse); __panic("return from __lwpkill"); } /* * This process is really dead. We can safely remove all traces of * it that remain: its context. */ void __freeproc(lwp) register lwp_t *lwp; /* completely stiff corpse */ { register caddr_t laststack; LWPTRACE(tr_PROCESS, 2, ("__freeproc %x\n", lwp)); lwp->lwp_run = RS_UNINIT; lwp->lwp_type = BADOBJECT_TYPE; /* free any special contexts */ __freectxt(lwp); lwp->lwp_lock = INVALIDLOCK; /* in case someone refs this mem */ /* * free the process descriptor itself. Once we do this, * the lwp must not be on any queues (it will be on * the free list after this). */ laststack = (caddr_t)lwp->lwp_stack; FREECHUNK(LwpType, lwp); return_stk(laststack); } /*VARARGS*/ /* * lwp_create() -- PRIMITIVE. * create a (runnable) lightweight process and start it running if * it's priority warrants it. * It is required that the new task start at a procedure entry * to make it easier to keep track of how to set up the stack (see machdep.c). * We assume that the stack points to the top of the (already allocated) * stack, and that the stack is mapped in. */ int lwp_create(va_alist) va_dcl /* arguments to new thread */ { va_list argptr; /* argument pointer for varargs package */ thread_t *tid; /* thread being created */ void (*pc)(); /* initial procedure defining the new task */ int prio; /* scheduling priority (higher preempts lower) */ int flags; /* options to create thread with */ stkalign_t *stack; /* top of client-supplied stack */ int nargs; /* number of arguments */ register lwp_t *baby; /* the new little process we are making */ int funarg; LWPINIT(); CLR_ERROR(); va_start (argptr); tid = va_arg(argptr, thread_t *); funarg = va_arg(argptr, int); pc = (void(*)())funarg; prio = va_arg(argptr, int); flags = va_arg(argptr, int); stack = va_arg(argptr, stkalign_t *); nargs = va_arg(argptr, int); ERROR((nargs > MAXARGS), LE_INVALIDARG); ERROR(((prio > __MaxPrio) || (prio < MINPRIO)), LE_ILLPRIO); GETCHUNK((lwp_t *), baby, LwpType); ERROR((baby == LWPNULL), LE_NOROOM); baby->lwp_stack = (int)stack; stack = (stkalign_t *)STACKALIGN(stack); if (stack != (stkalign_t *)0) { MAKE_STACK(baby, argptr, stack, __lwpkill, pc, nargs); } else { /* force suspension; can't run yet */ flags |= LWPSUSPEND; } va_end (argptr); baby->lwp_full = FALSE; INIT_QUEUE(&baby->lwp_ctx); baby->lwp_type = PROCESS_TYPE; __LwpRunCnt++; LWPTRACE(tr_PROCESS, 2, ("numprocesses %d\n", __LwpRunCnt)); baby->lwp_blockedon = NOBODY; baby->lwp_curmon = MONNULL; baby->lwp_suspended = FALSE; baby->lwp_prio = prio; baby->lwp_run = RS_UNINIT; baby->lwp_lwperrno = LE_NOERROR; baby->lwp_monlevel = 0; baby->lwp_flags = flags; baby->lwp_lock = UNIQUEID(); baby->lwp_cancel = 0; #ifdef MULTIPROCESSOR lwp_setref(baby); #endif MULTIPROCESSOR if (tid != (thread_t *)0) { tid->thread_id = (caddr_t)baby; tid->thread_key = baby->lwp_lock; } if (flags & LWPSUSPEND) { INS_QUEUE(&__SuspendQ, baby); baby->lwp_run = RS_SUSPEND; baby->lwp_suspended = TRUE; } else { UNBLOCK(baby); /* * In 4.1, a thread never calls lwp_create */ if (!runthreads) return (0); YIELD_CPU(prio); } return (0); } /* * lwp_resume -- PRIMITIVE. * make process eligible to run from suspended state and * run it if its priority warrants it. */ int lwp_resume(tid) thread_t tid; { register lwp_t *proc = (lwp_t *)(tid.thread_id); int s; CLR_ERROR(); s = splclock(); ERROR((tid.thread_key != proc->lwp_lock), LE_NONEXIST); proc->lwp_suspended = FALSE; if (proc->lwp_run == RS_SUSPEND) { /* not waiting for a message, etc. */ REMX_ITEM(&__SuspendQ, proc); UNBLOCK(proc); if (!runthreads) { (void) splx(s); return (0); } YIELD_CPU(proc->lwp_prio); } (void) splx(s); return (0); } /* * lwp_self -- PRIMITIVE. * Set identity of current thread. */ int lwp_self(tid) thread_t *tid; /* will hold identity of current thread */ { LWPINIT(); CLR_ERROR(); tid->thread_id = (caddr_t)__Curproc; tid->thread_key = __Curproc->lwp_lock; return (0); } /* * lwp_destroy -- PRIMITIVE. * destroy a lwp or agent and free all resources it owns * (such as monitor locks). */ int lwp_destroy(body) thread_t body; { register lwp_t *corpse = (lwp_t *)(body.thread_id); register int prio; register int kill_mutex; kill_mutex = splclock(); CLR_ERROR(); if (corpse == LWPNULL) { /* self-destruction */ body.thread_id = (caddr_t)__Curproc; corpse = __Curproc; body.thread_key = corpse->lwp_lock; } ERROR((body.thread_key != corpse->lwp_lock), LE_NONEXIST); prio = corpse->lwp_prio; /* * Remove this process from whatever queue it belonged to. * Must dequeue BEFORE freeing the context itself. * In effect, we make the process look like it's running from * the point of view of the cleanup routines as the process * will not be on any blocked queue. (It won't be on the __RunQ * either though). */ switch (corpse->lwp_run) { case RS_CONDVAR: REMX_ITEM(&(((condvar_t *)(corpse->lwp_blockedon))->cv_waiting), corpse); break; case RS_UNBLOCKED: REMX_ITEM(&__RunQ[prio], corpse); __Nrunnable--; break; case RS_SUSPEND: REMX_ITEM(&__SuspendQ, corpse); break; default: __panic("bad destroy"); } LWPTRACE(tr_PROCESS, 3, ("Remove process, %d left\n", __LwpRunCnt)); /* * Before calling freeproc, be sure that I'm not depending on * the client stack for anything. * At this point, we're sure that the client will die. * Since we are splclock nobody will try to save registers * on behalf of a non-valid __Curproc. */ if (corpse == __Curproc) { SETSTACK(); } __freeproc(corpse); --__LwpRunCnt; if (corpse == __Curproc) { __Curproc = &Nullproc; /* NB: if inter. now, this is the victim */ (void) splx(kill_mutex); __schedule(); /* NOTREACHED */ } else { /* removing somebody other than caller */ LWPTRACE(tr_PROCESS, 4, ("removing process in middle of run queue\n")); (void) splx(kill_mutex); if (!runthreads) { return (0); } WAIT(); } return (0); } /* * lwp_geterr() -- PRIMITIVE. * return the error number associated with the current lwp. */ lwp_err_t lwp_geterr() { return (__Curproc->lwp_lwperrno); } /* * initialize context caches and process info */ void __init_lwp() { register int i; extern int maxasynchio_cached; __Nrunnable = 0; LwpStatus = 0; /* default: normal exit status */ LwpType = __allocinit(sizeof (lwp_t), maxasynchio_cached, IFUNCNULL, FALSE); for (i = 0; i <= __MaxPrio; i++) INIT_QUEUE(&__RunQ[i]); INIT_QUEUE(&__SleepQ); INIT_QUEUE(&__SuspendQ); __LwpRunCnt = 0; }