Recent Entries 3
- pattern minor 112d agoSmall coroutine classWhat do you think about this? ``` #include #include #include #include template class coroutine { jmp_buf env_in_; jmp_buf env_out_; bool running_{}; char stack_[N]; public: coroutine() = default; auto running() const noexcept { return running_; } template void run(F&& f, A&& ...a) { if (setjmp(env_in_)) { return; } // else do nothing auto top(reinterpret_cast(&top)); alloca(top - (stack_ + N)); running_ = true; [this, f = ::std::forward(f)](A&& ...a) __attribute__ ((noinline)) { f(::std::ref(*this), ::std::forward(a)...); running_ = false; yield(); }(::std::forward(a)...); } void yield() noexcept { if (setjmp(env_out_)) { return; } else { longjmp(env_in_, 1); } } void resume() { assert(running_); if (setjmp(env_in_)) { return; } else { longjmp(env_out_, 1); } } }; ``` Usage: ``` #include #include "coroutine.hpp" int main() { coroutine<> c; c.run([](decltype(c)& c) { for (int i{}; i != 3; ++i) { ::std::cout << i << ::std::endl; c.yield(); } } ); while (c.running()) { c.resume(); } return 0; } ``` EDIT Change ``` auto top(reinterpret_cast(&top)); ``` to ``` char* top; top = reinterpret_cast(&top); ``` Also check out an updated version of the code.
- pattern minor 112d agoCoroutines in CPlease have a look at this little coroutines library `ccoro`: http://sam.nipl.net/code/ccoro I'd appreciate a general code and style review, and your kind comments! `ccoro.h` ``` /* * ccoro - Coroutines in C * Sam Watkins, 2009 * this code is public domain * * ccoro uses setjmp and longjmp to achieve coroutines in plain C. */ #ifndef CCORO_H #define CCORO_H 1 #ifdef __cplusplus extern "C" { #endif #include #if defined (__GNUC__) #define noret void __attribute__((noreturn)) #else #define noret void #endif extern int coro_pad; enum { coro_code_done = -1, coro_code_alloc = -2, coro_code_dead = -3 }; typedef struct coro coro; typedef void (*coro_func)(coro *caller); struct coro { coro *next; coro *prev; jmp_buf j; }; coro *new_coro(coro_func f); int yield(coro **c); int yield_val(coro **c, int val); #ifdef __cplusplus } #endif #endif ``` `ccoro.c` ``` /*/ 2>/dev/null; exec cc -c -Wall -Wextra -O2 ccoro.c ; exit $? # */ /* * ccoro - Coroutines in C * Sam Watkins, 2009 * this code is public domain * * ccoro uses setjmp and longjmp to achieve coroutines in plain C, * non-preemptive threading. It works by allocating each thread some stack * space on the normal stack. It makes sure that each thread has enough space * to run using a padding variable, of size 8k by default, which is inserted by * a function that starts the thread. Simple, huh? It uses some fairly simple * trickery to create new threads that don't overlap with the other threads. * * I don't claim that this code is legal by the book, but it seems to work * with at gcc, tcc, tendra and lcc on Linux i386 and x86_64, and with mingw * gcc-4.4 and Visual C++ 98 Express on Windows. * * As is, this requires C99. To run with C89, use an enum for coro_pad. * * An earlier version was reported not to work with tendra on netbsd. * I have fixed bugs since then so maybe it works now. * * An earlier version did not work with -O2 on some systems, it seems to be * work
- snippet minor 112d agoHow can this simple coroutine implementation be improved?This is a quick and dirty implementation of coroutines that implements yield by saving and restoring the stack to and from the heap. Here's an earlier version, which does the most naive possible thing, and just allocates enough space on the stack for every possible coroutine. The relevant bits from the current code are below, and the whole thing is here. I'm open to feedback both on pretty much anything: completely different approaches, how to restructure this approach, bugs, idiomatic C style pointers, etc. An obvious performance improvement would be to use ucontext, but that seems to be deprecated and somewhat broken on Mac OS X (although it works fine on the linux distros I've tried). I'm not sure what the next step is to make this 'better'. `typedef void (*coroutine_cb)(); static void scheduler(coroutine_cb); static int spawn(coroutine_cb); static void yield(int); jmp_buf scheduler_jmp; void *scheduler_rbp; int coro_pid; coroutine_cb scheduler_next_coro; #define MAX_COROS 100 struct { jmp_buf jmp; void *stack; long stack_sz; } coroutines[MAX_COROS]; static void scheduler(coroutine_cb coro) { printf("starting the scheduler...\n"); static int max_pid = 1; // we move down 0x1000 to give scheduler space to call functions and allocate stack variables without having them get // overwritten by the memcpy below. Before we did this, we had to manually copy memory instead of using memcpy // because we would overwrite memcpy's stack scheduler_rbp = __builtin_frame_address(0) - 0x1000; scheduler_next_coro = coro; int value = setjmp(scheduler_jmp); // value == 0 means just starting // value == -1 means spawning new coroutine // value positive means hop back to a specific pid if (value == 0 || value == -1) { coro_pid = max_pid++; printf("about to run coro %d...\n", coro_pid); char *buf = alloca(0x2000); // was 0x1000 when we didn't allocate extra space for scheduler stack asm volatile("" :: "m" (buf)); scheduler_ne