#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); */ }