326 lines
7.9 KiB
C
326 lines
7.9 KiB
C
/* Copyright (C) 1986 Sun Microsystems Inc. */
|
|
|
|
#include <lwp/common.h>
|
|
#include <lwp/queue.h>
|
|
#include <lwp/cntxt.h>
|
|
#include <machlwp/machdep.h>
|
|
#include <lwp/lwperror.h>
|
|
#include <lwp/process.h>
|
|
#include <lwp/schedule.h>
|
|
#include <lwp/alloc.h>
|
|
#include <lwp/condvar.h>
|
|
#include <lwp/monitor.h>
|
|
#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;
|
|
}
|