mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
199 lines
3.9 KiB
C
199 lines
3.9 KiB
C
#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); */
|
||
}
|