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