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