From 8aa3c86ce9a5610140f121294333f281bd8c33f1 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 16 Dec 2017 20:54:17 +0100 Subject: [PATCH] Add support of C++ for workers. --- README.md | 4 +-- m-worker.h | 60 ++++++++++++++++++++++++++++++++++++++++---- tests/test-mworker.c | 4 +-- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f319ec69..b4ad6572 100644 --- a/README.md +++ b/README.md @@ -3350,8 +3350,8 @@ by an asynchronous worker. cannot use return / goto to go outside the flow). 'input' is the list of input variables of the 'core' block within "( )". 'output' is the list of output variables of the 'core' block within "( )". -This macro needs either GCC (for nested function) or CLANG (for blocks) to -work. +This macro needs either GCC (for nested function) or CLANG (for blocks) +or a C++11 compiler (for lambda and functional) to work. NOTE1: Even if nested functions are used for GCC, it doesn't generate a trampoline and the stack doesn't need to be executable. diff --git a/m-worker.h b/m-worker.h index a4940d04..11cfa07c 100644 --- a/m-worker.h +++ b/m-worker.h @@ -49,32 +49,52 @@ So you need to compile with "-fblocks" and link with "-lBlocksRuntime" if you use clang & want to use the MACRO version. + if C++, you will use Lambda function (and std::function) instead + (We don't support pre-C++11 compiler). + Otherwise we go for nested function for the MACRO version. */ -#if defined(__has_extension) && !defined(WORKER_CLANG_BLOCK) +#ifdef __cplusplus +# define WORKER_CPP_FUNCTION 1 +# include +#elif defined(__has_extension) && !defined(WORKER_CLANG_BLOCK) # if __has_extension(blocks) -# define WORKER_CLANG_BLOCK 1 +# define WORKER_CLANG_BLOCK 1 # endif #endif + #ifndef WORKER_CLANG_BLOCK # define WORKER_CLANG_BLOCK 0 #endif +#ifndef WORKER_CPP_FUNCTION +# define WORKER_CPP_FUNCTION 0 +#endif + +/* Control that not both options are selected at the same times. + Note: there are not really incompatible, but if we use C++ we shall go to + lambda directly (there is no need to support blocks)! */ +#if WORKER_CLANG_BLOCK && WORKER_CPP_FUNCTION +# error WORKER_CPP_FUNCTION and WORKER_CLANG_BLOCK are both defined (not supported). +#endif /* This type defines a work order */ -typedef struct { +typedef struct work_order_s { struct worker_block_s *block; void * data; void (*func) (void *data); #if WORKER_CLANG_BLOCK void (^blockFunc)(void *data); #endif +#if WORKER_CPP_FUNCTION + std::function function; +#endif } worker_order_t; /* Define the macros needed to initialize an order. * * MACRO to be used to send an empty order to stop the thread * * MACRO to complete the not-used fields */ -#if WORKER_CLANG_BLOCK +#if WORKER_CLANG_BLOCK || WORKER_CPP_FUNCTION # define WORKER_EMPTY_ORDER { NULL, NULL, NULL, NULL } # define WORKER_EXTRA_ORDER , NULL #else @@ -83,7 +103,7 @@ typedef struct { #endif /* This type defines a worker */ -typedef struct { +typedef struct worker_thread_s { m_thread_t id; } worker_thread_t; @@ -150,6 +170,12 @@ workeri_thread(void *arg) if (w.func == NULL) w.blockFunc(w.data); else +#endif +#if WORKER_CPP_FUNCTION + //printf ("Running %s f=%p b=%p\n", (w.function == NULL) ? "Lambda" : "Function", w.func, w.blockFunc); + if (w.function) + w.function(w.data); + else #endif w.func(w.data); worker_queue_pop_release(g->queue_g); @@ -242,6 +268,25 @@ worker_spawn_block(worker_t g, worker_block_t block, void (^func)(void *data), v } #endif +#if WORKER_CPP_FUNCTION +/* Spawn or not the given work order to workers, + or do it ourself if no worker is available */ +static inline void +worker_spawn_function(worker_t g, worker_block_t block, std::function func, void *data) +{ + const worker_order_t w = { block, data, NULL, func }; + if (M_UNLIKELY (!worker_queue_full_p(g->queue_g)) + && worker_queue_push (g->queue_g, w) == true) { + //printf ("Sending data to thread as block: %p (block: %d / %d)\n", data, block->num_spawn, block->num_terminated_spawn); + atomic_fetch_add (&block->num_spawn, 1); + return; + } + //printf ("Running data ourself as block: %p\n", data); + /* No thread available. Call the function ourself */ + func (data); +} +#endif + /* Wait for all work orders to be finished */ static inline void worker_sync(worker_block_t block) @@ -271,12 +316,17 @@ worker_count(worker_t g) WORKER_DEF_DATA(_input, _output) \ WORKER_DEF_SUBBLOCK(_input, _output, _core) \ worker_spawn_block ((_worker), (_block), WORKER_SPAWN_SUBFUNC_NAME, &WORKER_SPAWN_DATA_NAME) +#elif WORKER_CPP_FUNCTION +// TODO: Explicit pass all arguments by reference. +#define WORKER_SPAWN(_worker, _block, _input, _core, _output) \ + worker_spawn_function ((_worker), (_block), [&](void *param) {(void)param ; _core } , NULL) #else #define WORKER_SPAWN(_worker, _block, _input, _core, _output) \ WORKER_DEF_DATA(_input, _output) \ WORKER_DEF_SUBFUNC(_input, _output, _core) \ worker_spawn ((_worker), (_block), WORKER_SPAWN_SUBFUNC_NAME, &WORKER_SPAWN_DATA_NAME) #endif + #define WORKER_SPAWN_STRUCT_NAME M_C(worker_data_s_, __LINE__) #define WORKER_SPAWN_DATA_NAME M_C(worker_data_, __LINE__) #define WORKER_SPAWN_SUBFUNC_NAME M_C(worker_subfunc_, __LINE__) diff --git a/tests/test-mworker.c b/tests/test-mworker.c index 99d0ccad..c10bb0ba 100644 --- a/tests/test-mworker.c +++ b/tests/test-mworker.c @@ -31,7 +31,7 @@ struct fib2_s { int x, n; }; static void subfunc_1 (void *data) { - struct fib2_s *f = data; + struct fib2_s *f = M_ASSIGN_CAST (struct fib2_s *, data); f->x = fib (f->n ); } static int fib(int n) @@ -58,7 +58,7 @@ static void test1(void) worker_clear(w_g); } -#if defined(__GNUC__) && (!defined(__clang__) || WORKER_CLANG_BLOCK) +#if defined(__GNUC__) && (!defined(__clang__) || WORKER_CLANG_BLOCK || WORKER_CPP_FUNCTION) /* The macro version will generate warnings about shadow variables. There is no way to avoid this. */