All of lore.kernel.org
 help / color / mirror / Atom feed
* [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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.