aboutsummaryrefslogtreecommitdiffstats
path: root/mps/code/pthrdext.c
diff options
context:
space:
mode:
authorNick Barnes2001-10-31 14:40:56 +0000
committerNick Barnes2001-10-31 14:40:56 +0000
commit7acfca905d76140f4cc0b09c9a12de237de364cd (patch)
tree3ed8babfa3a73d30f29e08ca5d5adcda4ca4e826 /mps/code/pthrdext.c
parentb7ce4893f9902d57cd67ac9a92fa6c3d5a8fc833 (diff)
downloademacs-7acfca905d76140f4cc0b09c9a12de237de364cd.tar.gz
emacs-7acfca905d76140f4cc0b09c9a12de237de364cd.zip
Branch imports for masters.
Copied from Perforce Change: 23678 ServerID: perforce.ravenbrook.com
Diffstat (limited to 'mps/code/pthrdext.c')
-rw-r--r--mps/code/pthrdext.c363
1 files changed, 363 insertions, 0 deletions
diff --git a/mps/code/pthrdext.c b/mps/code/pthrdext.c
new file mode 100644
index 00000000000..2651e06db36
--- /dev/null
+++ b/mps/code/pthrdext.c
@@ -0,0 +1,363 @@
1/* impl.c.pthreadext: POSIX THREAD EXTENSIONS
2 *
3 * $HopeName: MMsrc!pthrdext.c(trunk.2) $
4 * Copyright (C) 2000 Harlequin Limited. All rights reserved.
5 *
6 * .purpose: Provides extension to Pthreads.
7 *
8 * .design: see design.mps.pthreadext
9 *
10 * .acknowledgements: This was derived from code posted to
11 * comp.programming.threads by Dave Butenhof and Raymond Lau
12 * (<David.Butenhof@compaq.com>, <rlau@csc.com>).
13 */
14
15
16/* open sesame magic */
17#define _BSD_SOURCE 1
18#define _POSIX_C_SOURCE 1
19
20#include <pthread.h>
21#include <sched.h>
22#include <signal.h>
23#include <semaphore.h>
24#include <errno.h>
25#include <stdio.h>
26#include <stdlib.h>
27
28#include "pthrdext.h"
29#include "mpm.h"
30
31SRCID(pthreadext, "$HopeName$");
32
33
34/* PTHREADEXT_SIGSUSPEND, PTHREADEXT_SIGRESUME -- signals used
35 *
36 * See design.mps.pthreadext.impl.signals
37 */
38
39#define PTHREADEXT_SIGSUSPEND SIGXFSZ
40#define PTHREADEXT_SIGRESUME SIGPWR
41
42
43/* Static data initiatialized on first use of the module
44 * See design.mps.pthreadext.impl.static.*
45 */
46
47/* mutex */
48static pthread_mutex_t pthreadextMut = PTHREAD_MUTEX_INITIALIZER;
49
50/* semaphore */
51static sem_t pthreadextSem;
52
53/* initialization support */
54static pthread_once_t pthreadextOnce = PTHREAD_ONCE_INIT;
55static Bool pthreadextModuleInitialized = FALSE;
56
57
58/* Global variables protected by the mutex
59 * See design.mps.pthreadext.impl.global.*
60 */
61
62static PThreadext suspendingVictim = NULL; /* current victim */
63static RingStruct suspendedRing; /* PThreadext suspend ring */
64
65
66static void suspendSignalHandler(int sig, struct sigcontext scp);
67static void resumeSignalHandler(int sig);
68
69
70/* PThreadextModuleInit -- Initialize the PThreadext module
71 *
72 * See design.mps.pthreadext.impl.static.init
73 *
74 * Dynamically initialize all state when first used
75 * (called by pthread_once).
76 */
77
78static void PThreadextModuleInit(void)
79{
80 int status;
81 struct sigaction pthreadext_sigsuspend, pthreadext_sigresume;
82
83 AVER(pthreadextModuleInitialized == FALSE);
84
85 /* Initialize the ring of suspended threads */
86 RingInit(&suspendedRing);
87
88 /* Initialize the semaphore */
89 status = sem_init(&pthreadextSem, 0, 0);
90 AVER(status != -1);
91
92 /* Install the signal handlers for suspend/resume. */
93 /* We add PTHREADEXT_SIGRESUME to the sa_mask field for the */
94 /* PTHREADEXT_SIGSUSPEND handler. That avoids a race if one thread */
95 /* suspends the target while another resumes that same target. (The */
96 /* PTHREADEXT_SIGRESUME signal cannot be delivered before the */
97 /* target thread calls sigsuspend.) */
98
99 pthreadext_sigsuspend.sa_flags = 0;
100 pthreadext_sigsuspend.sa_handler = (__sighandler_t)suspendSignalHandler;
101 status = sigemptyset(&pthreadext_sigsuspend.sa_mask);
102 AVER(status == 0);
103 status = sigaddset(&pthreadext_sigsuspend.sa_mask, PTHREADEXT_SIGRESUME);
104 AVER(status == 0);
105
106 pthreadext_sigresume.sa_flags = 0;
107 pthreadext_sigresume.sa_handler = resumeSignalHandler;
108 status = sigemptyset(&pthreadext_sigresume.sa_mask);
109 AVER(status == 0);
110
111 status = sigaction(PTHREADEXT_SIGSUSPEND, &pthreadext_sigsuspend, NULL);
112 AVER(status == 0);
113
114 status = sigaction(PTHREADEXT_SIGRESUME, &pthreadext_sigresume, NULL);
115 AVER(status == 0);
116
117 pthreadextModuleInitialized = TRUE;
118}
119
120
121/* PThreadextCheck -- check the consistency of a PThreadext structure */
122
123extern Bool PThreadextCheck(PThreadext pthreadext)
124{
125 int status;
126
127 status = pthread_mutex_lock(&pthreadextMut);
128 AVER(status == 0);
129
130 CHECKS(PThreadext, pthreadext);
131 /* can't check ID */
132 CHECKL(RingCheck(&pthreadext->threadRing));
133 CHECKL(RingCheck(&pthreadext->idRing));
134 if (pthreadext->suspendedScp == NULL) {
135 /* not suspended */
136 CHECKL(RingIsSingle(&pthreadext->threadRing));
137 CHECKL(RingIsSingle(&pthreadext->idRing));
138 } else {
139 /* suspended */
140 Ring node, next;
141 CHECKL(!RingIsSingle(&pthreadext->threadRing));
142 RING_FOR(node, &pthreadext->idRing, next) {
143 PThreadext pt = RING_ELT(PThreadext, idRing, node);
144 CHECKL(pt->id == pthreadext->id);
145 CHECKL(pt->suspendedScp == pthreadext->suspendedScp);
146 }
147 }
148 status = pthread_mutex_unlock(&pthreadextMut);
149 AVER(status == 0);
150
151 return TRUE;
152}
153
154
155/* PThreadextInit -- Initialize a pthreadext */
156
157extern void PThreadextInit(PThreadext pthreadext, pthread_t id)
158{
159 int status;
160
161 /* The first call to init will initialize the package. */
162 status = pthread_once(&pthreadextOnce, PThreadextModuleInit);
163 AVER(status == 0);
164
165 pthreadext->id = id;
166 pthreadext->suspendedScp = NULL;
167 RingInit(&pthreadext->threadRing);
168 RingInit(&pthreadext->idRing);
169 pthreadext->sig = PThreadextSig;
170 AVERT(PThreadext, pthreadext);
171}
172
173
174/* PThreadextFinish -- Finish a pthreadext
175 *
176 * See design.mps.pthreadext.impl.finish
177 */
178
179extern void PThreadextFinish(PThreadext pthreadext)
180{
181 int status;
182
183 AVERT(PThreadext, pthreadext);
184
185 status = pthread_mutex_lock(&pthreadextMut);
186 AVER(status == 0);
187
188 if(pthreadext->suspendedScp == NULL) {
189 AVER(RingIsSingle(&pthreadext->threadRing));
190 AVER(RingIsSingle(&pthreadext->idRing));
191 } else {
192 /* In suspended state: remove from rings. */
193 AVER(!RingIsSingle(&pthreadext->threadRing));
194 RingRemove(&pthreadext->threadRing);
195 if(!RingIsSingle(&pthreadext->idRing))
196 RingRemove(&pthreadext->idRing);
197 }
198
199 status = pthread_mutex_unlock(&pthreadextMut);
200 AVER(status == 0);
201
202 RingFinish(&pthreadext->threadRing);
203 RingFinish(&pthreadext->idRing);
204 pthreadext->sig = SigInvalid;
205}
206
207
208/* suspendSignalHandler -- signal handler called when suspending a thread
209 *
210 * See design.mps.pthreadext.impl.suspend-handler
211 *
212 * The interface for determining the sigcontext might be platform specific.
213 *
214 * Handle PTHREADEXT_SIGSUSPEND in the target thread, to suspend it until
215 * receiving PTHREADEXT_SIGRESUME (resume). Note that this is run with both
216 * PTHREADEXT_SIGSUSPEND and PTHREADEXT_SIGRESUME blocked. Having
217 * PTHREADEXT_SIGRESUME blocked prevents a resume before we can finish the
218 * suspend protocol.
219 */
220
221static void suspendSignalHandler(int sig, struct sigcontext scp)
222{
223 sigset_t signal_set;
224
225 AVER(sig == PTHREADEXT_SIGSUSPEND);
226 UNUSED(sig);
227
228 /* Tell caller about the sigcontext. */
229 AVER(suspendingVictim != NULL);
230 suspendingVictim->suspendedScp = &scp;
231
232 /* Block all signals except PTHREADEXT_SIGRESUME while suspended. */
233 sigfillset(&signal_set);
234 sigdelset(&signal_set, PTHREADEXT_SIGRESUME);
235 sem_post(&pthreadextSem);
236 sigsuspend(&signal_set);
237
238 /* Once here, the resume signal handler has run to completion. */
239 return;
240}
241
242
243/* resumeSignalHandler -- signal handler called when resuming a thread
244 *
245 * See design.mps.pthreadext.impl.suspend-handler
246 */
247
248static void resumeSignalHandler(int sig)
249{
250 AVER(sig == PTHREADEXT_SIGRESUME);
251 UNUSED(sig);
252 return;
253}
254
255
256/* PThreadextSuspend -- suspend a thread
257 *
258 * See design.mps.pthreadext.impl.suspend
259 */
260
261Res PThreadextSuspend(PThreadext target, struct sigcontext **contextReturn)
262{
263 Ring node, next;
264 Res res;
265 int status;
266
267 AVERT(PThreadext, target);
268 AVER(contextReturn != NULL);
269 AVER(target->suspendedScp == NULL); /* multiple suspends illegal */
270
271 /* Serialize access to suspend, makes life easier */
272 status = pthread_mutex_lock(&pthreadextMut);
273 AVER(status == 0);
274 AVER(suspendingVictim == NULL);
275
276 /* Threads are added to the suspended ring on suspension */
277 /* If the same thread Id has already been suspended, then */
278 /* don't signal the thread, just add the target onto the id ring */
279 RING_FOR(node, &suspendedRing, next) {
280 PThreadext alreadySusp = RING_ELT(PThreadext, threadRing, node);
281 if (alreadySusp->id == target->id) {
282 RingAppend(&alreadySusp->idRing, &target->idRing);
283 target->suspendedScp = alreadySusp->suspendedScp;
284 goto noteSuspended;
285 }
286 }
287
288 /* Ok, we really need to suspend this thread. */
289 suspendingVictim = target;
290 status = pthread_kill(target->id, PTHREADEXT_SIGSUSPEND);
291 if (status != 0) {
292 res = ResFAIL;
293 goto unlock;
294 }
295
296 /* Wait for the victim to acknowledge suspension. */
297 while (sem_wait(&pthreadextSem) != 0) {
298 if (errno != EINTR) {
299 res = ResFAIL;
300 goto unlock;
301 }
302 }
303
304noteSuspended:
305 AVER(target->suspendedScp != NULL);
306 RingAppend(&suspendedRing, &target->threadRing);
307 *contextReturn = target->suspendedScp;
308 res = ResOK;
309
310unlock:
311 suspendingVictim = NULL;
312 status = pthread_mutex_unlock(&pthreadextMut);
313 AVER(status == 0);
314 return res;
315}
316
317
318/* PThreadextResume -- resume a suspended thread
319 *
320 * See design.mps.pthreadext.impl.resume
321 */
322
323Res PThreadextResume(PThreadext target)
324{
325 Res res;
326 int status;
327
328 AVERT(PThreadext, target);
329 AVER(pthreadextModuleInitialized); /* must have been a prior suspend */
330 AVER(target->suspendedScp != NULL);
331
332 /* Serialize access to suspend, makes life easier. */
333 status = pthread_mutex_lock(&pthreadextMut);
334 AVER(status == 0);
335
336 if (RingIsSingle(&target->idRing)) {
337 /* Really want to resume the thread. Signal it to continue. */
338 status = pthread_kill(target->id, PTHREADEXT_SIGRESUME);
339 if (status == 0) {
340 goto noteResumed;
341 } else {
342 res = ResFAIL;
343 goto unlock;
344 }
345
346 } else {
347 /* Leave thread suspended on behalf of another PThreadext. */
348 /* Remove it from the id ring */
349 RingRemove(&target->idRing);
350 goto noteResumed;
351 }
352
353noteResumed:
354 /* Remove the thread from the suspended ring */
355 RingRemove(&target->threadRing);
356 target->suspendedScp = NULL;
357 res = ResOK;
358
359unlock:
360 status = pthread_mutex_unlock(&pthreadextMut);
361 AVER(status == 0);
362 return res;
363}