1 /*
2 * PCL by Davide Libenzi (Portable Coroutine Library)
3 * Copyright (C) 2003..2010 Davide Libenzi
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * Davide Libenzi <davidel@xmailserver.org>
20 *
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include "pcl_config.h"
28 #include "pcl.h"
29 #include "pcl_private.h"
30
31 #if defined(CO_USE_SIGCONTEXT)
32 #include <signal.h>
33 #endif
34
35 #if defined(CO_USE_SIGCONTEXT)
36
37 /*
38 * Multi thread and CO_USE_SIGCONTEXT are a NO GO!
39 */
40 #if defined(CO_MULTI_THREAD)
41 #error "Sorry, it is not possible to use MT libpcl with CO_USE_SIGCONTEXT"
42 #endif
43
44 static volatile int ctx_called;
45 static co_ctx_t *ctx_creating;
46 static void *ctx_creating_func;
47 static sigset_t ctx_creating_sigs;
48 static co_ctx_t ctx_trampoline;
49 static co_ctx_t ctx_caller;
50
51 #endif /* #if defined(CO_USE_SIGCONTEXT) */
52
53
54 #if defined(CO_HAS_SIGSTACK)
co_ctx_sdir(unsigned long psp)55 static int co_ctx_sdir(unsigned long psp)
56 {
57 int nav = 0;
58 unsigned long csp = (unsigned long) &nav;
59
60 return psp > csp ? -1: +1;
61 }
62
co_ctx_stackdir(void)63 static int co_ctx_stackdir(void)
64 {
65 int cav = 0;
66
67 return co_ctx_sdir((unsigned long) &cav);
68 }
69 #endif
70
71 #if defined(CO_USE_UCONEXT)
72
co_set_context(co_ctx_t * ctx,void * func,char * stkbase,long stksiz)73 static int co_set_context(co_ctx_t *ctx, void *func, char *stkbase, long stksiz)
74 {
75 if (getcontext(&ctx->cc))
76 return -1;
77
78 ctx->cc.uc_link = NULL;
79
80 ctx->cc.uc_stack.ss_sp = stkbase;
81 ctx->cc.uc_stack.ss_size = stksiz - sizeof(long);
82 ctx->cc.uc_stack.ss_flags = 0;
83
84 makecontext(&ctx->cc, func, 1);
85
86 return 0;
87 }
88
co_switch_context(co_ctx_t * octx,co_ctx_t * nctx)89 static void co_switch_context(co_ctx_t *octx, co_ctx_t *nctx)
90 {
91 cothread_ctx *tctx = co_get_thread_ctx();
92
93 if (swapcontext(&octx->cc, &nctx->cc) < 0) {
94 fprintf(stderr, "[PCL] Context switch failed: curr=%p\n",
95 tctx->co_curr);
96 exit(1);
97 }
98 }
99
100 #else /* #if defined(CO_USE_UCONEXT) */
101
102 #if defined(CO_USE_SIGCONTEXT)
103
104 /*
105 * This code comes from the GNU Pth implementation and uses the
106 * sigstack/sigaltstack() trick.
107 *
108 * The ingenious fact is that this variant runs really on _all_ POSIX
109 * compliant systems without special platform kludges. But be _VERY_
110 * carefully when you change something in the following code. The slightest
111 * change or reordering can lead to horribly broken code. Really every
112 * function call in the following case is intended to be how it is, doubt
113 * me...
114 *
115 * For more details we strongly recommend you to read the companion
116 * paper ``Portable Multithreading -- The Signal Stack Trick for
117 * User-Space Thread Creation'' from Ralf S. Engelschall.
118 */
co_ctx_bootstrap(void)119 static void co_ctx_bootstrap(void)
120 {
121 cothread_ctx *tctx = co_get_thread_ctx();
122 co_ctx_t * volatile ctx_starting;
123 void (* volatile ctx_starting_func)(void);
124
125 /*
126 * Switch to the final signal mask (inherited from parent)
127 */
128 sigprocmask(SIG_SETMASK, &ctx_creating_sigs, NULL);
129
130 /*
131 * Move startup details from static storage to local auto
132 * variables which is necessary because it has to survive in
133 * a local context until the thread is scheduled for real.
134 */
135 ctx_starting = ctx_creating;
136 ctx_starting_func = (void (*)(void)) ctx_creating_func;
137
138 /*
139 * Save current machine state (on new stack) and
140 * go back to caller until we're scheduled for real...
141 */
142 if (!setjmp(ctx_starting->cc))
143 longjmp(ctx_caller.cc, 1);
144
145 /*
146 * The new thread is now running: GREAT!
147 * Now we just invoke its init function....
148 */
149 ctx_starting_func();
150
151 fprintf(stderr, "[PCL] Hmm, you really shouldn't reach this point: curr=%p\n",
152 tctx->co_curr);
153 exit(1);
154 }
155
co_ctx_trampoline(int sig)156 static void co_ctx_trampoline(int sig)
157 {
158 /*
159 * Save current machine state and _immediately_ go back with
160 * a standard "return" (to stop the signal handler situation)
161 * to let him remove the stack again. Notice that we really
162 * have do a normal "return" here, or the OS would consider
163 * the thread to be running on a signal stack which isn't
164 * good (for instance it wouldn't allow us to spawn a thread
165 * from within a thread, etc.)
166 */
167 if (setjmp(ctx_trampoline.cc) == 0) {
168 ctx_called = 1;
169 return;
170 }
171
172 /*
173 * Ok, the caller has longjmp'ed back to us, so now prepare
174 * us for the real machine state switching. We have to jump
175 * into another function here to get a new stack context for
176 * the auto variables (which have to be auto-variables
177 * because the start of the thread happens later).
178 */
179 co_ctx_bootstrap();
180 }
181
co_set_context(co_ctx_t * ctx,void * func,char * stkbase,long stksiz)182 static int co_set_context(co_ctx_t *ctx, void *func, char *stkbase, long stksiz)
183 {
184 struct sigaction sa;
185 struct sigaction osa;
186 sigset_t osigs;
187 sigset_t sigs;
188 #if defined(CO_HAS_SIGSTACK)
189 struct sigstack ss;
190 struct sigstack oss;
191 #elif defined(CO_HAS_SIGALTSTACK)
192 struct sigaltstack ss;
193 struct sigaltstack oss;
194 #else
195 #error "PCL: Unknown context stack type"
196 #endif
197
198 /*
199 * Preserve the SIGUSR1 signal state, block SIGUSR1,
200 * and establish our signal handler. The signal will
201 * later transfer control onto the signal stack.
202 */
203 sigemptyset(&sigs);
204 sigaddset(&sigs, SIGUSR1);
205 sigprocmask(SIG_BLOCK, &sigs, &osigs);
206 sa.sa_handler = co_ctx_trampoline;
207 sigemptyset(&sa.sa_mask);
208 sa.sa_flags = SA_ONSTACK;
209 if (sigaction(SIGUSR1, &sa, &osa) != 0)
210 return -1;
211
212 /*
213 * Set the new stack.
214 *
215 * For sigaltstack we're lucky [from sigaltstack(2) on
216 * FreeBSD 3.1]: ``Signal stacks are automatically adjusted
217 * for the direction of stack growth and alignment
218 * requirements''
219 *
220 * For sigstack we have to decide ourself [from sigstack(2)
221 * on Solaris 2.6]: ``The direction of stack growth is not
222 * indicated in the historical definition of struct sigstack.
223 * The only way to portably establish a stack pointer is for
224 * the application to determine stack growth direction.''
225 */
226 #if defined(CO_HAS_SIGALTSTACK)
227 ss.ss_sp = stkbase;
228 ss.ss_size = stksiz - sizeof(long);
229 ss.ss_flags = 0;
230 if (sigaltstack(&ss, &oss) < 0)
231 return -1;
232 #elif defined(CO_HAS_SIGSTACK)
233 if (co_ctx_stackdir() < 0)
234 ss.ss_sp = (stkbase + stksiz - sizeof(long));
235 else
236 ss.ss_sp = stkbase;
237 ss.ss_onstack = 0;
238 if (sigstack(&ss, &oss) < 0)
239 return -1;
240 #else
241 #error "PCL: Unknown context stack type"
242 #endif
243
244 /*
245 * Now transfer control onto the signal stack and set it up.
246 * It will return immediately via "return" after the setjmp()
247 * was performed. Be careful here with race conditions. The
248 * signal can be delivered the first time sigsuspend() is
249 * called.
250 */
251 ctx_called = 0;
252 kill(getpid(), SIGUSR1);
253 sigfillset(&sigs);
254 sigdelset(&sigs, SIGUSR1);
255 while (!ctx_called)
256 sigsuspend(&sigs);
257
258 /*
259 * Inform the system that we are back off the signal stack by
260 * removing the alternative signal stack. Be careful here: It
261 * first has to be disabled, before it can be removed.
262 */
263 #if defined(CO_HAS_SIGALTSTACK)
264 sigaltstack(NULL, &ss);
265 ss.ss_flags = SS_DISABLE;
266 if (sigaltstack(&ss, NULL) < 0)
267 return -1;
268 sigaltstack(NULL, &ss);
269 if (!(ss.ss_flags & SS_DISABLE))
270 return -1;
271 if (!(oss.ss_flags & SS_DISABLE))
272 sigaltstack(&oss, NULL);
273 #elif defined(CO_HAS_SIGSTACK)
274 if (sigstack(&oss, NULL))
275 return -1;
276 #else
277 #error "PCL: Unknown context stack type"
278 #endif
279
280 /*
281 * Restore the old SIGUSR1 signal handler and mask
282 */
283 sigaction(SIGUSR1, &osa, NULL);
284 sigprocmask(SIG_SETMASK, &osigs, NULL);
285
286 /*
287 * Set creation information.
288 */
289 ctx_creating = ctx;
290 ctx_creating_func = func;
291 memcpy(&ctx_creating_sigs, &osigs, sizeof(sigset_t));
292
293 /*
294 * Now enter the trampoline again, but this time not as a signal
295 * handler. Instead we jump into it directly.
296 */
297 if (setjmp(ctx_caller.cc) == 0)
298 longjmp(ctx_trampoline.cc, 1);
299
300 return 0;
301 }
302
303 #else /* #if defined(CO_USE_SIGCONTEXT) */
304
co_set_context(co_ctx_t * ctx,void * func,char * stkbase,long stksiz)305 static int co_set_context(co_ctx_t *ctx, void *func, char *stkbase, long stksiz)
306 {
307 char *stack;
308
309 stack = stkbase + stksiz - sizeof(long);
310
311 setjmp(ctx->cc);
312
313 #if defined(__GLIBC__) && defined(__GLIBC_MINOR__) \
314 && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined(JB_PC) && defined(JB_SP)
315 ctx->cc[0].__jmpbuf[JB_PC] = (int) func;
316 ctx->cc[0].__jmpbuf[JB_SP] = (int) stack;
317 #elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) \
318 && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined(__mc68000__)
319 ctx->cc[0].__jmpbuf[0].__aregs[0] = (long) func;
320 ctx->cc[0].__jmpbuf[0].__sp = (int *) stack;
321 #elif defined(__GNU_LIBRARY__) && defined(__i386__)
322 ctx->cc[0].__jmpbuf[0].__pc = func;
323 ctx->cc[0].__jmpbuf[0].__sp = stack;
324 #elif defined(__MINGW32__)
325 ctx->cc[5] = (long) func;
326 ctx->cc[4] = (long) stack;
327 #elif defined(__APPLE__)
328 /* START Apple */
329 #if defined(__x86_64__)
330 *(long *) ((char *) &ctx->cc + 56) = (long) func;
331 *(long *) ((char *) &ctx->cc + 16) = (long) stack;
332 #elif defined(__i386__)
333 *(long *) ((char *) &ctx->cc + 48) = (long) func;
334 *(long *) ((char *) &ctx->cc + 36) = (long) stack;
335 #elif defined(__arm__)
336 *(long *) ((char *) &ctx->cc + 32) = (long) func;
337 *(long *) ((char *) &ctx->cc + 28) = (long) stack;
338 #else
339 #error "PCL: Unsupported setjmp/longjmp OSX CPU. Please report to <davidel@xmailserver.org>"
340 #endif
341 /* END Apple */
342 #elif defined(_WIN32) && defined(_MSC_VER)
343 /* START Windows */
344 #if defined(_M_IX86)
345 ((_JUMP_BUFFER *) &ctx->cc)->Eip = (long) func;
346 ((_JUMP_BUFFER *) &ctx->cc)->Esp = (long) stack;
347 #elif defined(_M_AMD64)
348 ((_JUMP_BUFFER *) &ctx->cc)->Rip = (long) func;
349 ((_JUMP_BUFFER *) &ctx->cc)->Rsp = (long) stack;
350 #else
351 #error "PCL: Unsupported setjmp/longjmp Windows CPU. Please report to <davidel@xmailserver.org>"
352 #endif
353 /* END Windows */
354 #elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) \
355 && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && (defined(__powerpc64__) || defined(__powerpc__))
356 ctx->cc[0].__jmpbuf[JB_LR] = (int) func;
357 ctx->cc[0].__jmpbuf[JB_GPR1] = (int) stack;
358 #else
359 #error "PCL: Unsupported setjmp/longjmp platform. Please report to <davidel@xmailserver.org>"
360 #endif
361
362 return 0;
363 }
364
365 #endif /* #if defined(CO_USE_SIGCONTEXT) */
366
co_switch_context(co_ctx_t * octx,co_ctx_t * nctx)367 static void co_switch_context(co_ctx_t *octx, co_ctx_t *nctx)
368 {
369 if (setjmp(octx->cc) == 0)
370 longjmp(nctx->cc, 1);
371 }
372
373 #endif /* #if defined(CO_USE_UCONEXT) */
374
co_runner(void)375 static void co_runner(void)
376 {
377 cothread_ctx *tctx = co_get_thread_ctx();
378 coroutine *co = tctx->co_curr;
379
380 co->restarget = co->caller;
381 co->func(co->data);
382 co_exit();
383 }
384
co_create(void (* func)(void *),void * data,void * stack,int size)385 coroutine_t co_create(void (*func)(void *), void *data, void *stack, int size)
386 {
387 int alloc = 0;
388 coroutine *co;
389
390 if ((size &= ~(sizeof(long) - 1)) < CO_MIN_SIZE)
391 return NULL;
392 if (stack == NULL) {
393 size = (size + sizeof(coroutine) + CO_STK_ALIGN - 1) & ~(CO_STK_ALIGN - 1);
394 stack = malloc(size);
395 if (stack == NULL)
396 return NULL;
397 alloc = size;
398 }
399 co = stack;
400 stack = (char *) stack + CO_STK_COROSIZE;
401 co->alloc = alloc;
402 co->func = func;
403 co->data = data;
404 if (co_set_context(&co->ctx, co_runner, stack, size - CO_STK_COROSIZE) < 0) {
405 if (alloc)
406 free(co);
407 return NULL;
408 }
409
410 return (coroutine_t) co;
411 }
412
co_delete(coroutine_t coro)413 void co_delete(coroutine_t coro)
414 {
415 cothread_ctx *tctx = co_get_thread_ctx();
416 coroutine *co = (coroutine *) coro;
417
418 if (co == tctx->co_curr) {
419 fprintf(stderr, "[PCL] Cannot delete itself: curr=%p\n",
420 tctx->co_curr);
421 exit(1);
422 }
423 if (co->alloc)
424 free(co);
425 }
426
co_call(coroutine_t coro)427 void co_call(coroutine_t coro)
428 {
429 cothread_ctx *tctx = co_get_thread_ctx();
430 coroutine *co = (coroutine *) coro, *oldco = tctx->co_curr;
431
432 co->caller = tctx->co_curr;
433 tctx->co_curr = co;
434
435 co_switch_context(&oldco->ctx, &co->ctx);
436 }
437
co_resume(void)438 void co_resume(void)
439 {
440 cothread_ctx *tctx = co_get_thread_ctx();
441
442 co_call(tctx->co_curr->restarget);
443 tctx->co_curr->restarget = tctx->co_curr->caller;
444 }
445
co_del_helper(void * data)446 static void co_del_helper(void *data)
447 {
448 cothread_ctx *tctx;
449 coroutine *cdh;
450
451 for (;;) {
452 tctx = co_get_thread_ctx();
453 cdh = tctx->co_dhelper;
454 tctx->co_dhelper = NULL;
455 co_delete(tctx->co_curr->caller);
456 co_call((coroutine_t) cdh);
457 if (tctx->co_dhelper == NULL) {
458 fprintf(stderr,
459 "[PCL] Resume to delete helper coroutine: curr=%p caller=%p\n",
460 tctx->co_curr, tctx->co_curr->caller);
461 exit(1);
462 }
463 }
464 }
465
co_exit_to(coroutine_t coro)466 void co_exit_to(coroutine_t coro)
467 {
468 cothread_ctx *tctx = co_get_thread_ctx();
469 coroutine *co = (coroutine *) coro;
470
471 if (tctx->dchelper == NULL &&
472 (tctx->dchelper = co_create(co_del_helper, NULL,
473 tctx->stk, sizeof(tctx->stk))) == NULL) {
474 fprintf(stderr, "[PCL] Unable to create delete helper coroutine: curr=%p\n",
475 tctx->co_curr);
476 exit(1);
477 }
478 tctx->co_dhelper = co;
479
480 co_call((coroutine_t) tctx->dchelper);
481
482 fprintf(stderr, "[PCL] Stale coroutine called: curr=%p exitto=%p caller=%p\n",
483 tctx->co_curr, co, tctx->co_curr->caller);
484 exit(1);
485 }
486
co_exit(void)487 void co_exit(void)
488 {
489 cothread_ctx *tctx = co_get_thread_ctx();
490
491 co_exit_to((coroutine_t) tctx->co_curr->restarget);
492 }
493
co_current(void)494 coroutine_t co_current(void)
495 {
496 cothread_ctx *tctx = co_get_thread_ctx();
497
498 return (coroutine_t) tctx->co_curr;
499 }
500
co_get_data(coroutine_t coro)501 void *co_get_data(coroutine_t coro)
502 {
503 coroutine *co = (coroutine *) coro;
504
505 return co->data;
506 }
507
co_set_data(coroutine_t coro,void * data)508 void *co_set_data(coroutine_t coro, void *data)
509 {
510 coroutine *co = (coroutine *) coro;
511 void *odata;
512
513 odata = co->data;
514 co->data = data;
515
516 return odata;
517 }
518
519