Import of Empire 4.2.12

This commit is contained in:
Markus Armbruster 2003-08-23 12:23:04 +00:00
commit d8b7fdfae1
817 changed files with 126589 additions and 0 deletions

64
src/lib/lwp/Makefile Normal file
View file

@ -0,0 +1,64 @@
#
# Empire - A multi-player, client/server Internet based war game.
# Copyright (C) 1986-2000, Dave Pare, Jeff Bailey, Thomas Ruschak,
# Ken Stevens, Steve McClure
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# ---
#
# See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
# related information and legal notices. It is expected that any future
# projects/authors will amend these files as needed.
#
# Makefile - Wolfpack, 1996
# Note that these could have been listed 1 per line, but I chose to just
# stick them all together this way to shorten the file.
include ../../../build.conf
include ../../make.src
include ../../make.defs
LIB = $(SRCDIR)/lib/liblwp.a
OBJS = arch.o lwp.o queue.o sel.o sem.o status.o
MIPSOBJS = mipsarch.o
AIXOBJS = lwpInit.o lwpRestore.o lwpSave.o
GENMASTER = GLOBALCFLAGS="$(GLOBALCFLAGS)" GLOBALLFLAGS="$(GLOBALLFLAGS)"
all: $(LIB)
$(LIB): $(OBJS) $(EXTRAOBJS)
rm -f $(LIB)
ar cq $(LIB) $(OBJS) $(EXTRAOBJS)
$(RANLIB) $(LIB)
nt:
mipsultrix:
make EXTRAOBJS="$(MIPSOBJS)" $(GENMASTER)
aix:
make EXTRAOBJS="$(AIXOBJS)" $(GENMASTER)
clean:
-(rm -f $(OBJS) $(AIXOBJS) $(MIPSOBJS))
include ../../make.rules
include Makedepend

428
src/lib/lwp/arch.c Normal file
View file

@ -0,0 +1,428 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2000, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
* related information and legal notices. It is expected that any future
* projects/authors will amend these files as needed.
*
* ---
*
* arch.c: architecture-dependant process context code
*
* Known contributors to this file:
* Dave Pare, 1994
* Steve McClure, 1994-2000
*/
#include "prototypes.h"
#if defined(_EMPTH_LWP)
#if (!defined(AIX32))
#include "lwp.h"
#include "lwpint.h"
#if defined(hpc)
static struct lwpProc *tempcontext;
struct lwpProc *initcontext=NULL;
int startpoint;
startcontext()
{
int space[10000];
int x;
startpoint = (void *) &x;
if (!setjmp(initcontext->context)) longjmp(tempcontext->context,1);
if (!setjmp(tempcontext->context)) longjmp(LwpCurrent->context,1);
lwpEntryPoint();
}
void lwpInitContext(newp, sp)
struct lwpProc *newp;
void *sp;
{
struct lwpProc holder;
int endpoint;
if (initcontext == NULL) {
initcontext = (struct lwpProc *) malloc (sizeof(struct lwpProc));
tempcontext = &holder;
if (!setjmp(tempcontext->context)) startcontext();
}
tempcontext = newp;
endpoint = &endpoint;
if (endpoint < startpoint) {
if (!setjmp(LwpCurrent->context)) longjmp(initcontext->context,1);
} else {
LwpCurrent->size = endpoint - startpoint;
LwpCurrent->sbtm = realloc(LwpCurrent->sbtm, LwpCurrent->size);
memcpy(LwpCurrent->sbtm, startpoint, LwpCurrent->size);
if (!setjmp(LwpCurrent->context)) longjmp(initcontext->context,1);
memcpy(startpoint, LwpCurrent->sbtm, LwpCurrent->size);
}
}
#elif defined(hpux)
void lwpInitContext(newp, sp)
volatile struct lwpProc *volatile newp;
void *sp;
{
static jmp_buf *cpp;
extern struct lwpProc *LwpCurrent;
if (!lwpSave(LwpCurrent->context)) {
cpp = (jmp_buf *)&newp->context;
asm volatile ("ldw %0, %%sp": : "o" (sp));
if (!lwpSave(*cpp))
lwpRestore(LwpCurrent->context);
lwpEntryPoint();
}
}
int lwpSave(jb)
jmp_buf jb;
{
/* save stack pointer and return program counter */
asm ("stw %sp, 4(%arg0)");
asm ("stw %rp, 8(%arg0)");
/* save "callee save" registers */
asm ("stw %r3, 12(%arg0)");
asm ("stw %r4, 16(%arg0)");
asm ("stw %r5, 20(%arg0)");
asm ("stw %r6, 24(%arg0)");
asm ("stw %r7, 28(%arg0)");
asm ("stw %r8, 32(%arg0)");
asm ("stw %r9, 36(%arg0)");
asm ("stw %r10, 40(%arg0)");
asm ("stw %r11, 44(%arg0)");
asm ("stw %r12, 48(%arg0)");
asm ("stw %r13, 52(%arg0)");
asm ("stw %r14, 56(%arg0)");
asm ("stw %r15, 60(%arg0)");
asm ("stw %r16, 64(%arg0)");
asm ("stw %r17, 68(%arg0)");
asm ("stw %r18, 72(%arg0)");
/* save "callee save" space register */
asm volatile ("mfsp %sr3, %r1");
asm ("stw %r1, 0(%arg0)");
/* indicate "true return" from saved() */
asm ("ldi 0, %ret0");
asm (".LABEL _comefrom_");
}
void
lwpRestore(jb)
jmp_buf jb;
{
/* restore stack pointer and program counter */
asm volatile ("ldw 4(%arg0), %sp");
asm volatile ("ldw 8(%arg0), %rp");
/* restore "callee save" space register */
asm volatile ("ldw 0(%arg0), %r1");
asm volatile ("mtsp %r1, %sr3");
/* restore "callee save" registers */
asm volatile ("ldw 12(%arg0), %r3");
asm volatile ("ldw 16(%arg0), %r4");
asm volatile ("ldw 20(%arg0), %r5");
asm volatile ("ldw 24(%arg0), %r6");
asm volatile ("ldw 28(%arg0), %r7");
asm volatile ("ldw 32(%arg0), %r8");
asm volatile ("ldw 36(%arg0), %r9");
asm volatile ("ldw 40(%arg0), %r10");
asm volatile ("ldw 44(%arg0), %r11");
asm volatile ("ldw 48(%arg0), %r12");
asm volatile ("ldw 52(%arg0), %r13");
asm volatile ("ldw 56(%arg0), %r14");
asm volatile ("ldw 60(%arg0), %r15");
asm volatile ("ldw 64(%arg0), %r16");
asm volatile ("ldw 68(%arg0), %r17");
asm volatile ("ldw 72(%arg0), %r18");
/* warp to saved() to unwind the frame correctly */
asm volatile ("bl _comefrom_, %r0");
asm volatile ("ldi 1, %ret0");
}
#elif defined(BSD386)
void lwpInitContext(newp, sp)
struct lwpProc *newp;
void *sp;
{
newp->context[2] = (int)sp;
newp->context[0] = (int)lwpEntryPoint;
}
#elif defined(FBSD)
void lwpInitContext(newp, sp)
struct lwpProc *newp;
void *sp;
{
setjmp (newp->context);
newp->context->_jb[2] = (int)sp;
newp->context->_jb[3] = (int)sp;
newp->context->_jb[0] = (int)lwpEntryPoint;
}
#elif defined(__linux__)
void lwpInitContext(newp, sp)
struct lwpProc *newp;
void *sp;
{
#if defined(__GLIBC__) && (__GLIBC__ >= 2)
#if defined(__PPC__)
newp->context->__jmpbuf[JB_GPR1] = (int) sp;
newp->context->__jmpbuf[JB_LR] = (int) lwpEntryPoint;
#else
newp->context->__jmpbuf[JB_SP] = (int) sp;
newp->context->__jmpbuf[JB_BP] = (int) sp;
newp->context->__jmpbuf[JB_PC] = (int) lwpEntryPoint;
#endif
#else
newp->context->__sp = sp;
newp->context->__bp = sp;
newp->context->__pc = (void *)lwpEntryPoint;
#endif
}
#elif defined(SUN3)
void lwpInitContext(newp, sp)
struct lwpProc *newp;
void *sp;
{
newp->context[2] = (int)sp;
newp->context[3] = (int)lwpEntryPoint;
}
#elif defined(__vax)
#include <stdio.h>
void lwpInitContext(newp, stack)
struct lwpProc *newp;
void *stack;
{
int *sp = (int*)stack;
int *fp = 0;
/* Build root frame on new stack for lwpEntryPoint */
*--sp = 0; /* pc */
*--sp = (int)fp; /* fp */
*--sp = 0; /* ap */
*--sp = 0; /* psw */
*--sp = 0; /* condition handler */
fp = sp;
/* Build stack frame to return from. */
*--sp = (int)lwpEntryPoint+2;/* pc */
*--sp = (int)fp; /* fp */
*--sp = 0; /* ap */
*--sp = 0; /* psw */
*--sp = 0; /* condition handler */
fp = sp;
/* Fill in the context */
/* Note: This is *not* how libc fills out jump buffers. */
newp->context[0] = 0; /* r6 */
newp->context[1] = 0;
newp->context[2] = 0;
newp->context[3] = 0;
newp->context[4] = 0;
newp->context[5] = 0; /* r11 */
newp->context[6] = 0; /* ap */
newp->context[7] = (int)fp; /* fp */
return;
}
int lwpSave(jb)
jmp_buf jb;
{
asm("movl 4(ap), r0"); /* r0 = &jb */
asm("movl r6, (r0)"); /* jb[0] = r6 */
asm("movl r7, 4(r0)");
asm("movl r8, 8(r0)");
asm("movl r9, 12(r0)");
asm("movl r10, 16(r0)");
asm("movl r11, 20(r0)");
asm("movl ap, 24(r0)");
asm("movl fp, 28(r0)"); /* jb[7] = fp */
return 0;
}
void lwpRestore(jb)
jmp_buf jb;
{
asm("movl 4(ap), r0"); /* r0 = &jb */
asm("movl (r0), r6"); /* r6 = jb[0] */
asm("movl 4(r0), r7");
asm("movl 8(r0), r8");
asm("movl 12(r0), r9");
asm("movl 16(r0), r10");
asm("movl 20(r0), r11");
asm("movl 24(r0), ap");
asm("movl 28(r0), fp"); /* fp = jb[7] */
asm("movl $1, r0"); /* faked return 1 from lwpSave() */
asm("ret");
return;
}
#elif defined(SUN4)
void lwpInitContext(newp, sp)
struct lwpProc *newp;
void *sp;
{
static jmp_buf *cpp;
extern struct lwpProc *LwpCurrent;
bzero(newp->context, sizeof(newp->context));
newp->context[0] = (int)sp;
/* preserve cpp for new context */
cpp = (jmp_buf *)&newp->context;
if (!_setjmp(LwpCurrent->context)) {
/* create new context */
/* flush registers */
asm ("ta 0x03");
/* %o0 <- newp */
asm ("ld [%fp+0x44], %o0");
/* %o1 <- newp->context[0] */
asm ("ld [%o0], %o1");
/* create min frame on new stack */
asm ("save %o1,-96, %sp");
if (!_setjmp(*cpp))
_longjmp(LwpCurrent->context, 1);
lwpEntryPoint();
}
}
#elif defined(__USLC__) && defined(i386)
/* USL/Unixware on an Intel 386/486/... processor.
* Tested on Unixware v1.1.2, based on SYSV R4.2
*/
/* As per normal empire documentation, there is none.
*
* But, what we are attempting to do here is set up a longjump
* context buffer so that the lwpEntryPoint is called when
* the thread starts.
*
* I.E., what a setjmp/longjmp call set would do.
*
* How to figure this out? Well, without the setjmp code, you
* need to reverse engineer it by printing out the context buffer
* and the processor registers, and mapping which ones need
* to be set.
*
* Alternatively, you can single instruction step through the longjmp
* function, and figure out the offsets that it uses.
*
* Using offsets in bytes,
* context + 0x04 [1] -> esi (general purpose reg)
* context + 0x08 [2] -> edi (general purpose reg)
* context + 0x0C [3] -> ebp (general purpose or parameter passing)
* context + 0x10 [4] -> esp (stack)
* context + 0x14 [5] -> jump location for return
*/
void lwpInitContext(newp, sp)
struct lwpProc *newp;
void *sp;
{
newp->context[4] = (int)sp;
newp->context[5] = (int)lwpEntryPoint;
}
#elif defined UCONTEXT
/*
* Alternate aproach using setcontext en getcontext in stead of setjmp and
* longjump. This should work on any SVr4 machine independant of
* architecture. Unfortunaltely some changes are still nessesary in lwp.c.
* Tested on IRIX 5.3
*/
void lwpInitContext(newp, spp)
struct lwpProc *newp;
stack_t *spp;
{
getcontext (&(newp->context));
newp->context.uc_stack.ss_sp = spp->ss_sp;
newp->context.uc_stack.ss_size = spp->ss_size;
makecontext (&(newp->context), lwpEntryPoint, 0);
}
#elif defined(ALPHA)
#include <c_asm.h>
void lwpInitContext(newp, sp)
struct lwpProc *newp;
void *sp;
{
extern long *_gp;
/* register values obtained from setjmp.h */
_setjmp(newp->context);
newp->context[2] = (long)lwpEntryPoint; /* program counter */
newp->context[30] = (long)lwpEntryPoint; /* return address */
newp->context[31] = (long)lwpEntryPoint; /* fake program value (!) */
newp->context[34] = (long)sp; /* stack pointer */
}
int lwpSave(jb)
jmp_buf jb;
{
return _setjmp(jb);
}
void lwpRestore(jb)
jmp_buf jb;
{
/* resume, but get the pv from the jmp_buf */
asm("ldq %pv, 248(%a0)");
asm("stq %a0, 16(%sp)");
/* generates a warning, but functions just fine */
asm("bsr %ra, __longjump_resume");
}
#endif
#endif
#endif

625
src/lib/lwp/lwp.c Normal file
View file

@ -0,0 +1,625 @@
/*
* 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 <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include "lwp.h"
#include "lwpint.h"
#include "prototypes.h"
#if defined(_EMPTH_LWP)
#ifdef BOUNDS_CHECK
#include <bounds/fix-args.h>
#include <bounds/unchecked.h>
#endif
#ifdef hpc
extern struct lwpProc *initcontext;
extern int startpoint;
#endif
struct lwpQueue LwpSchedQ[LWP_MAX_PRIO], LwpDeadQ;
struct lwpProc *LwpCurrent = NULL;
char **LwpContextPtr;
int LwpMaxpri=0; /* maximum priority so far */
#ifdef POSIXSIGNALS
static sigset_t oldmask;
#else /* POSIXSIGNALS */
static int oldmask;
#endif /* POSIXSIGNALS */
/* for systems without strdup */
#ifdef NOSTRDUP
extern char *strdup();
#endif /* NOSTRDUP */
static void lwpStackCheckInit();
static int lwpStackCheck();
static void lwpStackCheckUsed();
/* check stack direction */
static int growsdown (x)
void *x;
{
int y;
#ifdef BOUNDS_CHECK
BOUNDS_CHECKING_OFF;
#endif
y = (x > (void *)&y);
#ifdef BOUNDS_CHECK
BOUNDS_CHECKING_ON;
#endif
return y;
}
/*
* lwpReschedule -- schedule another process. we also check for dead
* processes here and free them.
*/
void lwpReschedule()
{
extern struct lwpQueue LwpSchedQ[];
static int lcount = LCOUNT;
static struct lwpProc *nextp;
static int i;
#ifdef POSIXSIGNALS
static sigset_t tmask;
#endif /* POSIXSIGNALS */
if (LwpCurrent && (LwpCurrent->flags & LWP_STACKCHECK)) {
lwpStackCheck(LwpCurrent);
}
if (!--lcount) {
int p = lwpSetPriority(LWP_MAX_PRIO-1);
lcount = LCOUNT;
#ifdef POSIXSIGNALS
sigprocmask (SIG_SETMASK, &oldmask, &tmask);
sigprocmask (SIG_SETMASK, &tmask, &oldmask);
#else /* POSIXSIGNALS */
sigsetmask(sigsetmask(oldmask));
#endif /* POSIXSIGNALS */
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);
/* fprintf(stderr, "Destroying done\n"); */
}
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 */
#ifdef BOUNDS_CHECK
BOUNDS_CHECKING_OFF;
#endif
#if defined(hpc)
{
int endpoint;
endpoint = &endpoint;
if (initcontext == NULL || endpoint < startpoint) {
i = lwpSave(LwpCurrent->context);
} else {
LwpCurrent->size = endpoint - startpoint;
LwpCurrent->sbtm = realloc(LwpCurrent->sbtm, LwpCurrent->size);
memcpy(LwpCurrent->sbtm, startpoint, LwpCurrent->size);
if (i = lwpSave(LwpCurrent->context)) {
memcpy(startpoint, LwpCurrent->sbtm, LwpCurrent->size);
i = 1;
}
}
}
#else
i = lwpSave(LwpCurrent->context);
#endif
#ifdef BOUNDS_CHECK
BOUNDS_CHECKING_ON;
#endif
if (LwpCurrent != nextp &&
!(LwpCurrent && i)) {
/* restore previous context */
lwpStatus(nextp, "switch in", nextp->pri);
LwpCurrent = nextp;
*LwpContextPtr = LwpCurrent->ud;
#ifdef BOUNDS_CHECK
BOUNDS_CHECKING_OFF;
#endif
lwpRestore(LwpCurrent->context);
#ifdef BOUNDS_CHECK
BOUNDS_CHECKING_ON;
#endif
}
}
/*
* lwpEntryPoint -- process entry point.
*/
void lwpEntryPoint()
{
extern struct lwpProc *LwpCurrent;
#ifdef POSIXSIGNALS
sigset_t set;
#endif /* POSIXSIGNALS */
#ifdef BOUNDS_CHECK
BOUNDS_CHECKING_OFF;
#endif
#ifdef POSIXSIGNALS
sigemptyset (&set);
sigaddset (&set, SIGALRM);
sigprocmask (SIG_SETMASK, &set, &oldmask);
#else /* POSIXSIGNALS */
sigsetmask(SIGNALS);
#endif /* POSIXSIGNALS */
*LwpContextPtr = LwpCurrent->ud;
lwpStatus(LwpCurrent, "starting at entry point");
(*LwpCurrent->entry)(LwpCurrent->argc, LwpCurrent->argv,
LwpCurrent->ud);
lwpExit();
#ifdef BOUNDS_CHECK
BOUNDS_CHECKING_ON;
#endif
}
/*
* lwpCreate -- create a process.
*/
struct lwpProc *
lwpCreate(priority, entry, size, flags, name, desc, argc, argv, ud)
int priority;
void (*entry)();
int size;
int flags;
char *name;
char *desc;
int argc;
char *argv[];
void *ud;
{
extern struct lwpProc *LwpCurrent;
struct lwpProc *newp;
int *s, x;
#ifdef UCONTEXT
stack_t sp;
#else /* UCONTEXT */
void *sp;
#endif /* UCONTEXT */
unsigned long stackp;
if (!(newp = (struct lwpProc *)malloc(sizeof(struct lwpProc))))
return (0);
if (flags & LWP_STACKCHECK) {
/* Add a 1K buffer on each side of the stack */
size += 2 * LWP_REDZONE;
}
size += LWP_EXTRASTACK;
size += sizeof(stkalign_t);
if (!(s = (int *)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 ((newp->flags & LWP_STACKCHECK) == 0) {
stackp = growsdown((void *)&x) ?
(((long)s) + size - sizeof(stkalign_t) - LWP_EXTRASTACK) :
(long) s + LWP_EXTRASTACK;
#ifdef UCONTEXT
sp.ss_sp = (void *)(stackp & -sizeof(stkalign_t));
sp.ss_size = size;
sp.ss_flags = 0;
#else /* UCONTEXT */
sp = (void *)(stackp & -sizeof(stkalign_t));
#endif /* UCONTEXT */
} else {
if (growsdown(&x)) {
/* round address off to stkalign_t */
stackp = ((long)s) + size - LWP_REDZONE -
LWP_EXTRASTACK - sizeof(stkalign_t);
#ifdef UCONTEXT
sp.ss_sp = (void *)(stackp & -sizeof(stkalign_t));
sp.ss_size = size;
sp.ss_flags = 0;
newp->lowmark = (void *)(((long) sp.ss_sp) + LWP_EXTRASTACK);
#else /* UCONTEXT */
sp = (void *)(stackp & -sizeof(stkalign_t));
newp->lowmark = (void *)(((long) sp) + LWP_EXTRASTACK);
#endif /* UCONTEXT */
newp->himark = s;
} else {
stackp = ((long)s) + LWP_REDZONE + LWP_EXTRASTACK;
#ifdef UCONTEXT
sp.ss_sp = (void *)(((long)stackp) &
-sizeof(stkalign_t));
sp.ss_size = size;
sp.ss_flags = 0;
#else /* UCONTEXT */
sp = (void *)(((long)stackp) & -sizeof(stkalign_t));
#endif /* UCONTEXT */
newp->lowmark = (void *)s;
newp->himark = (void *)(((long)s) + size - LWP_REDZONE);
}
}
if (LWP_MAX_PRIO <= priority)
priority = LWP_MAX_PRIO-1;
if (LwpMaxpri < (newp->pri = priority))
LwpMaxpri = priority;
newp->sbtm = (void *)s;
newp->size = size;
newp->dead = 0;
if (flags & LWP_STACKCHECK)
lwpStackCheckInit(newp);
lwpStatus(newp, "creating process structure sbtm: %d",
(int)newp->sbtm);
lwpReady(newp);
lwpReady(LwpCurrent);
#ifdef UCONTEXT
lwpInitContext(newp, &sp); /* architecture-dependent: from arch.c */
#else /* UCONTEXT */
lwpInitContext(newp, sp); /* architecture-dependent: from arch.c */
#endif /* UCONTEXT */
lwpReschedule();
return (newp);
}
void lwpDestroy(proc)
struct lwpProc *proc;
{
if (proc->flags & LWP_STACKCHECK){
lwpStackCheckUsed(proc);
lwpStackCheck(proc);
}
lwpStatus(proc, "destroying sbtm: %d", (int)proc->sbtm);
proc->entry = 0;
proc->ud = 0;
proc->argv = 0;
free((char *)proc->sbtm);
free(proc->name);
free(proc->desc);
proc->name = 0;
proc->desc = 0;
proc->sbtm = 0;
proc->lowmark = 0;
proc->himark = 0;
free((char *)proc);
}
/*
* lwpReady -- put process on ready queue. if null, assume current.
*/
void lwpReady(p)
struct lwpProc *p;
{
extern struct lwpProc *LwpCurrent;
extern struct lwpQueue LwpSchedQ[];
if (!p)
p = LwpCurrent;
lwpStatus(p, "added to run queue");
lwpAddTail(&LwpSchedQ[p->pri], p);
}
/*
* return user's data
*/
void *lwpGetUD(p)
struct lwpProc *p;
{
if (!p)
p = LwpCurrent;
return (p->ud);
}
/*
* set user's data
*/
void lwpSetUD(p, ud)
struct lwpProc *p;
char *ud;
{
if (!p)
p = LwpCurrent;
p->ud = ud;
}
/*
* set name & desc
*/
void lwpSetDesc(p, name, desc)
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()
{
lwpStatus(LwpCurrent, "yielding control");
lwpReady(LwpCurrent);
lwpReschedule();
}
/*
* cause the current process to be scheduled for deletion.
*/
void lwpExit()
{
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(p)
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(new)
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(pri, ctxptr, flags)
int pri;
char **ctxptr;
int flags;
{
extern struct lwpQueue LwpSchedQ[];
extern struct lwpProc *LwpCurrent;
struct lwpQueue *q;
int i, *stack;
struct lwpProc *sel;
LwpContextPtr = ctxptr;
if (pri < 1)
pri = 1;
/* *LwpContextPtr = 0; */
if (!(LwpCurrent = (struct lwpProc *)calloc (1, sizeof(struct lwpProc))))
return (0);
if (!(stack = (int *)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;
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(newp)
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.
* NOTE:
* If an problem occurs, it is not corrected.
* The buffer is not cleaned up, nor is the thread terminated.
* Cleaning up the buffer would be a mistake, and terminating
* the thread, well, could be done. Should more like take
* down the entire process.
*/
static int lwpStackCheck(newp)
struct lwpProc *newp;
{
register int end, amt;
register unsigned int i;
register long *lp;
register int growsDown;
int marker;
if (!newp || !newp->himark || !newp->lowmark)
return(1);
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);
}
lwpStatus(newp, "Thread stack overflowed %d bytes (of %u)",
amt, newp->size - 2*LWP_REDZONE - sizeof(stkalign_t));
return(0);
}
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);
}
lwpStatus(newp, "Thread stack underflow %d bytes (of %u)",
amt, newp->size - 2*LWP_REDZONE - sizeof(stkalign_t));
return(0);
}
return(1);
}
/* lwpStackCheckUsed
*
* Figure out how much stack was used by this thread.
*/
static void lwpStackCheckUsed(newp)
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, "stack use: %u bytes (of %u total)",
(i * sizeof(long)) - LWP_REDZONE,
newp->size - 2*LWP_REDZONE - sizeof(stkalign_t));
}
#endif

76
src/lib/lwp/lwpInit.s Normal file
View file

@ -0,0 +1,76 @@
.set r0,0; .set SP,1; .set RTOC,2; .set r3,3; .set r4,4
.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
.set LR,8
.rename LwpInitContext{PR},""
.rename LwpInitContext{TC},"lwpInitContext"
.rename LwpEntryPoint{TC},"lwpEntryPoint"
.lglobl LwpInitContext{PR}
.globl .lwpInitContext
.globl lwpInitContext{DS}
.extern lwpEntryPoint{DS}
# .text section
.csect LwpInitContext{PR}
.function .lwpInitContext{PR},.lwpInitContext,2,0
.lwpInitContext: # 0x00000000 (LwpInitContext)
stu SP,-0x40(SP)
st r3,0x58(SP) # r3 = newp pointer
st r4,0x5c(SP) # r4 = sp pointer
.bf 234
st SP,0x0(r4)
st SP,0xf8(r3) # store prev SP
st r4,0xc(r3) # store entrypoint at newp[3]
l r3,LWPEntryPoint(RTOC)
l r4,0x0(r3)
l r3,0x58(SP) # load newp into r3
st r4,0x8(r3) # store sp at newp[2]
st RTOC,0x10(r3) # store RTOC at newp[4]
ai SP,SP,0x40
br
.ef 236
# traceback table
.long 0x00000000
.byte 0x00 # VERSION=0
.byte 0x00 # LANG=TB_C
.byte 0x20 # IS_GL=0,IS_EPROL=0,HAS_TBOFF=1
# INT_PROC=0,HAS_CTL=0,TOCLESS=0
# FP_PRESENT=0,LOG_ABORT=0
.byte 0x40 # INT_HNDL=0,NAME_PRESENT=1
# USES_ALLOCA=0,CL_DIS_INV=WALK_ONCOND
# SAVES_CR=0,SAVES_LR=0
.byte 0x80 # STORES_BC=1,FPR_SAVED=0
.byte 0x00 # GPR_SAVED=0
.byte 0x02 # FIXEDPARMS=2
.byte 0x01 # FLOATPARMS=0,PARMSONSTK=1
.long 0x00000000 #
.long 0x0000002c # TB_OFFSET
.short 14 # NAME_LEN
.byte "lwpInitContext"
# End of traceback table
# End csect LwpInitContext{PR}
# .data section
.toc # 0x00000050
LWPInitContext:
.tc LwpInitContext{TC},lwpInitContext{DS}
LWPEntryPoint:
.tc LwpEntryPoint{TC},lwpEntryPoint{DS}
.csect lwpInitContext{DS}
.long .lwpInitContext # "\0\0\0\0"
.long TOC{TC0} # "\0\0\0P"
.long 0x00000000 # "\0\0\0\0"
# End csect lwpInitContext{DS}
# .bss section

62
src/lib/lwp/lwpRestore.s Normal file
View file

@ -0,0 +1,62 @@
.set r0,0; .set SP,1; .set RTOC,2; .set r3,3; .set r4,4
.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
.set LR,8
.rename lwpRestore{PR},""
.rename lwpRestore{TC},"lwpRestore"
.lglobl lwpRestore{PR}
.globl .lwpRestore
.globl lwpRestore{DS}
# .text section
.csect lwpRestore{PR}
.lwpRestore: # 0x00000000 (lwpRestore)
l r5,0x8(r3)
l SP,0xc(r3)
l RTOC,0x10(r3)
mtlr r5
lil r3,0x1
br
# traceback table
.long 0x00000000
.byte 0x00 # VERSION=0
.byte 0x00 # LANG=TB_C
.byte 0x20 # IS_GL=0,IS_EPROL=0,HAS_TBOFF=1
# INT_PROC=0,HAS_CTL=0,TOCLESS=0
# FP_PRESENT=0,LOG_ABORT=0
.byte 0x40 # INT_HNDL=0,NAME_PRESENT=1
# USES_ALLOCA=0,CL_DIS_INV=WALK_ONCOND
# SAVES_CR=0,SAVES_LR=0
.byte 0x80 # STORES_BC=1,FPR_SAVED=0
.byte 0x00 # GPR_SAVED=0
.byte 0x01 # FIXEDPARMS=1
.byte 0x01 # FLOATPARMS=0,PARMSONSTK=1
.long 0x00000000 #
.long 0x00000010 # TB_OFFSET
.short 10 # NAME_LEN
.byte "lwpRestore"
# End of traceback table
# End csect lwpRestore{PR}
# .data section
.toc # 0x00000030
LWPRestore:
.tc lwpRestore{TC},lwpRestore{DS}
.csect lwpRestore{DS}
.long .lwpRestore # "\0\0\0\0"
.long TOC{TC0} # "\0\0\0000"
.long 0x00000000 # "\0\0\0\0"
# End csect lwpRestore{DS}
# .bss section

66
src/lib/lwp/lwpSave.s Normal file
View file

@ -0,0 +1,66 @@
.set r0,0; .set SP,1; .set RTOC,2; .set r3,3; .set r4,4
.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9
.set LR,8
.rename lwpSave{PR},""
.rename lwpSave{TC},"lwpSave"
.lglobl lwpSave{PR}
.globl .lwpSave
.globl lwpSave{DS}
# .text section
.csect lwpSave{PR}
.lwpSave: # 0x00000000 (lwpSave)
st SP,0xc(r3)
st RTOC,0x10(r3)
mflr r4
st r4,0x8(r3)
lil r3,0x0
br
# traceback table
.long 0x00000000
.byte 0x00 # VERSION=0
.byte 0x00 # LANG=TB_C
.byte 0x20 # IS_GL=0,IS_EPROL=0,HAS_TBOFF=1
# INT_PROC=0,HAS_CTL=0,TOCLESS=0
# FP_PRESENT=0,LOG_ABORT=0
.byte 0x40 # INT_HNDL=0,NAME_PRESENT=1
# USES_ALLOCA=0,CL_DIS_INV=WALK_ONCOND
# SAVES_CR=0,SAVES_LR=0
.byte 0x80 # STORES_BC=1,FPR_SAVED=0
.byte 0x00 # GPR_SAVED=0
.byte 0x01 # FIXEDPARMS=1
.byte 0x01 # FLOATPARMS=0,PARMSONSTK=1
.long 0x00000000 #
.long 0x00000014 # TB_OFFSET
.short 7 # NAME_LEN
.byte "lwpSave"
.byte 0 # padding
.byte 0 # padding
.byte 0 # padding
# End of traceback table
.long 0x00000000 # "\0\0\0\0"
# End csect lwpSave{PR}
# .data section
.toc # 0x00000038
LWPSave:
.tc lwpSave{TC},lwpSave{DS}
.csect lwpSave{DS}
.long .lwpSave # "\0\0\0\0"
.long TOC{TC0} # "\0\0\0008"
.long 0x00000000 # "\0\0\0\0"
# End csect lwpSave{DS}
# .bss section

105
src/lib/lwp/lwpint.h Normal file
View file

@ -0,0 +1,105 @@
/*
* lwpint.h -- lwp internal structures
*
* 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.
*/
#ifndef _LWPINT_H
#define _LWPINT_H
/* `liveness' counter: check signals every `n' visits to the scheduler */
/* note: the lower this value, the more responsive the system but the */
/* more inefficient the context switch time */
#define LCOUNT -1
#ifdef hpux
int lwpSave _PROTO((jmp_buf));
void lwpRestore _PROTO((jmp_buf));
#endif
#if defined(MIPS) || defined(AIX32) || defined(ALPHA) || defined(__vax)
int lwpSave _PROTO((jmp_buf));
void lwpRestore _PROTO((jmp_buf));
#elif defined(SUN4)
#define lwpSave(x) _setjmp(x)
#define lwpRestore(x) _longjmp(x, 1)
#elif defined (UCONTEXT)
#define lwpSave(x) getcontext(&(x))
#define lwpRestore(x) setcontext(&(x))
#else
#ifdef hpc
#define lwpSave(x) setjmp(x)
#define lwpRestore(x) longjmp(x, 1)
#else
#ifndef hpux
#define lwpSave(x) setjmp(x)
#define lwpRestore(x) longjmp(x, 1)
#endif /* hpux */
#endif /* hpc */
#endif
#ifdef AIX32
/* AIX needs 12 extra bytes above the stack; we add it here */
#define LWP_EXTRASTACK 3*sizeof(long)
#else
#define LWP_EXTRASTACK 0
#endif
#define LWP_REDZONE 1024 /* make this a multiple of 1024 */
/* XXX Note that this assumes sizeof(long) == 4 */
#define LWP_CHECKMARK 0x5a5a5a5aL
#define SIGNALS sigmask(SIGALRM)
#ifndef hpux
typedef double stkalign_t;
#else
typedef struct {
char x[64];
} stkalign_t;
#endif
/* internal routines */
void lwpAddTail _PROTO((struct lwpQueue *, struct lwpProc *));
struct lwpProc *lwpGetFirst _PROTO((struct lwpQueue *));
void lwpReschedule _PROTO((void));
void lwpReady _PROTO((struct lwpProc *));
void lwpOnalarm _PROTO((void));
#ifdef UCONTEXT
void lwpInitContext _PROTO((struct lwpProc *, stack_t *));
#else /* GETCONTEXT */
#ifdef hpc
void lwpInitContext _PROTO((struct lwpProc *, void *));
#else
#ifdef hpux
void lwpInitContext _PROTO((volatile struct lwpProc *volatile, void *));
#else
void lwpInitContext _PROTO((struct lwpProc *, void *));
#endif /* hpux */
#endif /* hpc */
#endif /* GETCONTEXT */
void lwpEntryPoint _PROTO((void));
void lwpInitSelect _PROTO((struct lwpProc *self));
void lwpDestroy _PROTO((struct lwpProc *proc));
#endif /* _LWP_H */

71
src/lib/lwp/mipsarch.s Normal file
View file

@ -0,0 +1,71 @@
.verstamp 3 0
.extern tzname 0
.extern LwpCurrent 4
.lcomm $$9 4
.text
.align 2
.file 2 "arch.new.c"
.globl lwpSave
.ent lwpSave 2
lwpSave:
.option O1
subu $sp, 32
sw $31, 28($sp)
sw $4, 32($sp)
.mask 0x80000000, -4
.frame $sp, 32, $31
sw $0, $$9
sw $30, 0($4)
sw $sp, 4($4)
sw $31, 8($4)
sd $16, 12($4)
sd $18, 20($4)
sd $20, 28($4)
sd $22, 36($4)
lw $2, $$9
.livereg 0x2000FF0E,0x00000FFF
lw $31, 28($sp)
addu $sp, 32
j $31
.end lwpSave
.text
.align 2
.file 2 "arch.new.c"
.globl lwpRestore
.ent lwpRestore 2
lwpRestore:
.option O1
subu $sp, 32
sw $31, 28($sp)
sw $4, 32($sp)
.mask 0x80000000, -4
.frame $sp, 32, $31
li $14, 1
sw $14, $$9
lw $30, 0($4)
lw $sp, 4($4)
lw $31, 8($4)
ld $16, 12($4)
ld $18, 20($4)
ld $20, 28($4)
ld $22, 36($4)
lw $2, $$9
.livereg 0x0000FF0E,0x00000FFF
addu $sp, 32
j $31
.end lwpRestore
.text
.align 2
.file 2 "arch.new.c"
.globl lwpInitContext
.ent lwpInitContext 2
lwpInitContext:
.option O1
.frame $sp, 0, $31
sw $5, 4($4)
la $14, lwpEntryPoint
sw $14, 8($4)
.livereg 0x0000FF0E,0x00000FFF
j $31
.end lwpInitContext

115
src/lib/lwp/misc/echo.c Normal file
View file

@ -0,0 +1,115 @@
/*
* server.c
*
* lwp (echo) connection handler server
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define SP_ACCEPT 3
#define SP_READER 3
#include "lwp.h"
struct context {
struct lwpProc *us;
struct sockaddr_in addr;
int fd;
};
/*ARGSUSED*/
int readConn(argc, argv, ud)
int argc;
char **argv;
void *ud;
{
struct context *ctx = (struct context *) ud;
char buf[1024];
int n;
while (1) {
printf("sleeping\n");
lwpSleepFd(ctx->fd, LWP_FD_READ);
printf("waiting to read\n");
if ((n = read(ctx->fd, buf, sizeof(buf))) <= 0)
break;
printf("got %d char\n", n);
lwpSleepFd(ctx->fd, LWP_FD_WRITE);
printf("waiting to write\n");
if (write(ctx->fd, buf, n) < 0)
break;
printf("wrote %d char\n", n);
}
printf("process/fd %d exiting\n", ctx->fd);
close(ctx->fd);
lwpExit();
/*NOTREACHED*/
}
int acceptConn(argc, argv)
int argc;
char **argv;
{
struct sockaddr_in sin;
int s;
int ns;
int len;
int maxfd;
struct context *ctx;
if (argc != 2) {
fprintf(stderr, "Usage: %s port\n", *argv);
exit(-1);
}
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("inet socket");
exit(-1);
}
sin.sin_family = AF_INET;
sin.sin_port = htons(atoi(argv[1]));
sin.sin_addr.s_addr = 0;
if (bind(s, &sin, sizeof(sin)) < 0) {
perror("inet socket bind");
exit(-1);
}
if (listen(s, LISTENMAXCONN) < 0) {
perror("inet socket listen");
exit(-1);
}
maxfd = getdtablesize() - 1;
while (1) {
lwpSleepFd(s, LWP_FD_READ);
len = sizeof(sin);
ns = accept(s, &sin, &len);
if (ns < 0) {
perror("accept");
exit(-1);
}
if (ns == maxfd) {
fprintf(stderr, "no more connections");
close(ns);
}
printf("got connection from %s\n", inet_ntoa(sin.sin_addr));
ctx = (struct context *) malloc(sizeof(*ctx));
ctx->addr = sin;
ctx->fd = ns;
ctx->us = lwpCreate(SP_READER, readConn, 8192, 0, 0, ctx);
}
/*NOTREACHED*/
}
int main(argc, argv)
int argc;
char **argv;
{
lwpInitSystem(1);
lwpCreate(SP_ACCEPT, acceptConn, 8192, argc, argv, 0);
lwpReschedule();
/*NOTREACHED*/
}

90
src/lib/lwp/misc/lwp.h Normal file
View file

@ -0,0 +1,90 @@
/*
* lwp.h -- prototypes and structures for lightweight processes
* 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.
*/
#ifndef _LWP_H
#define _LWP_H
#ifndef _PROTO
#ifdef __cplusplus
#define _PROTO(x) x
#else
#define _PROTO(x) ()
#endif
#endif
#include <setjmp.h>
#include <sys/time.h>
/* process control block. do *not* change the position of context */
struct lwpProc {
jmp_buf context; /* processor context area */
void *sbtm; /* bottom of stack attached to it */
int size; /* size of stack */
void (*entry)(); /* entry point */
int dead; /* whether the process can be rescheduled */
int pri; /* which scheduling queue we're on */
long runtime; /* time at which process is restarted */
int fd; /* fd we're blocking on */
int argc; /* initial arguments */
char **argv;
void *ud; /* user data */
struct lwpProc *next;
};
/* queue */
struct lwpQueue {
struct lwpProc *head;
struct lwpProc *tail;
};
/* semaphore */
struct lwpSem {
int count;
struct lwpQueue q;
};
#define LWP_FD_READ 0x1
#define LWP_FD_WRITE 0x2
#define LWP_MAX_PRIO 8
struct lwpProc *lwpInitSystem _PROTO((int));
struct lwpProc *lwpCreate _PROTO((int, void (*)(), int, int, char **, void *));
void lwpExit _PROTO((void));
void lwpTerminate _PROTO((struct lwpProc *));
void lwpYield _PROTO((void));
void lwpSleepFd _PROTO((int fd, int flags));
void lwpSleepUntil _PROTO((long until));
void lwpWakeupFd _PROTO((struct lwpProc *));
void *lwpGetUD _PROTO((struct lwpProc *));
void lwpSetUD _PROTO((struct lwpProc *, char *));
int lwpSetPriority _PROTO((int));
void lwpReschedule _PROTO(());
struct lwpSem *lwpCreateSem _PROTO((int));
void lwpSignal _PROTO((struct lwpSem *));
void lwpWait _PROTO((struct lwpSem *));
void lwpSelect _PROTO((int argc, char **argv));
extern struct lwpProc *LwpCurrent;
#endif /* _LWP_H */

BIN
src/lib/lwp/misc/lwp.tar.gz Normal file

Binary file not shown.

189
src/lib/lwp/misc/lwp.tex Normal file
View file

@ -0,0 +1,189 @@
\documentstyle[tgrind, a4]{article}
\title{The {\sc Rex} lightweight process library}
\author{Stephen Crane\\ (jsc@doc.ic.ac.uk)\thanks{Thanks to Mark Little
(m.c.little@ncl.ac.uk) for the Linux port}}
\begin{document}
\maketitle
This document describes the interface to and the behaviour underlying
my threads library for Rex.\footnote{Available as lwp.tar.gz by anonymous
ftp from gummo.doc.ic.ac.uk:/rex. Rex (Esprit project 2080) was
axed by Men in Suits.} It has been tested on Sun-3, Sun-4,
Mips, 386-BSD and Linux systems. Counting semi-colons, it is
260 lines long, including support for the different architectures (this
figure includes variable- but not function-declarations).
A word from our sponsor:
\begin{quote}
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 {\sc WITHOUT ANY WARRANTY}; without even the implied warranty of
{\sc 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.
\end{quote}
(Note that while this library is protected by the GNU copyleft, it is not
supported by the Free Software Foundation.)
\section{Threads}
Threads are prioritised and
non-preemptive. Operations supported on threads are:
\begin{tgrind}
\L{\LB{}\Tab{8}{\K{struct} pcb *initlp (\K{int} priority)}}
\L{\LB{}\Tab{8}{\K{struct} pcb *creatp (priority, entry, size, argc, argv, envp)}}
\L{\LB{}\Tab{8}{\K{void} readyp (\K{struct} pcb *p)}}
\L{\LB{}\Tab{8}{\K{void} yieldp (\K{void})}}
\L{\LB{}\Tab{8}{\K{void} *getenvp (\K{struct} pcb *p)}}
\L{\LB{}\Tab{8}{\K{void} setenvp (\K{struct} pcb *p, \K{void} *)}}
\L{\LB{}\Tab{8}{\K{void} suicidep (\K{void})}}
\L{\LB{}\Tab{8}{\K{void} destroyp (\K{struct} pcb *p)}}
\end{tgrind}
\begin{description}
\item[initlp] initialises the threads runtime, creating a thread with
specified priority for the invoker.
\item[creatp] creates a new thread with specified {\em priority}, {\em
entry} point, with a stack of {\em size}, {\em argc} arguments in {\em
argv} and a user-defined environment pointer.
\item[getenvp] returns the environment pointer associated with the given
thread. If the thread is null, the current thread is assumed.
\item[setenvp] reassigns the environment pointer associated with the
given thread.
\item[readyp] makes the specified thread ready to run, or the current
thread if null.
\item[yieldp] makes the current thread ready to run. If no thread of
higher priority is runnable, the current thread will run.
\item[suicidep] marks the invoking thread as dead. It will never be
rescheduled.
\item[destroyp] marks the specified thread as dead. It will be removed
at the next reschedule. If it is currently running, it will be
unaffected until the next reschedule.
\end{description}
\section{Semaphores}
For synchronisation, counting semaphores are provided. Available
operations are:
\begin{tgrind}
\L{\LB{}\Tab{8}{\K{struct} sem *creats (\K{int} count)}}
\L{\LB{}\Tab{8}{\K{void} signals (\K{struct} sem *s)}}
\L{\LB{}\Tab{8}{\K{void} waits (\K{struct} sem *s)}}
\end{tgrind}
\begin{description}
\item[creats] allocates a new semaphore from the heap and initialises its
count.
\item[signals] increments the semaphore's count, makes a waiting process
ready if there is one. If the readied process's priority is greater than
that of the signaller, a reschedule is done.
\item[waits] decrements the semaphore's count. If it becomes negative,
the current process is suspended and a reschedule is done.
\end{description}
\section{Signals}
The library is concerned with two types of signal, {\sc sigio} and {\sc
sigalrm}. These signals are normally blocked until the null process is
scheduled. It uses {\tt sigpause()} to reenable them and wait for one
to arrive. While awaiting a signal, the null process `runs' at maximum
priority. Thus users will not be able to cause a reschedule from
their handlers. When {\tt sigpause()} returns, the signal will have
been handled and the null process drops back down to the lowest priority
and yields to any thread which has been made ready to run from the
user-level handler.
These semantics make the library rather unresponsive to signals in the
presence of busy processes. If a more responsive system is required,
the constant {\sc LCOUNT} may be changed. This value determines the
number of times the {\tt reschedp ()} function must be called before
signals are re-enabled. If given a value of {\tt 1}, it will affect
context-switching time by about 50\%. Its default value is {\tt -1}.
\subsection{Input and output}
Input and output present a problem to threads, because they require
calls to the underlying {\sc Unix} system, which will block not only
the invoking thread, but also all others in the process. Thus, in
general, a thread must wait until the descriptor on which I/O is to
be done becomes ready for the operation. This is done by trapping
{\sc sigio}. Two routines are provided:
\begin{tgrind}
\L{\LB{}\Tab{8}{\K{int} sigioset (\K{int} fd, \K{void} (*han) (\K{void}
*, \K{int}), \K{void} *ctx)}}
\L{\LB{}\Tab{8}{\K{int} sigioclr (\K{int} fd)}}
\end{tgrind}
The general model adopted for processing {\sc sigio} is to install a
{\em handler} routine for the I/O descriptor using {\em sigioset} and
remove it using {\em sigioclr}. The user is responsible for setting up
the device correctly to generate {\sc sigio}. When {\sc sigio} arrives
for the descriptor, the handler will be called with a context pointer
as its argument. (In C++, this context is the instance pointer of the
invoking thread.)
\subsection{The timer}
A single routine is provided to block the invoking thread for the
specified time:
\begin{tgrind}
\L{\LB{}\Tab{8}{\K{void} delayp (\K{int} n)}}
\end{tgrind}
This routine blocks the invoker for {\em at least\/} the time specified.
If this is zero, a reschedule is done. Delays are implemented as a
delta queue, using {\sc sigalrm}. $n$ specifies a microsecond delay
which is of limited utility in practice.
\section{Performance}
\begin{figure}[htb]
\begin{center}
\begin{tabular}{||l|c|c|c||} \hline
Arch & ctxsw & creat & comment \\ \hline
sun3 & 308 & 778 & 3/240 \\ \hline
386bsd & 186 & 464 & 486/33 \\ \hline
sun4 & 96 & 436 & IPX \\ \hline
sun4 & 59 & 212 & Sparc-10 \\ \hline
linux & 56 & 382 & 486-DX2/50 \\ \hline
mips & 17 & 85 & Decstation \\ \hline
\end{tabular}
\caption{Performance with architecture (times in microseconds).}
\end{center}
\end{figure}
\begin{description}
\item[sun3] has very lightweight process initialisation, compared with
context switching.
\item[sun4] has a high context switch time as a result of {\tt setjmp ()} and
{\tt longjmp ()} implementations. Process initialisation is also relatively
heavyweight: it requires two calls to {\tt setjmp ()} and one to {\tt longjmp
()}.
\item[mips] provides its own context switching in assembly language.
\end{description}
\section{Porting to another architecture}
Although the threads library is quite portable, a few guidelines should
be observed when moving to a new architecture.
\begin{itemize}
\item Create two new files for your architecture/kernel (e.g. {\tt sun4.c}
and {\tt sun4.h}).
The `.c' file should contain any routines which your version of {\sc Unix} is
missing and a thread initialisation routine:
\begin{tgrind}
\L{\LB{}\Tab{8}{\K{void} initp (\K{struct} pcb *, \K{void} *)}}
\end{tgrind}
The `.h' file contains any machine-specific definitions.
\item If {\tt setjmp ()} and {\tt longjmp ()} don't work on your machine
(for example the {\sc Ultrix} {\tt longjmp ()} implementation checks that
the frame being jumped to is an ancestor of the current one), you will
have to write {\tt savep ()} and {\tt restorep ()} and put the following
define into your machine-specific header file:
\begin{tgrind}
\L{\LB{}\Tab{8}{\K{\#define} OWN\_CONTEXT\_SWITCH}}
\end{tgrind}
\item Compile and run the three test programs: {\tt producer.c}, {\tt
timer.c} and {\tt bm.c}.
\item Add the name of the architecture to `config'.
\item Send {\tt new-arch.[ch]}, any context diffs for the rest of the
library and the output of {\tt bm.c} to {\tt jsc@doc.ic.ac.uk}. Also
please let me know where you got the library from.
\end{itemize}
\end{document}

51
src/lib/lwp/queue.c Normal file
View file

@ -0,0 +1,51 @@
/*
* queue.c -- queue manipulation routines.
* 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 "lwp.h"
#include "lwpint.h"
#include "prototypes.h"
#if defined(_EMPTH_LWP)
struct lwpProc *lwpGetFirst(q)
struct lwpQueue *q;
{
struct lwpProc *head;
if ((head = q->head) && !(q->head = head->next))
q->tail = 0;
return (head);
}
void lwpAddTail(q, p)
register struct lwpQueue *q;
register struct lwpProc *p;
{
if (!q->tail)
q->head = p;
else
q->tail->next = p;
q->tail = p;
p->next = 0;
}
#endif

250
src/lib/lwp/sel.c Normal file
View file

@ -0,0 +1,250 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2000, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
* related information and legal notices. It is expected that any future
* projects/authors will amend these files as needed.
*
* ---
*
* sel.c: arrange to block on read/write file descriptors using lwp
*
* Known contributors to this file:
* Dave Pare, 1994
*/
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/file.h>
#include <sys/time.h>
#ifdef hpux
#include <stdio.h>
#endif
#include "bit.h"
#include "lwp.h"
#include "lwpint.h"
#include "prototypes.h"
#if defined(_EMPTH_LWP)
struct lwpSelect {
int maxfd;
int nfds;
int nfile;
fd_set readmask;
fd_set writemask;
struct lwpProc **wait;
struct lwpQueue delayq;
struct lwpProc *proc;
};
struct lwpSelect LwpSelect;
void lwpInitSelect(proc)
struct lwpProc *proc;
{
LwpSelect.maxfd = 0;
LwpSelect.nfds = 0;
#ifdef hpux
LwpSelect.nfile = _NFILE;
#else
LwpSelect.nfile = getdtablesize();
#endif
FD_ZERO(&LwpSelect.readmask);
FD_ZERO(&LwpSelect.writemask);
LwpSelect.wait = (struct lwpProc **)
calloc(LwpSelect.nfile, sizeof(char *));
LwpSelect.delayq.head = 0;
LwpSelect.delayq.tail = 0;
LwpSelect.proc = proc;
}
void lwpSleepFd(fd, mask)
int fd;
int mask;
{
extern struct lwpProc *LwpCurrent;
lwpStatus(LwpCurrent, "sleeping on fd %d", fd);
if (LwpSelect.wait[fd] != 0) {
lwpStatus(LwpCurrent,
"multiple sleeps attempted on file descriptor %d", fd);
return;
}
if (mask & LWP_FD_READ)
FD_SET(fd, &LwpSelect.readmask);
if (mask & LWP_FD_WRITE)
FD_SET(fd, &LwpSelect.writemask);
LwpSelect.nfds++;
if (LwpSelect.maxfd == 0 && LwpSelect.delayq.head == 0) {
/* select process is sleeping until first waiter arrives */
lwpStatus(LwpCurrent, "going to resched fd %d", fd);
lwpReady(LwpSelect.proc);
}
lwpStatus(LwpCurrent, "going to wait on fd %d", fd);
if (fd > LwpSelect.maxfd)
LwpSelect.maxfd = fd;
LwpSelect.wait[fd] = LwpCurrent;
LwpCurrent->fd = fd;
lwpReschedule();
}
void lwpWakeupFd(proc)
struct lwpProc *proc;
{
if (proc->fd < 0)
return;
lwpStatus(proc, "awakening; was sleeping on fd %d", proc->fd);
FD_CLR(proc->fd, &LwpSelect.readmask);
FD_CLR(proc->fd, &LwpSelect.writemask);
LwpSelect.nfds--;
LwpSelect.wait[proc->fd] = 0;
proc->fd = -1;
lwpReady(proc);
}
void lwpSleepUntil(until)
long until;
{
extern struct lwpProc *LwpCurrent;
lwpStatus(LwpCurrent, "sleeping for %d sec", until - time(0));
LwpCurrent->runtime = until;
if (LwpSelect.maxfd == 0 && LwpSelect.delayq.head == 0) {
/* select process is sleeping until first waiter arrives */
lwpReady(LwpSelect.proc);
}
lwpAddTail(&LwpSelect.delayq, LwpCurrent);
lwpReschedule();
}
/*ARGSUSED*/
void
lwpSelect(argc, argv)
int argc;
char **argv;
{
extern struct lwpProc *LwpCurrent;
struct lwpProc *us = LwpCurrent;
fd_set readmask;
fd_set writemask;
int n;
int fd;
time_t now;
time_t delta;
struct lwpProc *proc;
struct timeval tv;
struct lwpQueue save;
lwpStatus(us, "starting select loop");
FD_ZERO(&readmask);
FD_ZERO(&writemask);
while (1) {
while (1) {
if (LwpSelect.nfds)
break;
if (LwpSelect.delayq.head)
break;
/* wait for someone to lwpSleepFd or lwpSleepUntil */
LwpSelect.maxfd = 0;
lwpStatus(us, "no fds or sleepers, waiting");
lwpReschedule();
}
tv.tv_sec = 1000000;
tv.tv_usec = 0;
if (LwpSelect.delayq.head) {
time(&now);
proc = LwpSelect.delayq.head;
for ( ; proc != 0; proc = proc->next) {
delta = proc->runtime - now;
if (delta < tv.tv_sec)
tv.tv_sec = delta;
}
if (tv.tv_sec < 0)
tv.tv_sec = 0;
}
lwpStatus(us, "selecting; sleep %ld secs", (long)delta);
bcopy((s_char *)&LwpSelect.readmask, (s_char *)&readmask, sizeof(fd_set));
bcopy((s_char *)&LwpSelect.writemask, (s_char *)&writemask, sizeof(fd_set));
n = select(LwpSelect.maxfd + 1, &readmask, &writemask,
(fd_set *)0, &tv);
if (n < 0) {
if (errno == EINTR) {
/* go handle the signal */
lwpReady(us);
lwpReschedule();
continue;
}
lwpStatus(us,
"select failed (bad file descriptor?)");
exit(-1);
}
if (LwpSelect.delayq.head) {
/* sleeping proecss activity */
time(&now);
save.tail = save.head = 0;
while (NULL != (proc = lwpGetFirst(&LwpSelect.delayq))) {
if (now >= proc->runtime) {
lwpStatus(proc, "sleep done");
lwpReady(proc);
} else {
lwpAddTail(&save, proc);
}
}
LwpSelect.delayq = save;
}
if (n > 0) {
/* file descriptor activity */
for(fd = 0; fd <= LwpSelect.maxfd; fd++) {
if (LwpSelect.wait[fd] == 0)
continue;
if (FD_ISSET(fd, &readmask)) {
lwpStatus(LwpSelect.wait[fd],
"input ready");
lwpWakeupFd(LwpSelect.wait[fd]);
continue;
}
if (FD_ISSET(fd, &writemask)) {
lwpStatus(LwpSelect.wait[fd],
"output ready");
lwpWakeupFd(LwpSelect.wait[fd]);
continue;
}
}
}
lwpStatus(us, "fd dispatch completed");
lwpReady(LwpCurrent);
lwpReschedule();
}
/*NOTREACHED*/
}
#endif

90
src/lib/lwp/sem.c Normal file
View file

@ -0,0 +1,90 @@
/*
* lwpSem.c -- lwpSemaphore 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 <stdlib.h>
#include <string.h>
#include "lwp.h"
#include "lwpint.h"
#include "prototypes.h"
#if defined(_EMPTH_LWP)
/* for systems without strdup */
#ifdef NOSTRDUP
extern char *strdup();
#endif /* NOSTRDUP */
/*
* create a lwpSemaphore.
*/
struct lwpSem *lwpCreateSem(name, count)
char *name;
int count;
{
struct lwpSem *new;
if (!(new = (struct lwpSem *)malloc(sizeof(struct lwpSem))))
return (0);
new->name = strdup(name);
new->count = count;
new->q.head = new->q.tail = 0;
return (new);
}
/*
* signal a lwpSemaphore. We only yield here if
* the blocked process has a higher priority than ours'.
*/
void lwpSignal(s)
struct lwpSem *s;
{
extern struct lwpProc *LwpCurrent;
lwpStatus(LwpCurrent, "done with semaphore %s", s->name);
if (s->count++ < 0) {
struct lwpProc *p = lwpGetFirst(&s->q);
lwpStatus(LwpCurrent, "activating first waiter");
lwpReady(p);
if (LwpCurrent->pri < p->pri) {
lwpStatus(p, "priority is higher");
lwpYield();
}
}
}
/*
* wait on a lwpSemaphore
*/
void lwpWait(s)
struct lwpSem *s;
{
extern struct lwpProc *LwpCurrent;
lwpStatus(LwpCurrent, "checking semaphore %s", s->name);
if (--s->count < 0) {
lwpStatus(LwpCurrent, "blocking");
lwpAddTail(&s->q, LwpCurrent);
lwpReschedule();
}
}
#endif

69
src/lib/lwp/status.c Normal file
View file

@ -0,0 +1,69 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2000, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
* related information and legal notices. It is expected that any future
* projects/authors will amend these files as needed.
*
* ---
*
* status.c: Process and perhaps display status messages
*
* Known contributors to this file:
* Dave Pare, 1994
*/
#include <stdarg.h>
#ifdef BOUNDS_CHECK
#include <bounds/fix-args.h>
#endif
#include "lwp.h"
#include "prototypes.h"
#if defined(_EMPTH_LWP)
void lwpStatus(struct lwpProc *proc, char *format, ...)
{
va_list ap;
static struct timeval startTime;
struct timeval tv;
char buf[1024];
int sec, msec;
va_start(ap,format);
if (proc->flags & LWP_PRINT) {
if (startTime.tv_sec == 0)
gettimeofday(&startTime, 0);
gettimeofday(&tv, 0);
sec = tv.tv_sec - startTime.tv_sec;
msec = (tv.tv_usec - startTime.tv_usec) / 1000;
if (msec < 0) {
sec++;
msec += 1000;
}
vsprintf(buf, format, ap);
printf("%d:%02d.%03d %17s[%d]: %s\n", sec/60, sec%60, msec/10,
proc->name, proc->pri, buf);
}
va_end(ap);
}
#endif