[ccan] [PATCH 1/2] coroutine: New module
Rusty Russell
rusty at rustcorp.com.au
Tue Nov 29 14:47:38 AEDT 2016
David Gibson <david at 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 at 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.
More information about the ccan
mailing list