1#include "copyright.h"
2#include "qt.h"
3#include "stp.h"
4
5#ifndef NULL
6#define NULL	0
7#endif
8
9#define STP_STKSIZE (0x1000)
10
11/* `alignment' must be a power of 2. */
12#define STP_STKALIGN(sp, alignment) \
13  ((void *)((((qt_word_t)(sp)) + (alignment) - 1) & ~((alignment)-1)))
14
15
16/* The notion of a thread is merged with the notion of a queue.
17   Thread stuff: thread status (sp) and stuff to use during
18   (re)initialization.  Queue stuff: next thread in the queue
19   (next). */
20
21struct stp_t {
22  qt_t *sp;              /* QuickThreads handle. */
23  void *sto;             /* `malloc'-allocated stack. */
24  struct stp_t *next;    /* Next thread in the queue. */
25};
26
27
28/* A queue is a circular list of threads.  The queue head is a
29   designated list element.  If this is a uniprocessor-only
30   implementation we can store the `main' thread in this, but in a
31   multiprocessor there are several `heavy' threads but only one run
32   queue.  A fancier implementation might have private run queues,
33   which would lead to a simpler (trivial) implementation */
34
35typedef struct stp_q_t {
36  stp_t t;
37  stp_t *tail;
38} stp_q_t;
39
40
41/* Helper functions. */
42
43extern void *malloc (unsigned size);
44extern void perror (char const *msg);
45extern void free (void *sto);
46
47  void *
48xmalloc (unsigned size)
49{
50  void *sto;
51
52  sto = malloc (size);
53  if (!sto) {
54    perror ("malloc");
55    exit (1);
56  }
57  return (sto);
58}
59
60/* Queue access functions. */
61
62  static void
63stp_qinit (stp_q_t *q)
64{
65  q->t.next = q->tail = &q->t;
66}
67
68
69  static stp_t *
70stp_qget (stp_q_t *q)
71{
72  stp_t *t;
73
74  t = q->t.next;
75  q->t.next = t->next;
76  if (t->next == &q->t) {
77    if (t == &q->t) {		/* If it was already empty .. */
78      return (NULL);		/* .. say so. */
79    }
80    q->tail = &q->t;		/* Else now it is empty. */
81  }
82  return (t);
83}
84
85
86  static void
87stp_qput (stp_q_t *q, stp_t *t)
88{
89  q->tail->next = t;
90  t->next = &q->t;
91  q->tail = t;
92}
93
94
95/* Thread routines. */
96
97static stp_q_t stp_global_runq;	/* A queue of runable threads. */
98static stp_t stp_global_main;   /* Thread for the process. */
99static stp_t *stp_global_curr;	/* Currently-executing thread. */
100
101static void *stp_starthelp (qt_t *old, void *ignore0, void *ignore1);
102static void stp_only (void *pu, void *pt, qt_userf_t *f);
103static void *stp_aborthelp (qt_t *sp, void *old, void *null);
104static void *stp_yieldhelp (qt_t *sp, void *old, void *blockq);
105
106
107  void
108stp_init()
109{
110  stp_qinit (&stp_global_runq);
111}
112
113
114  void
115stp_start()
116{
117  stp_t *next;
118
119  while ((next = stp_qget (&stp_global_runq)) != NULL) {
120    stp_global_curr = next;
121    QT_BLOCK (stp_starthelp, 0, 0, next->sp);
122  }
123}
124
125
126  static void *
127stp_starthelp (qt_t *old, void *ignore0, void *ignore1)
128{
129  stp_global_main.sp = old;
130  stp_qput (&stp_global_runq, &stp_global_main);
131  /* return (garbage); */
132}
133
134
135  void
136stp_create (stp_userf_t *f, void *pu)
137{
138  stp_t *t;
139  void *sto;
140
141  t = xmalloc (sizeof(stp_t));
142  t->sto = xmalloc (STP_STKSIZE);
143  sto = STP_STKALIGN (t->sto, QT_STKALIGN);
144  t->sp = QT_SP (sto, STP_STKSIZE - QT_STKALIGN);
145  t->sp = QT_ARGS (t->sp, pu, t, (qt_userf_t *)f, stp_only);
146  stp_qput (&stp_global_runq, t);
147}
148
149
150  static void
151stp_only (void *pu, void *pt, qt_userf_t *f)
152{
153  stp_global_curr = (stp_t *)pt;
154  (*(stp_userf_t *)f)(pu);
155  stp_abort();
156  /* NOTREACHED */
157}
158
159
160  void
161stp_abort (void)
162{
163  stp_t *old, *newthread;
164
165  newthread = stp_qget (&stp_global_runq);
166  old = stp_global_curr;
167  stp_global_curr = newthread;
168  QT_ABORT (stp_aborthelp, old, (void *)NULL, newthread->sp);
169}
170
171
172  static void *
173stp_aborthelp (qt_t *sp, void *old, void *null)
174{
175  free (((stp_t *)old)->sto);
176  free (old);
177  /* return (garbage); */
178}
179
180
181  void
182stp_yield()
183{
184  stp_t *old, *newthread;
185
186  newthread = stp_qget (&stp_global_runq);
187  old = stp_global_curr;
188  stp_global_curr = newthread;
189  QT_BLOCK (stp_yieldhelp, old, &stp_global_runq, newthread->sp);
190}
191
192
193  static void *
194stp_yieldhelp (qt_t *sp, void *old, void *blockq)
195{
196  ((stp_t *)old)->sp = sp;
197  stp_qput ((stp_q_t *)blockq, (stp_t *)old);
198  /* return (garbage); */
199}
200