static Coroutine *coroutine_new(void) { const size_t stack_size = 1 << 20; CoroutineUContext *co; CoroutineThreadState *coTS; struct sigaction sa; struct sigaction osa; struct sigaltstack ss; struct sigaltstack oss; sigset_t sigs; sigset_t osigs; jmp_buf old_env; /* The way to manipulate stack is with the sigaltstack function. We * prepare a stack, with it delivering a signal to ourselves and then * put setjmp/longjmp where needed. * This has been done keeping coroutine-ucontext as a model and with the * pth ideas (GNU Portable Threads). See coroutine-ucontext for the basics * of the coroutines and see pth_mctx.c (from the pth project) for the * sigaltstack way of manipulating stacks. */ co = g_malloc0(sizeof(*co)); co->stack = g_malloc(stack_size); co->base.entry_arg = &old_env; /* stash away our jmp_buf */ coTS = coroutine_get_thread_state(); coTS->tr_handler = co; /* * Preserve the SIGUSR2 signal state, block SIGUSR2, * and establish our signal handler. The signal will * later transfer control onto the signal stack. */ sigemptyset(&sigs); sigaddset(&sigs, SIGUSR2); pthread_sigmask(SIG_BLOCK, &sigs, &osigs); sa.sa_handler = coroutine_trampoline; sigfillset(&sa.sa_mask); sa.sa_flags = SA_ONSTACK; if (sigaction(SIGUSR2, &sa, &osa) != 0) { abort(); } /* * Set the new stack. */ ss.ss_sp = co->stack; ss.ss_size = stack_size; ss.ss_flags = 0; if (sigaltstack(&ss, &oss) < 0) { abort(); } /* * Now transfer control onto the signal stack and set it up. * It will return immediately via "return" after the setjmp() * was performed. Be careful here with race conditions. The * signal can be delivered the first time sigsuspend() is * called. */ coTS->tr_called = 0; kill(getpid(), SIGUSR2); sigfillset(&sigs); sigdelset(&sigs, SIGUSR2); while (!coTS->tr_called) { sigsuspend(&sigs); } /* * Inform the system that we are back off the signal stack by * removing the alternative signal stack. Be careful here: It * first has to be disabled, before it can be removed. */ sigaltstack(NULL, &ss); ss.ss_flags = SS_DISABLE; if (sigaltstack(&ss, NULL) < 0) { abort(); } sigaltstack(NULL, &ss); if (!(oss.ss_flags & SS_DISABLE)) { sigaltstack(&oss, NULL); } /* * Restore the old SIGUSR2 signal handler and mask */ sigaction(SIGUSR2, &osa, NULL); pthread_sigmask(SIG_SETMASK, &osigs, NULL); /* * Now enter the trampoline again, but this time not as a signal * handler. Instead we jump into it directly. The functionally * redundant ping-pong pointer arithmentic is neccessary to avoid * type-conversion warnings related to the `volatile' qualifier and * the fact that `jmp_buf' usually is an array type. */ if (!setjmp(old_env)) { longjmp(coTS->tr_reenter, 1); } /* * Ok, we returned again, so now we're finished */ return &co->base; }