/*
 * Really Simple Threads package.  Copyright Robert S. Thau 1996.
 * This code may be used for any purpose.  It may be redistributed on
 * the following terms: 1) this paragraph and the next must appear
 * unaltered in any source distribution, and be prominently displayed
 * in the supporting documentation for any source or binary
 * distribution; 2) if you wish to distribute modified or derived
 * works, each such work must bear a notice of who last altered the
 * included version of Really Simple Threads, and when.  The notice of
 * alterations shall be attached to the derivative works in the same
 * manner as these two paragraphs, and shall be at least as prominent.
 * Redistribution for any purpose is otherwise unrestricted.
 *
 * THIS CODE IS IN AN UNFINISHED STATE, AND HAS NOT BEEN TESTED
 * TO THE POINT THAT THE AUTHOR BELIEVES IT CAN BE EXPECTED TO
 * WORK RELIABLY.  IT IS SUITABLE, AT THIS POINT, FOR EXPERIMENTATION
 * AND DEBUGGING ONLY.  TREAT ACCORDINGLY.
 */

#include <stdio.h>
#include "rsthreads.h"
#include <sys/types.h>
#include <signal.h>
#include <assert.h>
#include <sys/time.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#ifdef AIX
#include <sys/select.h>
#endif

void *malloc();
void *calloc();
void *realloc();
void free();

/*
 * This code has passed smoke tests on the following platforms,
 * at one time or another (but has changed since the last tests on some):
 *
 * Sparc -- SunOS
 * Alpha -- Digital Unix
 * MIPS(SGI) -- IRIX
 * Sparc -- Solaris (req. -DSOLARIS2)
 * MIPS(Dec) -- Ultrix (req. -DULTRIX)
 * HPPA -- HP-UX (req. -DHPUX)
 * IBM Power -- AIX 3.2 (req. being linked with three lines of assembly).
 * i386 -- Linux (req. -DLINUX)
 * i386 -- NetBSD
 *
 * We begin with the machine-dependant code.  This should #define
 * STACK_QUANTUM, define the MACHINE_STATE typedef, and define the
 * context_switch and set_machine_state routines, and ASYNCHRONIZE_FD
 * (which turns out to be different on different flavor unices).  The
 * rest of the code is at least supposed to be completely portable.
 */

/* Machine- and OS-dependant code BEGINS */

/*
 * Non-blocking I/O support tends to be a little random ---
 * on SunOS, O_NONBLOCK still blocks in accept(); on HP-UX,
 * O_NDELAY reads return 0 rather than an error condition.
 * And so forth...
 *
 * Just to make things more confusing, on some systems,
 * manual pages will mention behavior of a given system
 * call if a descriptor has been marked "non-blocking",
 * without being terribly explicit about what that means.
 */

#if defined(HPUX) || defined(IRIX) || defined(SOLARIS2) || defined(AUX)
#define ASYNCHRONIZE_FD(fd) fcntl (fd, F_SETFL, O_NONBLOCK)
#else
#define ASYNCHRONIZE_FD(fd) fcntl (fd, F_SETFL, O_NDELAY)
#endif

/* Stack sizing... try to provide some rough notion of how big is "big" */

#ifdef SIGSTKSIZE
#define STACK_QUANTUM SIGSTKSIZE
#else
#define STACK_QUANTUM 4096
#endif

/* Machine states for context-switching.  So far, we use jmp_bufs everywhere */

#include <setjmp.h>
typedef struct { jmp_buf b; } MACHINE_STATE;

#if defined(SOLARIS2) || defined(SYSV) || defined(SCO5)
#define SAVE_MACHINE_STATE(buf) setjmp((buf)->b)
#define RESTORE_MACHINE_STATE(buf) longjmp((buf)->b, 1)
#else
#define SAVE_MACHINE_STATE(buf) _setjmp((buf)->b)
#define RESTORE_MACHINE_STATE(buf) _longjmp((buf)->b, 1)
#endif

/*
 * Context switch.  Note that on some systems (well, AIX), voodoo
 * is required to keep _longjmp from complaining (correctly) that it
 * is not being used as intended.
 */

#ifdef AIX
extern int rst_trick();		/* Force rsthreads_aix.o to be linked in */
static int (*bogus)() = &rst_trick;
#endif

static void context_switch (MACHINE_STATE *new_state, MACHINE_STATE *old_state)
{
    if (SAVE_MACHINE_STATE (old_state) == 0) 
	RESTORE_MACHINE_STATE (new_state);
}

/* Thread setup code --- the really nasty part.
 *
 * The goal of this exercise is to initialize a machine_state
 * so that when we context-switch into that state, it will call
 * the supplied init_func().  On systems (all of them) where
 * machine_state is a jmp_buf, that boils down to rigging things
 * so that when we _longjmp there, that function will start running
 * on the new stack.
 *
 * On systems that support sigstack, we can do this by setting
 * the signal stack to be the new thread's stack and initializing
 * the jmp_buf with a setjmp() inside a signal handler --- it's
 * ugly, and it puts a few hundred bytes of crud on the bottom of
 * the stack that we don't really want, but it saves us from having
 * to actually understand the machine we are running on.
 *
 * On other systems, we have to fake up the jmp_buf directly,
 * which is often pretty easy if you know the structure of a jmp_buf.
 * You can either read up on your machine's calling conventions, or
 * just poke around in your setjmp.h looking for things labeled "PC"
 * and "SP", and hope for the best.  The latter is my approach, but
 * it may have strange results on machines like PowerPC, where a
 * function pointer is *not* a pointer to the code.  (If setjmp.h is
 * uninformative, telling gdb to "disassemble _longjmp" may yield
 * useful information).
 */

#ifdef LINUX
static void set_machine_state (MACHINE_STATE *state, void (*init_func)(),
			       char *stack_lo_addr, char *stack_hi_addr)
{
    SAVE_MACHINE_STATE (state);
    state->b->__pc = (char *)init_func;
    state->b->__sp = stack_hi_addr;
}
#elif defined(SOLARIS2)
static void set_machine_state (MACHINE_STATE *state, void (*init_func)(),
			       char *stack_lo_addr, char *stack_hi_addr)
{
    /* On Solaris, the sigaltstack and sigsetjmp combination behaves...
     * strangely.  ENOTOWN, for Pete's sake?
     */
    SAVE_MACHINE_STATE (state);
    (state->b)[2] = (int)init_func;	/* pc */
    (state->b)[1] = (int)(stack_hi_addr) - 1024; /* sp */
    (state->b)[1] &= ~7;
}
#elif defined(SCO5)
static void set_machine_state (MACHINE_STATE *state, void (*init_func)(),
			       char *stack_lo_addr, char *stack_hi_addr)
{
    /* Yet another platform where sigfoostack behaves strangely */
    SAVE_MACHINE_STATE (state);
    (state->b)[5] = (int)init_func;
    (state->b)[4] = (int)stack_hi_addr;
}
#else  /* Generic sigstack startup */
static MACHINE_STATE *creating_state = NULL;
static MACHINE_STATE running_state;
static void (*creating_func)();

/* Why is it that people just can't stop messing around with 
 * sigaltstack()?
 */

#ifdef ULTRIX
#define SA_ONSTACK SV_ONSTACK
#endif

#if defined(BSD)
#if BSD > 199305
#define BSD4_4
#endif
#endif

#if defined(SA_DISABLE)
#define SS_DISABLE SA_DISABLE
#endif

#if defined(__NetBSD__) || defined(SA_DISABLE)
#define ss_sp ss_base
#define BSD4_4
#endif

#if defined (BSD4_4) || defined(SOLARIS2)
#define USE_SIGALTSTACK
#endif

/* To create machine state for a new thread, we declare its stack segment
 * to be the signal stack, and then send ourselves a SIGUSR1, which causes
 * the following to run on the new stack:
 */

static void sigaction_trampoline () {
    MACHINE_STATE *starting_state = creating_state;
    void (*init_func)() = creating_func;
    sigset_t sigs;
    
    /* Make sure we are, in fact, creating a new thread */
    
    creating_state = NULL;
    assert (starting_state != NULL);

    /* Unblock the SIGUSR1 signal that got us here */
    
    sigemptyset (&sigs);
    sigaddset (&sigs, SIGUSR1);
    sigprocmask (SIG_UNBLOCK, &sigs, NULL);
    
    /* Save current machine state (on new stack), go back to current thread
     * until we're scheduled for real...
     */
    
    context_switch (&running_state, starting_state);

    /* New thread is now running; invoke its init function */
    
    init_func();
}

static void set_machine_state (MACHINE_STATE *state, void (*init_func)(),
			       char *stack_lo_addr, char *stack_hi_addr)
{
    /* Arrange for new thread's stack segment to be signal stack */
    
    struct sigaction sa;
    
#ifdef USE_SIGALTSTACK
    struct sigaltstack ss, oss;
    
    ss.ss_sp = stack_lo_addr + 16;
    ss.ss_sp = (char*)((long)ss.ss_sp &~ 0x15);
    ss.ss_flags = 0;
    ss.ss_size = stack_hi_addr - stack_lo_addr - 16;
    if (sigaltstack (&ss, NULL)) {
	perror ("sigaltstack");
	abort();
    }
#else    
    struct sigstack ss;
    
#if defined (HPUX)    
    ss.ss_sp = stack_lo_addr;
#else
    ss.ss_sp = stack_hi_addr;
    ss.ss_sp = (char*)((long)ss.ss_sp &~ 0x15);	/* Alpha is finicky */
#endif    
    ss.ss_onstack = 0;
    if (sigstack (&ss, NULL)) {
	perror ("sigstack");
	abort();
    }
#endif    

    /* Arrange for SIGUSR1 to transfer onto signal stack */

    memset ((char *)&sa, 0, sizeof(struct sigaction));
    sa.sa_handler = sigaction_trampoline;
    sigemptyset (&sa.sa_mask);
    sa.sa_flags = SA_ONSTACK;
    if (sigaction (SIGUSR1, &sa, NULL) != 0) {
	perror ("sigaction");
	abort();
    }

    /* Tell thread startup code on signal stack where to dump 
     * its machine_state, and what to do afterwards...
     */
    
    creating_state = state;
    creating_func = init_func;
    
    /* Transfer onto signal stack and set it up.  It will _longjmp back */
    
    if (SAVE_MACHINE_STATE (&running_state) == 0) {
	kill (getpid(), SIGUSR1);
	/* Wait for signal to arrive.  If you find yourself spinning
	 * here for any length of time, it's probably because SIGUSR1
	 * is still blocked from the last thread creation.
	 */
	while (1) continue;
    }

    /* Inform the system that we are back off the signal stack */

#ifdef USE_SIGALTSTACK    
    ss.ss_flags = 0;
    if (sigaltstack (&ss, &oss)) {
	perror ("sigaltstack");
	abort();
    }
    ss.ss_flags = SS_DISABLE;
    if (sigaltstack (&ss, &oss)) {
	perror ("sigaltstack2");
	abort();
    }
    sigaltstack (&ss, &oss);
    assert (oss.ss_flags & SS_DISABLE);
#else    
    if (sigstack (&ss, NULL)) {
	perror ("sigstack");
	abort();
    }
#endif    
}
#endif


/* Machine and OS-dependant code ENDS.  There should be no #ifdef's
 * past this point...
 */

/*****************************************************************
 *
 * Actual layout of the thread structure
 */

enum thread_state { thread_running, thread_waiting, thread_wait_mutex,
		    thread_wait_read, thread_wait_write, thread_wait_select,
		    thread_wait_alarm, thread_wait_notify, thread_wait_join,
		    thread_wait_pid,
		    thread_sched, thread_sleeping, thread_dead };

static char *thread_state_names[] = {
    /* Brief indication of what the thing is waiting for... */
    "-", "func", "mutex",
    "read", "write", "select",
    "alarm", "notify", "join", "waitpid",
    "sched", "sleep", "dead",
};

typedef struct dll_elt {
    thread_t t;
    struct dll_elt *next;
    struct dll_elt *prev;
} dll_elt;

typedef dll_elt dll;		/* For headers; same struct, different uses */

#define FIRST_THREAD(dll)    (dll).next->t
#define NEXT_THREAD(dll_elt) (dll_elt).next->t
#define PREV_THREAD(dll_elt) (dll_elt).prev->t

typedef struct rst_select_args {
    fd_set *readfds;
    fd_set *writefds;
    fd_set *exceptfds;
} rst_select_args;

typedef struct {
    pid_t pid;
    int *statusp;
    int flags;
    int rv;
    int errno;
} rst_wpid_args;

typedef union wait_arg {
    void *arg;			/* Arg to function */
    int fd;			/* File descriptor for I/O */
    rst_mutex *mutex;
    rst_cvar *cvar;
    rst_select_args *select_args;
    rst_wpid_args *waitpid_args;
    
    thread_t joinee;		/* For join() */
    void *join_status;		/* For thread being join()ed */
} wait_arg;

struct thread {
    dll_elt all;		/* link on linked list of all threads */
    
    dll_elt pri;		/* link on linked list of same pri threads */
    struct timeval last_ran;
    int timeout;
    
    enum thread_state state;
    int flags;
    int mask;
    
    int tid;			/* ID of this thread for debugging hooks */
    char *name;			/* Human readable name, for debugging  */

    void *(*init_func)(void *);
    int (*func)(void *);	/* wait fn, for "general wait" */
    wait_arg w;			/* Arg for func, or data for waiting cond */
    
    void (*io_err_handler)(void *, int);
    void *io_err_arg;

    void (*interrupt_handler)(void *);
    void *interrupt_arg;
    
    void (*alarm_handler)(void *);
    void *alarm_arg;
    time_t alarm_set;
    
    void **per_thread_data;
    long per_thread_alloc;
    
    struct rst_cleanup_block *cleanups;
    
    rst_mutex *locks_held;
    
    MACHINE_STATE machine_state;
    char *stack;
    long *beforeguard;
    long *afterguard;
};

static void default_intr_handler (void *dummy) { rst_exit (NULL); }

const rst_thread_parms rst_thread_defaults = {
    RST_DEFAULT_PRIORITY,
    0,				/* flags */
    4,				/* stacksize */
    default_intr_handler,	/* interrupt handler */
    NULL,			/* interrupt arg */
    "user thread",		/* name */
};

static thread_t sched_thread = NULL;
static thread_t current = NULL;
static dll prilevel [RST_MAX_PRIORITY + 1];
static dll all_threads;

/*****************************************************************
 *
 * Basic data-structure twiddling; maintaining the thread
 * lists.
 */

static void ll_init (dll *list) {
    list->next = list;
    list->prev = list;
    list->t = NULL;
}

static void init_sched_dlls () {
    int i;
    
    for (i = 0; i <= RST_MAX_PRIORITY; ++i)
	ll_init (&prilevel[i]);
    
    ll_init (&all_threads);
}

static void ll_init_elt (dll_elt *elt, thread_t t) { elt->t = t; }

static void ll_add_elt (dll *list, dll_elt *t) {
    dll_elt *current_tail = list->prev;
    
    t->next = current_tail->next;
    t->prev = current_tail;
    t->prev->next = t;
    t->next->prev = t;
}

static void ll_remove_elt (dll_elt *t) {
    t->prev->next = t->next;
    t->next->prev = t->prev;
}

/****************************************************************
 *
 * Debugger hooks, in particular to force the sp onto a current thread's
 * stack so we can get a backtrace; also listing threads and other
 * such creature comforts.  Note that the compiler is right to grip that
 * "list_rsthreads" and "rst_select_thread" are never invoked --- they
 * are only intended to be called from the debugger.
 */

void rst_set_thread_name (char *name) { current->name = name; }

static thread_t find_rsthread (int tid) {
    thread_t t;
    
    for (t = FIRST_THREAD(all_threads); t != NULL; t = NEXT_THREAD(t->all))
	if (t->tid == tid)
	    return t;
    
    return NULL;
}

/*
 * List running threads...
 */

void rst_thread_info_header (char *result) {
    sprintf (result, "%4s %-8s%-10s %s",
	     "TID", "WAITFOR", "WAIT-ARG", "NAME");
}

void rst_thread_info (thread_t t, char *cp) {
    sprintf (cp, "%4d %-8s", t->tid, thread_state_names[t->state]);
    cp += strlen(cp);
	
    switch (t->state) {
    case thread_wait_mutex:
	sprintf (cp, "%-10.10s ", t->w.mutex->name);
	break;
    case thread_wait_notify:
	sprintf (cp, "%-10.10s ", t->w.cvar->name);
	break;
    case thread_wait_read:
    case thread_wait_write:
	sprintf (cp, "%-10d ", t->w.fd);
	break;
    case thread_wait_join:
	sprintf (cp, "%-10d ", t->w.joinee->tid);
	break;
    default:
	sprintf (cp, "%-10.10s ", "-");
	break;
    }
	
    cp += strlen(cp);
    sprintf (cp, "%.55s", t->name);
}

static char rst_dbg_buf[80];

static char* rst_thread_info_header_dbg () {
    rst_thread_info_header (rst_dbg_buf);
    return rst_dbg_buf;
}

static char* rst_thread_info_dbg (thread_t t) {
    rst_thread_info (t, rst_dbg_buf);
    return rst_dbg_buf;
}

/* Dbx isn't smart enough to run a loop on its own.  Hence the
 * following, from Ben Laurie <ben@algroup.co.uk>:
 */

static void rst_thread_info_dbx_hook() {}

static void rst_thread_info_dbx() {
    thread_t t;
 
    rst_thread_info_header_dbg();
    rst_thread_info_dbx_hook();
    
    for (t = FIRST_THREAD(all_threads); t != NULL; t = NEXT_THREAD(t->all)) {
 	rst_thread_info_dbg(t);
 	rst_thread_info_dbx_hook();
    }
}


/*
 * Forcing the stack pointer onto the stack of a given thread, from
 * the scheduler.
 *
 * Note that this needs support from the debugger itself...
 */

static int debug_hook = 0;
static volatile thread_t debug_target;

#define DEBUG_HOOK \
  while (debug_hook) { \
    rst_inspection_breakpoint(); \
    context_switch (&sched_thread->machine_state, \
		    &current->machine_state); \
  }

static void sched_debug_hook () {
    while (debug_target != NULL) {
	current = debug_target; 
	debug_target = NULL; 
	debug_hook = 1;
	context_switch (&current->machine_state, &sched_thread->machine_state);
    }
}

static void rst_inspection_breakpoint () {
    /* Debugger stops here ... */
    /* ... well, debugger resumed.  User must want us to pick up
     * where we left off...
     */

    debug_hook = 0;
}

static int rst_select_thread(int tid) {
    thread_t t = find_rsthread(tid);
    
    if (t == NULL) {
	fprintf (stderr, "No such thread\n");
	return 0;
    }
    if (t->state == thread_dead) {
	fprintf (stderr, "Can't inspect stack of dead thread\n");
	return 0;
    }
    if (t->state == thread_sched) {
	/* scheduler has no DEBUG_HOOK code after its context_switches;
	 * resuming it could have unpredictable consequences.
	 */
	fprintf (stderr, "Don't confuse me like that!!!\n");
	return 0;
    }

    debug_target = t;
    return 1;
}

/*****************************************************************
 *
 * Basic thread ops.  Note that rst_kill and rst_exit do
 * not free the thread structure --- they just switch into the
 * scheduler's context, and let it do the deed.  I don't want to
 * be running on a stack which malloc() regards as free memory.
 */

/* Thread startup code.  Same on all systems, so the machine-dependant
 * part doesn't have to figure out how to pass in arguments.  The
 * set_machine_state function is supposed to make sure that this is the
 * code that first runs in any thread.
 */

static void thread_start_trampoline () {
    DEBUG_HOOK;
    rst_exit((*current->init_func)(current->w.arg));
    abort();			/* No return! */
}

static int next_tid = 0;

thread_t rst_spawn_parms (void *(*start_func)(void*), void *arg,
			  const rst_thread_parms *parms)
{
    thread_t t = (thread_t) calloc (sizeof (struct thread), 1);
    int stacksize = (parms->stacksize) * STACK_QUANTUM;
    
    /* Check parameters.  Note that the first struct thread created, in
     * rst_init, is for the thread running main(), on the system stack
     * segment, so it legitimately has a private stack segment of size zero.
     * That isn't legal under any other circumstances, so we check...
     */
    
    assert (0 <= parms->priority && parms->priority <= RST_MAX_PRIORITY);
    assert (all_threads.next == &all_threads || parms->stacksize > 0);
    if (t == NULL) abort();
    
    gettimeofday (&t->last_ran, NULL);
    
    t->init_func = start_func;
    t->w.arg = arg;
    t->flags = parms->flags & RST_USERFLAGS;
    t->mask = RST_SIG_FLAGS;

    if (t->flags & RST_INTERRUPTS_BLOCKED)
	t->mask &= ~RST_INTERRUPTED;
    
    if (t->flags & RST_ALARMS_BLOCKED)
	t->mask &= ~RST_ALARMED;
    
    t->tid = next_tid++;
    t->name = parms->name;
    
    t->locks_held = NULL;
    t->cleanups = NULL;
    t->per_thread_data = NULL;
    t->per_thread_alloc = 0;
    
    t->io_err_handler = NULL;
    t->alarm_handler = NULL;
    t->interrupt_handler = parms->interrupt_handler;
    t->interrupt_arg = parms->interrupt_arg;
    
    /* Set up stack and machine state */
    
    t->stack = malloc (stacksize + 2 * sizeof(long));

    if (t->stack == NULL) abort();
    
    t->beforeguard = (long*)t->stack;
    t->afterguard = (long*)(t->stack + stacksize + sizeof(long));
    *t->beforeguard = 0xdeadbeef;
    *t->afterguard = 0xdeadbeef;
    
    if (stacksize > 0)
        set_machine_state (&t->machine_state, thread_start_trampoline,
		           t->stack + sizeof(long),
		           (char*)t->afterguard - sizeof(long));
    
    t->state = thread_running;	/* Ready to go now. */

    /* Set up linked lists */
    
    ll_init_elt (&t->all, t);  ll_init_elt (&t->pri, t);
    
    ll_add_elt (&all_threads, &t->all);
    ll_add_elt (&prilevel [parms->priority], &t->pri);

    return t;
}

void destroy_thread_block (thread_t t) {
    assert (t->state == thread_dead);
    ll_remove_elt (&t->all);
    ll_remove_elt (&t->pri);
    free (t->stack);
    if (t->per_thread_data != NULL) free (t->per_thread_data);
    if (t->flags & RST_NO_JOIN) free (t);
}

thread_t rst_spawn (void *(*start_func)(void *), void *arg) {
    return rst_spawn_parms (start_func, arg, &rst_thread_defaults);
}

static void set_timeout (time_t timeout) {
    current->flags &= ~RST_TIMED_OUT;
    
    if (timeout == RST_WAIT) 
	current->timeout = -1;
    else if (timeout == RST_WAIT_SIGNAL)
	current->timeout = 0;
    else
	current->timeout = timeout + time(NULL);
}    
 
/****************************************************************
 *
 * Basic interactions with the scheduler.
 *
 * Note that rst_yield(), in addition to being a user-visible
 * function at the API level, is also what internal routines use
 * to give up control after putting the current thread in a wait
 * state.  It is therefore the central point used to coordinate
 * such things as running alarm() and interrupt() handlers at
 * appropriate times, and interaction with the debugging hooks.
 */

void rst_yield () {
    do {
	current->flags &= ~RST_TIMED_OUT;
	
	context_switch (&sched_thread->machine_state,
			&current->machine_state);
	DEBUG_HOOK;
    
	/* Just came back... check to see if we need to
	 * run the alarm or interrupt handler.  Save wait status and args
	 * while running it, in case the handler itself decides
	 * to block for some reason.
	 */
    
	if (current->flags & current->mask) {
	    enum thread_state saved_state = current->state;
	    wait_arg saved_arg = current->w;
	    int (*saved_func)(void *) = current->func;
	    int flags = current->flags & current->mask;
    
	    current->state = thread_running; 

	    if ((flags & RST_ALARMED) && current->alarm_handler) {
		current->flags &= ~RST_ALARMED;
		(*current->alarm_handler)(current->alarm_arg);
	    }

	    if ((flags & RST_INTERRUPTED) && current->interrupt_handler) {
		current->flags &= ~RST_INTERRUPTED;
		(*current->interrupt_handler)(current->interrupt_arg);
	    }
	
	    current->state = saved_state;
	    current->func = saved_func;
	    current->w = saved_arg;
	}
    } while (current->state != thread_running
	     && !(current->flags & RST_TIMED_OUT)
	     && ((current->timeout <= 0)
		 || !(current->flags & RST_SIG_FLAGS)));
}

/* Set a waiting thread running once whatever it was waiting for
 * has occured...
 */

static void setrun (thread_t t) {
    t->state = thread_running;

    /* Suppose the thread was waiting for something, timed out, but 
     * didn't get to run before what it was waiting for happened anyway.
     * Since the timeout and the event it was waiting for could have
     * happened in the other order, it'll eliminate a few race
     * conditions (e.g., lost notifies) to pretend that's what happened.
     */

    t->flags &= ~RST_TIMED_OUT;
}

#define WAIT_WRAPUP(rv) (current->state != thread_running? \
			 (current->state = thread_running, -1) \
			 : rv)

int rst_sleep (int timeout) {
    if (timeout == RST_NO_WAIT) return 0; /* sleep (0); hokay */
    set_timeout (timeout);
    current->state = thread_sleeping;
    rst_yield();
    current->flags &= ~RST_TIMED_OUT; /* special case --- sleep is
					   * *supposed* to time out.
					   */
    return WAIT_WRAPUP(0);
}
    
int rst_wait (int timeout, int (*wait_func)(void *), void *arg) {

    if (wait_func (arg)) return 0;
    if (timeout == RST_NO_WAIT) return -1;
    
    current->func = wait_func;
    current->w.arg = arg;
    current->state = thread_waiting;
    set_timeout (timeout);

    rst_yield();
    return WAIT_WRAPUP(0);
}

#define JOIN_READY(t) (t->w.joinee->state == thread_dead)

int rst_join (int timeout, thread_t t, void **retval) {

    if (t->state != thread_dead) {
	if (timeout == RST_NO_WAIT) return -1;
	if (t->flags & RST_NO_JOIN) return -1;
	
	set_timeout (timeout);
	current->state = thread_wait_join;
	current->w.joinee = t;
	
	rst_yield();

	if (t->state != thread_dead) return WAIT_WRAPUP(-1);
    }

    *retval = t->w.join_status;
    free (t);
    return WAIT_WRAPUP(0);
}

thread_t rst_current_thread () { return current; }

void rst_set_priority (thread_t t, int new_priority)
{
    assert (0 <= new_priority && new_priority <= RST_MAX_PRIORITY);

    ll_remove_elt (&t->pri);
    ll_add_elt (&prilevel [new_priority], &t->pri);
}

void rst_interrupt (thread_t t) {
    t->flags |= RST_INTERRUPTED;
}

/*****************************************************************
 *
 * Exits and cleanups.
 */

void rst_exit (void *status) {
    rst_cleanup_block *cleanup = current->cleanups;
    static void mutex_release_all (thread_t);
    static void rst_cleanup_per_thread (thread_t);

    /* Run all cleanups.  The order of the statements here is a little
     * tricky --- in case one of them longjmps, we want to have already
     * freed the cleanup structure when we run it, hence the this_blk
     * shimmy.
     */
    
    while (cleanup) {
	rst_cleanup_block this_blk;

	this_blk = *cleanup;
	if (cleanup->iown) free(cleanup);
	cleanup = current->cleanups = this_blk.next;
	(*this_blk.func)(this_blk.arg);
    }

    rst_cleanup_per_thread (current);
    mutex_release_all (current);

    /* Okay, time to kick the bucket for real */
    
    current->w.join_status = status;
    current->state = thread_dead;

    /* Switch into the scheduler and let it reap the thread structure;
     * can't free it here, or we'd be running on a stack which malloc()
     * regards as free memory, which would be a somewhat perilous situation.
     */

    context_switch (&sched_thread->machine_state, &current->machine_state);
}

void rst_cleanup_block_push (rst_cleanup_block *cleanup) {
    cleanup->next = current->cleanups;
    cleanup->iown = 0;
    current->cleanups = cleanup;
}

void rst_cleanup_push (void (*func)(void *), void *arg) {
    rst_cleanup_block *c =
	(rst_cleanup_block *)malloc(sizeof(rst_cleanup_block));

    c->func = func;
    c->arg = arg;
    c->iown = 1;
    c->next = current->cleanups;
    current->cleanups = c;
}

void rst_cleanup_pop () {
    rst_cleanup_block *top_cleanup = current->cleanups;
    
    assert (top_cleanup != NULL);
    current->cleanups = top_cleanup->next;
    if (top_cleanup->iown) free (top_cleanup);
}

/*****************************************************************
 *
 * Mutex thread ops
 */

#ifdef DEBUG_MUTEX
/* Debugging code --- put a breakpoint on mutex_screwup() and you
 * should find out when things go south.
 */

static void mutex_screwup () {}

static void check_mutex_chain (thread_t t) {
    rst_mutex *fast = t->locks_held, *slow = t->locks_held;

    /* Detect circularities in locks_held chain of this thread */

    while (fast != 0 && fast->next != 0) {
	fast = fast->next->next;
	slow = slow->next;
	if (fast == slow) {
	    mutex_screwup();
	    break;
	}
    }

    /* Detect mutexes which have sneaked onto the locks_held list with
     * the wrong owner somehow...
     */

    for (fast = t->locks_held; fast; fast = fast->next)
	if (fast->owner != t)
	    mutex_screwup();
}
#else
#define check_mutex_chain(t) (void)0
#endif

void rst_mutex_init (rst_mutex *m, char *name) {
    m->owner = NULL;
    m->name = name;
}

#define MUTEX_READY(t) (t->w.mutex->owner == NULL)

static int rst_lock_mutex_i (rst_mutex *m) {
    check_mutex_chain (current);
    
    if (m->owner == current) {
	++m->count;
	return 0;
    }
    
    while (m->owner != NULL && m->owner != current
	   && !(current->flags & current->mask))
    {
	current->state = thread_wait_mutex;
	current->w.mutex = m;
	rst_yield();
    }

    if (m->owner == NULL && !(current->flags & current->mask)) {
	m->next = current->locks_held;
	m->owner = current;
	m->count = 1;
	current->locks_held = m;
    }

    check_mutex_chain (current);
    return WAIT_WRAPUP(0);
}

int rst_lock_mutex (int timeout, rst_mutex *m) {
    if (m->owner != NULL && m->owner != current && timeout == RST_NO_WAIT)
	return -1;
    
    set_timeout (timeout);
    return rst_lock_mutex_i (m);
}

void rst_release_mutex (rst_mutex *m) {
    rst_mutex **l = &current->locks_held;
    
    assert (m->owner == current);
    if (--m->count > 0) return;

    check_mutex_chain (current);
    
    while (*l != NULL) {
        if (*l == m) {
	    *l = m->next;
	    m->owner = NULL;
	    check_mutex_chain (current);
	    return;
	}
	else {
	    l = &((*l)->next);
	}
    }

    assert (0 == "mutex data structure inconsistency!");
}

thread_t rst_mutex_holder (rst_mutex *m) { return m->owner; }

static void mutex_release_all (thread_t t) {
    rst_mutex *lock;
    
    check_mutex_chain (current);
    
    for (lock = t->locks_held; lock; lock = lock->next) 
	lock->owner = NULL;
}

/*****************************************************************
 *
 * Condition variable ops...
 */

void rst_cvar_init (rst_cvar *c, char *n) { c->name = n; }

int rst_await_notify (int timeout, rst_mutex *m, rst_cvar *c)
{
    if (timeout == RST_NO_WAIT) return -1; /* Ours not to reason why... */
    
    /* Note that we set our state to thread_wait_notify before releasing
     * the mutex.  As it stands, this doesn't make a difference; with a
     * preemptive thrads package, it would avoid a nasty race condition.
     */
    
    current->w.cvar = c;
    current->state = thread_wait_notify;
    set_timeout (timeout);

    rst_release_mutex (m);
    
    while (current->state == thread_wait_notify
	   && !(current->flags & current->mask))
	rst_yield();

    if (current->state == thread_running && !(current->flags & current->mask)) 
	return rst_lock_mutex_i (m);

    return WAIT_WRAPUP(0);
}

static void rst_notify (rst_cvar *c, int do_all) {
    thread_t t;

    for (t = FIRST_THREAD(all_threads); t != NULL; t = NEXT_THREAD(t->all)) {
	if (t->state == thread_wait_notify && t->w.cvar == c) {
	    setrun (t);
	    if (!do_all) return;
	}
    }
}

void rst_notify_one (rst_cvar *c) { rst_notify (c, 0); }
void rst_notify_all (rst_cvar *c) { rst_notify (c, 1); }

/*****************************************************************
 * Per-thread data
 */

static int num_per_thread = 0;
static int num_per_thread_alloc = 0;

typedef struct per_thread_entry {
    void (*destructor)(void *);
} per_thread_entry;

static per_thread_entry *per_thread_tab;

rst_per_thread_key rst_make_per_thread (void (*destructor)(void *))
{
    rst_per_thread_key idx = num_per_thread++;
    per_thread_entry *this_entry;
    
    if (idx >= num_per_thread_alloc) {
	if (!per_thread_tab) {
	    per_thread_tab = calloc (4, sizeof (per_thread_entry));
	    num_per_thread_alloc = 4;
	} else {
	    num_per_thread_alloc *= 2;
	    per_thread_tab = realloc (per_thread_tab,
				      num_per_thread_alloc
				      * sizeof (per_thread_entry));
	}

	if (per_thread_tab == NULL) {
	    assert ("malloc failed" == 0);
	}
    }

    this_entry = &per_thread_tab[idx];
    this_entry->destructor = destructor;

    return idx;
}

static void rst_alloc_per_thread (rst_per_thread_key key) {
    
    long old_size = current->per_thread_alloc * sizeof (void *);
    long new_size = (key + 1) * sizeof (void *);
    int i;

    current->per_thread_data =
	(old_size == 0)? (void **)malloc(new_size)
	: (void **)realloc((void *)current->per_thread_data, new_size);

    if (current->per_thread_data == NULL) {
	assert ("realloc failed in get/set_per_thread" == 0);
    }

    for (i = current->per_thread_alloc; i <= key; ++i)
	current->per_thread_data[i] = NULL;
	    
    current->per_thread_alloc = key + 1;
}

void *rst_get_per_thread (rst_per_thread_key key) {
    assert (0 <= key && key < num_per_thread);
    if (key >= current->per_thread_alloc) rst_alloc_per_thread (key);
    return current->per_thread_data[key];
}

void rst_set_per_thread (rst_per_thread_key key, void *value) {
    assert (0 <= key && key < num_per_thread);
    if (key >= current->per_thread_alloc) rst_alloc_per_thread (key);
    current->per_thread_data[key] = value;
}

static void rst_cleanup_per_thread (thread_t t) {
    int i;

    for (i = 0; i < t->per_thread_alloc; ++i)
	if (t->per_thread_data[i] && per_thread_tab[i].destructor)
	    (per_thread_tab[i].destructor)(t->per_thread_data[i]);
}

/****************************************************************
 * waitpid()
 */

int waitpid_ready (thread_t t) {
    rst_wpid_args *w = t->w.waitpid_args;
    w->rv = waitpid (w->pid, w->statusp, w->flags|WNOHANG);
    w->errno = errno;
    return (w->rv != 0);
}

int rst_waitpid (int timeout, pid_t pid, int *statusp, int flags) {
    int rv = waitpid (pid, statusp, flags|WNOHANG);
    rst_wpid_args w;
  
    if ((rv != 0) || (flags & WNOHANG)) return rv;
    if (timeout == RST_NO_WAIT) return -1;

    set_timeout (timeout);

    w.pid = pid;
    w.statusp = statusp;
    w.flags = flags;
    w.rv = 0;
    
    current->w.waitpid_args = &w;
    current->state = thread_wait_pid;

    rst_yield();

    return WAIT_WRAPUP (w.rv);
}


/*****************************************************************
 * I/O operations
 */

static int max_fd = 0;

int rst_prepare_fd (int fd) {
    if (fd > max_fd) max_fd = fd;
    return ASYNCHRONIZE_FD(fd);
}

static void rst_io_wait (int fd, enum thread_state state) {

    current->state = state;
    current->w.fd = fd;
    if (fd > max_fd) max_fd = fd;

    current->flags &= ~RST_IO_ERROR;
    
    while (current->state == state && !(current->flags & current->mask)) 
	rst_yield();
}

static int io_wrapup (int fd, int retval)
{
    current->flags &= ~RST_IO_ERROR;
    
    if (retval == -1 && current->flags & RST_TIMED_OUT) {
	errno = ETIMEDOUT;
	setrun (current);
    }
	
    if (retval == -1 && current->io_err_handler)
	(*current->io_err_handler)(current->io_err_arg, fd);

    return retval;
}

int rst_read_wait (int timeout, int fd) {
    set_timeout (timeout);
    rst_io_wait (fd, thread_wait_read);
    return io_wrapup (fd, (current->flags&current->mask)? -1 : 0);
}

int rst_write_wait (int timeout, int fd) {
    set_timeout (timeout);
    rst_io_wait (fd, thread_wait_write);
    return io_wrapup (fd, (current->flags&current->mask)? -1 : 0);
}

int rst_read (int timeout, int fd, char *buf, int len) {
    int retval;
    
    set_timeout (timeout);
	
    while ((retval = read (fd, buf, len)) == -1
	   && (errno == EAGAIN || errno == EWOULDBLOCK)
	   && !(current->flags & current->mask))
    {
	rst_io_wait (fd, thread_wait_read);
    }

    return io_wrapup (fd, retval);
}

int rst_accept (int timeout, int fd, struct sockaddr *sa, int *alen) {
    int retval;
    
    set_timeout (timeout);
	
    while ((retval = accept (fd, sa, alen)) == -1
	   && (errno == EAGAIN || errno == EWOULDBLOCK)
	   && !(current->flags & current->mask))
    {
	rst_io_wait (fd, thread_wait_read);
    }

    return io_wrapup (fd, retval);
}

int rst_write (int timeout, int fd, const char *buf, int len) {
    int retval;
    
    set_timeout (timeout);
	
    while ((retval = write (fd, buf, len)) == -1
	   && (errno == EAGAIN || errno == EWOULDBLOCK)
	   && !(current->flags & current->mask))
    {
	rst_io_wait (fd, thread_wait_write);
    }

    return io_wrapup (fd, retval);
}

int rst_connect (int timeout, int fd, struct sockaddr *addr, int addrlen) {
    int retval;
    
    set_timeout (timeout);
	
    if ((retval = connect (fd, addr, addrlen)) == -1
	&&(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS))
    {
	retval = 0;
	rst_io_wait (fd, thread_wait_write);
    }

    return io_wrapup (fd, retval);
}

void rst_set_io_err_handler (void (*handler)(void *, int), void *arg) {
    current->io_err_handler = handler;
    current->io_err_arg = arg;
}	
    
int rst_select (int timeout,
		fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
{
    rst_select_args arg;
    struct timeval tv;
    fd_set readspare, writespare, excspare;
    fd_set *readtemp = NULL, *writetemp = NULL, *exctemp = NULL;
    
    /* First, do a poll to see if anything is ready immediately.
     * If not, and the user doesn't want us to go to sleep, return immediate
     * error.
     */
    
    tv.tv_sec = 0;
    tv.tv_usec = 0;

    if (readfds) { readspare = *readfds; readtemp = &readspare; }
    if (writefds) { writespare = *writefds; writetemp = &writespare; }
    if (exceptfds) { excspare = *exceptfds; exctemp = &excspare; }
	
    if (select (max_fd + 1, readtemp, writetemp, exctemp, &tv) > 0) {
	if (readfds) *readfds = readspare;
	if (writefds) *writefds = writespare;
	if (exceptfds) *exceptfds = excspare;
	return 1;
    }
    
    if (timeout == RST_NO_WAIT)
	return -1;

    /* Nothing ready now.  Go to sleep. */

    arg.readfds = readfds;
    arg.writefds = writefds;
    arg.exceptfds = exceptfds;
    
    current->w.select_args = &arg;
    current->state = thread_wait_select;

    set_timeout(timeout);
    rst_yield();

    return WAIT_WRAPUP(1);
}

/* fd_set manipulation for handling of thread_wait_select... */ 

static void logior_fd_set (fd_set *dest, fd_set *src) {
    int count = sizeof(fd_set)/sizeof(int);
    int *dest_ptr = (int*)dest, *src_ptr = (int*)src;

    while (count-- > 0)
	*dest_ptr++ |= *src_ptr++;
}

static void select_arg_union (rst_select_args *dest,
			      rst_select_args *src)
{
    if (src->readfds) logior_fd_set (dest->readfds, src->readfds);
    if (src->writefds) logior_fd_set (dest->writefds, src->writefds);
    if (src->exceptfds) logior_fd_set (dest->exceptfds, src->exceptfds);
}

static int check_fd_set (fd_set *dest, fd_set *src) {
    int count = sizeof(fd_set)/sizeof(int);
    int *dest_ptr = (int*)dest, *src_ptr = (int*)src;

    while (count-- > 0)
	if (*dest_ptr++ & *src_ptr++)
	    return 1;

    return 0;
}

static int select_arg_match  (rst_select_args *dest,
			      rst_select_args *src)
{
    return ((src->readfds && check_fd_set (dest->readfds, src->readfds))
	    || (src->writefds && check_fd_set (dest->writefds, src->writefds))
	    || (src->exceptfds && check_fd_set (dest->exceptfds,
						src->exceptfds)));
}

static void logand_fd_set (fd_set *dest, fd_set *src) {
    int count = sizeof(fd_set)/sizeof(int);
    int *dest_ptr = (int*)dest, *src_ptr = (int*)src;

    while (count-- > 0)
	*dest_ptr++ &= *src_ptr++;
}

static void select_arg_intersect (rst_select_args *dest,
				  rst_select_args *src)
{
    if (dest->readfds) logand_fd_set (dest->readfds, src->readfds);
    if (dest->writefds) logand_fd_set (dest->writefds, src->writefds);
    if (dest->exceptfds) logand_fd_set (dest->exceptfds, src->exceptfds);
}


/* Rescheduling I/O-waiting threads ... */

static int io_check (int do_poll) {
    thread_t t;
    fd_set readfds;
    fd_set writefds;
    fd_set exceptfds;
    struct timeval delay;
    int num_set_running = 0;
    int rv;
    rst_select_args my_args;

    FD_ZERO (&readfds);
    FD_ZERO (&writefds);
    FD_ZERO (&exceptfds);

    my_args.readfds = &readfds; my_args.writefds = &writefds;
    my_args.exceptfds = &exceptfds;

    do {
	for (t = FIRST_THREAD(all_threads); t != NULL; t = NEXT_THREAD(t->all))
	{
	    if (t->state == thread_wait_read)
		FD_SET (t->w.fd, &readfds);
	    if (t->state == thread_wait_write)
		FD_SET (t->w.fd, &writefds);
	    if (t->state == thread_wait_read || t->state == thread_wait_write)
		FD_SET (t->w.fd, &exceptfds);
	    if (t->state == thread_wait_select) 
		select_arg_union (&my_args, t->w.select_args);
	}

	delay.tv_sec = do_poll ? 0 : 1;
	delay.tv_usec = 0;
    
	rv = select (max_fd + 1, &readfds, &writefds, &exceptfds, &delay);
	
	if (rv < 0 && errno != EINTR)
	    rst_inspection_breakpoint();
	
    } while (rv < 0 && debug_target == NULL);

    if (debug_target != NULL) return 0;
    
    for (t = FIRST_THREAD(all_threads); t != NULL; t = NEXT_THREAD(t->all))
    {
	int fd = t->w.fd;
	
	if ((t->state==thread_wait_write
	     && (FD_ISSET(fd, &writefds) || FD_ISSET (fd, &exceptfds)))
	    || (t->state == thread_wait_select
		&& select_arg_match (&my_args, t->w.select_args))
	    || (t->state==thread_wait_read
		&& (FD_ISSET(fd, &readfds) || FD_ISSET (fd, &exceptfds))))
	{
	    if (t->state == thread_wait_select)
		select_arg_intersect (t->w.select_args, &my_args);
	    else
		if (FD_ISSET (fd, &exceptfds))
		    t->flags |= RST_IO_ERROR;
	    
	    setrun (t);
	    ++num_set_running;
	}
    }

    return num_set_running;
}

/*****************************************************************
 *
 * Alarm and other signal manipulation.
 */

int rst_interrupted () {
    if (current->flags & RST_INTERRUPTED) {
	current->flags &= ~RST_INTERRUPTED;
	return 1;
    }
    else
	return 0;
}

int rst_alarmed () {
    if (current->flags & RST_ALARMED) {
	current->flags &= ~RST_ALARMED;
	return 1;
    }
    else
	return 0;
}

int rst_timed_out () {
    if (current->flags & RST_TIMED_OUT) {
	current->flags &= ~RST_TIMED_OUT;
	return 1;
    }
    else
	return 0;
}

void rst_block_interrupts () { current->mask &= ~RST_INTERRUPTED; }
void rst_block_alarms () { current->mask &= ~RST_ALARMED; }

void rst_unblock_interrupts () {
    current->mask |= RST_INTERRUPTED;
    
    if (current->flags & RST_INTERRUPTED && current->interrupt_handler)
	(*current->interrupt_handler) (current->interrupt_arg);
}

void rst_unblock_alarms () {
    current->mask |= RST_ALARMED;
    
    if (current->flags & RST_ALARMED && current->alarm_handler)
	(*current->alarm_handler) (current->alarm_arg);
}


void rst_set_alarm (int delay) {
    current->alarm_set = (delay != 0) ? delay + time(NULL) : 0;
}

void rst_set_alarm_handler (void (*handler)(void*), void *arg) {
    current->alarm_handler = handler;
    current->alarm_arg = arg;
}

void rst_set_interrupt_handler (void (*handler)(void*), void *arg) {
    current->interrupt_handler = handler;
    current->interrupt_arg = arg;
}

/*****************************************************************
 *
 * Scheduler, and more global state management.
 */

/* "Load averages" computed by number of runnable threads at each
 * context switch... this is crude, but it may be adequate.
 */

#define LOADAV_TICKS 8
#define INIT_LOADAV 2.0

static float avenrun;
static float nrun[LOADAV_TICKS];
static int runptr = 0;

static void record_nrun (int current_nrun) {
    
    float curnrun = (float)current_nrun;
    float totnrun = avenrun * LOADAV_TICKS - nrun[runptr];
    totnrun += curnrun;
    nrun[runptr++] = curnrun;
    
    if (runptr == LOADAV_TICKS) runptr = 0;

    avenrun = totnrun / LOADAV_TICKS;
}

static void nrun_setup () {
    int i;
    for (i = 0; i < LOADAV_TICKS; ++i) {
	nrun[i] = INIT_LOADAV;
    }
    avenrun = INIT_LOADAV;
}

/* Juggling the running threads, and making sure that we do have
 * something to run (if not, we put the process to sleep)...
 */

static void reschedule () {
    while (1) {
	thread_t t;
	time_t now = time(NULL);
	int num_runnable = 0;
    
	/* Go around the thread list looking for expired timeouts, and
	 * rst_waiting threads whose wait functions now return true;
	 * --- mark them runnable.  Also, look for threads waiting on a
	 * mutex, fd, etc., which is now available, and reap dead threads.
	 */

	for (t = FIRST_THREAD(all_threads); t != NULL; t = NEXT_THREAD(t->all))
	{
	    if (t->state == thread_dead) {
		thread_t dying = t;
		
		t = t->all.prev->t;
		
		assert (dying != sched_thread);
		destroy_thread_block (dying);
	    }
	    
	    if (t->alarm_set > 0 && t->alarm_set < now) {
		t->flags |= RST_ALARMED;
		t->alarm_set = 0;
	    }

	    if (t->timeout > 0 && t->timeout < now) {
		if (t->state == thread_sleeping)
		    t->state = thread_running;
		else if (t->state != thread_running)
		    t->flags |= RST_TIMED_OUT;
		
		t->timeout = 0;
	    }

	    if ((t->state == thread_wait_mutex && MUTEX_READY(t))
		|| (t->state == thread_wait_join && JOIN_READY(t))
		|| (t->state == thread_wait_pid && waitpid_ready(t))
		|| (t->state == thread_waiting && (t->func)(t->w.arg)))
	    {
		setrun (t);
	    }

	    if ((t->state == thread_running) || (t->flags & t->mask))
		++num_runnable;
	}
    
	/* If we have anything we can run right now, do a quick I/O poll
	 * and then return.  We have to do the poll, or
	 *
	 *     while (1) rst_yield()
	 *
	 * would starve out I/O.
	 */
    
	if (num_runnable > 0 || debug_target != NULL) {
	    num_runnable += io_check (1);
	    record_nrun (num_runnable);
	    return;
	}

	/* Sigh... nothing to run.  Block for a while, and see if things
	 * improve.
	 */

	record_nrun (0);
	io_check (0);
    }
}

static thread_t best_thread () {
    struct timeval oldest_yet;
    thread_t thread_to_run = NULL;
    int i;
    thread_t t;

    for (i = RST_MAX_PRIORITY; thread_to_run == NULL && i >= 0; --i) {
	for (t = FIRST_THREAD(prilevel[i]); t != NULL; t = NEXT_THREAD(t->pri))
	{
	    /* We run threads that are in state thread_running;
	     * also, threads with pending conditions in their flag bits.
	     * We run the highest priority thread available ---
	     * among runnable threads, of the same priority,
	     * the one that hasn't run in the longest time wins.
	     */
		
	    if ((t->state == thread_running
		 || (t->state != thread_dead && (t->flags & t->mask)))
		&& (thread_to_run == NULL
		    || (t->last_ran.tv_sec < oldest_yet.tv_sec
			|| (t->last_ran.tv_sec == oldest_yet.tv_sec
			    && t->last_ran.tv_usec < oldest_yet.tv_usec)
			)))
	    {
		thread_to_run = t;
		oldest_yet = t->last_ran;
	    }
	}
    }

    assert (thread_to_run != NULL); /* else reschedule() should not return */
    return thread_to_run;
}

static void *scheduler (void *dummy) {
    
    sched_thread->state = thread_sched;

    while (1) {

	reschedule();
	
	while (debug_target != NULL) {
	    sched_debug_hook();
	    reschedule();
	}
	
	current = best_thread();

	gettimeofday (&current->last_ran, NULL);
	
	context_switch (&current->machine_state, &sched_thread->machine_state);
	    
	if (*current->beforeguard != 0xdeadbeef
	    || *current->afterguard != 0xdeadbeef)
	{
	    assert ("Stack overflow" == 0);
	}
    }
}

void rst_init () {
    thread_t main_thread;
    rst_thread_parms my_params;
    
    init_sched_dlls();
    nrun_setup();

    my_params = rst_thread_defaults;
    my_params.name = "main() thread";
    my_params.stacksize = 0;	/* Not ordinarily legal, but right for
				 * main_thread, which we create first.
				 */
    
    main_thread = rst_spawn_parms (NULL, NULL, &my_params);

    my_params.stacksize = 4;
    my_params.name = "scheduler";
    sched_thread = rst_spawn_parms (scheduler, NULL, &my_params);

    current = sched_thread;	/* Just this once, for bootstrap,
				 * so thread startup trampoline does the
				 * right thing.
				 */
    context_switch (&sched_thread->machine_state, &main_thread->machine_state);
}

int rst_count_threads () {
    int count = 0;
    thread_t t;

    for (t = FIRST_THREAD(all_threads); t != NULL; t = NEXT_THREAD(t->all))
	if (t->state != thread_dead && t->state != thread_sched)
	    ++count;

    return count;
}

float rst_load_average () {
    return avenrun;
}
