From 16be72bba961064fbcd2aa3df037ead9fd45e0db Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Wed, 29 Nov 2023 00:19:51 +0100 Subject: [PATCH 01/21] ffi: thread safe callbacks (preliminary) --- ext/ffi/ffi.c | 46 +++++++++++++++++++++++++++++++++++++++++----- ext/ffi/php_ffi.h | 16 ++++++++++++++++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index b0a59ca85cc3f..6923df6691ff8 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -935,9 +935,11 @@ static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */ } /* }}} */ -static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, void* data) /* {{{ */ -{ - zend_ffi_callback_data *callback_data = (zend_ffi_callback_data*)data; +static void (*orig_interrupt_function)(zend_execute_data *execute_data); + +static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ + + zend_ffi_callback_data *callback_data = FFI_G(callback_data).data; zend_fcall_info fci; zend_ffi_type *ret_type; zval retval; @@ -951,13 +953,14 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v fci.param_count = callback_data->arg_count; fci.named_params = NULL; + if (callback_data->type->func.args) { int n = 0; zend_ffi_type *arg_type; ZEND_HASH_PACKED_FOREACH_PTR(callback_data->type->func.args, arg_type) { arg_type = ZEND_FFI_TYPE(arg_type); - zend_ffi_cdata_to_zval(NULL, args[n], arg_type, BP_VAR_R, &fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST), 0, 0); + zend_ffi_cdata_to_zval(NULL, FFI_G(callback_data).args[n], arg_type, BP_VAR_R, &fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST), 0, 0); n++; } ZEND_HASH_FOREACH_END(); } @@ -982,10 +985,40 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v ret_type = ZEND_FFI_TYPE(callback_data->type->func.ret_type); if (ret_type->kind != ZEND_FFI_TYPE_VOID) { - zend_ffi_zval_to_cdata(ret, ret_type, &retval); + zend_ffi_zval_to_cdata(FFI_G(callback_data).ret, ret_type, &retval); } zval_ptr_dtor(&retval); + + if (orig_interrupt_function) { + orig_interrupt_function(execute_data); + } + zend_interrupt_function = orig_interrupt_function; + + pthread_cond_signal(&FFI_G(vm_ack)); + pthread_mutex_unlock(&FFI_G(vm_lock)); +} + +static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, void* data) /* {{{ */ +{ + // get lock, first + pthread_mutex_lock(&FFI_G(vm_lock)); + zend_ffi_call_data call_data = { + .cif = cif, + .ret = ret, + .args = args, + .data = (zend_ffi_call_data *)data + }; + FFI_G(callback_data) = call_data; + + /** post interrupt request */ + orig_interrupt_function = zend_interrupt_function; + zend_interrupt_function = zend_ffi_interrupt_function; + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + + pthread_mutex_lock(&FFI_G(vm_lock)); + pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_lock)); + pthread_mutex_unlock(&FFI_G(vm_lock)); } /* }}} */ @@ -5518,6 +5551,9 @@ ZEND_MINIT_FUNCTION(ffi) return zend_ffi_preload(FFI_G(preload)); } + pthread_mutex_init(&FFI_G(vm_lock), NULL); + pthread_cond_init(&FFI_G(vm_ack), NULL); + return SUCCESS; } /* }}} */ diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 02a241c6bb691..e8f7f39b67b90 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -17,6 +17,9 @@ #ifndef PHP_FFI_H #define PHP_FFI_H +#include +#include + extern zend_module_entry ffi_module_entry; #define phpext_ffi_ptr &ffi_module_entry @@ -27,6 +30,15 @@ typedef enum _zend_ffi_api_restriction { } zend_ffi_api_restriction; typedef struct _zend_ffi_type zend_ffi_type; +typedef struct _zend_ffi_callback_data zend_ffi_callback_data; + + +typedef struct _zend_ffi_call_data { + ffi_cif* cif; + void* ret; + void** args; + zend_ffi_callback_data* data; +} zend_ffi_call_data; ZEND_BEGIN_MODULE_GLOBALS(ffi) zend_ffi_api_restriction restriction; @@ -35,6 +47,10 @@ ZEND_BEGIN_MODULE_GLOBALS(ffi) /* predefined ffi_types */ HashTable types; + pthread_mutex_t vm_lock; + pthread_cond_t vm_ack; + zend_ffi_call_data callback_data; + /* preloading */ char *preload; HashTable *scopes; /* list of preloaded scopes */ From 1de9669e37cc3df0fbe1878d8f96409162e02698 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Wed, 29 Nov 2023 00:59:50 +0100 Subject: [PATCH 02/21] ffi: make sure there are no in progress requests before posting a new one add zend_ffi_wait_request_barrier helper function add callback_in_progress flag --- ext/ffi/ffi.c | 51 +++++++++++++++++++++++++++++++++++------------ ext/ffi/php_ffi.h | 1 + 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 6923df6691ff8..43ebdbc5512f4 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -938,7 +938,9 @@ static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */ static void (*orig_interrupt_function)(zend_execute_data *execute_data); static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ - + if(!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) goto end; + + zend_ffi_callback_data *callback_data = FFI_G(callback_data).data; zend_fcall_info fci; zend_ffi_type *ret_type; @@ -989,35 +991,50 @@ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ } zval_ptr_dtor(&retval); + + zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); + pthread_cond_broadcast(&FFI_G(vm_ack)); + pthread_mutex_unlock(&FFI_G(vm_lock)); + end: if (orig_interrupt_function) { orig_interrupt_function(execute_data); } - zend_interrupt_function = orig_interrupt_function; +} - pthread_cond_signal(&FFI_G(vm_ack)); - pthread_mutex_unlock(&FFI_G(vm_lock)); +static void zend_ffi_wait_request_barrier(void){ + // get lock, first + pthread_mutex_lock(&FFI_G(vm_lock)); + + while(zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))){ + // we acquired the lock before the request could be serviced + // unlock it and wait for the flag + pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_lock)); + } } static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, void* data) /* {{{ */ { - // get lock, first - pthread_mutex_lock(&FFI_G(vm_lock)); + zend_ffi_wait_request_barrier(); + + // mutex is now locked, and request is not pending. + // start a new one + zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), true); + zend_ffi_call_data call_data = { .cif = cif, .ret = ret, .args = args, - .data = (zend_ffi_call_data *)data + .data = (zend_ffi_callback_data *)data }; FFI_G(callback_data) = call_data; - - /** post interrupt request */ - orig_interrupt_function = zend_interrupt_function; - zend_interrupt_function = zend_ffi_interrupt_function; + + // post interrupt request zend_atomic_bool_store_ex(&EG(vm_interrupt), true); - pthread_mutex_lock(&FFI_G(vm_lock)); - pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_lock)); + // mutex will be released by the VM interrupt + + zend_ffi_wait_request_barrier(); pthread_mutex_unlock(&FFI_G(vm_lock)); } /* }}} */ @@ -5551,6 +5568,10 @@ ZEND_MINIT_FUNCTION(ffi) return zend_ffi_preload(FFI_G(preload)); } + zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); + orig_interrupt_function = zend_interrupt_function; + zend_interrupt_function = zend_ffi_interrupt_function; + pthread_mutex_init(&FFI_G(vm_lock), NULL); pthread_cond_init(&FFI_G(vm_ack), NULL); @@ -5561,6 +5582,10 @@ ZEND_MINIT_FUNCTION(ffi) /* {{{ ZEND_RSHUTDOWN_FUNCTION */ ZEND_RSHUTDOWN_FUNCTION(ffi) { + zend_ffi_wait_request_barrier(); + pthread_mutex_unlock(&FFI_G(vm_lock)); + zend_interrupt_function = orig_interrupt_function; + if (FFI_G(callbacks)) { zend_hash_destroy(FFI_G(callbacks)); efree(FFI_G(callbacks)); diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index e8f7f39b67b90..5f3bda1dd3458 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -47,6 +47,7 @@ ZEND_BEGIN_MODULE_GLOBALS(ffi) /* predefined ffi_types */ HashTable types; + zend_atomic_bool callback_in_progress; pthread_mutex_t vm_lock; pthread_cond_t vm_ack; zend_ffi_call_data callback_data; From 56c24d4925c8062f9fe1d18a5e9b2b0bdcd2b06c Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Wed, 29 Nov 2023 01:22:03 +0100 Subject: [PATCH 03/21] code style --- ext/ffi/ffi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 43ebdbc5512f4..132ebb298c45a 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -937,7 +937,7 @@ static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */ static void (*orig_interrupt_function)(zend_execute_data *execute_data); -static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ +static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */ if(!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) goto end; @@ -1001,8 +1001,9 @@ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ orig_interrupt_function(execute_data); } } +/* }}} */ -static void zend_ffi_wait_request_barrier(void){ +static void zend_ffi_wait_request_barrier(void){ /* {{{ */ // get lock, first pthread_mutex_lock(&FFI_G(vm_lock)); @@ -1012,6 +1013,7 @@ static void zend_ffi_wait_request_barrier(void){ pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_lock)); } } +/* }}} */ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, void* data) /* {{{ */ { From 6423d3ad87709a6c147153b9a124f8076ab58a0b Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Wed, 29 Nov 2023 19:15:56 +0100 Subject: [PATCH 04/21] ffi: trace the requester and main thread IDs --- ext/ffi/ffi.c | 33 +++++++++++++++++++++++---------- ext/ffi/php_ffi.h | 2 ++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 132ebb298c45a..50fa4a541e971 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -938,8 +938,12 @@ static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */ static void (*orig_interrupt_function)(zend_execute_data *execute_data); static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */ - if(!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) goto end; + // this function must always run on the main thread + assert(pthread_self() == FFI_G(main_tid)); + if (!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { + goto end; + } zend_ffi_callback_data *callback_data = FFI_G(callback_data).data; zend_fcall_info fci; @@ -994,7 +998,6 @@ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); pthread_cond_broadcast(&FFI_G(vm_ack)); - pthread_mutex_unlock(&FFI_G(vm_lock)); end: if (orig_interrupt_function) { @@ -1003,7 +1006,7 @@ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ } /* }}} */ -static void zend_ffi_wait_request_barrier(void){ /* {{{ */ +static void zend_ffi_wait_request_barrier(bool release){ /* {{{ */ // get lock, first pthread_mutex_lock(&FFI_G(vm_lock)); @@ -1012,12 +1015,17 @@ static void zend_ffi_wait_request_barrier(void){ /* {{{ */ // unlock it and wait for the flag pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_lock)); } + + if(release){ + pthread_mutex_unlock(&FFI_G(vm_lock)); + } } /* }}} */ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, void* data) /* {{{ */ { - zend_ffi_wait_request_barrier(); + // wait for a previously initiated request to complete + zend_ffi_wait_request_barrier(false); // mutex is now locked, and request is not pending. // start a new one @@ -1030,14 +1038,18 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v .data = (zend_ffi_callback_data *)data }; FFI_G(callback_data) = call_data; + FFI_G(requester_tid) = pthread_self(); + + if(pthread_self() == FFI_G(main_tid)){ + + } // post interrupt request zend_atomic_bool_store_ex(&EG(vm_interrupt), true); - - // mutex will be released by the VM interrupt - - zend_ffi_wait_request_barrier(); pthread_mutex_unlock(&FFI_G(vm_lock)); + + // wait for the request to complete before returning + zend_ffi_wait_request_barrier(true); } /* }}} */ @@ -5570,6 +5582,8 @@ ZEND_MINIT_FUNCTION(ffi) return zend_ffi_preload(FFI_G(preload)); } + FFI_G(main_tid) = pthread_self(); + zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); orig_interrupt_function = zend_interrupt_function; zend_interrupt_function = zend_ffi_interrupt_function; @@ -5584,8 +5598,7 @@ ZEND_MINIT_FUNCTION(ffi) /* {{{ ZEND_RSHUTDOWN_FUNCTION */ ZEND_RSHUTDOWN_FUNCTION(ffi) { - zend_ffi_wait_request_barrier(); - pthread_mutex_unlock(&FFI_G(vm_lock)); + zend_ffi_wait_request_barrier(true); zend_interrupt_function = orig_interrupt_function; if (FFI_G(callbacks)) { diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 5f3bda1dd3458..9ad8a895b96d0 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -50,6 +50,8 @@ ZEND_BEGIN_MODULE_GLOBALS(ffi) zend_atomic_bool callback_in_progress; pthread_mutex_t vm_lock; pthread_cond_t vm_ack; + pthread_t main_tid; + pthread_t requester_tid; zend_ffi_call_data callback_data; /* preloading */ From 98eb07910a0b2e3beaf30e73bdf8fb15a7f491d6 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Wed, 29 Nov 2023 19:23:55 +0100 Subject: [PATCH 05/21] ffi: fix deadlock when the callback invocation is from the main thread --- ext/ffi/ffi.c | 31 ++++++++++++++++++++----------- ext/ffi/php_ffi.h | 1 - 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 50fa4a541e971..226bc29526caa 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -937,12 +937,12 @@ static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */ static void (*orig_interrupt_function)(zend_execute_data *execute_data); -static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */ +static void zend_ffi_dispatch_callback(void){ /* {{{ */ // this function must always run on the main thread assert(pthread_self() == FFI_G(main_tid)); if (!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { - goto end; + return; } zend_ffi_callback_data *callback_data = FFI_G(callback_data).data; @@ -998,8 +998,12 @@ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); pthread_cond_broadcast(&FFI_G(vm_ack)); +} +/* }}} */ + +static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */ + zend_ffi_dispatch_callback(); - end: if (orig_interrupt_function) { orig_interrupt_function(execute_data); } @@ -1038,18 +1042,23 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v .data = (zend_ffi_callback_data *)data }; FFI_G(callback_data) = call_data; - FFI_G(requester_tid) = pthread_self(); - if(pthread_self() == FFI_G(main_tid)){ - - } + bool is_main_thread = pthread_self() == FFI_G(main_tid); - // post interrupt request - zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + if(is_main_thread){ + // dispatch the callback directly + zend_ffi_dispatch_callback(); + } else { + // post interrupt request to acquire the main thread + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } + pthread_mutex_unlock(&FFI_G(vm_lock)); - // wait for the request to complete before returning - zend_ffi_wait_request_barrier(true); + if(!is_main_thread){ + // wait for the request to complete before returning + zend_ffi_wait_request_barrier(true); + } } /* }}} */ diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 9ad8a895b96d0..074aa39f4c653 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -51,7 +51,6 @@ ZEND_BEGIN_MODULE_GLOBALS(ffi) pthread_mutex_t vm_lock; pthread_cond_t vm_ack; pthread_t main_tid; - pthread_t requester_tid; zend_ffi_call_data callback_data; /* preloading */ From d762cfe5aa51fde92917025f4ab0ba9fab3f045c Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Wed, 29 Nov 2023 19:28:42 +0100 Subject: [PATCH 06/21] ffi: add tests/callback_threads --- ext/ffi/tests/callback_threads.phpt | 59 +++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 ext/ffi/tests/callback_threads.phpt diff --git a/ext/ffi/tests/callback_threads.phpt b/ext/ffi/tests/callback_threads.phpt new file mode 100644 index 0000000000000..186c83cc1c493 --- /dev/null +++ b/ext/ffi/tests/callback_threads.phpt @@ -0,0 +1,59 @@ +--TEST-- +FFI Thread safe callbacks +--EXTENSIONS-- +ffi +--SKIPIF-- + +--INI-- +ffi.enable=1 +--FILE-- +new($libc->type('pthread_t')); +$accum = 0; +$thread_func = function($arg) use($libc, &$accum){ + //$v = $libc->cast('int *', $arg)[0]; + FFI::free($arg); + usleep(10 * 1000); + $accum++; + //print("."); +}; + +for($i=0; $i<100; $i++){ + $arg = $libc->new('int', false); + $arg->cdata = $i; + + $libc->pthread_create( + FFI::addr($tid), NULL, + $thread_func, FFI::addr($arg) + ); + $libc->pthread_detach($tid->cdata); +} + +while($accum != 100){ + //print("w"); + usleep(1000); +} +print($accum); +?> +--EXPECT-- +100 \ No newline at end of file From 29d65505f602dcfdbd9c63266ad3ea4ac26f6907 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Wed, 29 Nov 2023 21:19:13 +0100 Subject: [PATCH 07/21] ffi: fix mutex unlock before zend_error_noreturn (fixes bug79177.phpt) --- ext/ffi/ffi.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 226bc29526caa..2277a6ec3fa4d 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -937,6 +937,13 @@ static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */ static void (*orig_interrupt_function)(zend_execute_data *execute_data); +static void zend_ffi_dispatch_callback_end(void){ /* {{{ */ + zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); + pthread_cond_broadcast(&FFI_G(vm_ack)); + pthread_mutex_unlock(&FFI_G(vm_lock)); +} +/* }}} */ + static void zend_ffi_dispatch_callback(void){ /* {{{ */ // this function must always run on the main thread assert(pthread_self() == FFI_G(main_tid)); @@ -986,6 +993,7 @@ static void zend_ffi_dispatch_callback(void){ /* {{{ */ free_alloca(fci.params, use_heap); if (EG(exception)) { + zend_ffi_dispatch_callback_end(); zend_error_noreturn(E_ERROR, "Throwing from FFI callbacks is not allowed"); } @@ -995,9 +1003,7 @@ static void zend_ffi_dispatch_callback(void){ /* {{{ */ } zval_ptr_dtor(&retval); - - zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); - pthread_cond_broadcast(&FFI_G(vm_ack)); + zend_ffi_dispatch_callback_end(); } /* }}} */ @@ -1051,9 +1057,8 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v } else { // post interrupt request to acquire the main thread zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + pthread_mutex_unlock(&FFI_G(vm_lock)); } - - pthread_mutex_unlock(&FFI_G(vm_lock)); if(!is_main_thread){ // wait for the request to complete before returning From ba483e63821ea648a09f3fe7be5c9905573718cc Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Wed, 29 Nov 2023 22:25:19 +0100 Subject: [PATCH 08/21] ffi: remove wrongly placed restore of interrupt handler add sync barrier on gshutdown too --- ext/ffi/ffi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 2277a6ec3fa4d..861b3530393a6 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -5613,7 +5613,6 @@ ZEND_MINIT_FUNCTION(ffi) ZEND_RSHUTDOWN_FUNCTION(ffi) { zend_ffi_wait_request_barrier(true); - zend_interrupt_function = orig_interrupt_function; if (FFI_G(callbacks)) { zend_hash_destroy(FFI_G(callbacks)); @@ -5726,6 +5725,7 @@ static ZEND_GINIT_FUNCTION(ffi) /* {{{ ZEND_GINIT_FUNCTION */ static ZEND_GSHUTDOWN_FUNCTION(ffi) { + zend_ffi_wait_request_barrier(true); if (ffi_globals->scopes) { zend_hash_destroy(ffi_globals->scopes); free(ffi_globals->scopes); From bce091d03daa6431640236b7a1c069c35a5c86b9 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Fri, 1 Dec 2023 00:13:52 +0100 Subject: [PATCH 09/21] ffi: have callbacks be handled by the thread that invoked them --- ext/ffi/ffi.c | 129 +++++++++++++++++++++++++++++++--------------- ext/ffi/php_ffi.h | 6 ++- 2 files changed, 93 insertions(+), 42 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 861b3530393a6..ad20e5083fb38 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -57,6 +57,13 @@ # define __BIGGEST_ALIGNMENT__ sizeof(size_t) #endif +//#define FFI_DEBUG +#ifdef FFI_DEBUG +#define FFI_DPRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) +#else +#define FFI_DPRINTF(fmt, ...) +#endif + ZEND_DECLARE_MODULE_GLOBALS(ffi) typedef enum _zend_ffi_tag_kind { @@ -227,6 +234,10 @@ static ZEND_COLD void zend_ffi_return_unsupported(zend_ffi_type *type); static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type); static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type); static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type); +static void zend_ffi_wait_cond( + pthread_mutex_t *mutex, pthread_cond_t *cond, + zend_atomic_bool *flag, bool wanted_value, bool release +); #if FFI_CLOSURES static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value); @@ -938,15 +949,13 @@ static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */ static void (*orig_interrupt_function)(zend_execute_data *execute_data); static void zend_ffi_dispatch_callback_end(void){ /* {{{ */ - zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); - pthread_cond_broadcast(&FFI_G(vm_ack)); - pthread_mutex_unlock(&FFI_G(vm_lock)); + //pthread_cond_broadcast(&FFI_G(vm_ack)); } /* }}} */ static void zend_ffi_dispatch_callback(void){ /* {{{ */ // this function must always run on the main thread - assert(pthread_self() == FFI_G(main_tid)); + //ZEND_ASSERT(pthread_self() == FFI_G(main_tid)); if (!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { return; @@ -993,7 +1002,6 @@ static void zend_ffi_dispatch_callback(void){ /* {{{ */ free_alloca(fci.params, use_heap); if (EG(exception)) { - zend_ffi_dispatch_callback_end(); zend_error_noreturn(E_ERROR, "Throwing from FFI callbacks is not allowed"); } @@ -1003,67 +1011,104 @@ static void zend_ffi_dispatch_callback(void){ /* {{{ */ } zval_ptr_dtor(&retval); - zend_ffi_dispatch_callback_end(); } /* }}} */ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */ - zend_ffi_dispatch_callback(); - + bool is_main_tid = FFI_G(callback_tid) == FFI_G(main_tid); + if(!is_main_tid){ + pthread_mutex_lock(&FFI_G(vm_response_lock)); + + FFI_DPRINTF("<-- ACK\n"); + // notify calling thread and release + pthread_cond_broadcast(&FFI_G(vm_ack)); + + // release mutex and wait for the unlock signal + FFI_DPRINTF("-- wait unlock --\n"); + pthread_cond_wait(&FFI_G(vm_unlock), &FFI_G(vm_response_lock)); + pthread_mutex_unlock(&FFI_G(vm_response_lock)); + FFI_DPRINTF("-- end\n"); + } + if (orig_interrupt_function) { orig_interrupt_function(execute_data); } } /* }}} */ -static void zend_ffi_wait_request_barrier(bool release){ /* {{{ */ +static void zend_ffi_wait_cond( + pthread_mutex_t *mutex, pthread_cond_t *cond, + zend_atomic_bool *flag, bool wanted_value, bool release +){ /* {{{ */ // get lock, first - pthread_mutex_lock(&FFI_G(vm_lock)); + pthread_mutex_lock(mutex); - while(zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))){ - // we acquired the lock before the request could be serviced - // unlock it and wait for the flag - pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_lock)); + // if we acquired the lock before the request could be serviced + // unlock it and wait for the flag + if(flag == NULL){ + pthread_cond_wait(cond, mutex); + } else { + while(zend_atomic_bool_load_ex(flag) != wanted_value){ + pthread_cond_wait(cond, mutex); + } } if(release){ - pthread_mutex_unlock(&FFI_G(vm_lock)); + pthread_mutex_unlock(mutex); } } /* }}} */ +static void zend_ffi_wait_request_barrier(bool release){ /* {{{ */ + zend_ffi_wait_cond(&FFI_G(vm_request_lock), &FFI_G(vm_unlock), &FFI_G(callback_in_progress), false, release); +} +/* }}} */ + static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, void* data) /* {{{ */ { // wait for a previously initiated request to complete zend_ffi_wait_request_barrier(false); - - // mutex is now locked, and request is not pending. - // start a new one - zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), true); - - zend_ffi_call_data call_data = { - .cif = cif, - .ret = ret, - .args = args, - .data = (zend_ffi_callback_data *)data - }; - FFI_G(callback_data) = call_data; - - bool is_main_thread = pthread_self() == FFI_G(main_tid); - - if(is_main_thread){ - // dispatch the callback directly + { + // mutex is now locked, and request is not pending. + // start a new one + zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), true); + + zend_ffi_call_data call_data = { + .cif = cif, + .ret = ret, + .args = args, + .data = (zend_ffi_callback_data *)data + }; + FFI_G(callback_data) = call_data; + + FFI_G(callback_tid) = pthread_self(); + bool is_main_thread = FFI_G(callback_tid) == FFI_G(main_tid); + + if(!is_main_thread){ + // post interrupt request to synchronize with the main thread + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + + // wait for the ack, keep the lock + //zend_ffi_wait_cond(&FFI_G(vm_response_lock), &FFI_G(vm_ack), &FFI_G(callback_in_progress), true, false); + + pthread_mutex_lock(&FFI_G(vm_response_lock)); + pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_response_lock)); + } + + // dispatch the callback + FFI_DPRINTF("dispatch from %ld, is_main=%d", pthread_self(), pthread_self() == FFI_G(main_tid)); zend_ffi_dispatch_callback(); - } else { - // post interrupt request to acquire the main thread - zend_atomic_bool_store_ex(&EG(vm_interrupt), true); - pthread_mutex_unlock(&FFI_G(vm_lock)); - } + FFI_DPRINTF("done\n"); - if(!is_main_thread){ - // wait for the request to complete before returning - zend_ffi_wait_request_barrier(true); + if(!is_main_thread){ + // unlock interrupt handler + zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); + FFI_DPRINTF("--> unlock\n"); + pthread_cond_broadcast(&FFI_G(vm_unlock)); + pthread_mutex_unlock(&FFI_G(vm_response_lock)); + } } + pthread_mutex_unlock(&FFI_G(vm_request_lock)); } /* }}} */ @@ -5602,8 +5647,10 @@ ZEND_MINIT_FUNCTION(ffi) orig_interrupt_function = zend_interrupt_function; zend_interrupt_function = zend_ffi_interrupt_function; - pthread_mutex_init(&FFI_G(vm_lock), NULL); + pthread_mutex_init(&FFI_G(vm_request_lock), NULL); + pthread_mutex_init(&FFI_G(vm_response_lock), NULL); pthread_cond_init(&FFI_G(vm_ack), NULL); + pthread_cond_init(&FFI_G(vm_unlock), NULL); return SUCCESS; } diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 074aa39f4c653..6905924b6f149 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -48,8 +48,12 @@ ZEND_BEGIN_MODULE_GLOBALS(ffi) HashTable types; zend_atomic_bool callback_in_progress; - pthread_mutex_t vm_lock; + + pthread_mutex_t vm_response_lock; + pthread_mutex_t vm_request_lock; pthread_cond_t vm_ack; + pthread_cond_t vm_unlock; + pthread_t callback_tid; pthread_t main_tid; zend_ffi_call_data callback_data; From 083cc9e47d182cb48135aa1d27d6e916e37df1e2 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Fri, 1 Dec 2023 00:28:31 +0100 Subject: [PATCH 10/21] ffi: fix bug79177 once again --- ext/ffi/ffi.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index ad20e5083fb38..ca1a0934ea1ea 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -949,7 +949,14 @@ static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */ static void (*orig_interrupt_function)(zend_execute_data *execute_data); static void zend_ffi_dispatch_callback_end(void){ /* {{{ */ - //pthread_cond_broadcast(&FFI_G(vm_ack)); + zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); + bool is_main_thread = FFI_G(callback_tid) == FFI_G(main_tid); + if(!is_main_thread){ + // unlock interrupt handler + FFI_DPRINTF("--> unlock\n"); + pthread_cond_broadcast(&FFI_G(vm_unlock)); + pthread_mutex_unlock(&FFI_G(vm_response_lock)); + } } /* }}} */ @@ -1002,6 +1009,9 @@ static void zend_ffi_dispatch_callback(void){ /* {{{ */ free_alloca(fci.params, use_heap); if (EG(exception)) { + // we're about to do a hard exit. unlock all mutexes + zend_ffi_dispatch_callback_end(); + pthread_mutex_unlock(&FFI_G(vm_request_lock)); zend_error_noreturn(E_ERROR, "Throwing from FFI callbacks is not allowed"); } @@ -1015,10 +1025,14 @@ static void zend_ffi_dispatch_callback(void){ /* {{{ */ /* }}} */ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */ + pthread_mutex_lock(&FFI_G(vm_response_lock)); + if (!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { + goto end; + } + bool is_main_tid = FFI_G(callback_tid) == FFI_G(main_tid); if(!is_main_tid){ - pthread_mutex_lock(&FFI_G(vm_response_lock)); - + FFI_DPRINTF("<-- ACK\n"); // notify calling thread and release pthread_cond_broadcast(&FFI_G(vm_ack)); @@ -1026,10 +1040,11 @@ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ // release mutex and wait for the unlock signal FFI_DPRINTF("-- wait unlock --\n"); pthread_cond_wait(&FFI_G(vm_unlock), &FFI_G(vm_response_lock)); - pthread_mutex_unlock(&FFI_G(vm_response_lock)); - FFI_DPRINTF("-- end\n"); } + end: + pthread_mutex_unlock(&FFI_G(vm_response_lock)); + FFI_DPRINTF("-- end\n"); if (orig_interrupt_function) { orig_interrupt_function(execute_data); } @@ -1100,13 +1115,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v zend_ffi_dispatch_callback(); FFI_DPRINTF("done\n"); - if(!is_main_thread){ - // unlock interrupt handler - zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); - FFI_DPRINTF("--> unlock\n"); - pthread_cond_broadcast(&FFI_G(vm_unlock)); - pthread_mutex_unlock(&FFI_G(vm_response_lock)); - } + zend_ffi_dispatch_callback_end(); } pthread_mutex_unlock(&FFI_G(vm_request_lock)); } From 34353aa8e09bac5f11c1d5f03a775a2cc5ae3964 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Fri, 1 Dec 2023 00:33:40 +0100 Subject: [PATCH 11/21] ffi: cleanup --- ext/ffi/ffi.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index ca1a0934ea1ea..bc86eae74520f 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -57,13 +57,6 @@ # define __BIGGEST_ALIGNMENT__ sizeof(size_t) #endif -//#define FFI_DEBUG -#ifdef FFI_DEBUG -#define FFI_DPRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) -#else -#define FFI_DPRINTF(fmt, ...) -#endif - ZEND_DECLARE_MODULE_GLOBALS(ffi) typedef enum _zend_ffi_tag_kind { @@ -953,7 +946,6 @@ static void zend_ffi_dispatch_callback_end(void){ /* {{{ */ bool is_main_thread = FFI_G(callback_tid) == FFI_G(main_tid); if(!is_main_thread){ // unlock interrupt handler - FFI_DPRINTF("--> unlock\n"); pthread_cond_broadcast(&FFI_G(vm_unlock)); pthread_mutex_unlock(&FFI_G(vm_response_lock)); } @@ -1033,18 +1025,15 @@ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ bool is_main_tid = FFI_G(callback_tid) == FFI_G(main_tid); if(!is_main_tid){ - FFI_DPRINTF("<-- ACK\n"); // notify calling thread and release pthread_cond_broadcast(&FFI_G(vm_ack)); // release mutex and wait for the unlock signal - FFI_DPRINTF("-- wait unlock --\n"); pthread_cond_wait(&FFI_G(vm_unlock), &FFI_G(vm_response_lock)); } end: pthread_mutex_unlock(&FFI_G(vm_response_lock)); - FFI_DPRINTF("-- end\n"); if (orig_interrupt_function) { orig_interrupt_function(execute_data); } @@ -1103,17 +1092,12 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v // post interrupt request to synchronize with the main thread zend_atomic_bool_store_ex(&EG(vm_interrupt), true); - // wait for the ack, keep the lock - //zend_ffi_wait_cond(&FFI_G(vm_response_lock), &FFI_G(vm_ack), &FFI_G(callback_in_progress), true, false); - pthread_mutex_lock(&FFI_G(vm_response_lock)); pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_response_lock)); } // dispatch the callback - FFI_DPRINTF("dispatch from %ld, is_main=%d", pthread_self(), pthread_self() == FFI_G(main_tid)); zend_ffi_dispatch_callback(); - FFI_DPRINTF("done\n"); zend_ffi_dispatch_callback_end(); } From 9164b168d067860cbdafb6b5d5a673bee01fa140 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Fri, 1 Dec 2023 01:07:37 +0100 Subject: [PATCH 12/21] ffi: initialize stack info for the new thread --- ext/ffi/ffi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index bc86eae74520f..41da6ab7b0589 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -1094,6 +1094,9 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v pthread_mutex_lock(&FFI_G(vm_response_lock)); pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_response_lock)); + + // prepare the stack call info/limits for the current thread + zend_call_stack_init(); } // dispatch the callback From 20dd9b129cd126dea7f88f03602e42f6c20bef07 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Fri, 1 Dec 2023 01:24:30 +0100 Subject: [PATCH 13/21] ffi: fix vm_ack <-> vm_unlock deadlock this was caused by the use of 2 separate mutexes --- ext/ffi/ffi.c | 13 ++++++------- ext/ffi/php_ffi.h | 1 - 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 41da6ab7b0589..38f90d39ad84c 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -947,7 +947,7 @@ static void zend_ffi_dispatch_callback_end(void){ /* {{{ */ if(!is_main_thread){ // unlock interrupt handler pthread_cond_broadcast(&FFI_G(vm_unlock)); - pthread_mutex_unlock(&FFI_G(vm_response_lock)); + pthread_mutex_unlock(&FFI_G(vm_request_lock)); } } /* }}} */ @@ -1017,7 +1017,7 @@ static void zend_ffi_dispatch_callback(void){ /* {{{ */ /* }}} */ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */ - pthread_mutex_lock(&FFI_G(vm_response_lock)); + pthread_mutex_lock(&FFI_G(vm_request_lock)); if (!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { goto end; } @@ -1029,11 +1029,11 @@ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ pthread_cond_broadcast(&FFI_G(vm_ack)); // release mutex and wait for the unlock signal - pthread_cond_wait(&FFI_G(vm_unlock), &FFI_G(vm_response_lock)); + pthread_cond_wait(&FFI_G(vm_unlock), &FFI_G(vm_request_lock)); } end: - pthread_mutex_unlock(&FFI_G(vm_response_lock)); + pthread_mutex_unlock(&FFI_G(vm_request_lock)); if (orig_interrupt_function) { orig_interrupt_function(execute_data); } @@ -1092,8 +1092,8 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v // post interrupt request to synchronize with the main thread zend_atomic_bool_store_ex(&EG(vm_interrupt), true); - pthread_mutex_lock(&FFI_G(vm_response_lock)); - pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_response_lock)); + // release mutex and wait for ack + pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_request_lock)); // prepare the stack call info/limits for the current thread zend_call_stack_init(); @@ -5644,7 +5644,6 @@ ZEND_MINIT_FUNCTION(ffi) zend_interrupt_function = zend_ffi_interrupt_function; pthread_mutex_init(&FFI_G(vm_request_lock), NULL); - pthread_mutex_init(&FFI_G(vm_response_lock), NULL); pthread_cond_init(&FFI_G(vm_ack), NULL); pthread_cond_init(&FFI_G(vm_unlock), NULL); diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 6905924b6f149..8f98fda67de41 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -49,7 +49,6 @@ ZEND_BEGIN_MODULE_GLOBALS(ffi) zend_atomic_bool callback_in_progress; - pthread_mutex_t vm_response_lock; pthread_mutex_t vm_request_lock; pthread_cond_t vm_ack; pthread_cond_t vm_unlock; From e20a564b6961cb7538c141d6989a172e4a242eb0 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Sat, 2 Dec 2023 21:52:27 +0100 Subject: [PATCH 14/21] ffi: add missing includes for php_ffi.h fixes parse errors in clangd --- ext/ffi/php_ffi.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 8f98fda67de41..c125872c7ea00 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -19,6 +19,8 @@ #include #include +#include "zend_compile.h" +#include "zend_API.h" extern zend_module_entry ffi_module_entry; #define phpext_ffi_ptr &ffi_module_entry From af12a991cc4df090c9f2cdf2355d465dc8292472 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Sat, 2 Dec 2023 23:37:18 +0100 Subject: [PATCH 15/21] enable TSRM mutex APIs outside of ZTS --- TSRM/TSRM.c | 246 +++++++++++++++++++++++----------------------- TSRM/TSRM.h | 64 ++++++------ ext/ffi/ffi.c | 26 ++--- ext/ffi/php_ffi.h | 3 +- 4 files changed, 173 insertions(+), 166 deletions(-) diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 09ff8cc49279e..4712ed53fd75c 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -12,6 +12,130 @@ #include "TSRM.h" + +#ifdef TSRM_DEBUG +#define TSRM_ERROR(args) tsrm_error args +#define TSRM_SAFE_RETURN_RSRC(array, offset, range) \ + { \ + int unshuffled_offset = TSRM_UNSHUFFLE_RSRC_ID(offset); \ + \ + if (offset==0) { \ + return &array; \ + } else if ((unshuffled_offset)>=0 && (unshuffled_offset)<(range)) { \ + TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Successfully fetched resource id %d for thread id %ld - 0x%0.8X", \ + unshuffled_offset, (long) thread_resources->thread_id, array[unshuffled_offset])); \ + return array[unshuffled_offset]; \ + } else { \ + TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Resource id %d is out of range (%d..%d)", \ + unshuffled_offset, TSRM_SHUFFLE_RSRC_ID(0), TSRM_SHUFFLE_RSRC_ID(thread_resources->count-1))); \ + return NULL; \ + } \ + } +#else +#define TSRM_ERROR(args) +#define TSRM_SAFE_RETURN_RSRC(array, offset, range) \ + if (offset==0) { \ + return &array; \ + } else { \ + return array[TSRM_UNSHUFFLE_RSRC_ID(offset)]; \ + } +#endif + + +/* + * Utility Functions + */ + +/* Obtain the current thread id */ +TSRM_API THREAD_T tsrm_thread_id(void) +{/*{{{*/ +#ifdef TSRM_WIN32 + return GetCurrentThreadId(); +#else + return pthread_self(); +#endif +}/*}}}*/ + + +/* Allocate a mutex */ +TSRM_API MUTEX_T tsrm_mutex_alloc(void) +{/*{{{*/ + MUTEX_T mutexp; +#ifdef TSRM_WIN32 + mutexp = malloc(sizeof(CRITICAL_SECTION)); + InitializeCriticalSection(mutexp); +#else + mutexp = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(mutexp,NULL); +#endif +#ifdef THR_DEBUG + printf("Mutex created thread: %d\n",mythreadid()); +#endif + return( mutexp ); +}/*}}}*/ + + +/* Free a mutex */ +TSRM_API void tsrm_mutex_free(MUTEX_T mutexp) +{/*{{{*/ + if (mutexp) { +#ifdef TSRM_WIN32 + DeleteCriticalSection(mutexp); + free(mutexp); +#else + pthread_mutex_destroy(mutexp); + free(mutexp); +#endif + } +#ifdef THR_DEBUG + printf("Mutex freed thread: %d\n",mythreadid()); +#endif +}/*}}}*/ + + +/* + Lock a mutex. + A return value of 0 indicates success +*/ +TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp) +{/*{{{*/ + TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex locked thread: %ld", tsrm_thread_id())); +#ifdef TSRM_WIN32 + EnterCriticalSection(mutexp); + return 0; +#else + return pthread_mutex_lock(mutexp); +#endif +}/*}}}*/ + + +/* + Unlock a mutex. + A return value of 0 indicates success +*/ +TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp) +{/*{{{*/ + TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex unlocked thread: %ld", tsrm_thread_id())); +#ifdef TSRM_WIN32 + LeaveCriticalSection(mutexp); + return 0; +#else + return pthread_mutex_unlock(mutexp); +#endif +}/*}}}*/ + +/* + Changes the signal mask of the calling thread +*/ +#ifdef HAVE_SIGPROCMASK +TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset) +{/*{{{*/ + TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Changed sigmask in thread: %ld", tsrm_thread_id())); + + return pthread_sigmask(how, set, oldset); +}/*}}}*/ +#endif + #ifdef ZTS #include @@ -74,33 +198,6 @@ int tsrm_error(int level, const char *format, ...); static int tsrm_error_level; static FILE *tsrm_error_file; -#ifdef TSRM_DEBUG -#define TSRM_ERROR(args) tsrm_error args -#define TSRM_SAFE_RETURN_RSRC(array, offset, range) \ - { \ - int unshuffled_offset = TSRM_UNSHUFFLE_RSRC_ID(offset); \ - \ - if (offset==0) { \ - return &array; \ - } else if ((unshuffled_offset)>=0 && (unshuffled_offset)<(range)) { \ - TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Successfully fetched resource id %d for thread id %ld - 0x%0.8X", \ - unshuffled_offset, (long) thread_resources->thread_id, array[unshuffled_offset])); \ - return array[unshuffled_offset]; \ - } else { \ - TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Resource id %d is out of range (%d..%d)", \ - unshuffled_offset, TSRM_SHUFFLE_RSRC_ID(0), TSRM_SHUFFLE_RSRC_ID(thread_resources->count-1))); \ - return NULL; \ - } \ - } -#else -#define TSRM_ERROR(args) -#define TSRM_SAFE_RETURN_RSRC(array, offset, range) \ - if (offset==0) { \ - return &array; \ - } else { \ - return array[TSRM_UNSHUFFLE_RSRC_ID(offset)]; \ - } -#endif #ifdef TSRM_WIN32 static DWORD tls_key; @@ -577,101 +674,6 @@ void ts_free_id(ts_rsrc_id id) }/*}}}*/ -/* - * Utility Functions - */ - -/* Obtain the current thread id */ -TSRM_API THREAD_T tsrm_thread_id(void) -{/*{{{*/ -#ifdef TSRM_WIN32 - return GetCurrentThreadId(); -#else - return pthread_self(); -#endif -}/*}}}*/ - - -/* Allocate a mutex */ -TSRM_API MUTEX_T tsrm_mutex_alloc(void) -{/*{{{*/ - MUTEX_T mutexp; -#ifdef TSRM_WIN32 - mutexp = malloc(sizeof(CRITICAL_SECTION)); - InitializeCriticalSection(mutexp); -#else - mutexp = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); - pthread_mutex_init(mutexp,NULL); -#endif -#ifdef THR_DEBUG - printf("Mutex created thread: %d\n",mythreadid()); -#endif - return( mutexp ); -}/*}}}*/ - - -/* Free a mutex */ -TSRM_API void tsrm_mutex_free(MUTEX_T mutexp) -{/*{{{*/ - if (mutexp) { -#ifdef TSRM_WIN32 - DeleteCriticalSection(mutexp); - free(mutexp); -#else - pthread_mutex_destroy(mutexp); - free(mutexp); -#endif - } -#ifdef THR_DEBUG - printf("Mutex freed thread: %d\n",mythreadid()); -#endif -}/*}}}*/ - - -/* - Lock a mutex. - A return value of 0 indicates success -*/ -TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp) -{/*{{{*/ - TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex locked thread: %ld", tsrm_thread_id())); -#ifdef TSRM_WIN32 - EnterCriticalSection(mutexp); - return 0; -#else - return pthread_mutex_lock(mutexp); -#endif -}/*}}}*/ - - -/* - Unlock a mutex. - A return value of 0 indicates success -*/ -TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp) -{/*{{{*/ - TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex unlocked thread: %ld", tsrm_thread_id())); -#ifdef TSRM_WIN32 - LeaveCriticalSection(mutexp); - return 0; -#else - return pthread_mutex_unlock(mutexp); -#endif -}/*}}}*/ - -/* - Changes the signal mask of the calling thread -*/ -#ifdef HAVE_SIGPROCMASK -TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset) -{/*{{{*/ - TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Changed sigmask in thread: %ld", tsrm_thread_id())); - - return pthread_sigmask(how, set, oldset); -}/*}}}*/ -#endif - - TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler) {/*{{{*/ void *retval = (void *) tsrm_new_thread_begin_handler; diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h index 3bb32e4da289a..f728d543ec43b 100644 --- a/TSRM/TSRM.h +++ b/TSRM/TSRM.h @@ -13,6 +13,14 @@ #ifndef TSRM_H #define TSRM_H +#ifdef TSRM_WIN32 +# ifndef TSRM_INCLUDE_FULL_WINDOWS_HEADERS +# define WIN32_LEAN_AND_MEAN +# endif +#else +# include +#endif + #if !defined(__CYGWIN__) && defined(WIN32) # define TSRM_WIN32 # include "Zend/zend_config.w32.h" @@ -38,17 +46,35 @@ typedef intptr_t tsrm_intptr_t; typedef uintptr_t tsrm_uintptr_t; -/* Only compile multi-threading functions if we're in ZTS mode */ -#ifdef ZTS - +/* Define THREAD_T and MUTEX_T */ #ifdef TSRM_WIN32 -# ifndef TSRM_INCLUDE_FULL_WINDOWS_HEADERS -# define WIN32_LEAN_AND_MEAN -# endif +# define THREAD_T DWORD +# define MUTEX_T CRITICAL_SECTION * #else -# include +# define THREAD_T pthread_t +# define MUTEX_T pthread_mutex_t * +#endif + +#include + +/* Debug support */ +#define TSRM_ERROR_LEVEL_ERROR 1 +#define TSRM_ERROR_LEVEL_CORE 2 +#define TSRM_ERROR_LEVEL_INFO 3 + +/* utility functions */ +TSRM_API THREAD_T tsrm_thread_id(void); +TSRM_API MUTEX_T tsrm_mutex_alloc(void); +TSRM_API void tsrm_mutex_free(MUTEX_T mutexp); +TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp); +TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp); +#ifdef HAVE_SIGPROCMASK +TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset); #endif +/* Only compile multi-threading functions if we're in ZTS mode */ +#ifdef ZTS + #if SIZEOF_SIZE_T == 4 # define TSRM_ALIGNED_SIZE(size) \ (((size) + INT32_C(15)) & ~INT32_C(15)) @@ -59,16 +85,7 @@ typedef uintptr_t tsrm_uintptr_t; typedef int ts_rsrc_id; -/* Define THREAD_T and MUTEX_T */ -#ifdef TSRM_WIN32 -# define THREAD_T DWORD -# define MUTEX_T CRITICAL_SECTION * -#else -# define THREAD_T pthread_t -# define MUTEX_T pthread_mutex_t * -#endif -#include typedef void (*ts_allocate_ctor)(void *); typedef void (*ts_allocate_dtor)(void *); @@ -105,11 +122,6 @@ TSRM_API void ts_free_thread(void); TSRM_API void ts_free_id(ts_rsrc_id id); -/* Debug support */ -#define TSRM_ERROR_LEVEL_ERROR 1 -#define TSRM_ERROR_LEVEL_CORE 2 -#define TSRM_ERROR_LEVEL_INFO 3 - typedef void (*tsrm_thread_begin_func_t)(THREAD_T thread_id); typedef void (*tsrm_thread_end_func_t)(THREAD_T thread_id); typedef void (*tsrm_shutdown_func_t)(void); @@ -118,16 +130,6 @@ typedef void (*tsrm_shutdown_func_t)(void); TSRM_API int tsrm_error(int level, const char *format, ...); TSRM_API void tsrm_error_set(int level, const char *debug_filename); -/* utility functions */ -TSRM_API THREAD_T tsrm_thread_id(void); -TSRM_API MUTEX_T tsrm_mutex_alloc(void); -TSRM_API void tsrm_mutex_free(MUTEX_T mutexp); -TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp); -TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp); -#ifdef HAVE_SIGPROCMASK -TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset); -#endif - TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler); TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler); TSRM_API void *tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler); diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 38f90d39ad84c..9c26994a1ca9f 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -26,6 +26,7 @@ #include "zend_closures.h" #include "zend_weakrefs.h" #include "main/SAPI.h" +#include "TSRM.h" #include @@ -228,7 +229,7 @@ static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type); static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type); static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type); static void zend_ffi_wait_cond( - pthread_mutex_t *mutex, pthread_cond_t *cond, + MUTEX_T mutex, pthread_cond_t *cond, zend_atomic_bool *flag, bool wanted_value, bool release ); @@ -947,7 +948,7 @@ static void zend_ffi_dispatch_callback_end(void){ /* {{{ */ if(!is_main_thread){ // unlock interrupt handler pthread_cond_broadcast(&FFI_G(vm_unlock)); - pthread_mutex_unlock(&FFI_G(vm_request_lock)); + tsrm_mutex_unlock(FFI_G(vm_request_lock)); } } /* }}} */ @@ -1003,7 +1004,7 @@ static void zend_ffi_dispatch_callback(void){ /* {{{ */ if (EG(exception)) { // we're about to do a hard exit. unlock all mutexes zend_ffi_dispatch_callback_end(); - pthread_mutex_unlock(&FFI_G(vm_request_lock)); + tsrm_mutex_unlock(FFI_G(vm_request_lock)); zend_error_noreturn(E_ERROR, "Throwing from FFI callbacks is not allowed"); } @@ -1017,7 +1018,7 @@ static void zend_ffi_dispatch_callback(void){ /* {{{ */ /* }}} */ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */ - pthread_mutex_lock(&FFI_G(vm_request_lock)); + tsrm_mutex_lock(FFI_G(vm_request_lock)); if (!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { goto end; } @@ -1029,11 +1030,11 @@ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ pthread_cond_broadcast(&FFI_G(vm_ack)); // release mutex and wait for the unlock signal - pthread_cond_wait(&FFI_G(vm_unlock), &FFI_G(vm_request_lock)); + pthread_cond_wait(&FFI_G(vm_unlock), FFI_G(vm_request_lock)); } end: - pthread_mutex_unlock(&FFI_G(vm_request_lock)); + tsrm_mutex_unlock(FFI_G(vm_request_lock)); if (orig_interrupt_function) { orig_interrupt_function(execute_data); } @@ -1045,7 +1046,7 @@ static void zend_ffi_wait_cond( zend_atomic_bool *flag, bool wanted_value, bool release ){ /* {{{ */ // get lock, first - pthread_mutex_lock(mutex); + tsrm_mutex_lock(mutex); // if we acquired the lock before the request could be serviced // unlock it and wait for the flag @@ -1058,13 +1059,13 @@ static void zend_ffi_wait_cond( } if(release){ - pthread_mutex_unlock(mutex); + tsrm_mutex_unlock(mutex); } } /* }}} */ static void zend_ffi_wait_request_barrier(bool release){ /* {{{ */ - zend_ffi_wait_cond(&FFI_G(vm_request_lock), &FFI_G(vm_unlock), &FFI_G(callback_in_progress), false, release); + zend_ffi_wait_cond(FFI_G(vm_request_lock), &FFI_G(vm_unlock), &FFI_G(callback_in_progress), false, release); } /* }}} */ @@ -1093,7 +1094,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v zend_atomic_bool_store_ex(&EG(vm_interrupt), true); // release mutex and wait for ack - pthread_cond_wait(&FFI_G(vm_ack), &FFI_G(vm_request_lock)); + pthread_cond_wait(&FFI_G(vm_ack), FFI_G(vm_request_lock)); // prepare the stack call info/limits for the current thread zend_call_stack_init(); @@ -1104,7 +1105,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v zend_ffi_dispatch_callback_end(); } - pthread_mutex_unlock(&FFI_G(vm_request_lock)); + tsrm_mutex_unlock(FFI_G(vm_request_lock)); } /* }}} */ @@ -5643,7 +5644,7 @@ ZEND_MINIT_FUNCTION(ffi) orig_interrupt_function = zend_interrupt_function; zend_interrupt_function = zend_ffi_interrupt_function; - pthread_mutex_init(&FFI_G(vm_request_lock), NULL); + FFI_G(vm_request_lock) = tsrm_mutex_alloc(); pthread_cond_init(&FFI_G(vm_ack), NULL); pthread_cond_init(&FFI_G(vm_unlock), NULL); @@ -5767,6 +5768,7 @@ static ZEND_GINIT_FUNCTION(ffi) /* {{{ ZEND_GINIT_FUNCTION */ static ZEND_GSHUTDOWN_FUNCTION(ffi) { + tsrm_mutex_free(ffi_globals->vm_request_lock); zend_ffi_wait_request_barrier(true); if (ffi_globals->scopes) { zend_hash_destroy(ffi_globals->scopes); diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index c125872c7ea00..b42c261180ab8 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -21,6 +21,7 @@ #include #include "zend_compile.h" #include "zend_API.h" +#include "TSRM.h" extern zend_module_entry ffi_module_entry; #define phpext_ffi_ptr &ffi_module_entry @@ -51,7 +52,7 @@ ZEND_BEGIN_MODULE_GLOBALS(ffi) zend_atomic_bool callback_in_progress; - pthread_mutex_t vm_request_lock; + MUTEX_T vm_request_lock; pthread_cond_t vm_ack; pthread_cond_t vm_unlock; pthread_t callback_tid; From 261a1d3a73411e1aea1e762ad2ce9270a43b93d2 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Sun, 3 Dec 2023 00:41:59 +0100 Subject: [PATCH 16/21] tsrm: add cond API (POSIX only for now) --- TSRM/TSRM.c | 42 ++++++++++++++++++++++++++++++++++++++++++ TSRM/TSRM.h | 6 ++++++ ext/ffi/ffi.c | 35 +++++++++++++++-------------------- ext/ffi/php_ffi.h | 8 ++++---- 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 4712ed53fd75c..2e4e89e6ff357 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -11,6 +11,8 @@ */ #include "TSRM.h" +#include +#include #ifdef TSRM_DEBUG @@ -56,6 +58,35 @@ TSRM_API THREAD_T tsrm_thread_id(void) #endif }/*}}}*/ +TSRM_API COND_T tsrm_cond_alloc(void) +{ + COND_T condp; +#ifdef TSRM_WIN32 +#error "TODO" +#else + condp = (pthread_cond_t *)malloc(sizeof(pthread_cond_t)); + pthread_cond_init(condp, NULL); +#endif + return( condp ); +} + +TSRM_API int tsrm_cond_wait(COND_T condp, MUTEX_T mutexp) +{ +#ifdef TSRM_WIN32 +#error "TODO" +#else + return pthread_cond_wait(condp, mutexp); +#endif +} + +TSRM_API int tsrm_cond_broadcast(COND_T condp) +{ +#ifdef TSRM_WIN32 +#error "TODO" +#else + return pthread_cond_broadcast(condp); +#endif +} /* Allocate a mutex */ TSRM_API MUTEX_T tsrm_mutex_alloc(void) @@ -92,6 +123,17 @@ TSRM_API void tsrm_mutex_free(MUTEX_T mutexp) #endif }/*}}}*/ +TSRM_API void tsrm_cond_free(COND_T condp) +{ +#ifdef TSRM_WIN32 +#error "TODO" +#else + if(condp){ + pthread_cond_destroy(condp); + free(condp); + } +#endif +} /* Lock a mutex. diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h index f728d543ec43b..23b61e7df8db2 100644 --- a/TSRM/TSRM.h +++ b/TSRM/TSRM.h @@ -53,6 +53,7 @@ typedef uintptr_t tsrm_uintptr_t; #else # define THREAD_T pthread_t # define MUTEX_T pthread_mutex_t * +# define COND_T pthread_cond_t * #endif #include @@ -65,9 +66,14 @@ typedef uintptr_t tsrm_uintptr_t; /* utility functions */ TSRM_API THREAD_T tsrm_thread_id(void); TSRM_API MUTEX_T tsrm_mutex_alloc(void); +TSRM_API COND_T tsrm_cond_alloc(void); TSRM_API void tsrm_mutex_free(MUTEX_T mutexp); TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp); TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp); +TSRM_API int tsrm_cond_wait(COND_T condp, MUTEX_T mutexp); +TSRM_API int tsrm_cond_broadcast(COND_T condp); +TSRM_API void tsrm_cond_free(COND_T condp); + #ifdef HAVE_SIGPROCMASK TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset); #endif diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 9c26994a1ca9f..1db42a8d9f4a7 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -28,8 +28,6 @@ #include "main/SAPI.h" #include "TSRM.h" -#include - #include #include #include @@ -229,7 +227,7 @@ static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type); static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type); static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type); static void zend_ffi_wait_cond( - MUTEX_T mutex, pthread_cond_t *cond, + MUTEX_T mutexp, COND_T condp, zend_atomic_bool *flag, bool wanted_value, bool release ); @@ -947,16 +945,13 @@ static void zend_ffi_dispatch_callback_end(void){ /* {{{ */ bool is_main_thread = FFI_G(callback_tid) == FFI_G(main_tid); if(!is_main_thread){ // unlock interrupt handler - pthread_cond_broadcast(&FFI_G(vm_unlock)); + tsrm_cond_broadcast(FFI_G(vm_unlock)); tsrm_mutex_unlock(FFI_G(vm_request_lock)); } } /* }}} */ static void zend_ffi_dispatch_callback(void){ /* {{{ */ - // this function must always run on the main thread - //ZEND_ASSERT(pthread_self() == FFI_G(main_tid)); - if (!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { return; } @@ -1027,10 +1022,10 @@ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ if(!is_main_tid){ // notify calling thread and release - pthread_cond_broadcast(&FFI_G(vm_ack)); + tsrm_cond_broadcast(FFI_G(vm_ack)); // release mutex and wait for the unlock signal - pthread_cond_wait(&FFI_G(vm_unlock), FFI_G(vm_request_lock)); + tsrm_cond_wait(FFI_G(vm_unlock), FFI_G(vm_request_lock)); } end: @@ -1042,30 +1037,30 @@ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ /* }}} */ static void zend_ffi_wait_cond( - pthread_mutex_t *mutex, pthread_cond_t *cond, + MUTEX_T mutexp, COND_T condp, zend_atomic_bool *flag, bool wanted_value, bool release ){ /* {{{ */ // get lock, first - tsrm_mutex_lock(mutex); + tsrm_mutex_lock(mutexp); // if we acquired the lock before the request could be serviced // unlock it and wait for the flag if(flag == NULL){ - pthread_cond_wait(cond, mutex); + tsrm_cond_wait(condp, mutexp); } else { while(zend_atomic_bool_load_ex(flag) != wanted_value){ - pthread_cond_wait(cond, mutex); + tsrm_cond_wait(condp, mutexp); } } if(release){ - tsrm_mutex_unlock(mutex); + tsrm_mutex_unlock(mutexp); } } /* }}} */ static void zend_ffi_wait_request_barrier(bool release){ /* {{{ */ - zend_ffi_wait_cond(FFI_G(vm_request_lock), &FFI_G(vm_unlock), &FFI_G(callback_in_progress), false, release); + zend_ffi_wait_cond(FFI_G(vm_request_lock), FFI_G(vm_unlock), &FFI_G(callback_in_progress), false, release); } /* }}} */ @@ -1086,7 +1081,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v }; FFI_G(callback_data) = call_data; - FFI_G(callback_tid) = pthread_self(); + FFI_G(callback_tid) = tsrm_thread_id(); bool is_main_thread = FFI_G(callback_tid) == FFI_G(main_tid); if(!is_main_thread){ @@ -1094,7 +1089,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v zend_atomic_bool_store_ex(&EG(vm_interrupt), true); // release mutex and wait for ack - pthread_cond_wait(&FFI_G(vm_ack), FFI_G(vm_request_lock)); + tsrm_cond_wait(FFI_G(vm_ack), FFI_G(vm_request_lock)); // prepare the stack call info/limits for the current thread zend_call_stack_init(); @@ -5638,15 +5633,15 @@ ZEND_MINIT_FUNCTION(ffi) return zend_ffi_preload(FFI_G(preload)); } - FFI_G(main_tid) = pthread_self(); + FFI_G(main_tid) = tsrm_thread_id(); zend_atomic_bool_store_ex(&FFI_G(callback_in_progress), false); orig_interrupt_function = zend_interrupt_function; zend_interrupt_function = zend_ffi_interrupt_function; FFI_G(vm_request_lock) = tsrm_mutex_alloc(); - pthread_cond_init(&FFI_G(vm_ack), NULL); - pthread_cond_init(&FFI_G(vm_unlock), NULL); + FFI_G(vm_ack) = tsrm_cond_alloc(); + FFI_G(vm_unlock) = tsrm_cond_alloc(); return SUCCESS; } diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index b42c261180ab8..315edfa39df44 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -53,10 +53,10 @@ ZEND_BEGIN_MODULE_GLOBALS(ffi) zend_atomic_bool callback_in_progress; MUTEX_T vm_request_lock; - pthread_cond_t vm_ack; - pthread_cond_t vm_unlock; - pthread_t callback_tid; - pthread_t main_tid; + COND_T vm_ack; + COND_T vm_unlock; + THREAD_T callback_tid; + THREAD_T main_tid; zend_ffi_call_data callback_data; /* preloading */ From 2f71fa338455ce3423a6286d92f66a27b9d6326d Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Sun, 3 Dec 2023 01:05:33 +0100 Subject: [PATCH 17/21] zend_globals_macros.h: add missing include --- Zend/zend_globals_macros.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Zend/zend_globals_macros.h b/Zend/zend_globals_macros.h index 59b3daca53fd4..d3cd9f923eeea 100644 --- a/Zend/zend_globals_macros.h +++ b/Zend/zend_globals_macros.h @@ -20,6 +20,8 @@ #ifndef ZEND_GLOBALS_MACROS_H #define ZEND_GLOBALS_MACROS_H +#include "zend_portability.h" + typedef struct _zend_compiler_globals zend_compiler_globals; typedef struct _zend_executor_globals zend_executor_globals; typedef struct _zend_php_scanner_globals zend_php_scanner_globals; From 391ca949ac9c263e5073d8f628b46e7a0f8160c0 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Sun, 3 Dec 2023 04:20:24 +0100 Subject: [PATCH 18/21] ffi: first version using fibers for callbacks --- Zend/zend_fibers.c | 8 ++-- Zend/zend_fibers.h | 4 ++ ext/ffi/ffi.c | 107 +++++++++++++++++++-------------------------- 3 files changed, 53 insertions(+), 66 deletions(-) diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index e669ab6b53382..77e4228fd026f 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -543,7 +543,7 @@ static void zend_fiber_cleanup(zend_fiber_context *context) fiber->caller = NULL; } -static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) +ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) { ZEND_ASSERT(Z_TYPE(transfer->value) == IS_NULL && "Initial transfer value to fiber context must be NULL"); ZEND_ASSERT(!transfer->flags && "No flags should be set on initial transfer"); @@ -615,7 +615,7 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) } /* Handles forwarding of result / error from a transfer into the running fiber. */ -static zend_always_inline void zend_fiber_delegate_transfer_result( +zend_always_inline void zend_fiber_delegate_transfer_result( zend_fiber_transfer *transfer, INTERNAL_FUNCTION_PARAMETERS ) { if (transfer->flags & ZEND_FIBER_TRANSFER_FLAG_ERROR) { @@ -627,7 +627,7 @@ static zend_always_inline void zend_fiber_delegate_transfer_result( RETURN_COPY_VALUE(&transfer->value); } -static zend_always_inline zend_fiber_transfer zend_fiber_switch_to( +zend_always_inline zend_fiber_transfer zend_fiber_switch_to( zend_fiber_context *context, zval *value, bool exception ) { zend_fiber_transfer transfer = { @@ -652,7 +652,7 @@ static zend_always_inline zend_fiber_transfer zend_fiber_switch_to( return transfer; } -static zend_always_inline zend_fiber_transfer zend_fiber_resume(zend_fiber *fiber, zval *value, bool exception) +zend_always_inline zend_fiber_transfer zend_fiber_resume(zend_fiber *fiber, zval *value, bool exception) { zend_fiber *previous = EG(active_fiber); diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index 5c81f44a642e4..9de1d79f4d555 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -134,8 +134,12 @@ struct _zend_fiber { /* These functions may be used to create custom fiber objects using the bundled fiber switching context. */ ZEND_API zend_result zend_fiber_init_context(zend_fiber_context *context, void *kind, zend_fiber_coroutine coroutine, size_t stack_size); +ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer); ZEND_API void zend_fiber_destroy_context(zend_fiber_context *context); ZEND_API void zend_fiber_switch_context(zend_fiber_transfer *transfer); +ZEND_API zend_fiber_transfer zend_fiber_resume(zend_fiber *fiber, zval *value, bool exception); +ZEND_API void zend_fiber_delegate_transfer_result(zend_fiber_transfer *transfer, INTERNAL_FUNCTION_PARAMETERS); +ZEND_API zend_fiber_transfer zend_fiber_switch_to(zend_fiber_context *context, zval *value, bool exception); #ifdef ZEND_CHECK_STACK_LIMIT ZEND_API void* zend_fiber_stack_limit(zend_fiber_stack *stack); ZEND_API void* zend_fiber_stack_base(zend_fiber_stack *stack); diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 1db42a8d9f4a7..2225e1919bf0f 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -14,6 +14,8 @@ +----------------------------------------------------------------------+ */ +#include "zend_API.h" +#include "zend_globals.h" #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -27,6 +29,8 @@ #include "zend_weakrefs.h" #include "main/SAPI.h" #include "TSRM.h" +#include "zend_fibers.h" +#include "zend_stack.h" #include #include @@ -907,7 +911,9 @@ static zend_always_inline zend_string *zend_ffi_mangled_func_name(zend_string *n /* }}} */ #if FFI_CLOSURES + typedef struct _zend_ffi_callback_data { + zend_fiber fiber; zend_fcall_info_cache fcc; zend_ffi_type *type; void *code; @@ -916,6 +922,7 @@ typedef struct _zend_ffi_callback_data { uint32_t arg_count; ffi_type *ret_type; ffi_type *arg_types[0] ZEND_ELEMENT_COUNT(arg_count); + zend_ffi_call_data ffi_args; } zend_ffi_callback_data; static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */ @@ -951,66 +958,18 @@ static void zend_ffi_dispatch_callback_end(void){ /* {{{ */ } /* }}} */ -static void zend_ffi_dispatch_callback(void){ /* {{{ */ - if (!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { - return; - } - - zend_ffi_callback_data *callback_data = FFI_G(callback_data).data; - zend_fcall_info fci; - zend_ffi_type *ret_type; - zval retval; - ALLOCA_FLAG(use_heap) - - fci.size = sizeof(zend_fcall_info); - ZVAL_UNDEF(&fci.function_name); - fci.retval = &retval; - fci.params = do_alloca(sizeof(zval) *callback_data->arg_count, use_heap); - fci.object = NULL; - fci.param_count = callback_data->arg_count; - fci.named_params = NULL; - - - if (callback_data->type->func.args) { - int n = 0; - zend_ffi_type *arg_type; - - ZEND_HASH_PACKED_FOREACH_PTR(callback_data->type->func.args, arg_type) { - arg_type = ZEND_FFI_TYPE(arg_type); - zend_ffi_cdata_to_zval(NULL, FFI_G(callback_data).args[n], arg_type, BP_VAR_R, &fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST), 0, 0); - n++; - } ZEND_HASH_FOREACH_END(); - } - - ZVAL_UNDEF(&retval); - if (zend_call_function(&fci, &callback_data->fcc) != SUCCESS) { - zend_throw_error(zend_ffi_exception_ce, "Cannot call callback"); - } - - if (callback_data->arg_count) { - int n = 0; - - for (n = 0; n < callback_data->arg_count; n++) { - zval_ptr_dtor(&fci.params[n]); - } - } - free_alloca(fci.params, use_heap); - - if (EG(exception)) { - // we're about to do a hard exit. unlock all mutexes - zend_ffi_dispatch_callback_end(); - tsrm_mutex_unlock(FFI_G(vm_request_lock)); - zend_error_noreturn(E_ERROR, "Throwing from FFI callbacks is not allowed"); - } - - ret_type = ZEND_FFI_TYPE(callback_data->type->func.ret_type); - if (ret_type->kind != ZEND_FFI_TYPE_VOID) { - zend_ffi_zval_to_cdata(FFI_G(callback_data).ret, ret_type, &retval); - } - - zval_ptr_dtor(&retval); +static void zend_ffi_fci_prepare( + zend_ffi_callback_data *data, + zend_fcall_info *fci +){ + fci->size = sizeof(*fci); + ZVAL_UNDEF(&fci->function_name); + fci->retval = &data->fiber.result; + fci->params = emalloc(sizeof(zval) * data->arg_count); + fci->object = NULL; + fci->param_count = data->arg_count; + fci->named_params = NULL; } -/* }}} */ static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */ tsrm_mutex_lock(FFI_G(vm_request_lock)); @@ -1079,6 +1038,8 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v .args = args, .data = (zend_ffi_callback_data *)data }; + ((zend_ffi_callback_data *)data)->ffi_args = call_data; + FFI_G(callback_data) = call_data; FFI_G(callback_tid) = tsrm_thread_id(); @@ -1096,8 +1057,25 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v } // dispatch the callback - zend_ffi_dispatch_callback(); - + if (call_data.data->type->func.args) { + int n = 0; + zend_ffi_type *arg_type; + + ZEND_HASH_PACKED_FOREACH_PTR(call_data.data->type->func.args, arg_type) { + arg_type = ZEND_FFI_TYPE(arg_type); + zend_ffi_cdata_to_zval(NULL, + call_data.data->ffi_args.args[n], arg_type, BP_VAR_R, + &call_data.data->fiber.fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST), + 0, 0); + n++; + } ZEND_HASH_FOREACH_END(); + } + + if (zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { + zend_fiber_resume(&call_data.data->fiber, NULL, false); + efree(call_data.data->fiber.fci.params); + } + zend_ffi_dispatch_callback_end(); } tsrm_mutex_unlock(FFI_G(vm_request_lock)); @@ -1141,6 +1119,12 @@ static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ * callback_data->callback = callback; callback_data->code = code; callback_data->arg_count = arg_count; + callback_data->fiber.fci_cache = fcc; + + zend_fiber_init_context(&callback_data->fiber.context, NULL, zend_fiber_execute, EG(fiber_stack_size)); + callback_data->fiber.previous = &callback_data->fiber.context; + + zend_ffi_fci_prepare(callback_data, &callback_data->fiber.fci); if (type->func.args) { int n = 0; @@ -5763,7 +5747,6 @@ static ZEND_GINIT_FUNCTION(ffi) /* {{{ ZEND_GINIT_FUNCTION */ static ZEND_GSHUTDOWN_FUNCTION(ffi) { - tsrm_mutex_free(ffi_globals->vm_request_lock); zend_ffi_wait_request_barrier(true); if (ffi_globals->scopes) { zend_hash_destroy(ffi_globals->scopes); From e8b2b5f5fa098ca9657918bd9d0f13924a4a6562 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Sun, 3 Dec 2023 04:57:14 +0100 Subject: [PATCH 19/21] ffi: fix tests - skip locking in main thread --- ext/ffi/ffi.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 2225e1919bf0f..dd82cccad950c 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -972,6 +972,10 @@ static void zend_ffi_fci_prepare( } static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */ + if(FFI_G(callback_tid) == FFI_G(main_tid)){ + goto end; + } + tsrm_mutex_lock(FFI_G(vm_request_lock)); if (!zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { goto end; @@ -1072,7 +1076,14 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v } if (zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { - zend_fiber_resume(&call_data.data->fiber, NULL, false); + // call PHP function + zend_fiber_transfer transfer = zend_fiber_resume(&call_data.data->fiber, NULL, false); + zend_ffi_type *ret_type = ZEND_FFI_TYPE(call_data.data->type->func.ret_type); + if(ret_type->kind != ZEND_FFI_TYPE_VOID){ + // extract return value from fiber + zend_ffi_zval_to_cdata(call_data.ret, ret_type, &call_data.data->fiber.result); + } + efree(call_data.data->fiber.fci.params); } @@ -5613,6 +5624,10 @@ ZEND_MINIT_FUNCTION(ffi) zend_ffi_ctype_handlers.get_properties = zend_fake_get_properties; zend_ffi_ctype_handlers.get_gc = zend_fake_get_gc; + FFI_G(vm_request_lock) = tsrm_mutex_alloc(); + FFI_G(vm_ack) = tsrm_cond_alloc(); + FFI_G(vm_unlock) = tsrm_cond_alloc(); + if (FFI_G(preload)) { return zend_ffi_preload(FFI_G(preload)); } @@ -5623,10 +5638,6 @@ ZEND_MINIT_FUNCTION(ffi) orig_interrupt_function = zend_interrupt_function; zend_interrupt_function = zend_ffi_interrupt_function; - FFI_G(vm_request_lock) = tsrm_mutex_alloc(); - FFI_G(vm_ack) = tsrm_cond_alloc(); - FFI_G(vm_unlock) = tsrm_cond_alloc(); - return SUCCESS; } /* }}} */ @@ -5748,6 +5759,10 @@ static ZEND_GINIT_FUNCTION(ffi) static ZEND_GSHUTDOWN_FUNCTION(ffi) { zend_ffi_wait_request_barrier(true); + tsrm_cond_free(ffi_globals->vm_ack); + tsrm_cond_free(ffi_globals->vm_unlock); + tsrm_mutex_free(ffi_globals->vm_request_lock); + if (ffi_globals->scopes) { zend_hash_destroy(ffi_globals->scopes); free(ffi_globals->scopes); From e2fce83191943dbee16e8140957fdeec25a9da90 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Sun, 3 Dec 2023 05:18:05 +0100 Subject: [PATCH 20/21] tsrm: implement win32 cond API --- TSRM/TSRM.c | 10 ++++++---- ext/ffi/ffi.c | 1 - ext/ffi/php_ffi.h | 1 - 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 2e4e89e6ff357..224e6d6cb187a 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -62,7 +62,8 @@ TSRM_API COND_T tsrm_cond_alloc(void) { COND_T condp; #ifdef TSRM_WIN32 -#error "TODO" + condp = (PCONDITION_VARIABLE)malloc(sizeof(CONDITION_VARIABLE)); + InitializeConditionVariable(condp); #else condp = (pthread_cond_t *)malloc(sizeof(pthread_cond_t)); pthread_cond_init(condp, NULL); @@ -73,7 +74,7 @@ TSRM_API COND_T tsrm_cond_alloc(void) TSRM_API int tsrm_cond_wait(COND_T condp, MUTEX_T mutexp) { #ifdef TSRM_WIN32 -#error "TODO" + return SleepConditionVariableCS(condp, mutexp, INFINITE) ? 0 : -1; #else return pthread_cond_wait(condp, mutexp); #endif @@ -82,7 +83,8 @@ TSRM_API int tsrm_cond_wait(COND_T condp, MUTEX_T mutexp) TSRM_API int tsrm_cond_broadcast(COND_T condp) { #ifdef TSRM_WIN32 -#error "TODO" + WakeAllConditionVariable(condp); + return 0; #else return pthread_cond_broadcast(condp); #endif @@ -126,7 +128,7 @@ TSRM_API void tsrm_mutex_free(MUTEX_T mutexp) TSRM_API void tsrm_cond_free(COND_T condp) { #ifdef TSRM_WIN32 -#error "TODO" + free(condp); #else if(condp){ pthread_cond_destroy(condp); diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index dd82cccad950c..10b209a9629db 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -30,7 +30,6 @@ #include "main/SAPI.h" #include "TSRM.h" #include "zend_fibers.h" -#include "zend_stack.h" #include #include diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 315edfa39df44..2b31e48056363 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -18,7 +18,6 @@ #define PHP_FFI_H #include -#include #include "zend_compile.h" #include "zend_API.h" #include "TSRM.h" From dc5496fe4de8ed2c14b6a3d3b8eeae45eeca7e3a Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Sun, 3 Dec 2023 20:42:09 +0100 Subject: [PATCH 21/21] fix build errors --- TSRM/TSRM.c | 2 -- TSRM/TSRM.h | 17 +++++++++-------- ext/ffi/ffi.c | 29 ++++++++++++++++------------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 224e6d6cb187a..2139d430ae266 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -11,8 +11,6 @@ */ #include "TSRM.h" -#include -#include #ifdef TSRM_DEBUG diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h index 23b61e7df8db2..0bd9bb6282621 100644 --- a/TSRM/TSRM.h +++ b/TSRM/TSRM.h @@ -13,14 +13,6 @@ #ifndef TSRM_H #define TSRM_H -#ifdef TSRM_WIN32 -# ifndef TSRM_INCLUDE_FULL_WINDOWS_HEADERS -# define WIN32_LEAN_AND_MEAN -# endif -#else -# include -#endif - #if !defined(__CYGWIN__) && defined(WIN32) # define TSRM_WIN32 # include "Zend/zend_config.w32.h" @@ -31,6 +23,14 @@ #include #include +#ifdef TSRM_WIN32 +# ifndef TSRM_INCLUDE_FULL_WINDOWS_HEADERS +# define WIN32_LEAN_AND_MEAN +# endif +#else +# include +#endif + #ifdef TSRM_WIN32 # ifdef TSRM_EXPORTS # define TSRM_API __declspec(dllexport) @@ -50,6 +50,7 @@ typedef uintptr_t tsrm_uintptr_t; #ifdef TSRM_WIN32 # define THREAD_T DWORD # define MUTEX_T CRITICAL_SECTION * +# define COND_T PCONDITION_VARIABLE #else # define THREAD_T pthread_t # define MUTEX_T pthread_mutex_t * diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 10b209a9629db..fbf85e57a90d0 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -30,6 +30,7 @@ #include "main/SAPI.h" #include "TSRM.h" #include "zend_fibers.h" +#include "zend_call_stack.h" #include #include @@ -918,10 +919,10 @@ typedef struct _zend_ffi_callback_data { void *code; void *callback; ffi_cif cif; - uint32_t arg_count; + zend_ffi_call_data ffi_args; ffi_type *ret_type; + uint32_t arg_count; ffi_type *arg_types[0] ZEND_ELEMENT_COUNT(arg_count); - zend_ffi_call_data ffi_args; } zend_ffi_callback_data; static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */ @@ -932,9 +933,10 @@ static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */ if (callback_data->fcc.function_handler->common.fn_flags & ZEND_ACC_CLOSURE) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(callback_data->fcc.function_handler)); } + ffi_type **arg_types = callback_data->arg_types; for (int i = 0; i < callback_data->arg_count; ++i) { - if (callback_data->arg_types[i]->type == FFI_TYPE_STRUCT) { - efree(callback_data->arg_types[i]); + if (arg_types[i]->type == FFI_TYPE_STRUCT) { + efree(arg_types[i]); } } if (callback_data->ret_type->type == FFI_TYPE_STRUCT) { @@ -1076,7 +1078,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v if (zend_atomic_bool_load_ex(&FFI_G(callback_in_progress))) { // call PHP function - zend_fiber_transfer transfer = zend_fiber_resume(&call_data.data->fiber, NULL, false); + zend_fiber_resume(&call_data.data->fiber, NULL, false); zend_ffi_type *ret_type = ZEND_FFI_TYPE(call_data.data->type->func.ret_type); if(ret_type->kind != ZEND_FFI_TYPE_VOID){ // extract return value from fiber @@ -1135,6 +1137,7 @@ static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ * callback_data->fiber.previous = &callback_data->fiber.context; zend_ffi_fci_prepare(callback_data, &callback_data->fiber.fci); + ffi_type **arg_types = callback_data->arg_types; if (type->func.args) { int n = 0; @@ -1142,12 +1145,12 @@ static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ * ZEND_HASH_PACKED_FOREACH_PTR(type->func.args, arg_type) { arg_type = ZEND_FFI_TYPE(arg_type); - callback_data->arg_types[n] = zend_ffi_get_type(arg_type); - if (!callback_data->arg_types[n]) { + arg_types[n] = zend_ffi_get_type(arg_type); + if (!arg_types[n]) { zend_ffi_pass_unsupported(arg_type); for (int i = 0; i < n; ++i) { - if (callback_data->arg_types[i]->type == FFI_TYPE_STRUCT) { - efree(callback_data->arg_types[i]); + if (arg_types[i]->type == FFI_TYPE_STRUCT) { + efree(arg_types[i]); } } efree(callback_data); @@ -1161,8 +1164,8 @@ static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ * if (!callback_data->ret_type) { zend_ffi_return_unsupported(type->func.ret_type); for (int i = 0; i < callback_data->arg_count; ++i) { - if (callback_data->arg_types[i]->type == FFI_TYPE_STRUCT) { - efree(callback_data->arg_types[i]); + if (arg_types[i]->type == FFI_TYPE_STRUCT) { + efree(arg_types[i]); } } efree(callback_data); @@ -1179,8 +1182,8 @@ static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ * zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback"); free_on_failure: ; for (int i = 0; i < callback_data->arg_count; ++i) { - if (callback_data->arg_types[i]->type == FFI_TYPE_STRUCT) { - efree(callback_data->arg_types[i]); + if (arg_types[i]->type == FFI_TYPE_STRUCT) { + efree(arg_types); } } if (callback_data->ret_type->type == FFI_TYPE_STRUCT) {