From fa8d01934681195aacea8b95c78f27ca6a660bdd Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Sat, 8 Apr 2023 01:41:32 +0200 Subject: Simplify async function --- CMakeLists.txt | 13 +- source/kit/async_function.h | 219 ++++++++-------------------- source/kit/dynamic_array.c | 3 - source/test/unittests/async_function.test.c | 138 ++++-------------- 4 files changed, 97 insertions(+), 276 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bd3356..b199e4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,10 +6,11 @@ if(NOT DEFINED CMAKE_BUILD_PARALLEL_LEVEL) set(CMAKE_BUILD_PARALLEL_LEVEL 4 CACHE STRING "" FORCE) endif() -option(KIT_DISABLE_SYSTEM_MALLOC "Disable system memory allocator" OFF) -option(KIT_DISABLE_SYSTEM_THREADS "Disable system threads" OFF) -option(KIT_ENABLE_CUSTOM_ALLOC_DISPATCH "Enable custom allocator dispatch" OFF) -option(KIT_ENABLE_TESTING "Enable testing" ON) +option(KIT_DISABLE_SYSTEM_MALLOC "Disable system memory allocator" OFF) +option(KIT_DISABLE_SYSTEM_THREADS "Disable system threads" OFF) +option(KIT_ENABLE_CUSTOM_ALLOC_DISPATCH "Enable custom allocator dispatch" OFF) +option(KIT_ENABLE_CUSTOM_ASYNC_FUNCTION_DISPATCH "Enable custom async function dispatch" OFF) +option(KIT_ENABLE_TESTING "Enable testing" ON) project( kit @@ -118,6 +119,10 @@ if(KIT_ENABLE_CUSTOM_ALLOC_DISPATCH) target_compile_definitions(kit PUBLIC KIT_ENABLE_CUSTOM_ALLOC_DISPATCH) endif() +if(KIT_ENABLE_CUSTOM_ASYNC_FUNCTION_DISPATCH) + target_compile_definitions(kit PUBLIC KIT_ENABLE_CUSTOM_ASYNC_FUNCTION_DISPATCH) +endif() + if(NOT KIT_DISABLE_SYSTEM_THREADS) find_package(Threads REQUIRED) target_link_libraries(kit PUBLIC Threads::Threads) diff --git a/source/kit/async_function.h b/source/kit/async_function.h index 6bb2e83..6b4616d 100644 --- a/source/kit/async_function.h +++ b/source/kit/async_function.h @@ -7,32 +7,16 @@ extern "C" { #endif -enum { - KIT_AF_REQUEST_EXECUTE = 0, - KIT_AF_REQUEST_RESUME = 1, - KIT_AF_REQUEST_JOIN = 2, - KIT_AF_REQUEST_RESUME_AND_JOIN = 3 -}; - typedef struct { int _; } kit_af_void; -typedef void (*kit_af_state_machine)(void *self_void_, int request_); -typedef void (*kit_af_execute)(void *state, void *coro_state, - int request); +typedef void (*kit_af_state_machine)(void *self_void_); -typedef struct { - long long _internal; - void *state; - kit_af_execute execute; -} kit_af_execution_context; - -#define KIT_AF_STATE_DATA \ - struct { \ - int _index; \ - kit_af_state_machine _state_machine; \ - kit_af_execution_context _context; \ +#define KIT_AF_STATE_DATA \ + struct { \ + int _index; \ + kit_af_state_machine _state_machine; \ } typedef struct { @@ -41,6 +25,21 @@ typedef struct { #define KIT_AF_INTERNAL(coro_) (*((kit_af_type_void *) (coro_))) +#ifdef KIT_ENABLE_CUSTOM_ASYNC_FUNCTION_DISPATCH +/* Application should implement this function if custom async + * function dispatch is enabled. + * + * See KIT_ENABLE_CUSTOM_ASYNC_FUNCTION_DISPATCH macro. + */ +void kit_async_function_dispatch(void *promise); +#else +static void kit_async_function_dispatch(void *promise) { + /* Dynamic dispatch by default. + */ + KIT_AF_INTERNAL(promise)._state_machine(promise); +} +#endif + #define KIT_AF_STATE(ret_type_, name_, ...) \ struct name_##_coro_state_ { \ KIT_AF_STATE_DATA; \ @@ -48,22 +47,13 @@ typedef struct { __VA_ARGS__ \ } -#define KIT_AF_DECL(name_) void name_(void *self_void_, int request_) - -#define KIT_CORO_IMPL(name_) \ - KIT_AF_DECL(name_) { \ - struct name_##_coro_state_ *self = \ - (struct name_##_coro_state_ *) self_void_; \ - if (request_ != KIT_AF_REQUEST_EXECUTE) { \ - if (self->_context.execute != NULL) \ - self->_context.execute(self->_context.state, self_void_, \ - request_); \ - else if (request_ == KIT_AF_REQUEST_JOIN || \ - request_ == KIT_AF_REQUEST_RESUME_AND_JOIN) \ - self->_state_machine(self_void_, KIT_AF_REQUEST_EXECUTE); \ - return; \ - } \ - switch (self->_index) { \ +#define KIT_AF_DECL(name_) void name_(void *self_void_) + +#define KIT_CORO_IMPL(name_) \ + KIT_AF_DECL(name_) { \ + struct name_##_coro_state_ *self = \ + (struct name_##_coro_state_ *) self_void_; \ + switch (self->_index) { \ case 0:; #define KIT_AF_LINE_() __LINE__ @@ -94,6 +84,12 @@ typedef struct { #define KIT_STATIC_CORO_VOID(name_, ...) \ KIT_STATIC_CORO(kit_af_void, name_, __VA_ARGS__) +#define KIT_AF_EXECUTE(promise_) \ + kit_async_function_dispatch(&(promise_)) + +#define KIT_AF_NEXT(promise_) \ + (kit_async_function_dispatch(&(promise_)), (promise_).return_value) + #define KIT_AF_YIELD(...) \ do { \ self->_index = KIT_AF_LINE_(); \ @@ -122,44 +118,40 @@ typedef struct { return; \ } while (0) -#define KIT_AF_AWAIT(promise_) \ - do { \ - case KIT_AF_LINE_(): \ - if ((promise_)._index != -1) { \ - self->_index = KIT_AF_LINE_(); \ - (promise_)._state_machine(&(promise_), \ - KIT_AF_REQUEST_RESUME_AND_JOIN); \ - } \ - if ((promise_)._index != -1) \ - return; \ +#define KIT_AF_AWAIT(promise_) \ + do { \ + case KIT_AF_LINE_(): \ + if ((promise_)._index != -1) { \ + self->_index = KIT_AF_LINE_(); \ + kit_async_function_dispatch(&(promise_)); \ + } \ + if ((promise_)._index != -1) \ + return; \ } while (0) -#define KIT_AF_YIELD_AWAIT(promise_) \ - do { \ - case KIT_AF_LINE_(): \ - if ((promise_)._index != -1) { \ - self->_index = KIT_AF_LINE_(); \ - (promise_)._state_machine(&(promise_), \ - KIT_AF_REQUEST_RESUME_AND_JOIN); \ - self->return_value = (promise_).return_value; \ - return; \ - } \ +#define KIT_AF_YIELD_AWAIT(promise_) \ + do { \ + case KIT_AF_LINE_(): \ + if ((promise_)._index != -1) { \ + self->_index = KIT_AF_LINE_(); \ + kit_async_function_dispatch(&(promise_)); \ + self->return_value = (promise_).return_value; \ + return; \ + } \ } while (0) #define KIT_AF_TYPE(coro_) struct coro_##_coro_state_ -#define KIT_AF_INITIAL(coro_) \ - ._index = 0, ._state_machine = (coro_), \ - ._context = { .state = NULL, .execute = NULL } +#define KIT_AF_INITIAL(coro_) ._index = 0, ._state_machine = (coro_) #define KIT_AF_CREATE(promise_, coro_, ...) \ KIT_AF_TYPE(coro_) \ promise_ = { KIT_AF_INITIAL(coro_), __VA_ARGS__ } -#define KIT_AF_INIT(promise_, coro_, ...) \ - do { \ - KIT_AF_CREATE(kit_af_temp_, coro_, __VA_ARGS__); \ - (promise_) = kit_af_temp_; \ +#define KIT_AF_INIT(promise_, coro_, ...) \ + do { \ + KIT_AF_CREATE(kit_af_temp_, coro_, __VA_ARGS__); \ + memcpy(&(promise_), &kit_af_temp_, sizeof kit_af_temp_); \ } while (0) #define KIT_AF_INIT_EXPLICIT(promise_, size_, coro_func_) \ @@ -168,59 +160,6 @@ typedef struct { (promise_)._state_machine = (coro_func_); \ } while (0) -#define KIT_AF_EXECUTION_CONTEXT(promise_, ...) \ - do { \ - kit_af_execution_context kit_af_temp_ = { ._internal = 0, \ - __VA_ARGS__ }; \ - (promise_)._context = kit_af_temp_; \ - } while (0) - -#define KIT_AF_RESUME(promise_) \ - (promise_)._state_machine(&(promise_), KIT_AF_REQUEST_RESUME) - -#define KIT_AF_RESUME_N(promises_, size_) \ - do { \ - int kit_af_index_; \ - for (kit_af_index_ = 0; kit_af_index_ < (size_); \ - kit_af_index_++) \ - KIT_AF_RESUME((promises_)[kit_af_index_]); \ - } while (0) - -#define KIT_AF_JOIN(promise_) \ - ((promise_)._state_machine(&(promise_), KIT_AF_REQUEST_JOIN), \ - (promise_).return_value) - -#define KIT_AF_JOIN_N(promises_, size_) \ - do { \ - int kit_af_index_; \ - for (kit_af_index_ = 0; kit_af_index_ < (size_); \ - kit_af_index_++) \ - (void) KIT_AF_JOIN((promises_)[kit_af_index_]); \ - } while (0) - -#define KIT_AF_RESUME_AND_JOIN(promise_) \ - ((promise_)._state_machine(&(promise_), \ - KIT_AF_REQUEST_RESUME_AND_JOIN), \ - (promise_).return_value) - -#define KIT_AF_RESUME_AND_JOIN_N(promises_, size_) \ - do { \ - KIT_AF_RESUME_N((promises_), (size_)); \ - KIT_AF_JOIN_N((promises_), (size_)); \ - } while (0) - -#define KIT_AF_RESUME_ALL(promises_) \ - KIT_AF_RESUME_N((promises_), \ - sizeof(promises_) / sizeof((promises_)[0])) - -#define KIT_AF_JOIN_ALL(promises_) \ - KIT_AF_JOIN_N((promises_), \ - sizeof(promises_) / sizeof((promises_)[0])) - -#define KIT_AF_RESUME_AND_JOIN_ALL(promises_) \ - KIT_AF_RESUME_AND_JOIN_N((promises_), sizeof(promises_) / \ - sizeof((promises_)[0])) - #define KIT_AF_FINISHED(promise_) ((promise_)._index == -1) #define KIT_AF_FINISHED_N(return_, promises_, size_) \ @@ -239,43 +178,12 @@ typedef struct { KIT_AF_FINISHED_N((return_), (promises_), \ sizeof(promises_) / sizeof((promises_)[0])) -#define KIT_AF_AWAIT_N(promises_, size_) \ - do { \ - int kit_af_done_; \ - case KIT_AF_LINE_(): \ - self->_index = KIT_AF_LINE_(); \ - KIT_AF_RESUME_AND_JOIN_N((promises_), (size_)); \ - KIT_AF_FINISHED_N(kit_af_done_, (promises_), (size_)); \ - if (!kit_af_done_) \ - return; \ - } while (0) - -#define KIT_AF_AWAIT_ALL(promises_) \ - KIT_AF_AWAIT_N((promises_), \ - sizeof(promises_) / sizeof((promises_)[0])) - -#define KIT_AF_EXECUTE(promise_) \ - KIT_AF_INTERNAL(promise_)._state_machine((promise_), \ - KIT_AF_REQUEST_EXECUTE) - #ifndef KIT_DISABLE_SHORT_NAMES -# ifndef KIT_DISABLE_AF_SELF_SHORTCUT -# define af self-> -# endif - -# define af_request kit_af_request -# define AF_REQUEST_EXECUTE KIT_AF_REQUEST_EXECUTE -# define AF_REQUEST_RESUME KIT_AF_REQUEST_RESUME -# define AF_REQUEST_JOIN KIT_AF_REQUEST_JOIN -# define AF_REQUEST_RESUME_AND_JOIN KIT_AF_REQUEST_RESUME_AND_JOIN # define af_void kit_af_void # define af_state_machine kit_af_state_machine -# define af_execute kit_af_execute -# define af_execution_context kit_af_execution_context # define af_type_void kit_af_type_void # define AF_STATE_DATA KIT_AF_STATE_DATA -# define AF_INTERNAL KIT_AF_INTERNAL # define AF_STATE KIT_AF_STATE # define AF_DECL KIT_AF_DECL # define CORO_IMPL KIT_CORO_IMPL @@ -286,6 +194,8 @@ typedef struct { # define STATIC_CORO KIT_STATIC_CORO # define STATIC_CORO_VOID KIT_STATIC_CORO_VOID # define CORO_VOID KIT_CORO_VOID +# define AF_EXECUTE KIT_AF_EXECUTE +# define AF_NEXT KIT_AF_NEXT # define AF_YIELD KIT_AF_YIELD # define AF_YIELD_VOID KIT_AF_YIELD_VOID # define AF_RETURN KIT_AF_RETURN @@ -297,22 +207,9 @@ typedef struct { # define AF_CREATE KIT_AF_CREATE # define AF_INIT KIT_AF_INIT # define AF_INIT_EXPLICIT KIT_AF_INIT_EXPLICIT -# define AF_EXECUTION_CONTEXT KIT_AF_EXECUTION_CONTEXT -# define AF_RESUME KIT_AF_RESUME -# define AF_RESUME_N KIT_AF_RESUME_N -# define AF_JOIN KIT_AF_JOIN -# define AF_JOIN_N KIT_AF_JOIN_N -# define AF_RESUME_AND_JOIN KIT_AF_RESUME_AND_JOIN -# define AF_RESUME_AND_JOIN_N KIT_AF_RESUME_AND_JOIN_N -# define AF_RESUME_ALL KIT_AF_RESUME_ALL -# define AF_JOIN_ALL KIT_AF_JOIN_ALL -# define AF_RESUME_AND_JOIN_ALL KIT_AF_RESUME_AND_JOIN_ALL # define AF_FINISHED KIT_AF_FINISHED # define AF_FINISHED_N KIT_AF_FINISHED_N # define AF_FINISHED_ALL KIT_AF_FINISHED_ALL -# define AF_AWAIT_N KIT_AF_AWAIT_N -# define AF_AWAIT_ALL KIT_AF_AWAIT_ALL -# define AF_EXECUTE KIT_AF_EXECUTE #endif #ifdef __cplusplus diff --git a/source/kit/dynamic_array.c b/source/kit/dynamic_array.c index d3eaf29..aece99e 100644 --- a/source/kit/dynamic_array.c +++ b/source/kit/dynamic_array.c @@ -8,7 +8,6 @@ void kit_da_init(kit_da_void_t *array, ptrdiff_t element_size, assert(array != NULL); assert(element_size > 0); assert(size >= 0); - assert(alloc.allocate != NULL); memset(array, 0, sizeof(kit_da_void_t)); @@ -44,8 +43,6 @@ void kit_da_resize(kit_da_void_t *array, ptrdiff_t element_size, } else { ptrdiff_t capacity = eval_capacity(array->capacity, size); - assert(array->alloc.allocate != NULL); - void *bytes = kit_alloc_dispatch( array->alloc, KIT_ALLOCATE, element_size * capacity, 0, NULL); diff --git a/source/test/unittests/async_function.test.c b/source/test/unittests/async_function.test.c index 5adc0e7..a10dbff 100644 --- a/source/test/unittests/async_function.test.c +++ b/source/test/unittests/async_function.test.c @@ -21,8 +21,9 @@ CORO_IMPL(test_bar) { CORO_END STATIC_CORO(int, test_gen, int i; int min; int max;) { - for (af i = af min; af i < af max; af i++) AF_YIELD(af i); - AF_RETURN(af max); + for (self->i = self->min; self->i < self->max; self->i++) + AF_YIELD(self->i); + AF_RETURN(self->max); } CORO_END @@ -34,54 +35,19 @@ STATIC_CORO_VOID(test_task, ) { CORO_END STATIC_CORO_VOID(test_nest_task, AF_TYPE(test_task) promise;) { - AF_INIT(af promise, test_task, ); - AF_AWAIT(af promise); - AF_AWAIT(af promise); - AF_AWAIT(af promise); + AF_INIT(self->promise, test_task, ); + AF_AWAIT(self->promise); + AF_AWAIT(self->promise); + AF_AWAIT(self->promise); } CORO_END STATIC_CORO(int, test_nest_generator, AF_TYPE(test_gen) promise;) { - AF_INIT(af promise, test_gen, .min = 1, .max = 3); - AF_YIELD_AWAIT(af promise); + AF_INIT(self->promise, test_gen, .min = 1, .max = 3); + AF_YIELD_AWAIT(self->promise); } CORO_END -STATIC_CORO(int, test_join_multiple, AF_TYPE(test_bar) promises[3];) { - int i; - for (i = 0; i < 3; i++) - AF_INIT(af promises[i], test_bar, .return_value = 0); - AF_RESUME_AND_JOIN_ALL(af promises); - AF_RETURN(af promises[0].return_value + - af promises[1].return_value + - af promises[2].return_value); -} -CORO_END - -STATIC_CORO(int, test_await_multiple, - AF_TYPE(test_bar) promises[3];) { - int i; - for (i = 0; i < 3; i++) - AF_INIT(af promises[i], test_bar, .return_value = 0); - AF_AWAIT_ALL(af promises); - AF_RETURN(af promises[0].return_value + - af promises[1].return_value + - af promises[2].return_value); -} -CORO_END - -void test_execute_lazy(void *_, void *coro, int request) { - if (request == AF_REQUEST_RESUME) - return; - AF_EXECUTE(coro); -} - -void test_execute_immediate(void *_, void *coro, int request) { - if (request == AF_REQUEST_JOIN) - return; - AF_EXECUTE(coro); -} - TEST("coroutine create") { AF_CREATE(promise, test_foo, ); REQUIRE(!AF_FINISHED(promise)); @@ -106,111 +72,67 @@ TEST("coroutine init with value") { REQUIRE(!AF_FINISHED(promise)); } -TEST("coroutine resume") { +TEST("coroutine create with value") { AF_CREATE(promise, test_foo, .return_value = -1); - AF_RESUME(promise); REQUIRE(promise.return_value == -1); REQUIRE(!AF_FINISHED(promise)); } -TEST("coroutine resume and join") { +TEST("coroutine execute and return") { AF_CREATE(promise, test_foo, ); - REQUIRE(AF_RESUME_AND_JOIN(promise) == 42); + REQUIRE(AF_NEXT(promise) == 42); REQUIRE(AF_FINISHED(promise)); } -TEST("coroutine resume and join manually") { - AF_CREATE(promise, test_foo, ); - AF_RESUME(promise); - REQUIRE(AF_JOIN(promise) == 42); - REQUIRE(AF_FINISHED(promise)); -} - -TEST("coroutine suspend") { +TEST("coroutine execute two steps") { AF_CREATE(promise, test_bar, .return_value = 0); - REQUIRE(AF_RESUME_AND_JOIN(promise) == 0); - REQUIRE(AF_RESUME_AND_JOIN(promise) == 42); + AF_EXECUTE(promise); + REQUIRE(promise.return_value == 0); + AF_EXECUTE(promise); + REQUIRE(promise.return_value == 42); } TEST("coroutine generator") { int i; AF_CREATE(promise, test_gen, .min = 10, .max = 15); - for (i = 0; i <= 5; i++) - REQUIRE(AF_RESUME_AND_JOIN(promise) == 10 + i); + for (i = 0; i <= 5; i++) REQUIRE(AF_NEXT(promise) == 10 + i); } TEST("coroutine status finished") { AF_CREATE(promise, test_bar, ); REQUIRE(!AF_FINISHED(promise)); - (void) AF_RESUME_AND_JOIN(promise); + AF_EXECUTE(promise); REQUIRE(!AF_FINISHED(promise)); - (void) AF_RESUME_AND_JOIN(promise); + AF_EXECUTE(promise); REQUIRE(AF_FINISHED(promise)); } TEST("coroutine task") { AF_CREATE(promise, test_task, ); - (void) AF_RESUME_AND_JOIN(promise); + AF_EXECUTE(promise); REQUIRE(!AF_FINISHED(promise)); - (void) AF_RESUME_AND_JOIN(promise); + AF_EXECUTE(promise); REQUIRE(!AF_FINISHED(promise)); - (void) AF_RESUME_AND_JOIN(promise); + AF_EXECUTE(promise); REQUIRE(AF_FINISHED(promise)); } TEST("coroutine nested task") { AF_CREATE(promise, test_nest_task, ); - (void) AF_RESUME_AND_JOIN(promise); + AF_EXECUTE(promise); REQUIRE(!AF_FINISHED(promise)); - (void) AF_RESUME_AND_JOIN(promise); + AF_EXECUTE(promise); REQUIRE(!AF_FINISHED(promise)); - (void) AF_RESUME_AND_JOIN(promise); + AF_EXECUTE(promise); REQUIRE(AF_FINISHED(promise)); } TEST("coroutine nested generator") { AF_CREATE(promise, test_nest_generator, ); - REQUIRE(AF_RESUME_AND_JOIN(promise) == 1); - REQUIRE(AF_RESUME_AND_JOIN(promise) == 2); - REQUIRE(AF_RESUME_AND_JOIN(promise) == 3); + REQUIRE(AF_NEXT(promise) == 1); + REQUIRE(AF_NEXT(promise) == 2); + REQUIRE(AF_NEXT(promise) == 3); REQUIRE(!AF_FINISHED(promise)); - (void) AF_RESUME_AND_JOIN(promise); - REQUIRE(AF_FINISHED(promise)); -} - -TEST("coroutine join multiple") { - AF_CREATE(promise, test_join_multiple, ); - REQUIRE(AF_RESUME_AND_JOIN(promise) == 0); - REQUIRE(AF_FINISHED(promise)); -} - -TEST("coroutine await multiple") { - AF_CREATE(promise, test_await_multiple, ); - REQUIRE(AF_RESUME_AND_JOIN(promise) == 0); - REQUIRE(AF_RESUME_AND_JOIN(promise) == 42 * 3); - REQUIRE(AF_FINISHED(promise)); -} - -TEST("coroutine custom execution context lazy") { - AF_CREATE(promise, test_foo, .return_value = 0); - AF_EXECUTION_CONTEXT(promise, .state = NULL, - .execute = test_execute_lazy); - AF_RESUME(promise); - REQUIRE(promise.return_value == 0); - REQUIRE(!AF_FINISHED(promise)); - (void) AF_JOIN(promise); - REQUIRE(promise.return_value == 42); - REQUIRE(AF_FINISHED(promise)); -} - -TEST("coroutine custom execution context immediate") { - AF_CREATE(promise, test_foo, .return_value = 0); - AF_EXECUTION_CONTEXT(promise, .state = NULL, - .execute = test_execute_immediate); - AF_RESUME(promise); - REQUIRE(promise.return_value == 42); - REQUIRE(AF_FINISHED(promise)); - (void) AF_JOIN(promise); - REQUIRE(promise.return_value == 42); + AF_EXECUTE(promise); REQUIRE(AF_FINISHED(promise)); } -- cgit v1.2.3