diff --git a/lib/coroutine.h b/lib/coroutine.h new file mode 100644 index 00000000..f64a9540 --- /dev/null +++ b/lib/coroutine.h @@ -0,0 +1,22 @@ +/* + * BIRD Coroutines + * + * (c) 2017 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_COROUTINE_H_ +#define _BIRD_COROUTINE_H_ + +// The structure is completely opaque, implemented by sysdep +typedef struct coroutine coroutine; + +coroutine *coro_new(struct pool *pool, void (*entry_point)(void *arg), void *arg); +void coro_suspend(void); +void coro_resume(coroutine *c); + +struct birdsock; +int coro_sk_read(struct birdsock *s); + +#endif diff --git a/nest/cli.c b/nest/cli.c index b8f6956c..bf4b47a8 100644 --- a/nest/cli.c +++ b/nest/cli.c @@ -66,8 +66,8 @@ #include "nest/bird.h" #include "nest/cli.h" #include "conf/conf.h" +#include "lib/coroutine.h" #include "lib/string.h" -#include "sysdep/unix/unix.h" // FIXME pool *cli_pool; diff --git a/sysdep/unix/Makefile b/sysdep/unix/Makefile index f592399c..9da83613 100644 --- a/sysdep/unix/Makefile +++ b/sysdep/unix/Makefile @@ -1,4 +1,4 @@ -src := io.c krt.c log.c main.c random.c +src := io.c krt.c log.c main.c random.c coroutine.c obj := $(src-o-files) $(all-daemon) $(cf-local) diff --git a/sysdep/unix/coroutine.c b/sysdep/unix/coroutine.c new file mode 100644 index 00000000..3d648eb4 --- /dev/null +++ b/sysdep/unix/coroutine.c @@ -0,0 +1,135 @@ +/* + * BIRD Coroutines + * + * (c) 2017 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include + +#include "nest/bird.h" +#include "lib/coroutine.h" +#include "lib/resource.h" +#include "lib/socket.h" +#include "sysdep/unix/unix.h" + +struct coroutine { + resource r; + ucontext_t ctx; + void *stack; + void (*entry_point)(void *arg); + void *arg; +}; + +static ucontext_t *main_context; +static coroutine *coro_current; // NULL for main context + +#define CORO_STACK_SIZE 65536 + +static void +coro_free(resource *r) +{ + coroutine *c = (coroutine *) r; + xfree(c->stack); +} + +static void +coro_dump(resource *r UNUSED) +{ + debug("\n"); +} + +static size_t +coro_memsize(resource *r) +{ + coroutine *c = (coroutine *) r; + return sizeof(*c) + CORO_STACK_SIZE + 2*ALLOC_OVERHEAD; +} + +static struct resclass coro_class = { + .name = "Coroutine", + .size = sizeof(struct coroutine), + .free = coro_free, + .dump = coro_dump, + .memsize = coro_memsize, +}; + +static void +coro_do_start(void) +{ + ASSERT(coro_current); + coro_current->entry_point(coro_current->arg); + bug("Coroutine returned unexpectedly"); +} + +struct coroutine * +coro_new(pool *p, void (*entry_point)(void *), void *arg) +{ + if (!main_context) + { + main_context = xmalloc(sizeof(*main_context)); + if (getcontext(main_context) < 0) + bug("getcontext() failed"); + } + + coroutine *c = ralloc(p, &coro_class); + c->entry_point = entry_point; + c->arg = arg; + if (getcontext(&c->ctx) < 0) + bug("getcontext() failed"); + c->stack = xmalloc(CORO_STACK_SIZE); + c->ctx.uc_stack.ss_sp = c->stack; + c->ctx.uc_stack.ss_size = CORO_STACK_SIZE; + + makecontext(&c->ctx, coro_do_start, 0); + + return c; +} + +void +coro_suspend(void) +{ + ASSERT(coro_current); + ASSERT(main_context); + coroutine *c = coro_current; + coro_current = NULL; + swapcontext(&c->ctx, main_context); + ASSERT(coro_current == c); +} + +void +coro_resume(coroutine *c) +{ + ASSERT(!coro_current); + coro_current = c; + swapcontext(main_context, &c->ctx); + ASSERT(!coro_current); +} + +/* Coroutine-based I/O */ + +static int +coro_sk_rx_hook(sock *sk, uint size UNUSED) +{ + ASSERT(sk->rx_coroutine); + ASSERT(!coro_current); + coro_resume(sk->rx_coroutine); + return 0; +} + +int +coro_sk_read(sock *s) +{ + ASSERT(coro_current); + s->rx_coroutine = coro_current; + s->rx_hook = coro_sk_rx_hook; + coro_suspend(); + s->rx_hook = NULL; + return s->rpos - s->rbuf; +} diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 44d0d8b5..0cf48c9d 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -2665,113 +2665,3 @@ test_old_bird(char *path) die("I found another BIRD running."); close(fd); } - -/* EXPERIMENTAL: Support for coroutines */ - -#include - -struct coroutine { - resource r; - ucontext_t ctx; - void *stack; - void (*entry_point)(void *arg); - void *arg; -}; - -static ucontext_t *main_context; -static coroutine *coro_current; // NULL for main context - -static void -coro_free(resource *r) -{ - coroutine *c = (coroutine *) r; - xfree(c->stack); -} - -static void -coro_dump(resource *r UNUSED) -{ - debug("\n"); -} - -static struct resclass coro_class = { - .name = "Coroutine", - .size = sizeof(struct coroutine), - .free = coro_free, - .dump = coro_dump, - // FIXME: Implement memsize -}; - -static void -coro_do_start(void) -{ - ASSERT(coro_current); - coro_current->entry_point(coro_current->arg); - bug("Coroutine returned unexpectedly"); -} - -struct coroutine * -coro_new(pool *p, void (*entry_point)(void *), void *arg) -{ - if (!main_context) - { - main_context = xmalloc(sizeof(*main_context)); - if (getcontext(main_context) < 0) - bug("getcontext() failed"); - } - - coroutine *c = ralloc(p, &coro_class); - c->entry_point = entry_point; - c->arg = arg; - if (getcontext(&c->ctx) < 0) - bug("getcontext() failed"); - c->stack = xmalloc(65536); - c->ctx.uc_stack.ss_sp = c->stack; - c->ctx.uc_stack.ss_size = 65536; - - makecontext(&c->ctx, coro_do_start, 0); - - return c; -} - -// Return to main context -void -coro_suspend(void) -{ - ASSERT(coro_current); - ASSERT(main_context); - coroutine *c = coro_current; - coro_current = NULL; - swapcontext(&c->ctx, main_context); - ASSERT(coro_current == c); -} - -// Resume context -void -coro_resume(coroutine *c) -{ - ASSERT(!coro_current); - coro_current = c; - swapcontext(main_context, &c->ctx); - ASSERT(!coro_current); -} - -static int -coro_sk_rx_hook(sock *sk, uint size UNUSED) -{ - ASSERT(sk->rx_coroutine); - ASSERT(!coro_current); - coro_resume(sk->rx_coroutine); - return 0; -} - -int -coro_sk_read(sock *s) -{ - ASSERT(coro_current); - s->rx_coroutine = coro_current; - s->rx_hook = coro_sk_rx_hook; - coro_suspend(); - s->rx_hook = NULL; - return s->rpos - s->rbuf; -} diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index c625a859..35615831 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -23,6 +23,7 @@ #include #include "nest/bird.h" +#include "lib/coroutine.h" #include "lib/lists.h" #include "lib/resource.h" #include "lib/socket.h" diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index 4a9b4253..dcaab729 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -102,13 +102,6 @@ int sk_open_unix(struct birdsock *s, char *name); void *tracked_fopen(struct pool *, char *name, char *mode); void test_old_bird(char *path); -/* Co-routines */ -typedef struct coroutine coroutine; -coroutine *coro_new(struct pool *pool, void (*entry_point)(void *arg), void *arg); -void coro_suspend(void); -void coro_resume(coroutine *c); -int coro_sk_read(struct birdsock *s); - /* krt.c bits */ void krt_io_init(void);