* [PATCH 1/2] coroutine: New module
@ 2016-11-28 13:18 David Gibson
2016-11-28 13:18 ` [PATCH 2/2] generator: Rewrite to use coroutine module David Gibson
2016-11-29 3:47 ` [PATCH 1/2] coroutine: New module Rusty Russell
0 siblings, 2 replies; 3+ messages in thread
From: David Gibson @ 2016-11-28 13:18 UTC (permalink / raw)
To: ccan, rusty
This is essentially a wrapper around ucontext.h, but the idea is that
alternative back end implementations could be used in future.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
ccan/coroutine/LICENSE | 1 +
ccan/coroutine/_info | 40 ++++++++
ccan/coroutine/coroutine.c | 115 +++++++++++++++++++++++
ccan/coroutine/coroutine.h | 219 ++++++++++++++++++++++++++++++++++++++++++++
ccan/coroutine/test/api-1.c | 52 +++++++++++
ccan/coroutine/test/api-2.c | 110 ++++++++++++++++++++++
ccan/coroutine/test/api-3.c | 90 ++++++++++++++++++
7 files changed, 627 insertions(+)
create mode 120000 ccan/coroutine/LICENSE
create mode 100644 ccan/coroutine/_info
create mode 100644 ccan/coroutine/coroutine.c
create mode 100644 ccan/coroutine/coroutine.h
create mode 100644 ccan/coroutine/test/api-1.c
create mode 100644 ccan/coroutine/test/api-2.c
create mode 100644 ccan/coroutine/test/api-3.c
diff --git a/ccan/coroutine/LICENSE b/ccan/coroutine/LICENSE
new file mode 120000
index 0000000..dc314ec
--- /dev/null
+++ b/ccan/coroutine/LICENSE
@@ -0,0 +1 @@
+../../licenses/LGPL-2.1
\ No newline at end of file
diff --git a/ccan/coroutine/_info b/ccan/coroutine/_info
new file mode 100644
index 0000000..d59a099
--- /dev/null
+++ b/ccan/coroutine/_info
@@ -0,0 +1,40 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * coroutine - Co-routines
+ *
+ * This code has helper functions for implementing co-routines, that
+ * is, explicit co-operative context switching. It's intended to
+ * provide similar functionality to
+ *
+ * Author: David Gibson <david@gibson.dropbear.id.au>
+ * License: LGPL (v2.1 or any later version)
+ *
+ * Ccanlint:
+ * // Context switching really confuses valgrind
+ * tests_pass_valgrind FAIL
+ */
+int main(int argc, char *argv[])
+{
+ /* Expect exactly one argument */
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/ptrint\n");
+ printf("ccan/compiler\n");
+ printf("ccan/build_assert\n");
+ return 0;
+ }
+
+ if (strcmp(argv[1], "ported") == 0) {
+#if !HAVE_UCONTEXT
+ printf("Requires working ucontext.h\n");
+#endif
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/ccan/coroutine/coroutine.c b/ccan/coroutine/coroutine.c
new file mode 100644
index 0000000..fd4089b
--- /dev/null
+++ b/ccan/coroutine/coroutine.c
@@ -0,0 +1,115 @@
+/* GNU LGPL version 2 (or later) - see LICENSE file for details */
+#include <assert.h>
+#include <string.h>
+
+#include <ccan/ptrint/ptrint.h>
+#include <ccan/build_assert/build_assert.h>
+#include <ccan/coroutine/coroutine.h>
+
+/*
+ * Stack management
+ */
+
+struct coroutine_stack {
+ uint64_t magic;
+ size_t size;
+};
+
+struct coroutine_stack *coroutine_stack_init(void *buf, size_t bufsize,
+ size_t metasize)
+{
+ struct coroutine_stack *stack;
+
+ BUILD_ASSERT(COROUTINE_STK_OVERHEAD == sizeof(*stack));
+#ifdef MINSIGSTKSZ
+ BUILD_ASSERT(COROUTINE_MIN_STKSZ >= MINSIGSTKSZ);
+#endif
+
+ if (bufsize < (COROUTINE_MIN_STKSZ + sizeof(*stack) + metasize))
+ return NULL;
+
+#if HAVE_STACK_GROWS_UPWARDS
+ stack = (char *)buf + metasize;
+#else
+ stack = (struct coroutine_stack *)
+ ((char *)buf + bufsize - metasize) - 1;
+#endif
+
+ stack->magic = COROUTINE_STACK_MAGIC;
+ stack->size = bufsize - sizeof(*stack) - metasize;
+
+ return stack;
+}
+
+void coroutine_stack_release(struct coroutine_stack *stack)
+{
+ memset(stack, 0, sizeof(*stack));
+}
+
+bool coroutine_stack_valid(struct coroutine_stack *stack)
+{
+ return stack
+ && (stack->magic == COROUTINE_STACK_MAGIC)
+ && (stack->size >= COROUTINE_MIN_STKSZ);
+}
+
+size_t coroutine_stack_size(const struct coroutine_stack *stack)
+{
+ return stack->size;
+}
+
+#if HAVE_UCONTEXT
+static void coroutine_uc_stack(stack_t *uc_stack,
+ const struct coroutine_stack *stack)
+{
+ uc_stack->ss_size = coroutine_stack_size(stack) - sizeof(*stack);
+
+#if HAVE_STACK_GROWS_UPWARDS
+ uc_stack->ss_sp = (void *)(stack + 1);
+#else
+ uc_stack->ss_sp = (char *)stack - uc_stack->ss_size;
+#endif
+}
+#endif /* HAVE_UCONTEXT */
+
+/*
+ * Coroutine switching
+ */
+
+#if HAVE_UCONTEXT
+void coroutine_init(struct coroutine_state *cs,
+ void (*fn)(void *), void *arg,
+ struct coroutine_stack *stack)
+{
+ getcontext (&cs->uc);
+
+ coroutine_uc_stack(&cs->uc.uc_stack, stack);
+
+ if (HAVE_POINTER_SAFE_MAKECONTEXT) {
+ makecontext(&cs->uc, (void *)fn, 1, arg);
+ } else {
+ ptrdiff_t si = ptr2int(arg);
+ ptrdiff_t mask = (1UL << (sizeof(int) * 8)) - 1;
+ int lo = si & mask;
+ int hi = si >> (sizeof(int) * 8);
+
+ makecontext(&cs->uc, (void *)fn, 2, lo, hi);
+ }
+
+}
+
+void coroutine_jump(const struct coroutine_state *to)
+{
+ setcontext(&to->uc);
+ assert(0);
+}
+
+void coroutine_switch(struct coroutine_state *from,
+ const struct coroutine_state *to)
+{
+ int rc;
+
+ rc = swapcontext(&from->uc, &to->uc);
+ assert(rc == 0);
+}
+#endif /* HAVE_UCONTEXT */
diff --git a/ccan/coroutine/coroutine.h b/ccan/coroutine/coroutine.h
new file mode 100644
index 0000000..6eba4a3
--- /dev/null
+++ b/ccan/coroutine/coroutine.h
@@ -0,0 +1,219 @@
+/* Licensed under LGPLv2.1+ - see LICENSE file for details */
+#ifndef CCAN_COROUTINE_H
+#define CCAN_COROUTINE_H
+#include "config.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include <ccan/compiler/compiler.h>
+
+/**
+ * struct coroutine_stack
+ *
+ * Describes a stack suitable for executing a coroutine. This
+ * structure is always contained within the stack it describes.
+ */
+struct coroutine_stack;
+
+/**
+ * struct coroutine_state
+ *
+ * Describes the state of an in-progress coroutine.
+ */
+struct coroutine_state;
+
+/*
+ * Stack management
+ */
+
+/**
+ * COROUTINE_STK_OVERHEAD - internal stack overhead
+ *
+ * Number of bytes of a stack which coroutine needs for its own
+ * tracking information.
+ */
+#define COROUTINE_STK_OVERHEAD (sizeof(uint64_t) + sizeof(size_t))
+
+/**
+ * COROUTINE_MIN_STKSZ - Minimum coroutine stack size
+ *
+ * Contains the minimum size for a coroutine stack (not including
+ * overhead). On systems with MINSTKSZ, guaranteed to be at least as
+ * large as MINSTKSZ.
+ */
+#define COROUTINE_MIN_STKSZ 2048
+
+/**
+ * COROUTINE_STACK_MAGIC - Magic number for coroutine stacks
+ */
+#define COROUTINE_STACK_MAGIC 0xc040c040574c574c
+
+
+/**
+ * coroutine_stack_init - Prepare a coroutine stack in an existing buffer
+ * @buf: buffer to use for the coroutine stack
+ * @bufsize: size of @buf
+ * @metasize: size of metadata to add to the stack (not including
+ * coroutine internal overhead)
+ *
+ * Prepares @buf for use as a coroutine stack, returning a
+ * coroutine_stack *, allocated from within the buffer. Returns NULL
+ * on failure.
+ *
+ * This will fail if the bufsize < (COROUTINE_MIN_STKSZ +
+ * COROUTINE_STK_OVERHEAD + metasize).
+ */
+struct coroutine_stack *coroutine_stack_init(void *buf, size_t bufsize,
+ size_t metasize);
+
+/**
+ * coroutine_stack_init - Stop using a coroutine stack
+ * @stack: coroutine stack to release
+ *
+ * This releases @stack, making it no longer suitable for use as a
+ * coroutine stack.
+ */
+void coroutine_stack_release(struct coroutine_stack *stack);
+
+/**
+ * coroutine_stack_valid - Determine if a coroutine stack looks valid
+ * @stack: stack to check
+ *
+ * Returns true if @stack appears to be a valid coroutine stack, false
+ * otherwise.
+ */
+bool coroutine_stack_valid(struct coroutine_stack *stack);
+
+/**
+ * coroutine_stack_to_metadata - Returns pointer to user's metadata
+ * allocated within the stack
+ * @stack: coroutine stack
+ * @metasize: size of metadata
+ *
+ * Returns a pointer to the metadata area within @stack. This is of
+ * size given at initialization time, and won't be overwritten by
+ * coroutines executing on the stack. It's up to the caller what to
+ * put in here. @metasize must be equal to the value passed to
+ * coroutine_stack_init().
+ */
+static inline void *coroutine_stack_to_metadata(struct coroutine_stack *stack,
+ size_t metasize)
+{
+#if HAVE_STACK_GROWS_UPWARDS
+ return (char *)stack - metasize;
+#else
+ return (char *)stack + COROUTINE_STK_OVERHEAD;
+#endif
+}
+
+/**
+ * coroutine_stack_from_metadata - Returns pointer to coroutine stack
+ * pointer given pointer to user metadata
+ * @metadat: user metadata within a stack
+ * @metasize: size of metadata
+ *
+ * Returns a pointer to the coroutine_stack handle within a stack.
+ * The argument must be a pointer returned by
+ * coroutine_stack_to_metadata() at an earlier time. @metasize must be
+ * equal to the value passed to coroutine_stack_init().
+ */
+static inline struct coroutine_stack *
+coroutine_stack_from_metadata(void *metadata, size_t metasize)
+{
+#if HAVE_STACK_GROWS_UPWARDS
+ return (struct coroutine_stack *)((char *)metadata + metasize);
+#else
+ return (struct coroutine_stack *)((char *)metadata
+ - COROUTINE_STK_OVERHEAD);
+#endif
+}
+
+/**
+ * coroutine_stack_size - Return size of a coroutine stack
+ * @stack: coroutine stack
+ *
+ * Returns the size of the coroutine stack @stack. This does not
+ * include the overhead of struct coroutine_stack or metdata.
+ */
+size_t coroutine_stack_size(const struct coroutine_stack *stack);
+
+/*
+ * Coroutine switching
+ */
+
+#if HAVE_UCONTEXT
+#include <ucontext.h>
+#define COROUTINE_AVAILABLE 1
+#else
+#define COROUTINE_AVAILABLE 0
+#endif
+
+struct coroutine_state {
+#if HAVE_UCONTEXT
+ ucontext_t uc;
+#endif /* HAVE_UCONTEXT */
+};
+
+#if COROUTINE_AVAILABLE
+
+/**
+ * coroutine_init - Prepare a coroutine for execution
+ * @cs: coroutine_state structure to initialize
+ * @fn: function to start executing in the coroutine
+ * @arg: argument for @fn
+ * @stack: stack to use for the coroutine
+ *
+ * Prepares @cs as a new coroutine which will execute starting with
+ * function @fn, using stack @stack.
+ */
+void coroutine_init(struct coroutine_state *cs,
+ void (*fn)(void *), void *arg,
+ struct coroutine_stack *stack);
+
+/**
+ * coroutine_jump - Irreversibly switch to executing a coroutine
+ * @to: coroutine to switch to
+ *
+ * Immediately jump to executing coroutine @to (at whatever point in
+ * execution it was up to). Never returns.
+ */
+void NORETURN coroutine_jump(const struct coroutine_state *to);
+
+/**
+ * coroutine_switch - Switch coroutines
+ * @from: coroutine in which to store current execution state
+ * @to: coroutine to switch to
+ *
+ * Stop executing the current routine, saving its state in @from, and
+ * switch to executing the coroutine @to. Returns only when something
+ * switches or jumps back to @from.
+ */
+void coroutine_switch(struct coroutine_state *from,
+ const struct coroutine_state *to);
+
+#else
+
+static inline void coroutine_init(struct coroutine_state *cs,
+ void (*fn)(void *), void *arg,
+ struct coroutine_stack *stack)
+{
+ assert(0);
+}
+
+static inline void NORETURN coroutine_jump(const struct coroutine_state *to)
+{
+ assert(0);
+}
+
+static inline void coroutine_switch(struct coroutine_state *from,
+ const struct coroutine_state *to)
+{
+ assert(0);
+}
+
+#endif /* !COROUTINE_AVAILABLE */
+
+#endif /* CCAN_COROUTINE_H */
diff --git a/ccan/coroutine/test/api-1.c b/ccan/coroutine/test/api-1.c
new file mode 100644
index 0000000..1eb49f1
--- /dev/null
+++ b/ccan/coroutine/test/api-1.c
@@ -0,0 +1,52 @@
+#include <stdlib.h>
+
+#include <ccan/coroutine/coroutine.h>
+#include <ccan/tap/tap.h>
+
+static int global = 0;
+
+static void trivial_fn(void *p)
+{
+ struct coroutine_state *ret = (struct coroutine_state *)p;
+
+ global = 1;
+
+ coroutine_jump(ret);
+}
+
+static void test_trivial(struct coroutine_stack *stack)
+{
+ struct coroutine_state t, master;
+
+ if (!COROUTINE_AVAILABLE) {
+ skip(1, "Coroutines not available");
+ return;
+ }
+
+ coroutine_init(&t, trivial_fn, &master, stack);
+ coroutine_switch(&master, &t);
+
+ ok1(global == 1);
+}
+
+
+int main(void)
+{
+ char buf[COROUTINE_MIN_STKSZ + COROUTINE_STK_OVERHEAD];
+ struct coroutine_stack *stack;
+
+ /* This is how many tests you plan to run */
+ plan_tests(4);
+
+ stack = coroutine_stack_init(buf, sizeof(buf), 0);
+ ok1(stack != NULL);
+ ok1(coroutine_stack_valid(stack));
+ ok1(coroutine_stack_size(stack) == COROUTINE_MIN_STKSZ);
+
+ test_trivial(stack);
+
+ coroutine_stack_release(stack);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}
diff --git a/ccan/coroutine/test/api-2.c b/ccan/coroutine/test/api-2.c
new file mode 100644
index 0000000..ec100ac
--- /dev/null
+++ b/ccan/coroutine/test/api-2.c
@@ -0,0 +1,110 @@
+#include <stdlib.h>
+
+#include <ccan/coroutine/coroutine.h>
+#include <ccan/tap/tap.h>
+
+struct state {
+ struct coroutine_state c1, c2;
+ struct coroutine_state master;
+ int val;
+};
+
+static void f1(void *p)
+{
+ struct state *state = (struct state *)p;
+
+ coroutine_switch(&state->c1, &state->c2);
+
+ ok(state->val == 17, "state->val == %d [expected 17]", state->val);
+ state->val = 23;
+
+ coroutine_switch(&state->c1, &state->c2);
+
+ ok(state->val == 24, "state->val == %d [expected 24]", state->val);
+
+ coroutine_switch(&state->c1, &state->c2);
+
+ ok(state->val == 26, "state->val == %d [expected 26]", state->val);
+
+ coroutine_switch(&state->c1, &state->c2);
+
+ ok(state->val == 29, "state->val == %d [expected 29]", state->val);
+
+ coroutine_switch(&state->c1, &state->c2);
+}
+
+static void f2(void *p)
+{
+ struct state *state = (struct state *)p;
+
+ state->val = 17;
+
+ coroutine_switch(&state->c2, &state->c1);
+
+ ok(state->val == 23, "state->val == %d [expected 23]", state->val);
+ state->val += 1;
+
+ coroutine_switch(&state->c2, &state->c1);
+
+ state->val += 2;
+
+ coroutine_switch(&state->c2, &state->c1);
+
+ state->val += 3;
+
+ coroutine_switch(&state->c2, &state->c1);
+
+ coroutine_jump(&state->master);
+}
+
+static void test1(size_t bufsz)
+{
+ void *buf1, *buf2;
+ struct coroutine_stack *stack1, *stack2;
+
+ buf1 = malloc(bufsz);
+ ok1(buf1 != NULL);
+ stack1 = coroutine_stack_init(buf1, bufsz, 0);
+ diag("buf1=%p stack1=%p bufsz=0x%zx overhead=0x%zx\n",
+ buf1, stack1, bufsz, COROUTINE_STK_OVERHEAD);
+ ok1(coroutine_stack_valid(stack1));
+ ok1(coroutine_stack_size(stack1) == bufsz - COROUTINE_STK_OVERHEAD);
+
+ buf2 = malloc(bufsz);
+ ok1(buf2 != NULL);
+ stack2 = coroutine_stack_init(buf2, bufsz, 0);
+ ok1(coroutine_stack_valid(stack2));
+ ok1(coroutine_stack_size(stack2) == bufsz - COROUTINE_STK_OVERHEAD);
+
+ if (COROUTINE_AVAILABLE) {
+ struct state s;
+
+ coroutine_init(&s.c1, f1, &s, stack1);
+ coroutine_init(&s.c2, f2, &s, stack2);
+
+ coroutine_switch(&s.master, &s.c1);
+ } else {
+ skip(5, "Coroutines not available");
+ }
+
+ ok(1, "Completed test1");
+
+ coroutine_stack_release(stack1);
+ ok1(!coroutine_stack_valid(stack1));
+ free(buf1);
+ coroutine_stack_release(stack2);
+ ok1(!coroutine_stack_valid(stack2));
+ free(buf2);
+}
+
+
+int main(void)
+{
+ /* This is how many tests you plan to run */
+ plan_tests(14);
+
+ test1(8192);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}
diff --git a/ccan/coroutine/test/api-3.c b/ccan/coroutine/test/api-3.c
new file mode 100644
index 0000000..3ea10b1
--- /dev/null
+++ b/ccan/coroutine/test/api-3.c
@@ -0,0 +1,90 @@
+#include <stdlib.h>
+
+#include <ccan/coroutine/coroutine.h>
+#include <ccan/tap/tap.h>
+
+/* Test metadata */
+#define META_MAGIC 0x4d86aa82ec1892f6
+#define BUFSIZE 8192
+
+struct metadata {
+ uint64_t magic;
+};
+
+struct state {
+ struct coroutine_state ret;
+ unsigned long total;
+};
+
+/* Touch a bunch of stack */
+static void clobber(void *p)
+{
+ struct state *s = (struct state *)p;
+ char buf[BUFSIZE - COROUTINE_MIN_STKSZ];
+ int i;
+
+ for (i = 0; i < sizeof(buf); i++) {
+ buf[i] = random() & 0xff;
+ }
+
+ diag("Wrote random to buffer\n");
+
+ s->total = 0;
+ for (i = 0; i < sizeof(buf); i++) {
+ s->total += buf[i];
+ }
+
+ coroutine_jump(&s->ret);
+}
+
+static void test_metadata(struct coroutine_stack *stack)
+{
+ struct metadata *meta;
+
+ meta = coroutine_stack_to_metadata(stack, sizeof(*meta));
+ ok1(coroutine_stack_from_metadata(meta, sizeof(*meta)) == stack);
+
+ meta->magic = META_MAGIC;
+ ok1(meta->magic == META_MAGIC);
+
+ if (COROUTINE_AVAILABLE) {
+ struct coroutine_state t;
+ struct state s = {
+ };
+
+ coroutine_init(&t, clobber, &s, stack);
+ coroutine_switch(&s.ret, &t);
+ ok1(s.total != 0);
+ } else {
+ skip(1, "Coroutines not available");
+ }
+
+ ok1(coroutine_stack_to_metadata(stack, sizeof(*meta)) == meta);
+ ok1(coroutine_stack_from_metadata(meta, sizeof(*meta)) == stack);
+ ok1(meta->magic == META_MAGIC);
+}
+
+int main(void)
+{
+ char buf[BUFSIZE];
+ struct coroutine_stack *stack;
+
+ /* This is how many tests you plan to run */
+ plan_tests(9);
+
+ /* Fix seed so we get consistent, though pseudo-random results */
+ srandom(0);
+
+ stack = coroutine_stack_init(buf, sizeof(buf), sizeof(struct metadata));
+ ok1(stack != NULL);
+ ok1(coroutine_stack_valid(stack));
+ ok1(coroutine_stack_size(stack)
+ == BUFSIZE - COROUTINE_STK_OVERHEAD - sizeof(struct metadata));
+
+ test_metadata(stack);
+
+ coroutine_stack_release(stack);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}
--
2.9.3
_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 2/2] generator: Rewrite to use coroutine module
2016-11-28 13:18 [PATCH 1/2] coroutine: New module David Gibson
@ 2016-11-28 13:18 ` David Gibson
2016-11-29 3:47 ` [PATCH 1/2] coroutine: New module Rusty Russell
1 sibling, 0 replies; 3+ messages in thread
From: David Gibson @ 2016-11-28 13:18 UTC (permalink / raw)
To: ccan, rusty
Use the new coroutine module to abstract away our dependence on
ucontext.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
ccan/generator/_info | 9 ++++++---
ccan/generator/generator.c | 39 ++++++++++++++++++------------------
ccan/generator/generator.h | 49 ++++++++++++++--------------------------------
3 files changed, 41 insertions(+), 56 deletions(-)
diff --git a/ccan/generator/_info b/ccan/generator/_info
index 11753a5..489b9dd 100644
--- a/ccan/generator/_info
+++ b/ccan/generator/_info
@@ -2,6 +2,8 @@
#include <stdio.h>
#include <string.h>
+#include <ccan/coroutine/coroutine.h>
+
/**
* generator - generators for C
*
@@ -56,18 +58,19 @@ int main(int argc, char *argv[])
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/build_assert\n");
- printf("ccan/ptrint\n");
printf("ccan/alignof\n");
+ printf("ccan/coroutine\n");
printf("ccan/cppmagic\n");
printf("ccan/compiler\n");
return 0;
}
if (strcmp(argv[1], "ported") == 0) {
-#if HAVE_UCONTEXT
+#if COROUTINE_AVAILABLE
printf("\n");
+ return 1;
#else
- printf("Needs ucontext support\n");
+ printf("Needs coroutine support\n");
#endif
}
diff --git a/ccan/generator/generator.c b/ccan/generator/generator.c
index d217665..656cbe7 100644
--- a/ccan/generator/generator.c
+++ b/ccan/generator/generator.c
@@ -10,10 +10,18 @@
#define DEFAULT_STATE_SIZE 8192
#define STATE_ALIGN ALIGNOF(struct generator_)
-void *generator_new_(generator_wrapper_ *fn, size_t retsize)
+static size_t generator_metasize(size_t retsize)
+{
+ retsize = (retsize + STATE_ALIGN) & ~(STATE_ALIGN - 1);
+ return sizeof(struct generator_) + retsize;
+}
+
+void *generator_new_(void (*fn)(void *), size_t retsize)
{
char *base;
size_t size = DEFAULT_STATE_SIZE;
+ size_t metasize = generator_metasize(retsize);
+ struct coroutine_stack *stack;
void *ret;
struct generator_ *gen;
@@ -22,33 +30,26 @@ void *generator_new_(generator_wrapper_ *fn, size_t retsize)
abort();
retsize = (retsize + STATE_ALIGN) & ~(STATE_ALIGN - 1);
- ret = base + size - retsize;
- gen = (struct generator_ *)ret - 1;
+
+ stack = coroutine_stack_init(base, size, metasize);
+ gen = coroutine_stack_to_metadata(stack, metasize);
+ ret = gen + 1;
gen->base = base;
gen->complete = false;
- getcontext(&gen->gen);
-
- gen->gen.uc_stack.ss_sp = gen->base;
- gen->gen.uc_stack.ss_size = (char *)gen - base;
-
- if (HAVE_POINTER_SAFE_MAKECONTEXT) {
- makecontext(&gen->gen, (void *)fn, 1, ret);
- } else {
- ptrdiff_t si = ptr2int(ret);
- ptrdiff_t mask = (1UL << (sizeof(int) * 8)) - 1;
- int lo = si & mask;
- int hi = si >> (sizeof(int) * 8);
-
- makecontext(&gen->gen, (void *)fn, 2, lo, hi);
- }
+ coroutine_init(&gen->gen, fn, ret, stack);
return ret;
}
-void generator_free_(void *ret)
+void generator_free_(void *ret, size_t retsize)
{
struct generator_ *gen = generator_state_(ret);
+ size_t metasize = generator_metasize(retsize);
+ struct coroutine_stack *stack;
+
+ stack = coroutine_stack_from_metadata(gen, metasize);
+ coroutine_stack_release(stack);
free(gen->base);
}
diff --git a/ccan/generator/generator.h b/ccan/generator/generator.h
index 121f14a..7122f55 100644
--- a/ccan/generator/generator.h
+++ b/ccan/generator/generator.h
@@ -3,10 +3,6 @@
#define CCAN_GENERATOR_H
#include "config.h"
-#if !HAVE_UCONTEXT
-#error Generators require working ucontext.h functions
-#endif
-
#if !HAVE_TYPEOF
#error Generators require typeof
#endif
@@ -18,20 +14,22 @@
#include <assert.h>
#include <stddef.h>
#include <stdbool.h>
-#include <ucontext.h>
-#include <ccan/ptrint/ptrint.h>
-#include <ccan/build_assert/build_assert.h>
#include <ccan/cppmagic/cppmagic.h>
#include <ccan/compiler/compiler.h>
+#include <ccan/coroutine/coroutine.h>
+
+#if !COROUTINE_AVAILABLE
+#error Generators require coroutines
+#endif
/*
* Internals - included just for the use of inlines and macros
*/
struct generator_ {
- ucontext_t gen;
- ucontext_t caller;
+ struct coroutine_state gen;
+ struct coroutine_state caller;
bool complete;
void *base;
};
@@ -51,15 +49,8 @@ struct generator_incomplete_;
#define generator_rtype_(gen_) \
typeof((*(gen_))((struct generator_incomplete_ *)NULL))
-#if HAVE_POINTER_SAFE_MAKECONTEXT
-#define generator_wrapper_args_() void *ret
-#else
-#define generator_wrapper_args_() int lo, int hi
-#endif
-typedef void generator_wrapper_(generator_wrapper_args_());
-
-void *generator_new_(generator_wrapper_ *fn, size_t retsize);
-void generator_free_(void *ret);
+void *generator_new_(void (*fn)(void *), size_t retsize);
+void generator_free_(void *ret, size_t retsize);
/*
* API
@@ -128,22 +119,15 @@ void generator_free_(void *ret);
#define generator_def_(name_, rtype_, storage_, ...) \
static void name_##_generator_(rtype_ *ret_ \
generator_parms_inner_(__VA_ARGS__)); \
- static void name_##_generator__(generator_wrapper_args_()) \
+ static void name_##_generator__(void *ret) \
{ \
struct generator_ *gen; \
UNNEEDED generator_argstruct_(__VA_ARGS__) *args; \
- CPPMAGIC_IFELSE(HAVE_POINTER_SAFE_MAKECONTEXT) \
- () \
- (ptrdiff_t hilo = ((ptrdiff_t)hi << (8*sizeof(int))) \
- + (ptrdiff_t)lo; \
- rtype_ *ret = (rtype_ *)int2ptr(hilo); \
- BUILD_ASSERT(sizeof(struct generator_ *) \
- <= 2*sizeof(int));) \
gen = generator_state_(ret); \
args = generator_argp_(ret); \
name_##_generator_(ret generator_args_unpack_(__VA_ARGS__)); \
gen->complete = true; \
- setcontext(&gen->caller); \
+ coroutine_jump(&gen->caller); \
assert(0); \
} \
storage_ generator_t(rtype_) \
@@ -184,10 +168,8 @@ void generator_free_(void *ret);
#define generator_yield(val_) \
do { \
struct generator_ *gen_ = generator_state_(ret_); \
- int rc; \
*(ret_) = (val_); \
- rc = swapcontext(&gen_->gen, &gen_->caller); \
- assert(rc == 0); \
+ coroutine_switch(&gen_->gen, &gen_->caller); \
} while (0)
/**
@@ -202,13 +184,11 @@ void generator_free_(void *ret);
static inline void *generator_next_(void *ret_)
{
struct generator_ *gen = generator_state_(ret_);
- int rc;
if (gen->complete)
return NULL;
- rc = swapcontext(&gen->caller, &gen->gen);
- assert(rc == 0);
+ coroutine_switch(&gen->caller, &gen->gen);
return gen->complete ? NULL : ret_;
}
@@ -234,6 +214,7 @@ static inline void *generator_next_(void *ret_)
})
#define generator_free(gen_) \
- generator_free_((generator_rtype_(gen_) *)(gen_))
+ generator_free_((generator_rtype_(gen_) *)(gen_), \
+ sizeof(generator_rtype_(gen_)))
#endif /* CCAN_GENERATOR_H */
--
2.9.3
_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH 1/2] coroutine: New module
2016-11-28 13:18 [PATCH 1/2] coroutine: New module David Gibson
2016-11-28 13:18 ` [PATCH 2/2] generator: Rewrite to use coroutine module David Gibson
@ 2016-11-29 3:47 ` Rusty Russell
1 sibling, 0 replies; 3+ messages in thread
From: Rusty Russell @ 2016-11-29 3:47 UTC (permalink / raw)
To: David Gibson, ccan
David Gibson <david@gibson.dropbear.id.au> writes:
> + * This code has helper functions for implementing co-routines, that
> + * is, explicit co-operative context switching. It's intended to
> + * provide similar functionality to
> + *
> + * Author: David Gibson <david@gibson.dropbear.id.au>
I believe David Gibson provides more functionality than this module :)
> +bool coroutine_stack_valid(struct coroutine_stack *stack)
> +{
> + return stack
> + && (stack->magic == COROUTINE_STACK_MAGIC)
> + && (stack->size >= COROUTINE_MIN_STKSZ);
> +}
My (loose) standard here has been as in ccan/list: a pass-through
function which aborts if abortstring is non-NULL:
struct coroutine_stack *coroutine_stack_check(const struct coroutine_stack *stack, const char *abortstr);
Then in the header:
#ifdef CCAN_COROUTINE_DEBUG
#define coroutine_stack_debug(s, loc) coroutine_stack_check((s), loc)
#define coroutine_state_debug(s, loc) coroutine_state_check((s), loc)
#else
#define coroutine_stack_debug(s, loc) ((void)loc, s)
#define coroutine_state_debug(s, loc) ((void)loc, s)
#endif
And sprinkle those liberally through your code. That means they get a
nice crash if CCAN_COROUTINE_DEBUG is defined, otherwise they can still
call coroutine_stack_check() themselves if they want.
> +#if HAVE_UCONTEXT
> +void coroutine_init(struct coroutine_state *cs,
> + void (*fn)(void *), void *arg,
> + struct coroutine_stack *stack)
> +{
> + getcontext (&cs->uc);
> +
> + coroutine_uc_stack(&cs->uc.uc_stack, stack);
> +
> + if (HAVE_POINTER_SAFE_MAKECONTEXT) {
> + makecontext(&cs->uc, (void *)fn, 1, arg);
> + } else {
> + ptrdiff_t si = ptr2int(arg);
> + ptrdiff_t mask = (1UL << (sizeof(int) * 8)) - 1;
> + int lo = si & mask;
> + int hi = si >> (sizeof(int) * 8);
> +
> + makecontext(&cs->uc, (void *)fn, 2, lo, hi);
> + }
> +
> +}
> +
> +void coroutine_jump(const struct coroutine_state *to)
> +{
> + setcontext(&to->uc);
> + assert(0);
> +}
> +
> +void coroutine_switch(struct coroutine_state *from,
> + const struct coroutine_state *to)
> +{
> + int rc;
> +
> + rc = swapcontext(&from->uc, &to->uc);
> + assert(rc == 0);
> +}
> +#endif /* HAVE_UCONTEXT */
> diff --git a/ccan/coroutine/coroutine.h b/ccan/coroutine/coroutine.h
> new file mode 100644
> index 0000000..6eba4a3
> --- /dev/null
> +++ b/ccan/coroutine/coroutine.h
> @@ -0,0 +1,219 @@
> +/* Licensed under LGPLv2.1+ - see LICENSE file for details */
> +#ifndef CCAN_COROUTINE_H
> +#define CCAN_COROUTINE_H
> +#include "config.h"
> +
> +#include <stddef.h>
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <assert.h>
> +
> +#include <ccan/compiler/compiler.h>
> +
> +/**
> + * struct coroutine_stack
> + *
> + * Describes a stack suitable for executing a coroutine. This
> + * structure is always contained within the stack it describes.
> + */
> +struct coroutine_stack;
> +
> +/**
> + * struct coroutine_state
> + *
> + * Describes the state of an in-progress coroutine.
> + */
> +struct coroutine_state;
> +
> +/*
> + * Stack management
> + */
> +
> +/**
> + * COROUTINE_STK_OVERHEAD - internal stack overhead
> + *
> + * Number of bytes of a stack which coroutine needs for its own
> + * tracking information.
> + */
> +#define COROUTINE_STK_OVERHEAD (sizeof(uint64_t) + sizeof(size_t))
> +
> +/**
> + * COROUTINE_MIN_STKSZ - Minimum coroutine stack size
> + *
> + * Contains the minimum size for a coroutine stack (not including
> + * overhead). On systems with MINSTKSZ, guaranteed to be at least as
> + * large as MINSTKSZ.
> + */
> +#define COROUTINE_MIN_STKSZ 2048
> +
> +/**
> + * COROUTINE_STACK_MAGIC - Magic number for coroutine stacks
> + */
> +#define COROUTINE_STACK_MAGIC 0xc040c040574c574c
> +
> +
> +/**
> + * coroutine_stack_init - Prepare a coroutine stack in an existing buffer
> + * @buf: buffer to use for the coroutine stack
> + * @bufsize: size of @buf
> + * @metasize: size of metadata to add to the stack (not including
> + * coroutine internal overhead)
> + *
> + * Prepares @buf for use as a coroutine stack, returning a
> + * coroutine_stack *, allocated from within the buffer. Returns NULL
> + * on failure.
> + *
> + * This will fail if the bufsize < (COROUTINE_MIN_STKSZ +
> + * COROUTINE_STK_OVERHEAD + metasize).
> + */
> +struct coroutine_stack *coroutine_stack_init(void *buf, size_t bufsize,
> + size_t metasize);
> +
> +/**
> + * coroutine_stack_init - Stop using a coroutine stack
> + * @stack: coroutine stack to release
> + *
> + * This releases @stack, making it no longer suitable for use as a
> + * coroutine stack.
> + */
> +void coroutine_stack_release(struct coroutine_stack *stack);
> +
> +/**
> + * coroutine_stack_valid - Determine if a coroutine stack looks valid
> + * @stack: stack to check
> + *
> + * Returns true if @stack appears to be a valid coroutine stack, false
> + * otherwise.
> + */
> +bool coroutine_stack_valid(struct coroutine_stack *stack);
> +
> +/**
> + * coroutine_stack_to_metadata - Returns pointer to user's metadata
> + * allocated within the stack
> + * @stack: coroutine stack
> + * @metasize: size of metadata
> + *
> + * Returns a pointer to the metadata area within @stack. This is of
> + * size given at initialization time, and won't be overwritten by
> + * coroutines executing on the stack. It's up to the caller what to
> + * put in here. @metasize must be equal to the value passed to
> + * coroutine_stack_init().
> + */
> +static inline void *coroutine_stack_to_metadata(struct coroutine_stack *stack,
> + size_t metasize)
> +{
> +#if HAVE_STACK_GROWS_UPWARDS
> + return (char *)stack - metasize;
> +#else
> + return (char *)stack + COROUTINE_STK_OVERHEAD;
> +#endif
> +}
> +
> +/**
> + * coroutine_stack_from_metadata - Returns pointer to coroutine stack
> + * pointer given pointer to user metadata
> + * @metadat: user metadata within a stack
> + * @metasize: size of metadata
> + *
> + * Returns a pointer to the coroutine_stack handle within a stack.
> + * The argument must be a pointer returned by
> + * coroutine_stack_to_metadata() at an earlier time. @metasize must be
> + * equal to the value passed to coroutine_stack_init().
> + */
> +static inline struct coroutine_stack *
> +coroutine_stack_from_metadata(void *metadata, size_t metasize)
> +{
> +#if HAVE_STACK_GROWS_UPWARDS
> + return (struct coroutine_stack *)((char *)metadata + metasize);
> +#else
> + return (struct coroutine_stack *)((char *)metadata
> + - COROUTINE_STK_OVERHEAD);
> +#endif
> +}
> +
> +/**
> + * coroutine_stack_size - Return size of a coroutine stack
> + * @stack: coroutine stack
> + *
> + * Returns the size of the coroutine stack @stack. This does not
> + * include the overhead of struct coroutine_stack or metdata.
> + */
> +size_t coroutine_stack_size(const struct coroutine_stack *stack);
>
> +/**
> + * coroutine_init - Prepare a coroutine for execution
> + * @cs: coroutine_state structure to initialize
> + * @fn: function to start executing in the coroutine
> + * @arg: argument for @fn
> + * @stack: stack to use for the coroutine
> + *
> + * Prepares @cs as a new coroutine which will execute starting with
> + * function @fn, using stack @stack.
> + */
> +void coroutine_init(struct coroutine_state *cs,
> + void (*fn)(void *), void *arg,
> + struct coroutine_stack *stack);
Minor suggestion, typesafe wrapper might be nice here:
#include <ccan/typesafe_cb/typesafe_cb.h>
void coroutine_init_(struct coroutine_state *cs,
void (*fn)(void *), void *arg,
struct coroutine_stack *stack);
#define coroutine_init(cs, fn, arg, stack) \
coroutine_init_((cs), \
typesafe_cb(void, void *, (fn), (arg)), \
(void *)(arg), (stack))
But generally, very nice.
Cheers,
Rusty.
_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2016-11-29 4:04 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-11-28 13:18 [PATCH 1/2] coroutine: New module David Gibson
2016-11-28 13:18 ` [PATCH 2/2] generator: Rewrite to use coroutine module David Gibson
2016-11-29 3:47 ` [PATCH 1/2] coroutine: New module Rusty Russell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox