Created
November 28, 2023 01:29
-
-
Save smx-smx/608d2f339d1a822df4b1e125c3a33ca7 to your computer and use it in GitHub Desktop.
PHP FFI Thread Safe patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c | |
index 9be5ac3405..81ac091750 100644 | |
--- a/ext/ffi/ffi.c | |
+++ b/ext/ffi/ffi.c | |
@@ -922,9 +922,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; | |
@@ -938,13 +940,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(); | |
} | |
@@ -969,10 +972,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)); | |
} | |
/* }}} */ | |
@@ -5447,6 +5480,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 02a241c6bb..e8f7f39b67 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 <ffi.h> | |
+#include <pthread.h> | |
+ | |
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 */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <pthread.h> | |
#include <stdint.h> | |
typedef int (*callback_t)(int); | |
typedef void *(*__start_routine)(void *); | |
void make_thread(callback_t cb, int arg){ | |
pthread_t tid; | |
pthread_create(&tid, NULL, (__start_routine)cb, (void *)(uintptr_t)(arg)); | |
pthread_detach(tid); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$ffi = FFI::cdef( | |
'void make_thread(int (*cb)(void *), int arg);', | |
'./libtest.so' | |
); | |
$cb_func = function($arg){ | |
print("hi from thread\n"); | |
return 0; | |
}; | |
for($i=0; $i<2000; $i++){ | |
$ffi->make_thread($cb_func, 0xBEEF); | |
} | |
print("-- survived\n"); | |
sleep(100); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment