From e5e15c04bc58c34906e6d7cfcbad68d1a5617563 Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Fri, 27 Sep 2024 17:25:36 -0600 Subject: wip --- libcr/CMakeLists.txt | 10 ++++ libcr/coroutine.c | 2 +- libcr/coroutine.h | 130 ---------------------------------------- libcr/include/libcr/coroutine.h | 130 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 131 deletions(-) create mode 100644 libcr/CMakeLists.txt delete mode 100644 libcr/coroutine.h create mode 100644 libcr/include/libcr/coroutine.h (limited to 'libcr') diff --git a/libcr/CMakeLists.txt b/libcr/CMakeLists.txt new file mode 100644 index 0000000..ad7997f --- /dev/null +++ b/libcr/CMakeLists.txt @@ -0,0 +1,10 @@ +# libcr/CMakeLists.txt - TODO +# +# Copyright (C) 2024 Luke T. Shumaker +# SPDX-Licence-Identifier: AGPL-3.0-or-later + +add_library(libcr INTERFACE) +target_sources(libcr INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/coroutine.c +) +target_include_directories(libcr SYSTEM INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) diff --git a/libcr/coroutine.c b/libcr/coroutine.c index 12d4a25..34a96b6 100644 --- a/libcr/coroutine.c +++ b/libcr/coroutine.c @@ -10,7 +10,7 @@ #include #include -#include "coroutine.h" +#include "libcr/coroutine.h" /* Configuration **************************************************************/ diff --git a/libcr/coroutine.h b/libcr/coroutine.h deleted file mode 100644 index 4d83181..0000000 --- a/libcr/coroutine.h +++ /dev/null @@ -1,130 +0,0 @@ -/* coroutine.h - Simple embeddable coroutine implementation - * - * Copyright (C) 2024 Luke T. Shumaker - * SPDX-Licence-Identifier: AGPL-3.0-or-later - */ - -/** - * A coroutine is a cooperatively-multitasked form of threading. - * - * This coroutine.{h,c} form a lightweight coroutine implementation - * for non-multithreaded environments (only one coroutine may be - * running at a time (so no m:n scheduling), coroutine_{add,main} may - * not be called concurrently with eachother). - * - * Unlike many other lightweight or embeddable coroutine - * implementations, coroutine.{h,c} allow coroutines to use the stack - * as normal C functions and do not forbid switch() blocks in - * coroutines. - * - * See also: coroutine_rpc.h is a request/response system built on top - * of coroutine.{h,c}. - * - * See also: coroutine_chan.h is a 1-way channel system built on top - * of coroutine.{h,c}. - */ -#ifndef _COROUTINE_H_ -#define _COROUTINE_H_ - -#include /* for size_t */ -#include /* for bool */ - -/* typedefs *******************************************************************/ - -/** - * A cid_t is a "coroutine ID", used to refer to a coroutine. - * - * May be reused if a coroutine exits. - * - * Valid IDs are `1 <= cid <= COROUTINE_NUM`; `0` is used for - * "none/invalid". - */ -typedef size_t cid_t; - -/** - * The root function for a coroutine; a coroutine function should be - * declared as - * - * COROUTINE myfunc(void *_args) { - * myargtype *args = args; - * cr_begin(); - * - * ... - * - * cr_end(); - * } - * - * The function MUST NOT ever return (GCC enforces this); call - * cr_exit() or cr_end() instead of returning. - * - * When creating a coroutine with coroutine_add(), the bit before - * cr_begin() runs before coroutine_add() returns; if `_args` points - * to a place on another coroutine's stack that may go away, then this - * is an opportunity to copy it to this coroutine's stack. Otherwise - * you shouldn't do anything else before calling cr_begin(). - * Specifically, coroutine_add() and - * cr_{yield,pause_and_yield,exit,end}() are explicitly forbidden to - * call from within a coroutine before cr_begin() (note that the - * cr_rpc_*() and cr_chan_*() macros call these functions). - */ -typedef void (*cr_fn_t)(void *args); -#define COROUTINE __attribute__ ((noreturn)) void - -/* managing coroutines ********************************************************/ - -/** - * Call `fn(args)` in a new coroutine with stack size `stack_size`. - * - * See the doc comment on c_fn_t for the requirements imposed on fn. - * - * May be called from outside any coroutine (before calling - * coroutine_main()) or from inside of a coroutine (after it cas - * called cr_begin()). - * - * Returns the cid of the newly-created coroutine. May return 0 if - * there are already COROUTINE_NUM active coroutines. - */ -cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args); - -/** - * Like coroutine_add_with_stack_size(), but uses a default stack size so - * you don't need to think about it. - */ -#define coroutine_add(fn, args) coroutine_add_with_stack_size(0, fn, args) - -/** - * The main scheduler loop. - * - * "Should" never return, but will print a message to stderr and - * return if the program has deadlocked and there are no runnable - * coroutines. So be sure to call coroutine_add() at least once - * before calling this. - */ -void coroutine_main(void); - -/* inside of coroutines *******************************************************/ - -/** cr_begin() goes at the beginning of a coroutine, after it has initialized its stack. */ -void cr_begin( void); -/** cr_exit() terminates the currently-running coroutine. */ -__attribute__ ((noreturn)) void cr_exit(void); -/** cr_yield() switches to another coroutine (if there is another runnable coroutine to switch to). */ -void cr_yield(void); -/** cr_pause_and_yield() marks the current coroutine as not-runnable and switches to another coroutine. */ -void cr_pause_and_yield(void); -/** cr_unpause() marks a coroutine as runnable that has previously marked itself as non-runnable with cr_pause_and_yield(). */ -void cr_unpause(cid_t); -/** cr_unpause_from_sighandler() is like cr_unpause(), but safe to call from a signal handler that might race with the coroutine actually pausing itself. */ -void cr_unpause_from_sighandler(cid_t); -/** cr_end() is a counterpart to cr_begin(), but is really just cr_exit(). */ -#define cr_end cr_exit - -/** cr_getcid() returns the cid of the currently-running coroutine. */ -cid_t cr_getcid(void); - -/** - * It is not possible for one coroutine to kill another or to mark - * another as paused; a coroutine may only do those things to itself. - */ - -#endif /* _COROUTINE_H_ */ diff --git a/libcr/include/libcr/coroutine.h b/libcr/include/libcr/coroutine.h new file mode 100644 index 0000000..5c2d608 --- /dev/null +++ b/libcr/include/libcr/coroutine.h @@ -0,0 +1,130 @@ +/* libcr/coroutine.h - Simple embeddable coroutine implementation + * + * Copyright (C) 2024 Luke T. Shumaker + * SPDX-Licence-Identifier: AGPL-3.0-or-later + */ + +/** + * A coroutine is a cooperatively-multitasked form of threading. + * + * This coroutine.{h,c} form a lightweight coroutine implementation + * for non-multithreaded environments (only one coroutine may be + * running at a time (so no m:n scheduling), coroutine_{add,main} may + * not be called concurrently with eachother). + * + * Unlike many other lightweight or embeddable coroutine + * implementations, coroutine.{h,c} allow coroutines to use the stack + * as normal C functions and do not forbid switch() blocks in + * coroutines. + * + * See also: coroutine_rpc.h is a request/response system built on top + * of coroutine.{h,c}. + * + * See also: coroutine_chan.h is a 1-way channel system built on top + * of coroutine.{h,c}. + */ +#ifndef _COROUTINE_H_ +#define _COROUTINE_H_ + +#include /* for size_t */ +#include /* for bool */ + +/* typedefs *******************************************************************/ + +/** + * A cid_t is a "coroutine ID", used to refer to a coroutine. + * + * May be reused if a coroutine exits. + * + * Valid IDs are `1 <= cid <= COROUTINE_NUM`; `0` is used for + * "none/invalid". + */ +typedef size_t cid_t; + +/** + * The root function for a coroutine; a coroutine function should be + * declared as + * + * COROUTINE myfunc(void *_args) { + * myargtype *args = args; + * cr_begin(); + * + * ... + * + * cr_end(); + * } + * + * The function MUST NOT ever return (GCC enforces this); call + * cr_exit() or cr_end() instead of returning. + * + * When creating a coroutine with coroutine_add(), the bit before + * cr_begin() runs before coroutine_add() returns; if `_args` points + * to a place on another coroutine's stack that may go away, then this + * is an opportunity to copy it to this coroutine's stack. Otherwise + * you shouldn't do anything else before calling cr_begin(). + * Specifically, coroutine_add() and + * cr_{yield,pause_and_yield,exit,end}() are explicitly forbidden to + * call from within a coroutine before cr_begin() (note that the + * cr_rpc_*() and cr_chan_*() macros call these functions). + */ +typedef void (*cr_fn_t)(void *args); +#define COROUTINE __attribute__ ((noreturn)) void + +/* managing coroutines ********************************************************/ + +/** + * Call `fn(args)` in a new coroutine with stack size `stack_size`. + * + * See the doc comment on c_fn_t for the requirements imposed on fn. + * + * May be called from outside any coroutine (before calling + * coroutine_main()) or from inside of a coroutine (after it cas + * called cr_begin()). + * + * Returns the cid of the newly-created coroutine. May return 0 if + * there are already COROUTINE_NUM active coroutines. + */ +cid_t coroutine_add_with_stack_size(size_t stack_size, cr_fn_t fn, void *args); + +/** + * Like coroutine_add_with_stack_size(), but uses a default stack size so + * you don't need to think about it. + */ +#define coroutine_add(fn, args) coroutine_add_with_stack_size(0, fn, args) + +/** + * The main scheduler loop. + * + * "Should" never return, but will print a message to stderr and + * return if the program has deadlocked and there are no runnable + * coroutines. So be sure to call coroutine_add() at least once + * before calling this. + */ +void coroutine_main(void); + +/* inside of coroutines *******************************************************/ + +/** cr_begin() goes at the beginning of a coroutine, after it has initialized its stack. */ +void cr_begin( void); +/** cr_exit() terminates the currently-running coroutine. */ +__attribute__ ((noreturn)) void cr_exit(void); +/** cr_yield() switches to another coroutine (if there is another runnable coroutine to switch to). */ +void cr_yield(void); +/** cr_pause_and_yield() marks the current coroutine as not-runnable and switches to another coroutine. */ +void cr_pause_and_yield(void); +/** cr_unpause() marks a coroutine as runnable that has previously marked itself as non-runnable with cr_pause_and_yield(). */ +void cr_unpause(cid_t); +/** cr_unpause_from_sighandler() is like cr_unpause(), but safe to call from a signal handler that might race with the coroutine actually pausing itself. */ +void cr_unpause_from_sighandler(cid_t); +/** cr_end() is a counterpart to cr_begin(), but is really just cr_exit(). */ +#define cr_end cr_exit + +/** cr_getcid() returns the cid of the currently-running coroutine. */ +cid_t cr_getcid(void); + +/** + * It is not possible for one coroutine to kill another or to mark + * another as paused; a coroutine may only do those things to itself. + */ + +#endif /* _COROUTINE_H_ */ -- cgit v1.2.3-2-g168b