Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Use libcoro to get away from ucontext for Lion

ucontext in Lion is straight up buggy. These functions have been marked
as deprecated for a while so it's not totally surprising, but still
ridic. libcoro has implemented a very similar API using setjmp and
longjmp internally (among other configurable backends). Switching over
to setjmp\longjmp, along with the pthread* hook changes fixes fiber
support in Lion.

Closes gh-28
  • Loading branch information...
commit f144c861d4fd0df350091f36cd09800adfd42b00 1 parent 23127dc
@laverdet authored
View
1  .gitignore
@@ -1,5 +1,6 @@
*.dylib
*.dSYM
+*.o
*.so
*.node
*.swp
View
12 src/Makefile
@@ -9,17 +9,21 @@ ifeq ($(NODE_PLATFORM), darwin)
CPP_NODEFLAGS = -bundle -undefined dynamic_lookup
endif
COROUTINE_SO_FULL := $(shell echo `pwd`/$(COROUTINE_SO))
+CPPFLAGS += -DCORO_SJLJ
all: $(COROUTINE_SO) fibers.node
+libcoro.o: libcoro/coro.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -fPIC -c -o $@ $^
+
$(COROUTINE_SO_FULL): $(COROUTINE_SO)
-$(COROUTINE_SO): coroutine.cc
- $(CXX) $(CPPFLAGS) $(CPP_DYFLAGS) -o $@ $^ -lpthread -ldl
+$(COROUTINE_SO): coroutine.cc libcoro.o
+ $(CXX) $(CPP_DYFLAGS) $(CXXFLAGS) $(CPPFLAGS) -o $@ $^ -lpthread -ldl
fibers.node: fibers.cc $(COROUTINE_SO_FULL)
- $(CXX) $(CPPFLAGS) $(CPP_NODEFLAGS) -o $@ $^
+ $(CXX) $(CPP_NODEFLAGS) $(CPPFLAGS) -o $@ $^
clean:
- -$(RM) fibers.node $(COROUTINE_SO)
+ -$(RM) fibers.node libcoro.o $(COROUTINE_SO)
-$(RM) -r *.dSYM
View
21 src/coroutine.cc
@@ -3,7 +3,6 @@
#define __GNU_SOURCE
#include <dlfcn.h>
#include <pthread.h>
-#include <ucontext.h>
#include <stdexcept>
#include <stack>
@@ -176,9 +175,9 @@ vector<pthread_dtor_t> Thread::dtors;
* Coroutine class definition
*/
size_t Coroutine::stack_size = 0;
-void Coroutine::trampoline(Coroutine &that) {
+void Coroutine::trampoline(void* that) {
while (true) {
- that.entry(const_cast<void*>(that.arg));
+ static_cast<Coroutine*>(that)->entry(const_cast<void*>(static_cast<Coroutine*>(that)->arg));
}
}
@@ -202,10 +201,11 @@ Coroutine::Coroutine(Thread& t, entry_t& entry, void* arg) :
stack(stack_size),
entry(entry),
arg(arg) {
- getcontext(&context);
- context.uc_stack.ss_size = stack_size;
- context.uc_stack.ss_sp = &stack[0];
- makecontext(&context, (void(*)(void))trampoline, 1, this);
+ coro_create(&context, trampoline, this, &stack[0], stack_size);
+}
+
+Coroutine::~Coroutine() {
+ coro_destroy(&context);
}
Coroutine& Coroutine::create_fiber(entry_t* entry, void* arg) {
@@ -228,7 +228,7 @@ void Coroutine::run() {
assert(&current != this);
thread.current_fiber = this;
- swapcontext(&current.context, &context);
+ coro_transfer(&current.context, &context);
if (thread.delete_me) {
// This means finish() was called on the coroutine and the pool was full so this coroutine needs
@@ -246,7 +246,7 @@ void Coroutine::finish(Coroutine& next) {
assert(thread.current_fiber == this);
thread.fiber_did_finish(*this);
thread.current_fiber = &next;
- swapcontext(&context, &next.context);
+ coro_transfer(&context, &next.context);
}
void* Coroutine::bottom() const {
@@ -423,8 +423,9 @@ int pthread_equal(pthread_t left, pthread_t right) {
}
#endif
-int pthread_join(pthread_t thread, void** retval) {
+int pthread_join(void* thread, void** retval) {
assert(Loader::initialized);
+ cout <<(void*)thread <<"\n";
// pthread_join should return EDEADLK if you try to join with yourself..
return pthread_join(reinterpret_cast<Thread*>(thread)->handle, retval);
}
View
12 src/coroutine.h
@@ -1,11 +1,7 @@
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE
-#endif
-
#include <stdlib.h>
-#include <ucontext.h>
#include <ext/pool_allocator.h>
#include <vector>
+#include "libcoro/coro.h"
class Coroutine {
public:
@@ -17,15 +13,15 @@ class Coroutine {
// around that, as there is no constructor.
struct char_noinit { char x; };
class Thread& thread;
- ucontext_t context;
+ coro_context context;
std::vector<char_noinit, __gnu_cxx::__pool_alloc<char_noinit> > stack;
std::vector<void*> fls_data;
entry_t* entry;
void* arg;
static size_t stack_size;
- static void trampoline(Coroutine& that);
- ~Coroutine() {}
+ static void trampoline(void* that);
+ ~Coroutine();
/**
* Constructor for currently running "fiber". This is really just original thread, but we
View
26 src/libcoro/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2000-2009 Marc Alexander Lehmann <schmorp@schmorp.de>
+
+Redistribution and use in source and binary forms, with or without modifica-
+tion, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
+CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
+CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
+ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Alternatively, the following files carry an additional notice that
+explicitly allows relicensing under the GPLv2: coro.c, coro.h.
+
View
6 src/libcoro/README
@@ -0,0 +1,6 @@
+Configuration, documentation etc. is provided in the coro.h file. Please
+note that the file conftest.c in this distribution is under the GPL. It is
+not needed for proper operation of this library though, for that, coro.h
+and coro.c suffice.
+
+Marc Lehmann <schmorp@schmorp.de>
View
154 src/libcoro/conftest.c
@@ -0,0 +1,154 @@
+/*
+ * This file was taken from pth-1.40/aclocal.m4
+ * The original copyright is below.
+ *
+ * GNU Pth - The GNU Portable Threads
+ * Copyright (c) 1999-2001 Ralf S. Engelschall <rse@engelschall.com>
+ *
+ * This file is part of GNU Pth, a non-preemptive thread scheduling
+ * library which can be found at http://www.gnu.org/software/pth/.
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA, or contact Marc Lehmann <schmorp@schmorp.de>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(TEST_sigstack) || defined(TEST_sigaltstack)
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+#endif
+#if defined(TEST_makecontext)
+#include <ucontext.h>
+#endif
+union alltypes {
+ long l;
+ double d;
+ void *vp;
+ void (*fp)(void);
+ char *cp;
+};
+static volatile char *handler_addr = (char *)0xDEAD;
+#if defined(TEST_sigstack) || defined(TEST_sigaltstack)
+static volatile int handler_done = 0;
+void handler(int sig)
+{
+ char garbage[1024];
+ int i;
+ auto int dummy;
+ for (i = 0; i < 1024; i++)
+ garbage[i] = 'X';
+ handler_addr = (char *)&dummy;
+ handler_done = 1;
+ return;
+}
+#endif
+#if defined(TEST_makecontext)
+static ucontext_t uc_handler;
+static ucontext_t uc_main;
+void handler(void)
+{
+ char garbage[1024];
+ int i;
+ auto int dummy;
+ for (i = 0; i < 1024; i++)
+ garbage[i] = 'X';
+ handler_addr = (char *)&dummy;
+ swapcontext(&uc_handler, &uc_main);
+ return;
+}
+#endif
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ char *skaddr;
+ char *skbuf;
+ int sksize;
+ char result[1024];
+ int i;
+ sksize = 32768;
+ skbuf = (char *)malloc(sksize*2+2*sizeof(union alltypes));
+ if (skbuf == NULL)
+ exit(1);
+ for (i = 0; i < sksize*2+2*sizeof(union alltypes); i++)
+ skbuf[i] = 'A';
+ skaddr = skbuf+sizeof(union alltypes);
+#if defined(TEST_sigstack) || defined(TEST_sigaltstack)
+ {
+ struct sigaction sa;
+#if defined(TEST_sigstack)
+ struct sigstack ss;
+#elif defined(TEST_sigaltstack) && defined(HAVE_STACK_T)
+ stack_t ss;
+#else
+ struct sigaltstack ss;
+#endif
+#if defined(TEST_sigstack)
+ ss.ss_sp = (void *)(skaddr + sksize);
+ ss.ss_onstack = 0;
+ if (sigstack(&ss, NULL) < 0)
+ exit(1);
+#elif defined(TEST_sigaltstack)
+ ss.ss_sp = (void *)(skaddr + sksize);
+ ss.ss_size = sksize;
+ ss.ss_flags = 0;
+ if (sigaltstack(&ss, NULL) < 0)
+ exit(1);
+#endif
+ memset((void *)&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = handler;
+ sa.sa_flags = SA_ONSTACK;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGUSR1, &sa, NULL);
+ kill(getpid(), SIGUSR1);
+ while (!handler_done)
+ /*nop*/;
+ }
+#endif
+#if defined(TEST_makecontext)
+ {
+ if (getcontext(&uc_handler) != 0)
+ exit(1);
+ uc_handler.uc_link = NULL;
+ uc_handler.uc_stack.ss_sp = (void *)(skaddr + sksize);
+ uc_handler.uc_stack.ss_size = sksize;
+ uc_handler.uc_stack.ss_flags = 0;
+ makecontext(&uc_handler, handler, 1);
+ swapcontext(&uc_main, &uc_handler);
+ }
+#endif
+ if (handler_addr == (char *)0xDEAD)
+ exit(1);
+ if (handler_addr < skaddr+sksize) {
+ /* stack was placed into lower area */
+ if (*(skaddr+sksize) != 'A')
+ sprintf(result, "(skaddr)+(sksize)-%d,(sksize)-%d",
+ sizeof(union alltypes), sizeof(union alltypes));
+ else
+ strcpy(result, "(skaddr)+(sksize),(sksize)");
+ }
+ else {
+ /* stack was placed into higher area */
+ if (*(skaddr+sksize*2) != 'A')
+ sprintf(result, "(skaddr),(sksize)-%d", sizeof(union alltypes));
+ else
+ strcpy(result, "(skaddr),(sksize)");
+ }
+ printf("%s\n", result);
+ exit(0);
+}
+
View
470 src/libcoro/coro.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (c) 2001-2011 Marc Alexander Lehmann <schmorp@schmorp.de>
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
+ * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
+ * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
+ * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License ("GPL") version 2 or any later version,
+ * in which case the provisions of the GPL are applicable instead of
+ * the above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the BSD license, indicate your decision
+ * by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file under
+ * either the BSD or the GPL.
+ *
+ * This library is modelled strictly after Ralf S. Engelschalls article at
+ * http://www.gnu.org/software/pth/rse-pmt.ps. So most of the credit must
+ * go to Ralf S. Engelschall <rse@engelschall.com>.
+ */
+
+#include "coro.h"
+
+#include <string.h>
+
+/*****************************************************************************/
+/* ucontext/setjmp/asm backends */
+/*****************************************************************************/
+#if CORO_UCONTEXT || CORO_SJLJ || CORO_LOSER || CORO_LINUX || CORO_IRIX || CORO_ASM
+
+# if CORO_UCONTEXT
+# include <stddef.h>
+# endif
+
+# if !defined(STACK_ADJUST_PTR)
+# if __sgi
+/* IRIX is decidedly NON-unix */
+# define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss) - 8)
+# define STACK_ADJUST_SIZE(sp,ss) ((ss) - 8)
+# elif (__i386__ && CORO_LINUX) || (_M_IX86 && CORO_LOSER)
+# define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss))
+# define STACK_ADJUST_SIZE(sp,ss) (ss)
+# elif (__amd64__ && CORO_LINUX) || ((_M_AMD64 || _M_IA64) && CORO_LOSER)
+# define STACK_ADJUST_PTR(sp,ss) ((char *)(sp) + (ss) - 8)
+# define STACK_ADJUST_SIZE(sp,ss) (ss)
+# else
+# define STACK_ADJUST_PTR(sp,ss) (sp)
+# define STACK_ADJUST_SIZE(sp,ss) (ss)
+# endif
+# endif
+
+# include <stdlib.h>
+
+# if CORO_SJLJ
+# include <stdio.h>
+# include <signal.h>
+# include <unistd.h>
+# endif
+
+static coro_func coro_init_func;
+static void *coro_init_arg;
+static coro_context *new_coro, *create_coro;
+
+static void
+coro_init (void)
+{
+ volatile coro_func func = coro_init_func;
+ volatile void *arg = coro_init_arg;
+
+ coro_transfer (new_coro, create_coro);
+
+#if __GCC_HAVE_DWARF2_CFI_ASM && __amd64
+ asm (".cfi_undefined rip");
+#endif
+
+ func ((void *)arg);
+
+ /* the new coro returned. bad. just abort() for now */
+ abort ();
+}
+
+# if CORO_SJLJ
+
+static volatile int trampoline_done;
+
+/* trampoline signal handler */
+static void
+trampoline (int sig)
+{
+ if (coro_setjmp (new_coro->env))
+ coro_init (); /* start it */
+ else
+ trampoline_done = 1;
+}
+
+# endif
+
+# if CORO_ASM
+
+ #if _WIN32
+ #define CORO_WIN_TIB 1
+ #endif
+
+ asm (
+ "\t.text\n"
+ "\t.globl coro_transfer\n"
+ "coro_transfer:\n"
+ /* windows, of course, gives a shit on the amd64 ABI and uses different registers */
+ /* http://blogs.msdn.com/freik/archive/2005/03/17/398200.aspx */
+ #if __amd64
+ #ifdef WIN32
+ /* TODO: xmm6..15 also would need to be saved. sigh. */
+ #define NUM_SAVED 8
+ "\tpushq %rsi\n"
+ "\tpushq %rdi\n"
+ "\tpushq %rbp\n"
+ "\tpushq %rbx\n"
+ "\tpushq %r12\n"
+ "\tpushq %r13\n"
+ "\tpushq %r14\n"
+ "\tpushq %r15\n"
+ #if CORO_WIN_TIB
+ "\tpushq %fs:0x0\n"
+ "\tpushq %fs:0x8\n"
+ "\tpushq %fs:0xc\n"
+ #endif
+ "\tmovq %rsp, (%rcx)\n"
+ "\tmovq (%rdx), %rsp\n"
+ #if CORO_WIN_TIB
+ "\tpopq %fs:0xc\n"
+ "\tpopq %fs:0x8\n"
+ "\tpopq %fs:0x0\n"
+ #endif
+ "\tpopq %r15\n"
+ "\tpopq %r14\n"
+ "\tpopq %r13\n"
+ "\tpopq %r12\n"
+ "\tpopq %rbx\n"
+ "\tpopq %rbp\n"
+ "\tpopq %rdi\n"
+ "\tpopq %rsi\n"
+ #else
+ #define NUM_SAVED 6
+ "\tpushq %rbp\n"
+ "\tpushq %rbx\n"
+ "\tpushq %r12\n"
+ "\tpushq %r13\n"
+ "\tpushq %r14\n"
+ "\tpushq %r15\n"
+ "\tmovq %rsp, (%rdi)\n"
+ "\tmovq (%rsi), %rsp\n"
+ "\tpopq %r15\n"
+ "\tpopq %r14\n"
+ "\tpopq %r13\n"
+ "\tpopq %r12\n"
+ "\tpopq %rbx\n"
+ "\tpopq %rbp\n"
+ #endif
+ #elif __i386
+ #define NUM_SAVED 4
+ "\tpushl %ebp\n"
+ "\tpushl %ebx\n"
+ "\tpushl %esi\n"
+ "\tpushl %edi\n"
+ #if CORO_WIN_TIB
+ "\tpushl %fs:0\n"
+ "\tpushl %fs:4\n"
+ "\tpushl %fs:8\n"
+ #endif
+ "\tmovl %esp, (%eax)\n"
+ "\tmovl (%edx), %esp\n"
+ #if CORO_WIN_TIB
+ "\tpopl %fs:8\n"
+ "\tpopl %fs:4\n"
+ "\tpopl %fs:0\n"
+ #endif
+ "\tpopl %edi\n"
+ "\tpopl %esi\n"
+ "\tpopl %ebx\n"
+ "\tpopl %ebp\n"
+ #else
+ #error unsupported architecture
+ #endif
+ "\tret\n"
+ );
+
+# endif
+
+void
+coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, long ssize)
+{
+ coro_context nctx;
+# if CORO_SJLJ
+ stack_t ostk, nstk;
+ struct sigaction osa, nsa;
+ sigset_t nsig, osig;
+# endif
+
+ if (!coro)
+ return;
+
+ coro_init_func = coro;
+ coro_init_arg = arg;
+
+ new_coro = ctx;
+ create_coro = &nctx;
+
+# if CORO_SJLJ
+ /* we use SIGUSR2. first block it, then fiddle with it. */
+
+ sigemptyset (&nsig);
+ sigaddset (&nsig, SIGUSR2);
+ sigprocmask (SIG_BLOCK, &nsig, &osig);
+
+ nsa.sa_handler = trampoline;
+ sigemptyset (&nsa.sa_mask);
+ nsa.sa_flags = SA_ONSTACK;
+
+ if (sigaction (SIGUSR2, &nsa, &osa))
+ {
+ perror ("sigaction");
+ abort ();
+ }
+
+ /* set the new stack */
+ nstk.ss_sp = STACK_ADJUST_PTR (sptr, ssize); /* yes, some platforms (IRIX) get this wrong. */
+ nstk.ss_size = STACK_ADJUST_SIZE (sptr, ssize);
+ nstk.ss_flags = 0;
+
+ if (sigaltstack (&nstk, &ostk) < 0)
+ {
+ perror ("sigaltstack");
+ abort ();
+ }
+
+ trampoline_done = 0;
+ kill (getpid (), SIGUSR2);
+ sigfillset (&nsig); sigdelset (&nsig, SIGUSR2);
+
+ while (!trampoline_done)
+ sigsuspend (&nsig);
+
+ sigaltstack (0, &nstk);
+ nstk.ss_flags = SS_DISABLE;
+ if (sigaltstack (&nstk, 0) < 0)
+ perror ("sigaltstack");
+
+ sigaltstack (0, &nstk);
+ if (~nstk.ss_flags & SS_DISABLE)
+ abort ();
+
+ if (~ostk.ss_flags & SS_DISABLE)
+ sigaltstack (&ostk, 0);
+
+ sigaction (SIGUSR2, &osa, 0);
+ sigprocmask (SIG_SETMASK, &osig, 0);
+
+# elif CORO_LOSER
+
+ coro_setjmp (ctx->env);
+ #if __CYGWIN__ && __i386
+ ctx->env[8] = (long) coro_init;
+ ctx->env[7] = (long) ((char *)sptr + ssize) - sizeof (long);
+ #elif __CYGWIN__ && __x86_64
+ ctx->env[7] = (long) coro_init;
+ ctx->env[6] = (long) ((char *)sptr + ssize) - sizeof (long);
+ #elif defined(__MINGW32__)
+ ctx->env[5] = (long) coro_init;
+ ctx->env[4] = (long) ((char *)sptr + ssize) - sizeof (long);
+ #elif defined(_M_IX86)
+ ((_JUMP_BUFFER *)&ctx->env)->Eip = (long) coro_init;
+ ((_JUMP_BUFFER *)&ctx->env)->Esp = (long) STACK_ADJUST_PTR (sptr, ssize) - sizeof (long);
+ #elif defined(_M_AMD64)
+ ((_JUMP_BUFFER *)&ctx->env)->Rip = (__int64) coro_init;
+ ((_JUMP_BUFFER *)&ctx->env)->Rsp = (__int64) STACK_ADJUST_PTR (sptr, ssize) - sizeof (__int64);
+ #elif defined(_M_IA64)
+ ((_JUMP_BUFFER *)&ctx->env)->StIIP = (__int64) coro_init;
+ ((_JUMP_BUFFER *)&ctx->env)->IntSp = (__int64) STACK_ADJUST_PTR (sptr, ssize) - sizeof (__int64);
+ #else
+ #error "microsoft libc or architecture not supported"
+ #endif
+
+# elif CORO_LINUX
+
+ coro_setjmp (ctx->env);
+ #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined (JB_PC) && defined (JB_SP)
+ ctx->env[0].__jmpbuf[JB_PC] = (long) coro_init;
+ ctx->env[0].__jmpbuf[JB_SP] = (long) STACK_ADJUST_PTR (sptr, ssize) - sizeof (long);
+ #elif __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 0 && defined (__mc68000__)
+ ctx->env[0].__jmpbuf[0].__aregs[0] = (long int)coro_init;
+ ctx->env[0].__jmpbuf[0].__sp = (int *) ((char *)sptr + ssize) - sizeof (long);
+ #elif defined (__GNU_LIBRARY__) && defined (__i386__)
+ ctx->env[0].__jmpbuf[0].__pc = (char *) coro_init;
+ ctx->env[0].__jmpbuf[0].__sp = (void *) ((char *)sptr + ssize) - sizeof (long);
+ #elif defined (__GNU_LIBRARY__) && defined (__amd64__)
+ ctx->env[0].__jmpbuf[JB_PC] = (long) coro_init;
+ ctx->env[0].__jmpbuf[0].__sp = (void *) ((char *)sptr + ssize) - sizeof (long);
+ #else
+ #error "linux libc or architecture not supported"
+ #endif
+
+# elif CORO_IRIX
+
+ coro_setjmp (ctx->env, 0);
+ ctx->env[JB_PC] = (__uint64_t)coro_init;
+ ctx->env[JB_SP] = (__uint64_t)STACK_ADJUST_PTR (sptr, ssize) - sizeof (long);
+
+# elif CORO_ASM
+
+ ctx->sp = (void **)(ssize + (char *)sptr);
+ *--ctx->sp = (void *)abort; /* needed for alignment only */
+ *--ctx->sp = (void *)coro_init;
+
+ #if CORO_WIN_TIB
+ *--ctx->sp = 0; /* ExceptionList */
+ *--ctx->sp = (char *)sptr + ssize; /* StackBase */
+ *--ctx->sp = sptr; /* StackLimit */
+ #endif
+
+ ctx->sp -= NUM_SAVED;
+ memset (ctx->sp, 0, sizeof (*ctx->sp) * NUM_SAVED);
+
+# elif CORO_UCONTEXT
+
+ getcontext (&(ctx->uc));
+
+ ctx->uc.uc_link = 0;
+ ctx->uc.uc_stack.ss_sp = sptr;
+ ctx->uc.uc_stack.ss_size = (size_t)ssize;
+ ctx->uc.uc_stack.ss_flags = 0;
+
+ makecontext (&(ctx->uc), (void (*)())coro_init, 0);
+
+# endif
+
+ coro_transfer (create_coro, new_coro);
+}
+
+/*****************************************************************************/
+/* pthread backend */
+/*****************************************************************************/
+#elif CORO_PTHREAD
+
+/* this mutex will be locked by the running coroutine */
+pthread_mutex_t coro_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct coro_init_args
+{
+ coro_func func;
+ void *arg;
+ coro_context *self, *main;
+};
+
+static pthread_t null_tid;
+
+/* I'd so love to cast pthread_mutex_unlock to void (*)(void *)... */
+static void
+mutex_unlock_wrapper (void *arg)
+{
+ pthread_mutex_unlock ((pthread_mutex_t *)arg);
+}
+
+static void *
+coro_init (void *args_)
+{
+ struct coro_init_args *args = (struct coro_init_args *)args_;
+ coro_func func = args->func;
+ void *arg = args->arg;
+
+ pthread_mutex_lock (&coro_mutex);
+
+ /* we try to be good citizens and use deferred cancellation and cleanup handlers */
+ pthread_cleanup_push (mutex_unlock_wrapper, &coro_mutex);
+ coro_transfer (args->self, args->main);
+ func (arg);
+ pthread_cleanup_pop (1);
+
+ return 0;
+}
+
+void
+coro_transfer (coro_context *prev, coro_context *next)
+{
+ pthread_cond_signal (&next->cv);
+ pthread_cond_wait (&prev->cv, &coro_mutex);
+#if __FreeBSD__ /* freebsd is of course broken and needs manual testcancel calls... yay... */
+ pthread_testcancel ();
+#endif
+}
+
+void
+coro_create (coro_context *ctx, coro_func coro, void *arg, void *sptr, long ssize)
+{
+ static coro_context nctx;
+ static int once;
+
+ if (!once)
+ {
+ once = 1;
+
+ pthread_mutex_lock (&coro_mutex);
+ pthread_cond_init (&nctx.cv, 0);
+ null_tid = pthread_self ();
+ }
+
+ pthread_cond_init (&ctx->cv, 0);
+
+ if (coro)
+ {
+ pthread_attr_t attr;
+ struct coro_init_args args;
+
+ args.func = coro;
+ args.arg = arg;
+ args.self = ctx;
+ args.main = &nctx;
+
+ pthread_attr_init (&attr);
+#if __UCLIBC__
+ /* exists, but is borked */
+ /*pthread_attr_setstacksize (&attr, (size_t)ssize);*/
+#else
+ pthread_attr_setstack (&attr, sptr, (size_t)ssize);
+#endif
+ pthread_attr_setscope (&attr, PTHREAD_SCOPE_PROCESS);
+ pthread_create (&ctx->id, &attr, coro_init, &args);
+
+ coro_transfer (args.main, args.self);
+ }
+ else
+ ctx->id = null_tid;
+}
+
+void
+coro_destroy (coro_context *ctx)
+{
+ if (!pthread_equal (ctx->id, null_tid))
+ {
+ pthread_cancel (ctx->id);
+ pthread_mutex_unlock (&coro_mutex);
+ pthread_join (ctx->id, 0);
+ pthread_mutex_lock (&coro_mutex);
+ }
+
+ pthread_cond_destroy (&ctx->cv);
+}
+
+#else
+# error unsupported backend
+#endif
+
View
308 src/libcoro/coro.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2001-2009 Marc Alexander Lehmann <schmorp@schmorp.de>
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
+ * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
+ * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
+ * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License ("GPL") version 2 or any later version,
+ * in which case the provisions of the GPL are applicable instead of
+ * the above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the BSD license, indicate your decision
+ * by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file under
+ * either the BSD or the GPL.
+ *
+ * This library is modelled strictly after Ralf S. Engelschalls article at
+ * http://www.gnu.org/software/pth/rse-pmt.ps. So most of the credit must
+ * go to Ralf S. Engelschall <rse@engelschall.com>.
+ *
+ * This coroutine library is very much stripped down. You should either
+ * build your own process abstraction using it or - better - just use GNU
+ * Portable Threads, http://www.gnu.org/software/pth/.
+ *
+ */
+
+/*
+ * 2006-10-26 Include stddef.h on OS X to work around one of its bugs.
+ * Reported by Michael_G_Schwern.
+ * 2006-11-26 Use _setjmp instead of setjmp on GNU/Linux.
+ * 2007-04-27 Set unwind frame info if gcc 3+ and ELF is detected.
+ * Use _setjmp instead of setjmp on _XOPEN_SOURCE >= 600.
+ * 2007-05-02 Add assembly versions for x86 and amd64 (to avoid reliance
+ * on SIGUSR2 and sigaltstack in Crossfire).
+ * 2008-01-21 Disable CFI usage on anything but GNU/Linux.
+ * 2008-03-02 Switched to 2-clause BSD license with GPL exception.
+ * 2008-04-04 New (but highly unrecommended) pthreads backend.
+ * 2008-04-24 Reinstate CORO_LOSER (had wrong stack adjustments).
+ * 2008-10-30 Support assembly method on x86 with and without frame pointer.
+ * 2008-11-03 Use a global asm statement for CORO_ASM, idea by pippijn.
+ * 2008-11-05 Hopefully fix misaligned stacks with CORO_ASM/SETJMP.
+ * 2008-11-07 rbp wasn't saved in CORO_ASM on x86_64.
+ * introduce coro_destroy, which is a nop except for pthreads.
+ * speed up CORO_PTHREAD. Do no longer leak threads either.
+ * coro_create now allows one to create source coro_contexts.
+ * do not rely on makecontext passing a void * correctly.
+ * try harder to get _setjmp/_longjmp.
+ * major code cleanup/restructuring.
+ * 2008-11-10 the .cfi hacks are no longer needed.
+ * 2008-11-16 work around a freebsd pthread bug.
+ * 2008-11-19 define coro_*jmp symbols for easier porting.
+ * 2009-06-23 tentative win32-backend support for mingw32 (Yasuhiro Matsumoto).
+ * 2010-12-03 tentative support for uclibc (which lacks all sorts of things).
+ * 2011-05-30 set initial callee-saved-registers to zero with CORO_ASM.
+ * use .cfi_undefined rip on linux-amd64 for better backtraces.
+ * 2011-06-08 maybe properly implement weird windows amd64 calling conventions.
+ * 2011-07-03 rely on __GCC_HAVE_DWARF2_CFI_ASM for cfi detection.
+ */
+
+#ifndef CORO_H
+#define CORO_H
+
+#if __cplusplus
+extern "C" {
+#endif
+
+#define CORO_VERSION 2
+
+/*
+ * Changes since API version 1:
+ * replaced bogus -DCORO_LOOSE with gramatically more correct -DCORO_LOSER
+ */
+
+/*
+ * This library consists of only three files
+ * coro.h, coro.c and LICENSE (and optionally README)
+ *
+ * It implements what is known as coroutines, in a hopefully
+ * portable way. At the moment you have to define which kind
+ * of implementation flavour you want:
+ *
+ * -DCORO_UCONTEXT
+ *
+ * This flavour uses SUSv2's get/set/swap/makecontext functions that
+ * unfortunately only newer unices support.
+ *
+ * -DCORO_SJLJ
+ *
+ * This flavour uses SUSv2's setjmp/longjmp and sigaltstack functions to
+ * do it's job. Coroutine creation is much slower than UCONTEXT, but
+ * context switching is often a bit cheaper. It should work on almost
+ * all unices.
+ *
+ * -DCORO_LINUX
+ *
+ * Old GNU/Linux systems (<= glibc-2.1) only work with this implementation
+ * (it is very fast and therefore recommended over other methods, but
+ * doesn't work with anything newer).
+ *
+ * -DCORO_LOSER
+ *
+ * Microsoft's highly proprietary platform doesn't support sigaltstack, and
+ * this automatically selects a suitable workaround for this platform.
+ * (untested)
+ *
+ * -DCORO_IRIX
+ *
+ * SGI's version of Microsoft's NT ;)
+ *
+ * -DCORO_ASM
+ *
+ * Handcoded assembly, known to work only on a few architectures/ABI:
+ * GCC + x86/IA32 and amd64/x86_64 + GNU/Linux and a few BSDs.
+ *
+ * -DCORO_PTHREAD
+ *
+ * Use the pthread API. You have to provide <pthread.h> and -lpthread.
+ * This is likely the slowest backend, and it also does not support fork(),
+ * so avoid it at all costs.
+ *
+ * If you define neither of these symbols, coro.h will try to autodetect
+ * the model. This currently works for CORO_LOSER only. For the other
+ * alternatives you should check (e.g. using autoconf) and define the
+ * following symbols: HAVE_UCONTEXT_H / HAVE_SETJMP_H / HAVE_SIGALTSTACK.
+ */
+
+/*
+ * This is the type for the initialization function of a new coroutine.
+ */
+typedef void (*coro_func)(void *);
+
+/*
+ * A coroutine state is saved in the following structure. Treat it as an
+ * opaque type. errno and sigmask might be saved, but don't rely on it,
+ * implement your own switching primitive if you need that.
+ */
+typedef struct coro_context coro_context;
+
+/*
+ * This function creates a new coroutine. Apart from a pointer to an
+ * uninitialised coro_context, it expects a pointer to the entry function
+ * and the single pointer value that is given to it as argument.
+ *
+ * Allocating/deallocating the stack is your own responsibility.
+ *
+ * As a special case, if coro, arg, sptr and ssize are all zero,
+ * then an "empty" coro_context will be created that is suitable
+ * as an initial source for coro_transfer.
+ *
+ * This function is not reentrant, but putting a mutex around it
+ * will work.
+ */
+void coro_create (coro_context *ctx, /* an uninitialised coro_context */
+ coro_func coro, /* the coroutine code to be executed */
+ void *arg, /* a single pointer passed to the coro */
+ void *sptr, /* start of stack area */
+ long ssize); /* size of stack area */
+
+/*
+ * The following prototype defines the coroutine switching function. It is
+ * usually implemented as a macro, so watch out.
+ *
+ * This function is thread-safe and reentrant.
+ */
+#if 0
+void coro_transfer (coro_context *prev, coro_context *next);
+#endif
+
+/*
+ * The following prototype defines the coroutine destroy function. It is
+ * usually implemented as a macro, so watch out. It also serves
+ * no purpose unless you want to use the CORO_PTHREAD backend,
+ * where it is used to clean up the thread. You are responsible
+ * for freeing the stack and the context itself.
+ *
+ * This function is thread-safe and reentrant.
+ */
+#if 0
+void coro_destroy (coro_context *ctx);
+#endif
+
+/*
+ * That was it. No other user-visible functions are implemented here.
+ */
+
+/*****************************************************************************/
+
+#if !defined(CORO_LOSER) && !defined(CORO_UCONTEXT) \
+ && !defined(CORO_SJLJ) && !defined(CORO_LINUX) \
+ && !defined(CORO_IRIX) && !defined(CORO_ASM) \
+ && !defined(CORO_PTHREAD)
+# if defined(WINDOWS) || defined(_WIN32)
+# define CORO_LOSER 1 /* you don't win with windoze */
+# elif defined(__linux) && (defined(__x86) || defined (__amd64))
+# define CORO_ASM 1
+# elif defined(HAVE_UCONTEXT_H)
+# define CORO_UCONTEXT 1
+# elif defined(HAVE_SETJMP_H) && defined(HAVE_SIGALTSTACK)
+# define CORO_SJLJ 1
+# else
+error unknown or unsupported architecture
+# endif
+#endif
+
+/*****************************************************************************/
+
+#if CORO_UCONTEXT
+
+# include <ucontext.h>
+
+struct coro_context {
+ ucontext_t uc;
+};
+
+# define coro_transfer(p,n) swapcontext (&((p)->uc), &((n)->uc))
+# define coro_destroy(ctx) (void *)(ctx)
+
+#elif CORO_SJLJ || CORO_LOSER || CORO_LINUX || CORO_IRIX
+
+# if defined(CORO_LINUX) && !defined(_GNU_SOURCE)
+# define _GNU_SOURCE /* for linux libc */
+# endif
+
+# if !CORO_LOSER
+# include <unistd.h>
+# endif
+
+/* solaris is hopelessly borked, it expands _XOPEN_UNIX to nothing */
+# if __sun
+# undef _XOPEN_UNIX
+# define _XOPEN_UNIX 1
+# endif
+
+# include <setjmp.h>
+
+# if _XOPEN_UNIX > 0 || defined (_setjmp)
+# define coro_jmp_buf jmp_buf
+# define coro_setjmp(env) _setjmp (env)
+# define coro_longjmp(env) _longjmp ((env), 1)
+# elif CORO_LOSER
+# define coro_jmp_buf jmp_buf
+# define coro_setjmp(env) setjmp (env)
+# define coro_longjmp(env) longjmp ((env), 1)
+# else
+# define coro_jmp_buf sigjmp_buf
+# define coro_setjmp(env) sigsetjmp (env, 0)
+# define coro_longjmp(env) siglongjmp ((env), 1)
+# endif
+
+struct coro_context {
+ coro_jmp_buf env;
+};
+
+# define coro_transfer(p,n) do { if (!coro_setjmp ((p)->env)) coro_longjmp ((n)->env); } while (0)
+# define coro_destroy(ctx) (void *)(ctx)
+
+#elif CORO_ASM
+
+struct coro_context {
+ void **sp; /* must be at offset 0 */
+};
+
+void __attribute__ ((__noinline__, __regparm__(2)))
+coro_transfer (coro_context *prev, coro_context *next);
+
+# define coro_destroy(ctx) (void *)(ctx)
+
+#elif CORO_PTHREAD
+
+# include <pthread.h>
+
+extern pthread_mutex_t coro_mutex;
+
+struct coro_context {
+ pthread_cond_t cv;
+ pthread_t id;
+};
+
+void coro_transfer (coro_context *prev, coro_context *next);
+void coro_destroy (coro_context *ctx);
+
+#endif
+
+#if __cplusplus
+}
+#endif
+
+#endif
+
Please sign in to comment.
Something went wrong with that request. Please try again.