diff --git a/system/include/emscripten/threading.h b/system/include/emscripten/threading.h index 362cd561aa4c4..1d3f41df2fc29 100644 --- a/system/include/emscripten/threading.h +++ b/system/include/emscripten/threading.h @@ -9,11 +9,13 @@ #include #include -#include #include // for EMSCRIPTEN_RESULT #include +// Legacy proxying functions. See proxying.h for the new proxying system. +#include "threading_legacy.h" + #ifdef __cplusplus extern "C" { #endif @@ -44,182 +46,6 @@ int emscripten_futex_wait(volatile void/*uint32_t*/ * _Nonnull addr, uint32_t va // Returns -EINVAL if addr is null. int emscripten_futex_wake(volatile void/*uint32_t*/ * _Nonnull addr, int count); -typedef struct em_queued_call em_queued_call; - -// Encode function signatures into a single uint32_t integer. -// N.B. This encoding scheme is internal to the implementation, and can change -// in the future. Do not depend on the exact numbers in this scheme. -#define EM_FUNC_SIGNATURE unsigned int - -// The encoding scheme is as follows: -// - highest three bits identify the type of the return value -#define EM_FUNC_SIG_RETURN_VALUE_MASK (0x7U << 29) - -#define EM_FUNC_SIG_RETURN_VALUE_V 0 -#define EM_FUNC_SIG_RETURN_VALUE_I (0x1U << 29) -#define EM_FUNC_SIG_RETURN_VALUE_I64 (0x2U << 29) -#define EM_FUNC_SIG_RETURN_VALUE_F (0x3U << 29) -#define EM_FUNC_SIG_RETURN_VALUE_D (0x4U << 29) - -// - next highest four bits specify the number of input parameters to the -// function (allowed values are 0-12, inclusively) -#define EM_FUNC_SIG_NUM_PARAMETERS_SHIFT 25 -#define EM_FUNC_SIG_NUM_PARAMETERS_MASK (0xFU << EM_FUNC_SIG_NUM_PARAMETERS_SHIFT) -#define EM_FUNC_SIG_WITH_N_PARAMETERS(x) (((EM_FUNC_SIGNATURE)(x)) << EM_FUNC_SIG_NUM_PARAMETERS_SHIFT) - -// - starting from the lowest bits upwards, each pair of two subsequent bits -// specifies the type of an input parameter. -// That is, bits 1:0 encode the type of the first input, bits 3:2 encode the -// type of the second input, and so on. -#define EM_FUNC_SIG_ARGUMENTS_TYPE_MASK (~(EM_FUNC_SIG_RETURN_VALUE_MASK | EM_FUNC_SIG_NUM_PARAMETERS_MASK)) -#define EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_MASK 0x3U -#define EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_SHIFT 2 - -#define EM_FUNC_SIG_PARAM_I 0 -#define EM_FUNC_SIG_PARAM_I64 0x1U -#define EM_FUNC_SIG_PARAM_F 0x2U -#define EM_FUNC_SIG_PARAM_D 0x3U -#define EM_FUNC_SIG_SET_PARAM(i, type) ((EM_FUNC_SIGNATURE)(type) << (2*i)) - -// Extra types used in WebGL glGet*() calls (not used in proxying) -#define EM_FUNC_SIG_PARAM_B 0x4U -#define EM_FUNC_SIG_PARAM_F2I 0x5U - -// In total, the above encoding scheme gives the following 32-bit structure for -// the proxied function signatures (highest -> lowest bit order): -// RRRiiiiSbbaa99887766554433221100 -// where RRR is return type -// iiii is the number of inputs -// S denotes a special function (internal proxying mechanism for functions -// related to built-in threading APIs, like thread creation itself) -// 00-bb encode the type of up to 12 function parameters - -#define EM_FUNC_SIG_V (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(0)) -#define EM_FUNC_SIG_D (EM_FUNC_SIG_RETURN_VALUE_D | EM_FUNC_SIG_WITH_N_PARAMETERS(0)) -#define EM_FUNC_SIG_VI (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_F)) -#define EM_FUNC_SIG_FI (EM_FUNC_SIG_RETURN_VALUE_F | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_DI (EM_FUNC_SIG_RETURN_VALUE_D | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(2) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VIF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(2) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F)) -#define EM_FUNC_SIG_VFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(2) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F)) -#define EM_FUNC_SIG_VIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(3) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VIIF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(3) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F)) -#define EM_FUNC_SIG_VIFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(3) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F)) -#define EM_FUNC_SIG_VFFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(3) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F)) -#define EM_FUNC_SIG_VIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VIIFI (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VIFFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_F)) -#define EM_FUNC_SIG_VFFFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_F)) -#define EM_FUNC_SIG_IIFFF (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_F)) -#define EM_FUNC_SIG_VIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(5) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VIFFFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(5) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_F)) -#define EM_FUNC_SIG_VIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(6) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VIIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(7) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(8) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VIIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(9) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(8, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VIIIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(10) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(8, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(9, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_VIIIIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(11) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(8, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(9, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(10, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_I (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(0)) -#define EM_FUNC_SIG_II (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_III (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(2) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_IIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(3) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_IIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_IIIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(5) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_IIIIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(6) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_IIIIIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(7) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_IIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(8) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I)) -#define EM_FUNC_SIG_IIIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(9) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(8, EM_FUNC_SIG_PARAM_I)) - -#define EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(x) ((((EM_FUNC_SIGNATURE)x) & EM_FUNC_SIG_NUM_PARAMETERS_MASK) >> EM_FUNC_SIG_NUM_PARAMETERS_SHIFT) - -// There are some built-in special proxied functions, that embed the signatures -// inside the above encoding scheme -#define EM_FUNC_SIG_SPECIAL_INTERNAL (1 << 24) -#define EM_PROXIED_FUNC_SPECIAL(x) (EM_FUNC_SIG_SPECIAL_INTERNAL | ((x) << 20)) - -#define EM_PROXIED_RESIZE_OFFSCREENCANVAS (EM_PROXIED_FUNC_SPECIAL(0) | EM_FUNC_SIG_IIII) -#define EM_PROXIED_JS_FUNCTION (EM_PROXIED_FUNC_SPECIAL(1) | EM_FUNC_SIG_D) - -// Runs the given function synchronously on the main Emscripten runtime thread. -// If this thread is the main thread, the operation is immediately performed, -// and the result is returned. -// If the current thread is not the main Emscripten runtime thread (but a -// pthread), the function -// will be proxied to be called by the main thread. -// - Calling emscripten_sync_* functions requires that the application was -// compiled with pthreads support enabled (-pthread) and that the -// browser supports SharedArrayBuffer specification. -int emscripten_sync_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void *func_ptr __attribute__((nonnull)), ...); - -// The 'async' variant of the run_in_main_thread functions are otherwise the -// same as the synchronous ones, except that the operation is performed in a -// fire and forget manner. The call is placed to the command queue of the main -// Emscripten runtime thread, but its completion is not waited for. As a result, -// if the function did have a return value, the return value is not received. -// - Note that multiple asynchronous commands from a single pthread/Worker are -// guaranteed to be executed on the main thread in the program order they -// were called in. -void emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void *func_ptr __attribute__((nonnull)), ...); - -// The 'async_waitable' variant of the run_in_main_runtime_thread functions run -// like the 'async' variants, except that while the operation starts off -// asynchronously, the result is then later waited upon to receive the return -// value. -// - The object returned by this function call is dynamically allocated, and -// should be freed up via a call to emscripten_async_waitable_close() after -// the wait has been performed. -em_queued_call *emscripten_async_waitable_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void *func_ptr __attribute__((nonnull)), ...); - -// Since we can't validate the function pointer type, allow implicit casting of -// functions to void* without complaining. -#define emscripten_sync_run_in_main_runtime_thread(sig, func_ptr, ...) emscripten_sync_run_in_main_runtime_thread_((sig), (void*)(func_ptr),##__VA_ARGS__) -#define emscripten_async_run_in_main_runtime_thread(sig, func_ptr, ...) emscripten_async_run_in_main_runtime_thread_((sig), (void*)(func_ptr),##__VA_ARGS__) -#define emscripten_async_waitable_run_in_main_runtime_thread(sig, func_ptr, ...) emscripten_async_waitable_run_in_main_runtime_thread_((sig), (void*)(func_ptr),##__VA_ARGS__) - -EMSCRIPTEN_RESULT emscripten_wait_for_call_v(em_queued_call *call __attribute__((nonnull)), double timeoutMSecs); -EMSCRIPTEN_RESULT emscripten_wait_for_call_i(em_queued_call *call __attribute__((nonnull)), double timeoutMSecs, int *outResult); - -void emscripten_async_waitable_close(em_queued_call *call __attribute__((nonnull))); - -// Runs the given function on the specified thread. If we are currently on -// that target thread then we just execute the call synchronously; otherwise it -// is queued on that thread to execute asynchronously. -// Returns 1 if it executed the code (i.e., it was on the target thread), and 0 -// otherwise. -int emscripten_dispatch_to_thread_args(pthread_t target_thread, - EM_FUNC_SIGNATURE sig, - void* func_ptr __attribute__((nonnull)), - void* satellite, - va_list args); -int emscripten_dispatch_to_thread_(pthread_t target_thread, - EM_FUNC_SIGNATURE sig, - void* func_ptr __attribute__((nonnull)), - void* satellite, - ...); -#define emscripten_dispatch_to_thread( \ - target_thread, sig, func_ptr, satellite, ...) \ - emscripten_dispatch_to_thread_( \ - (target_thread), (sig), (void*)(func_ptr), (satellite), ##__VA_ARGS__) - -// Similar to emscripten_dispatch_to_thread, but always runs the -// function asynchronously, even if on the same thread. This is less efficient -// but may be simpler to reason about in some cases. -int emscripten_dispatch_to_thread_async_args(pthread_t target_thread, - EM_FUNC_SIGNATURE sig, - void* func_ptr __attribute__((nonnull)), - void* satellite, - va_list args); -int emscripten_dispatch_to_thread_async_(pthread_t target_thread, - EM_FUNC_SIGNATURE sig, - void* func_ptr __attribute__((nonnull)), - void* satellite, - ...); -#define emscripten_dispatch_to_thread_async( \ - target_thread, sig, func_ptr, satellite, ...) \ - emscripten_dispatch_to_thread_async_( \ - (target_thread), (sig), (void*)(func_ptr), (satellite), ##__VA_ARGS__) - // Returns 1 if the current thread is the thread that hosts the Emscripten // runtime. int emscripten_is_main_runtime_thread(void); @@ -241,9 +67,6 @@ void emscripten_current_thread_process_queued_calls(void); // Returns the thread ID of the thread that hosts the Emscripten runtime. pthread_t emscripten_main_runtime_thread_id(void); -#define emscripten_main_browser_thread_id() emscripten_main_runtime_thread_id() -#pragma clang deprecated(emscripten_main_browser_thread_id, "use emscripten_main_runtime_thread_id instead") - // Synchronously sleeps the calling thread for the given number of milliseconds. // Note: Calling this on the main browser thread is _very_ _very_ bad for // application logic throttling, because it does not save any battery, it will diff --git a/system/include/emscripten/threading_legacy.h b/system/include/emscripten/threading_legacy.h new file mode 100644 index 0000000000000..3bc9a0d2027a8 --- /dev/null +++ b/system/include/emscripten/threading_legacy.h @@ -0,0 +1,201 @@ +/* + * Copyright 2015 The Emscripten Authors. All rights reserved. + * Emscripten is available under two separate licenses, the MIT license and the + * University of Illinois/NCSA Open Source License. Both these licenses can be + * found in the LICENSE file. + * + * Legacy proxying functions. See proxying.h for the new proxying system. + */ + +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define emscripten_main_browser_thread_id() emscripten_main_runtime_thread_id() +#pragma clang deprecated(emscripten_main_browser_thread_id, "use emscripten_main_runtime_thread_id instead") + +typedef struct em_queued_call em_queued_call; + +// Encode function signatures into a single uint32_t integer. +// N.B. This encoding scheme is internal to the implementation, and can change +// in the future. Do not depend on the exact numbers in this scheme. +#define EM_FUNC_SIGNATURE unsigned int + +// The encoding scheme is as follows: +// - highest three bits identify the type of the return value +#define EM_FUNC_SIG_RETURN_VALUE_MASK (0x7U << 29) + +#define EM_FUNC_SIG_RETURN_VALUE_V 0 +#define EM_FUNC_SIG_RETURN_VALUE_I (0x1U << 29) +#define EM_FUNC_SIG_RETURN_VALUE_I64 (0x2U << 29) +#define EM_FUNC_SIG_RETURN_VALUE_F (0x3U << 29) +#define EM_FUNC_SIG_RETURN_VALUE_D (0x4U << 29) + +// - next highest four bits specify the number of input parameters to the +// function (allowed values are 0-12, inclusively) +#define EM_FUNC_SIG_NUM_PARAMETERS_SHIFT 25 +#define EM_FUNC_SIG_NUM_PARAMETERS_MASK (0xFU << EM_FUNC_SIG_NUM_PARAMETERS_SHIFT) +#define EM_FUNC_SIG_WITH_N_PARAMETERS(x) (((EM_FUNC_SIGNATURE)(x)) << EM_FUNC_SIG_NUM_PARAMETERS_SHIFT) + +// - starting from the lowest bits upwards, each pair of two subsequent bits +// specifies the type of an input parameter. +// That is, bits 1:0 encode the type of the first input, bits 3:2 encode the +// type of the second input, and so on. +#define EM_FUNC_SIG_ARGUMENTS_TYPE_MASK (~(EM_FUNC_SIG_RETURN_VALUE_MASK | EM_FUNC_SIG_NUM_PARAMETERS_MASK)) +#define EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_MASK 0x3U +#define EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_SHIFT 2 + +#define EM_FUNC_SIG_PARAM_I 0 +#define EM_FUNC_SIG_PARAM_I64 0x1U +#define EM_FUNC_SIG_PARAM_F 0x2U +#define EM_FUNC_SIG_PARAM_D 0x3U +#define EM_FUNC_SIG_SET_PARAM(i, type) ((EM_FUNC_SIGNATURE)(type) << (2*i)) + +// Extra types used in WebGL glGet*() calls (not used in proxying) +#define EM_FUNC_SIG_PARAM_B 0x4U +#define EM_FUNC_SIG_PARAM_F2I 0x5U + +// In total, the above encoding scheme gives the following 32-bit structure for +// the proxied function signatures (highest -> lowest bit order): +// RRRiiiiSbbaa99887766554433221100 +// where RRR is return type +// iiii is the number of inputs +// S denotes a special function (internal proxying mechanism for functions +// related to built-in threading APIs, like thread creation itself) +// 00-bb encode the type of up to 12 function parameters + +#define EM_FUNC_SIG_V (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(0)) +#define EM_FUNC_SIG_D (EM_FUNC_SIG_RETURN_VALUE_D | EM_FUNC_SIG_WITH_N_PARAMETERS(0)) +#define EM_FUNC_SIG_VI (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_F)) +#define EM_FUNC_SIG_FI (EM_FUNC_SIG_RETURN_VALUE_F | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_DI (EM_FUNC_SIG_RETURN_VALUE_D | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(2) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VIF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(2) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F)) +#define EM_FUNC_SIG_VFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(2) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F)) +#define EM_FUNC_SIG_VIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(3) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VIIF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(3) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F)) +#define EM_FUNC_SIG_VIFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(3) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F)) +#define EM_FUNC_SIG_VFFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(3) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F)) +#define EM_FUNC_SIG_VIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VIIFI (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VIFFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_F)) +#define EM_FUNC_SIG_VFFFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_F)) +#define EM_FUNC_SIG_IIFFF (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_F)) +#define EM_FUNC_SIG_VIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(5) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VIFFFF (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(5) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_F) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_F)) +#define EM_FUNC_SIG_VIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(6) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VIIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(7) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(8) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VIIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(9) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(8, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VIIIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(10) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(8, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(9, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_VIIIIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_V | EM_FUNC_SIG_WITH_N_PARAMETERS(11) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(8, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(9, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(10, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_I (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(0)) +#define EM_FUNC_SIG_II (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(1) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_III (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(2) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_IIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(3) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_IIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(4) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_IIIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(5) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_IIIIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(6) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_IIIIIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(7) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_IIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(8) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I)) +#define EM_FUNC_SIG_IIIIIIIIII (EM_FUNC_SIG_RETURN_VALUE_I | EM_FUNC_SIG_WITH_N_PARAMETERS(9) | EM_FUNC_SIG_SET_PARAM(0, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(1, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(2, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(3, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(4, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(5, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(6, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(7, EM_FUNC_SIG_PARAM_I) | EM_FUNC_SIG_SET_PARAM(8, EM_FUNC_SIG_PARAM_I)) + +#define EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(x) ((((EM_FUNC_SIGNATURE)x) & EM_FUNC_SIG_NUM_PARAMETERS_MASK) >> EM_FUNC_SIG_NUM_PARAMETERS_SHIFT) + +// There are some built-in special proxied functions, that embed the signatures +// inside the above encoding scheme +#define EM_FUNC_SIG_SPECIAL_INTERNAL (1 << 24) +#define EM_PROXIED_FUNC_SPECIAL(x) (EM_FUNC_SIG_SPECIAL_INTERNAL | ((x) << 20)) + +#define EM_PROXIED_RESIZE_OFFSCREENCANVAS (EM_PROXIED_FUNC_SPECIAL(0) | EM_FUNC_SIG_IIII) +#define EM_PROXIED_JS_FUNCTION (EM_PROXIED_FUNC_SPECIAL(1) | EM_FUNC_SIG_D) + +// Runs the given function synchronously on the main Emscripten runtime thread. +// If this thread is the main thread, the operation is immediately performed, +// and the result is returned. +// If the current thread is not the main Emscripten runtime thread (but a +// pthread), the function +// will be proxied to be called by the main thread. +// - Calling emscripten_sync_* functions requires that the application was +// compiled with pthreads support enabled (-pthread) and that the +// browser supports SharedArrayBuffer specification. +int emscripten_sync_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void *func_ptr __attribute__((nonnull)), ...); + +// The 'async' variant of the run_in_main_thread functions are otherwise the +// same as the synchronous ones, except that the operation is performed in a +// fire and forget manner. The call is placed to the command queue of the main +// Emscripten runtime thread, but its completion is not waited for. As a result, +// if the function did have a return value, the return value is not received. +// - Note that multiple asynchronous commands from a single pthread/Worker are +// guaranteed to be executed on the main thread in the program order they +// were called in. +void emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void *func_ptr __attribute__((nonnull)), ...); + +// The 'async_waitable' variant of the run_in_main_runtime_thread functions run +// like the 'async' variants, except that while the operation starts off +// asynchronously, the result is then later waited upon to receive the return +// value. +// - The object returned by this function call is dynamically allocated, and +// should be freed up via a call to emscripten_async_waitable_close() after +// the wait has been performed. +em_queued_call *emscripten_async_waitable_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void *func_ptr __attribute__((nonnull)), ...); + +// Since we can't validate the function pointer type, allow implicit casting of +// functions to void* without complaining. +#define emscripten_sync_run_in_main_runtime_thread(sig, func_ptr, ...) emscripten_sync_run_in_main_runtime_thread_((sig), (void*)(func_ptr),##__VA_ARGS__) +#define emscripten_async_run_in_main_runtime_thread(sig, func_ptr, ...) emscripten_async_run_in_main_runtime_thread_((sig), (void*)(func_ptr),##__VA_ARGS__) +#define emscripten_async_waitable_run_in_main_runtime_thread(sig, func_ptr, ...) emscripten_async_waitable_run_in_main_runtime_thread_((sig), (void*)(func_ptr),##__VA_ARGS__) + +EMSCRIPTEN_RESULT emscripten_wait_for_call_v(em_queued_call *call __attribute__((nonnull)), double timeoutMSecs); +EMSCRIPTEN_RESULT emscripten_wait_for_call_i(em_queued_call *call __attribute__((nonnull)), double timeoutMSecs, int *outResult); + +void emscripten_async_waitable_close(em_queued_call *call __attribute__((nonnull))); + +// Runs the given function on the specified thread. If we are currently on +// that target thread then we just execute the call synchronously; otherwise it +// is queued on that thread to execute asynchronously. +// Returns 1 if it executed the code (i.e., it was on the target thread), and 0 +// otherwise. +int emscripten_dispatch_to_thread_args(pthread_t target_thread, + EM_FUNC_SIGNATURE sig, + void* func_ptr __attribute__((nonnull)), + void* satellite, + va_list args); +int emscripten_dispatch_to_thread_(pthread_t target_thread, + EM_FUNC_SIGNATURE sig, + void* func_ptr __attribute__((nonnull)), + void* satellite, + ...); +#define emscripten_dispatch_to_thread( \ + target_thread, sig, func_ptr, satellite, ...) \ + emscripten_dispatch_to_thread_( \ + (target_thread), (sig), (void*)(func_ptr), (satellite), ##__VA_ARGS__) + +// Similar to emscripten_dispatch_to_thread, but always runs the +// function asynchronously, even if on the same thread. This is less efficient +// but may be simpler to reason about in some cases. +int emscripten_dispatch_to_thread_async_args(pthread_t target_thread, + EM_FUNC_SIGNATURE sig, + void* func_ptr __attribute__((nonnull)), + void* satellite, + va_list args); +int emscripten_dispatch_to_thread_async_(pthread_t target_thread, + EM_FUNC_SIGNATURE sig, + void* func_ptr __attribute__((nonnull)), + void* satellite, + ...); +#define emscripten_dispatch_to_thread_async( \ + target_thread, sig, func_ptr, satellite, ...) \ + emscripten_dispatch_to_thread_async_( \ + (target_thread), (sig), (void*)(func_ptr), (satellite), ##__VA_ARGS__) + +#ifdef __cplusplus +} +#endif diff --git a/system/lib/pthread/library_pthread.c b/system/lib/pthread/library_pthread.c index 4a5ca5de788c6..773e086681821 100644 --- a/system/lib/pthread/library_pthread.c +++ b/system/lib/pthread/library_pthread.c @@ -12,12 +12,9 @@ #include #include #include -#include #include #include -#include #include -#include #include #include #include @@ -116,290 +113,12 @@ void emscripten_thread_sleep(double msecs) { EM_THREAD_STATUS_SLEEPING, EM_THREAD_STATUS_RUNNING); } -// Allocator and deallocator for em_queued_call objects. -static em_queued_call* em_queued_call_malloc() { - em_queued_call* call = (em_queued_call*)malloc(sizeof(em_queued_call)); - assert(call); // Not a programming error, but use assert() in debug builds to catch OOM scenarios. - if (call) { - call->operationDone = 0; - call->functionPtr = 0; - call->satelliteData = 0; - } - return call; -} - -static void em_queued_call_free(em_queued_call* call) { - if (call) - free(call->satelliteData); - free(call); -} - -static void init_em_queued_call_args(em_queued_call* q, - EM_FUNC_SIGNATURE sig, - va_list args) { - EM_FUNC_SIGNATURE argumentsType = sig & EM_FUNC_SIG_ARGUMENTS_TYPE_MASK; - int numArguments = EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(sig); - for (int i = 0; i < numArguments; ++i) { - switch ((argumentsType & EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_MASK)) { - case EM_FUNC_SIG_PARAM_I: - q->args[i].i = va_arg(args, int); - break; - case EM_FUNC_SIG_PARAM_I64: - q->args[i].i64 = va_arg(args, int64_t); - break; - case EM_FUNC_SIG_PARAM_F: - q->args[i].f = (float)va_arg(args, double); - break; - case EM_FUNC_SIG_PARAM_D: - q->args[i].d = va_arg(args, double); - break; - } - argumentsType >>= EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_SHIFT; - } -} - -static em_queued_call* em_queued_call_create(EM_FUNC_SIGNATURE sig, - void* func, - void* satellite, - va_list args) { - em_queued_call* call = em_queued_call_malloc(); - if (call) { - call->functionEnum = sig; - call->functionPtr = func; - call->satelliteData = satellite; - init_em_queued_call_args(call, sig, args); - } - return call; -} - -void emscripten_async_waitable_close(em_queued_call* call) { - assert(call->operationDone); - em_queued_call_free(call); -} - -static void _do_call(void* arg) { - em_queued_call* q = (em_queued_call*)arg; - // C function pointer - assert(EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(q->functionEnum) <= EM_QUEUED_CALL_MAX_ARGS); - switch (q->functionEnum) { - case EM_PROXIED_RESIZE_OFFSCREENCANVAS: - q->returnValue.i = - _emscripten_set_offscreencanvas_size(q->args[0].cp, q->args[1].i, q->args[2].i); - break; - case EM_PROXIED_JS_FUNCTION: - q->returnValue.d = - _emscripten_receive_on_main_thread_js((intptr_t)q->functionPtr, q->callingThread, q->args[0].i, &q->args[1].d); - break; - case EM_FUNC_SIG_V: - ((em_func_v)q->functionPtr)(); - break; - case EM_FUNC_SIG_VI: - ((em_func_vi)q->functionPtr)(q->args[0].i); - break; - case EM_FUNC_SIG_VF: - ((em_func_vf)q->functionPtr)(q->args[0].f); - break; - case EM_FUNC_SIG_VII: - ((em_func_vii)q->functionPtr)(q->args[0].i, q->args[1].i); - break; - case EM_FUNC_SIG_VIF: - ((em_func_vif)q->functionPtr)(q->args[0].i, q->args[1].f); - break; - case EM_FUNC_SIG_VFF: - ((em_func_vff)q->functionPtr)(q->args[0].f, q->args[1].f); - break; - case EM_FUNC_SIG_VIII: - ((em_func_viii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i); - break; - case EM_FUNC_SIG_VIIF: - ((em_func_viif)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].f); - break; - case EM_FUNC_SIG_VIFF: - ((em_func_viff)q->functionPtr)(q->args[0].i, q->args[1].f, q->args[2].f); - break; - case EM_FUNC_SIG_VFFF: - ((em_func_vfff)q->functionPtr)(q->args[0].f, q->args[1].f, q->args[2].f); - break; - case EM_FUNC_SIG_VIIII: - ((em_func_viiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i); - break; - case EM_FUNC_SIG_VIIFI: - ((em_func_viifi)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].f, q->args[3].i); - break; - case EM_FUNC_SIG_VIFFF: - ((em_func_vifff)q->functionPtr)(q->args[0].i, q->args[1].f, q->args[2].f, q->args[3].f); - break; - case EM_FUNC_SIG_VFFFF: - ((em_func_vffff)q->functionPtr)(q->args[0].f, q->args[1].f, q->args[2].f, q->args[3].f); - break; - case EM_FUNC_SIG_VIIIII: - ((em_func_viiiii)q->functionPtr)( - q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i); - break; - case EM_FUNC_SIG_VIFFFF: - ((em_func_viffff)q->functionPtr)( - q->args[0].i, q->args[1].f, q->args[2].f, q->args[3].f, q->args[4].f); - break; - case EM_FUNC_SIG_VIIIIII: - ((em_func_viiiiii)q->functionPtr)( - q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i); - break; - case EM_FUNC_SIG_VIIIIIII: - ((em_func_viiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, - q->args[4].i, q->args[5].i, q->args[6].i); - break; - case EM_FUNC_SIG_VIIIIIIII: - ((em_func_viiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, - q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i); - break; - case EM_FUNC_SIG_VIIIIIIIII: - ((em_func_viiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, - q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i); - break; - case EM_FUNC_SIG_VIIIIIIIIII: - ((em_func_viiiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, - q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i, - q->args[9].i); - break; - case EM_FUNC_SIG_VIIIIIIIIIII: - ((em_func_viiiiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, - q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i, - q->args[9].i, q->args[10].i); - break; - case EM_FUNC_SIG_I: - q->returnValue.i = ((em_func_i)q->functionPtr)(); - break; - case EM_FUNC_SIG_II: - q->returnValue.i = ((em_func_ii)q->functionPtr)(q->args[0].i); - break; - case EM_FUNC_SIG_III: - q->returnValue.i = ((em_func_iii)q->functionPtr)(q->args[0].i, q->args[1].i); - break; - case EM_FUNC_SIG_IIII: - q->returnValue.i = ((em_func_iiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i); - break; - case EM_FUNC_SIG_IIIII: - q->returnValue.i = - ((em_func_iiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i); - break; - case EM_FUNC_SIG_IIIIII: - q->returnValue.i = ((em_func_iiiiii)q->functionPtr)( - q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i); - break; - case EM_FUNC_SIG_IIIIIII: - q->returnValue.i = ((em_func_iiiiiii)q->functionPtr)( - q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i); - break; - case EM_FUNC_SIG_IIIIIIII: - q->returnValue.i = ((em_func_iiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, - q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i); - break; - case EM_FUNC_SIG_IIIIIIIII: - q->returnValue.i = ((em_func_iiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, - q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i); - break; - case EM_FUNC_SIG_IIIIIIIIII: - q->returnValue.i = - ((em_func_iiiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, - q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i); - break; - default: - assert(0 && "Invalid Emscripten pthread _do_call opcode!"); - } - - // If the caller is detached from this operation, it is the main thread's responsibility to free - // up the call object. - if (q->calleeDelete) { - em_queued_call_free(q); - // No need to wake a listener, nothing is listening to this since the call object is detached. - } else { - // The caller owns this call object, it is listening to it and will free it up. - q->operationDone = 1; - emscripten_futex_wake(&q->operationDone, INT_MAX); - } -} - -EMSCRIPTEN_RESULT emscripten_wait_for_call_v(em_queued_call* call, double timeoutMSecs) { - int r; - - int done = atomic_load(&call->operationDone); - if (!done) { - double now = emscripten_get_now(); - double waitEndTime = now + timeoutMSecs; - emscripten_set_current_thread_status(EM_THREAD_STATUS_WAITPROXY); - while (!done && now < waitEndTime) { - r = emscripten_futex_wait(&call->operationDone, 0, waitEndTime - now); - done = atomic_load(&call->operationDone); - now = emscripten_get_now(); - } - emscripten_set_current_thread_status(EM_THREAD_STATUS_RUNNING); - } - if (done) - return EMSCRIPTEN_RESULT_SUCCESS; - else - return EMSCRIPTEN_RESULT_TIMED_OUT; -} - -EMSCRIPTEN_RESULT emscripten_wait_for_call_i( - em_queued_call* call, double timeoutMSecs, int* outResult) { - EMSCRIPTEN_RESULT res = emscripten_wait_for_call_v(call, timeoutMSecs); - if (res == EMSCRIPTEN_RESULT_SUCCESS && outResult) - *outResult = call->returnValue.i; - return res; -} - static struct pthread __main_pthread; pthread_t emscripten_main_runtime_thread_id() { return &__main_pthread; } -static pthread_t normalize_thread(pthread_t target_thread) { - assert(target_thread); - if (target_thread == EM_CALLBACK_THREAD_CONTEXT_MAIN_RUNTIME_THREAD) { - return emscripten_main_runtime_thread_id(); - } - if (target_thread == EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD) { - return pthread_self(); - } - return target_thread; -} - -// Execute `call` and return 1 only if already on the `target_thread`. Otherwise -// return 0. -static int maybe_call_on_current_thread(pthread_t target_thread, - em_queued_call* call) { - if (pthread_equal(target_thread, pthread_self())) { - _do_call(call); - return 1; - } - return 0; -} - -// Execute or proxy `call`. Return 1 if the work was executed or otherwise -// return 0. -static int do_dispatch_to_thread(pthread_t target_thread, - em_queued_call* call) { - target_thread = normalize_thread(target_thread); - if (maybe_call_on_current_thread(target_thread, call)) { - return 1; - } - emscripten_proxy_async( - emscripten_proxy_get_system_queue(), target_thread, _do_call, call); - return 0; -} - -void emscripten_async_run_in_main_thread(em_queued_call* call) { - do_dispatch_to_thread(emscripten_main_runtime_thread_id(), call); -} - -static void sync_run_in_main_thread(em_queued_call* call) { - emscripten_async_run_in_main_thread(call); - - // Enter to wait for the operation to complete. - emscripten_wait_for_call_v(call, INFINITY); -} - void emscripten_current_thread_process_queued_calls() { emscripten_proxy_execute_queue(emscripten_proxy_get_system_queue()); } @@ -409,163 +128,6 @@ void emscripten_main_thread_process_queued_calls() { emscripten_current_thread_process_queued_calls(); } -int emscripten_sync_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void* func_ptr, ...) { - em_queued_call q = {sig, func_ptr}; - - va_list args; - va_start(args, func_ptr); - init_em_queued_call_args(&q, sig, args); - va_end(args); - sync_run_in_main_thread(&q); - return q.returnValue.i; -} - -double _emscripten_run_on_main_thread_js(int index, int num_args, int64_t* buffer, int sync) { - em_queued_call q; - em_queued_call *c; - if (sync) { - q.operationDone = 0; - q.satelliteData = 0; - c = &q; - } else { - c = em_queued_call_malloc(); - } - c->calleeDelete = !sync; - c->functionEnum = EM_PROXIED_JS_FUNCTION; - // Index not needed to ever be more than 32-bit. - c->functionPtr = (void*)(intptr_t)index; - c->callingThread = pthread_self(); - assert(num_args+1 <= EM_QUEUED_JS_CALL_MAX_ARGS); - // The types are only known at runtime in these calls, so we store values that - // must be able to contain any valid JS value, including a 64-bit BigInt if - // BigInt support is enabled. We store to an i64, which can contain both a - // BigInt and a JS Number which is a 64-bit double. - c->args[0].i = num_args; - for (int i = 0; i < num_args; i++) { - c->args[i+1].i64 = buffer[i]; - } - - if (sync) { - sync_run_in_main_thread(&q); - // TODO: support BigInt return values somehow. - return q.returnValue.d; - } else { - // 'async' runs are fire and forget, where the caller detaches itself from the call object after - // returning here, and it is the callee's responsibility to free up the memory after the call - // has been performed. - emscripten_async_run_in_main_thread(c); - return 0; - } -} - -void emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void* func_ptr, ...) { - em_queued_call* q = em_queued_call_malloc(); - if (!q) - return; - q->functionEnum = sig; - q->functionPtr = func_ptr; - - va_list args; - va_start(args, func_ptr); - init_em_queued_call_args(q, sig, args); - va_end(args); - // 'async' runs are fire and forget, where the caller detaches itself from the call object after - // returning here, and it is the callee's responsibility to free up the memory after the call has - // been performed. - q->calleeDelete = 1; - emscripten_async_run_in_main_thread(q); -} - -em_queued_call* emscripten_async_waitable_run_in_main_runtime_thread_( - EM_FUNC_SIGNATURE sig, void* func_ptr, ...) { - em_queued_call* q = em_queued_call_malloc(); - if (!q) - return NULL; - q->functionEnum = sig; - q->functionPtr = func_ptr; - - va_list args; - va_start(args, func_ptr); - init_em_queued_call_args(q, sig, args); - va_end(args); - // 'async waitable' runs are waited on by the caller, so the call object needs to remain alive for - // the caller to access it after the operation is done. The caller is responsible in cleaning up - // the object after done. - q->calleeDelete = 0; - emscripten_async_run_in_main_thread(q); - return q; -} - -int emscripten_dispatch_to_thread_args(pthread_t target_thread, - EM_FUNC_SIGNATURE sig, - void* func_ptr, - void* satellite, - va_list args) { - em_queued_call* q = em_queued_call_create(sig, func_ptr, satellite, args); - assert(q); - // TODO: handle errors in a better way, this pattern appears in several places - // in this file. The current behavior makes the calling thread hang as - // it waits (for synchronous calls). - // If we failed to allocate, return 0 which means we did not execute anything - // (we also never will in that case). - if (!q) - return 0; - - // `q` will not be used after it is called, so let the call clean it up. - q->calleeDelete = 1; - return do_dispatch_to_thread(target_thread, q); -} - -int emscripten_dispatch_to_thread_(pthread_t target_thread, - EM_FUNC_SIGNATURE sig, - void* func_ptr, - void* satellite, - ...) { - va_list args; - va_start(args, satellite); - int ret = emscripten_dispatch_to_thread_args( - target_thread, sig, func_ptr, satellite, args); - va_end(args); - return ret; -} - -int emscripten_dispatch_to_thread_async_args(pthread_t target_thread, - EM_FUNC_SIGNATURE sig, - void* func_ptr, - void* satellite, - va_list args) { - // Check if we are already on the target thread. - if (pthread_equal(target_thread, pthread_self())) { - // Setup is the same as in emscripten_dispatch_to_thread_args. - em_queued_call* q = em_queued_call_create(sig, func_ptr, satellite, args); - assert(q); - if (!q) - return 0; - q->calleeDelete = 1; - - // Schedule the call to run later on this thread. - emscripten_set_timeout(_do_call, 0, q); - return 0; - } - - // Otherwise, dispatch as usual. - return emscripten_dispatch_to_thread_args( - target_thread, sig, func_ptr, satellite, args); -} - -int emscripten_dispatch_to_thread_async_(pthread_t target_thread, - EM_FUNC_SIGNATURE sig, - void* func_ptr, - void* satellite, - ...) { - va_list args; - va_start(args, satellite); - int ret = emscripten_dispatch_to_thread_async_args( - target_thread, sig, func_ptr, satellite, args); - va_end(args); - return ret; -} - int _emscripten_thread_is_valid(pthread_t thread) { return thread->self == thread; } diff --git a/system/lib/pthread/proxying_legacy.c b/system/lib/pthread/proxying_legacy.c new file mode 100644 index 0000000000000..bf9294fa0474c --- /dev/null +++ b/system/lib/pthread/proxying_legacy.c @@ -0,0 +1,519 @@ +/* + * Copyright 2023 The Emscripten Authors. All rights reserved. + * Emscripten is available under two separate licenses, the MIT license and the + * University of Illinois/NCSA Open Source License. Both these licenses can be + * found in the LICENSE file. + */ +#include +#include +#include + +#include + +#include "../internal/pthread_impl.h" + +#include "threading_internal.h" +#include "emscripten_internal.h" + +typedef union em_variant_val { + int i; + int64_t i64; + float f; + double d; + void *vp; + char *cp; +} em_variant_val; + +typedef struct em_queued_call { + int functionEnum; + void *functionPtr; + _Atomic uint32_t operationDone; + em_variant_val args[EM_QUEUED_JS_CALL_MAX_ARGS]; + em_variant_val returnValue; + + // Sets the PThread.currentProxiedOperationCallerThread global for the + // duration of the proxied call. + pthread_t callingThread; + + // An optional pointer to a secondary data block that should be free()d when + // this queued call is freed. + void *satelliteData; + + // If true, the caller has "detached" itself from this call object and the + // Emscripten main runtime thread should free up this em_queued_call object + // after it has been executed. If false, the caller is in control of the + // memory. + int calleeDelete; +} em_queued_call; + +// Proxied C/C++ functions support at most this many arguments. Dispatch is +// static/strongly typed by signature. +#define EM_QUEUED_CALL_MAX_ARGS 11 + +typedef void (*em_func_v)(void); +typedef void (*em_func_vi)(int); +typedef void (*em_func_vf)(float); +typedef void (*em_func_vii)(int, int); +typedef void (*em_func_vif)(int, float); +typedef void (*em_func_vff)(float, float); +typedef void (*em_func_viii)(int, int, int); +typedef void (*em_func_viif)(int, int, float); +typedef void (*em_func_viff)(int, float, float); +typedef void (*em_func_vfff)(float, float, float); +typedef void (*em_func_viiii)(int, int, int, int); +typedef void (*em_func_viifi)(int, int, float, int); +typedef void (*em_func_vifff)(int, float, float, float); +typedef void (*em_func_vffff)(float, float, float, float); +typedef void (*em_func_viiiii)(int, int, int, int, int); +typedef void (*em_func_viffff)(int, float, float, float, float); +typedef void (*em_func_viiiiii)(int, int, int, int, int, int); +typedef void (*em_func_viiiiiii)(int, int, int, int, int, int, int); +typedef void (*em_func_viiiiiiii)(int, int, int, int, int, int, int, int); +typedef void (*em_func_viiiiiiiii)(int, int, int, int, int, int, int, int, int); +typedef void (*em_func_viiiiiiiiii)(int, int, int, int, int, int, int, int, int, int); +typedef void (*em_func_viiiiiiiiiii)(int, int, int, int, int, int, int, int, int, int, int); +typedef int (*em_func_i)(void); +typedef int (*em_func_ii)(int); +typedef int (*em_func_iii)(int, int); +typedef int (*em_func_iiii)(int, int, int); +typedef int (*em_func_iiiii)(int, int, int, int); +typedef int (*em_func_iiiiii)(int, int, int, int, int); +typedef int (*em_func_iiiiiii)(int, int, int, int, int, int); +typedef int (*em_func_iiiiiiii)(int, int, int, int, int, int, int); +typedef int (*em_func_iiiiiiiii)(int, int, int, int, int, int, int, int); +typedef int (*em_func_iiiiiiiiii)(int, int, int, int, int, int, int, int, int); + +// Allocator and deallocator for em_queued_call objects. +static em_queued_call* em_queued_call_malloc() { + em_queued_call* call = (em_queued_call*)malloc(sizeof(em_queued_call)); + assert(call); // Not a programming error, but use assert() in debug builds to catch OOM scenarios. + if (call) { + call->operationDone = 0; + call->functionPtr = 0; + call->satelliteData = 0; + } + return call; +} + +static void em_queued_call_free(em_queued_call* call) { + if (call) + free(call->satelliteData); + free(call); +} + +static void init_em_queued_call_args(em_queued_call* q, + EM_FUNC_SIGNATURE sig, + va_list args) { + EM_FUNC_SIGNATURE argumentsType = sig & EM_FUNC_SIG_ARGUMENTS_TYPE_MASK; + int numArguments = EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(sig); + for (int i = 0; i < numArguments; ++i) { + switch ((argumentsType & EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_MASK)) { + case EM_FUNC_SIG_PARAM_I: + q->args[i].i = va_arg(args, int); + break; + case EM_FUNC_SIG_PARAM_I64: + q->args[i].i64 = va_arg(args, int64_t); + break; + case EM_FUNC_SIG_PARAM_F: + q->args[i].f = (float)va_arg(args, double); + break; + case EM_FUNC_SIG_PARAM_D: + q->args[i].d = va_arg(args, double); + break; + } + argumentsType >>= EM_FUNC_SIG_ARGUMENT_TYPE_SIZE_SHIFT; + } +} + +static em_queued_call* em_queued_call_create(EM_FUNC_SIGNATURE sig, + void* func, + void* satellite, + va_list args) { + em_queued_call* call = em_queued_call_malloc(); + if (call) { + call->functionEnum = sig; + call->functionPtr = func; + call->satelliteData = satellite; + init_em_queued_call_args(call, sig, args); + } + return call; +} + +void emscripten_async_waitable_close(em_queued_call* call) { + assert(call->operationDone); + em_queued_call_free(call); +} + +static void _do_call(void* arg) { + em_queued_call* q = (em_queued_call*)arg; + // C function pointer + assert(EM_FUNC_SIG_NUM_FUNC_ARGUMENTS(q->functionEnum) <= EM_QUEUED_CALL_MAX_ARGS); + switch (q->functionEnum) { + case EM_PROXIED_RESIZE_OFFSCREENCANVAS: + q->returnValue.i = + _emscripten_set_offscreencanvas_size(q->args[0].cp, q->args[1].i, q->args[2].i); + break; + case EM_PROXIED_JS_FUNCTION: + q->returnValue.d = + _emscripten_receive_on_main_thread_js((intptr_t)q->functionPtr, q->callingThread, q->args[0].i, &q->args[1].d); + break; + case EM_FUNC_SIG_V: + ((em_func_v)q->functionPtr)(); + break; + case EM_FUNC_SIG_VI: + ((em_func_vi)q->functionPtr)(q->args[0].i); + break; + case EM_FUNC_SIG_VF: + ((em_func_vf)q->functionPtr)(q->args[0].f); + break; + case EM_FUNC_SIG_VII: + ((em_func_vii)q->functionPtr)(q->args[0].i, q->args[1].i); + break; + case EM_FUNC_SIG_VIF: + ((em_func_vif)q->functionPtr)(q->args[0].i, q->args[1].f); + break; + case EM_FUNC_SIG_VFF: + ((em_func_vff)q->functionPtr)(q->args[0].f, q->args[1].f); + break; + case EM_FUNC_SIG_VIII: + ((em_func_viii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i); + break; + case EM_FUNC_SIG_VIIF: + ((em_func_viif)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].f); + break; + case EM_FUNC_SIG_VIFF: + ((em_func_viff)q->functionPtr)(q->args[0].i, q->args[1].f, q->args[2].f); + break; + case EM_FUNC_SIG_VFFF: + ((em_func_vfff)q->functionPtr)(q->args[0].f, q->args[1].f, q->args[2].f); + break; + case EM_FUNC_SIG_VIIII: + ((em_func_viiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i); + break; + case EM_FUNC_SIG_VIIFI: + ((em_func_viifi)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].f, q->args[3].i); + break; + case EM_FUNC_SIG_VIFFF: + ((em_func_vifff)q->functionPtr)(q->args[0].i, q->args[1].f, q->args[2].f, q->args[3].f); + break; + case EM_FUNC_SIG_VFFFF: + ((em_func_vffff)q->functionPtr)(q->args[0].f, q->args[1].f, q->args[2].f, q->args[3].f); + break; + case EM_FUNC_SIG_VIIIII: + ((em_func_viiiii)q->functionPtr)( + q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i); + break; + case EM_FUNC_SIG_VIFFFF: + ((em_func_viffff)q->functionPtr)( + q->args[0].i, q->args[1].f, q->args[2].f, q->args[3].f, q->args[4].f); + break; + case EM_FUNC_SIG_VIIIIII: + ((em_func_viiiiii)q->functionPtr)( + q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i); + break; + case EM_FUNC_SIG_VIIIIIII: + ((em_func_viiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, + q->args[4].i, q->args[5].i, q->args[6].i); + break; + case EM_FUNC_SIG_VIIIIIIII: + ((em_func_viiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, + q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i); + break; + case EM_FUNC_SIG_VIIIIIIIII: + ((em_func_viiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, + q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i); + break; + case EM_FUNC_SIG_VIIIIIIIIII: + ((em_func_viiiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, + q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i, + q->args[9].i); + break; + case EM_FUNC_SIG_VIIIIIIIIIII: + ((em_func_viiiiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, + q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i, + q->args[9].i, q->args[10].i); + break; + case EM_FUNC_SIG_I: + q->returnValue.i = ((em_func_i)q->functionPtr)(); + break; + case EM_FUNC_SIG_II: + q->returnValue.i = ((em_func_ii)q->functionPtr)(q->args[0].i); + break; + case EM_FUNC_SIG_III: + q->returnValue.i = ((em_func_iii)q->functionPtr)(q->args[0].i, q->args[1].i); + break; + case EM_FUNC_SIG_IIII: + q->returnValue.i = ((em_func_iiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i); + break; + case EM_FUNC_SIG_IIIII: + q->returnValue.i = + ((em_func_iiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i); + break; + case EM_FUNC_SIG_IIIIII: + q->returnValue.i = ((em_func_iiiiii)q->functionPtr)( + q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i); + break; + case EM_FUNC_SIG_IIIIIII: + q->returnValue.i = ((em_func_iiiiiii)q->functionPtr)( + q->args[0].i, q->args[1].i, q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i); + break; + case EM_FUNC_SIG_IIIIIIII: + q->returnValue.i = ((em_func_iiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, + q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i); + break; + case EM_FUNC_SIG_IIIIIIIII: + q->returnValue.i = ((em_func_iiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, + q->args[2].i, q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i); + break; + case EM_FUNC_SIG_IIIIIIIIII: + q->returnValue.i = + ((em_func_iiiiiiiiii)q->functionPtr)(q->args[0].i, q->args[1].i, q->args[2].i, + q->args[3].i, q->args[4].i, q->args[5].i, q->args[6].i, q->args[7].i, q->args[8].i); + break; + default: + assert(0 && "Invalid Emscripten pthread _do_call opcode!"); + } + + // If the caller is detached from this operation, it is the main thread's responsibility to free + // up the call object. + if (q->calleeDelete) { + em_queued_call_free(q); + // No need to wake a listener, nothing is listening to this since the call object is detached. + } else { + // The caller owns this call object, it is listening to it and will free it up. + q->operationDone = 1; + emscripten_futex_wake(&q->operationDone, INT_MAX); + } +} + +static pthread_t normalize_thread(pthread_t target_thread) { + assert(target_thread); + if (target_thread == EM_CALLBACK_THREAD_CONTEXT_MAIN_RUNTIME_THREAD) { + return emscripten_main_runtime_thread_id(); + } + if (target_thread == EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD) { + return pthread_self(); + } + return target_thread; +} + +// Execute `call` and return 1 only if already on the `target_thread`. Otherwise +// return 0. +static int maybe_call_on_current_thread(pthread_t target_thread, + em_queued_call* call) { + if (pthread_equal(target_thread, pthread_self())) { + _do_call(call); + return 1; + } + return 0; +} + +// Execute or proxy `call`. Return 1 if the work was executed or otherwise +// return 0. +static int do_dispatch_to_thread(pthread_t target_thread, + em_queued_call* call) { + target_thread = normalize_thread(target_thread); + if (maybe_call_on_current_thread(target_thread, call)) { + return 1; + } + emscripten_proxy_async( + emscripten_proxy_get_system_queue(), target_thread, _do_call, call); + return 0; +} + +int emscripten_dispatch_to_thread_args(pthread_t target_thread, + EM_FUNC_SIGNATURE sig, + void* func_ptr, + void* satellite, + va_list args) { + em_queued_call* q = em_queued_call_create(sig, func_ptr, satellite, args); + assert(q); + // TODO: handle errors in a better way, this pattern appears in several places + // in this file. The current behavior makes the calling thread hang as + // it waits (for synchronous calls). + // If we failed to allocate, return 0 which means we did not execute anything + // (we also never will in that case). + if (!q) + return 0; + + // `q` will not be used after it is called, so let the call clean it up. + q->calleeDelete = 1; + return do_dispatch_to_thread(target_thread, q); +} + +void emscripten_async_run_in_main_thread(em_queued_call* call) { + do_dispatch_to_thread(emscripten_main_runtime_thread_id(), call); +} + +int emscripten_dispatch_to_thread_(pthread_t target_thread, + EM_FUNC_SIGNATURE sig, + void* func_ptr, + void* satellite, + ...) { + va_list args; + va_start(args, satellite); + int ret = emscripten_dispatch_to_thread_args( + target_thread, sig, func_ptr, satellite, args); + va_end(args); + return ret; +} + +int emscripten_dispatch_to_thread_async_args(pthread_t target_thread, + EM_FUNC_SIGNATURE sig, + void* func_ptr, + void* satellite, + va_list args) { + // Check if we are already on the target thread. + if (pthread_equal(target_thread, pthread_self())) { + // Setup is the same as in emscripten_dispatch_to_thread_args. + em_queued_call* q = em_queued_call_create(sig, func_ptr, satellite, args); + assert(q); + if (!q) + return 0; + q->calleeDelete = 1; + + // Schedule the call to run later on this thread. + emscripten_set_timeout(_do_call, 0, q); + return 0; + } + + // Otherwise, dispatch as usual. + return emscripten_dispatch_to_thread_args( + target_thread, sig, func_ptr, satellite, args); +} + +int emscripten_dispatch_to_thread_async_(pthread_t target_thread, + EM_FUNC_SIGNATURE sig, + void* func_ptr, + void* satellite, + ...) { + va_list args; + va_start(args, satellite); + int ret = emscripten_dispatch_to_thread_async_args( + target_thread, sig, func_ptr, satellite, args); + va_end(args); + return ret; +} + +static void sync_run_in_main_thread(em_queued_call* call) { + emscripten_async_run_in_main_thread(call); + + // Enter to wait for the operation to complete. + emscripten_wait_for_call_v(call, INFINITY); +} + +int emscripten_sync_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void* func_ptr, ...) { + em_queued_call q = {sig, func_ptr}; + + va_list args; + va_start(args, func_ptr); + init_em_queued_call_args(&q, sig, args); + va_end(args); + sync_run_in_main_thread(&q); + return q.returnValue.i; +} + +double _emscripten_run_on_main_thread_js(int index, int num_args, int64_t* buffer, int sync) { + em_queued_call q; + em_queued_call *c; + if (sync) { + q.operationDone = 0; + q.satelliteData = 0; + c = &q; + } else { + c = em_queued_call_malloc(); + } + c->calleeDelete = !sync; + c->functionEnum = EM_PROXIED_JS_FUNCTION; + // Index not needed to ever be more than 32-bit. + c->functionPtr = (void*)(intptr_t)index; + c->callingThread = pthread_self(); + assert(num_args+1 <= EM_QUEUED_JS_CALL_MAX_ARGS); + // The types are only known at runtime in these calls, so we store values that + // must be able to contain any valid JS value, including a 64-bit BigInt if + // BigInt support is enabled. We store to an i64, which can contain both a + // BigInt and a JS Number which is a 64-bit double. + c->args[0].i = num_args; + for (int i = 0; i < num_args; i++) { + c->args[i+1].i64 = buffer[i]; + } + + if (sync) { + sync_run_in_main_thread(&q); + // TODO: support BigInt return values somehow. + return q.returnValue.d; + } else { + // 'async' runs are fire and forget, where the caller detaches itself from the call object after + // returning here, and it is the callee's responsibility to free up the memory after the call + // has been performed. + emscripten_async_run_in_main_thread(c); + return 0; + } +} + +void emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIGNATURE sig, void* func_ptr, ...) { + em_queued_call* q = em_queued_call_malloc(); + if (!q) + return; + q->functionEnum = sig; + q->functionPtr = func_ptr; + + va_list args; + va_start(args, func_ptr); + init_em_queued_call_args(q, sig, args); + va_end(args); + // 'async' runs are fire and forget, where the caller detaches itself from the call object after + // returning here, and it is the callee's responsibility to free up the memory after the call has + // been performed. + q->calleeDelete = 1; + emscripten_async_run_in_main_thread(q); +} + +em_queued_call* emscripten_async_waitable_run_in_main_runtime_thread_( + EM_FUNC_SIGNATURE sig, void* func_ptr, ...) { + em_queued_call* q = em_queued_call_malloc(); + if (!q) + return NULL; + q->functionEnum = sig; + q->functionPtr = func_ptr; + + va_list args; + va_start(args, func_ptr); + init_em_queued_call_args(q, sig, args); + va_end(args); + // 'async waitable' runs are waited on by the caller, so the call object needs to remain alive for + // the caller to access it after the operation is done. The caller is responsible in cleaning up + // the object after done. + q->calleeDelete = 0; + emscripten_async_run_in_main_thread(q); + return q; +} + +EMSCRIPTEN_RESULT emscripten_wait_for_call_v(em_queued_call* call, double timeoutMSecs) { + int r; + + int done = atomic_load(&call->operationDone); + if (!done) { + double now = emscripten_get_now(); + double waitEndTime = now + timeoutMSecs; + emscripten_set_current_thread_status(EM_THREAD_STATUS_WAITPROXY); + while (!done && now < waitEndTime) { + r = emscripten_futex_wait(&call->operationDone, 0, waitEndTime - now); + done = atomic_load(&call->operationDone); + now = emscripten_get_now(); + } + emscripten_set_current_thread_status(EM_THREAD_STATUS_RUNNING); + } + if (done) + return EMSCRIPTEN_RESULT_SUCCESS; + else + return EMSCRIPTEN_RESULT_TIMED_OUT; +} + +EMSCRIPTEN_RESULT emscripten_wait_for_call_i( + em_queued_call* call, double timeoutMSecs, int* outResult) { + EMSCRIPTEN_RESULT res = emscripten_wait_for_call_v(call, timeoutMSecs); + if (res == EMSCRIPTEN_RESULT_SUCCESS && outResult) + *outResult = call->returnValue.i; + return res; +} diff --git a/system/lib/pthread/threading_internal.h b/system/lib/pthread/threading_internal.h index 245db45baf52a..a2b6f01f3f73d 100644 --- a/system/lib/pthread/threading_internal.h +++ b/system/lib/pthread/threading_internal.h @@ -9,78 +9,10 @@ #include -typedef union em_variant_val { - int i; - int64_t i64; - float f; - double d; - void *vp; - char *cp; -} em_variant_val; - // Proxied JS function can support a few more arguments than proxied C/C++ // functions, because the dispatch is variadic and signature independent. #define EM_QUEUED_JS_CALL_MAX_ARGS 20 -// Proxied C/C++ functions support at most this many arguments. Dispatch is -// static/strongly typed by signature. -#define EM_QUEUED_CALL_MAX_ARGS 11 - -typedef struct em_queued_call { - int functionEnum; - void *functionPtr; - _Atomic uint32_t operationDone; - em_variant_val args[EM_QUEUED_JS_CALL_MAX_ARGS]; - em_variant_val returnValue; - - // Sets the PThread.currentProxiedOperationCallerThread global for the - // duration of the proxied call. - pthread_t callingThread; - - // An optional pointer to a secondary data block that should be free()d when - // this queued call is freed. - void *satelliteData; - - // If true, the caller has "detached" itself from this call object and the - // Emscripten main runtime thread should free up this em_queued_call object - // after it has been executed. If false, the caller is in control of the - // memory. - int calleeDelete; -} em_queued_call; - -typedef void (*em_func_v)(void); -typedef void (*em_func_vi)(int); -typedef void (*em_func_vf)(float); -typedef void (*em_func_vii)(int, int); -typedef void (*em_func_vif)(int, float); -typedef void (*em_func_vff)(float, float); -typedef void (*em_func_viii)(int, int, int); -typedef void (*em_func_viif)(int, int, float); -typedef void (*em_func_viff)(int, float, float); -typedef void (*em_func_vfff)(float, float, float); -typedef void (*em_func_viiii)(int, int, int, int); -typedef void (*em_func_viifi)(int, int, float, int); -typedef void (*em_func_vifff)(int, float, float, float); -typedef void (*em_func_vffff)(float, float, float, float); -typedef void (*em_func_viiiii)(int, int, int, int, int); -typedef void (*em_func_viffff)(int, float, float, float, float); -typedef void (*em_func_viiiiii)(int, int, int, int, int, int); -typedef void (*em_func_viiiiiii)(int, int, int, int, int, int, int); -typedef void (*em_func_viiiiiiii)(int, int, int, int, int, int, int, int); -typedef void (*em_func_viiiiiiiii)(int, int, int, int, int, int, int, int, int); -typedef void (*em_func_viiiiiiiiii)(int, int, int, int, int, int, int, int, int, int); -typedef void (*em_func_viiiiiiiiiii)(int, int, int, int, int, int, int, int, int, int, int); -typedef int (*em_func_i)(void); -typedef int (*em_func_ii)(int); -typedef int (*em_func_iii)(int, int); -typedef int (*em_func_iiii)(int, int, int); -typedef int (*em_func_iiiii)(int, int, int, int); -typedef int (*em_func_iiiiii)(int, int, int, int, int); -typedef int (*em_func_iiiiiii)(int, int, int, int, int, int); -typedef int (*em_func_iiiiiiii)(int, int, int, int, int, int, int); -typedef int (*em_func_iiiiiiiii)(int, int, int, int, int, int, int, int); -typedef int (*em_func_iiiiiiiiii)(int, int, int, int, int, int, int, int, int); - #define EM_THREAD_NAME_MAX 32 #define EM_THREAD_STATUS int diff --git a/tools/system_libs.py b/tools/system_libs.py index 3ae3d59ad8651..8eadd8a749b09 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -1058,6 +1058,7 @@ def get_files(self): 'library_pthread.c', 'em_task_queue.c', 'proxying.c', + 'proxying_legacy.c', 'thread_mailbox.c', 'pthread_create.c', 'pthread_kill.c',