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