blob: bfacc893b000d66e3c2900e95d4845186aaac5de [file] [log] [blame]
#include "copyright.h"
#include "qt.h"
#include "stp.h"
#ifndef NULL
#define NULL 0
#endif
#define STP_STKSIZE (0x1000)
/* `alignment' must be a power of 2. */
#define STP_STKALIGN(sp, alignment) \
((void *)((((qt_word_t)(sp)) + (alignment) - 1) & ~((alignment)-1)))
/* The notion of a thread is merged with the notion of a queue.
Thread stuff: thread status (sp) and stuff to use during
(re)initialization. Queue stuff: next thread in the queue
(next). */
struct stp_t {
qt_t *sp; /* QuickThreads handle. */
void *sto; /* `malloc'-allocated stack. */
struct stp_t *next; /* Next thread in the queue. */
};
/* A queue is a circular list of threads. The queue head is a
designated list element. If this is a uniprocessor-only
implementation we can store the `main' thread in this, but in a
multiprocessor there are several `heavy' threads but only one run
queue. A fancier implementation might have private run queues,
which would lead to a simpler (trivial) implementation */
typedef struct stp_q_t {
stp_t t;
stp_t *tail;
} stp_q_t;
/* Helper functions. */
extern void *malloc (unsigned size);
extern void perror (char const *msg);
extern void free (void *sto);
void *
xmalloc (unsigned size)
{
void *sto;
sto = malloc (size);
if (!sto) {
perror ("malloc");
exit (1);
}
return (sto);
}
/* Queue access functions. */
static void
stp_qinit (stp_q_t *q)
{
q->t.next = q->tail = &q->t;
}
static stp_t *
stp_qget (stp_q_t *q)
{
stp_t *t;
t = q->t.next;
q->t.next = t->next;
if (t->next == &q->t) {
if (t == &q->t) { /* If it was already empty .. */
return (NULL); /* .. say so. */
}
q->tail = &q->t; /* Else now it is empty. */
}
return (t);
}
static void
stp_qput (stp_q_t *q, stp_t *t)
{
q->tail->next = t;
t->next = &q->t;
q->tail = t;
}
/* Thread routines. */
static stp_q_t stp_global_runq; /* A queue of runable threads. */
static stp_t stp_global_main; /* Thread for the process. */
static stp_t *stp_global_curr; /* Currently-executing thread. */
static void *stp_starthelp (qt_t *old, void *ignore0, void *ignore1);
static void stp_only (void *pu, void *pt, qt_userf_t *f);
static void *stp_aborthelp (qt_t *sp, void *old, void *null);
static void *stp_yieldhelp (qt_t *sp, void *old, void *blockq);
void
stp_init()
{
stp_qinit (&stp_global_runq);
}
void
stp_start()
{
stp_t *next;
while ((next = stp_qget (&stp_global_runq)) != NULL) {
stp_global_curr = next;
QT_BLOCK (stp_starthelp, 0, 0, next->sp);
}
}
static void *
stp_starthelp (qt_t *old, void *ignore0, void *ignore1)
{
stp_global_main.sp = old;
stp_qput (&stp_global_runq, &stp_global_main);
/* return (garbage); */
}
void
stp_create (stp_userf_t *f, void *pu)
{
stp_t *t;
void *sto;
t = xmalloc (sizeof(stp_t));
t->sto = xmalloc (STP_STKSIZE);
sto = STP_STKALIGN (t->sto, QT_STKALIGN);
t->sp = QT_SP (sto, STP_STKSIZE - QT_STKALIGN);
t->sp = QT_ARGS (t->sp, pu, t, (qt_userf_t *)f, stp_only);
stp_qput (&stp_global_runq, t);
}
static void
stp_only (void *pu, void *pt, qt_userf_t *f)
{
stp_global_curr = (stp_t *)pt;
(*(stp_userf_t *)f)(pu);
stp_abort();
/* NOTREACHED */
}
void
stp_abort (void)
{
stp_t *old, *newthread;
newthread = stp_qget (&stp_global_runq);
old = stp_global_curr;
stp_global_curr = newthread;
QT_ABORT (stp_aborthelp, old, (void *)NULL, newthread->sp);
}
static void *
stp_aborthelp (qt_t *sp, void *old, void *null)
{
free (((stp_t *)old)->sto);
free (old);
/* return (garbage); */
}
void
stp_yield()
{
stp_t *old, *newthread;
newthread = stp_qget (&stp_global_runq);
old = stp_global_curr;
stp_global_curr = newthread;
QT_BLOCK (stp_yieldhelp, old, &stp_global_runq, newthread->sp);
}
static void *
stp_yieldhelp (qt_t *sp, void *old, void *blockq)
{
((stp_t *)old)->sp = sp;
stp_qput ((stp_q_t *)blockq, (stp_t *)old);
/* return (garbage); */
}