-
Notifications
You must be signed in to change notification settings - Fork 7.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make FFI callbacks thread safe #12823
base: master
Are you sure you want to change the base?
Conversation
… one add zend_ffi_wait_request_barrier helper function add callback_in_progress flag
I'd like to see a simple test for this, passing the callback to pthread_create() (called via FFI) and having code executed on that other thread, possibly with a small sleep in the callback, showing that it's indeed blocking the main thread. |
Hmm an issue i just noticed is that, if i want to use platform-independent mutexes, i cannot use |
Few existing FFI tests are failed. |
The way I implemented this, it's the other way around. Is it preferrable to have the callback serviced by the requesting thread? I guess we could do it by having the interrupt handler stall, signal the FFI thread, and wait until it finishes... but is there an advantage in doing this? |
add sync barrier on gshutdown too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this should be merged.
Transferring execution of all callbacks to main thread can't be safe and will definitely lead to more problems, deadlocks, Thread Local Storage mess, global context pollution, etc...
Often this just doesn't make any sense by design. E.g. callback_threads.phpt
starts threads, but executes their code in context of main thread.
It's better to just disable calls to FFI callbacks in context of non-main threads.
The The alternative would be to manage this in the native code, but it would require wrapping all PHP callbacks in a native trampoline to manage the mutex locking/unlocking. This requires an extra (native) layers for the PHP code that wants to declare a callback and would make the callers more complicated. In essence, I am not aiming to enable multiple PHP threads. What I could do is reverse the flow, to have the callback thread run the callback instead. |
I modified the logic so that the interrupt handler now notifies the thread that invoked the callback, and goes to sleep.
|
this was caused by the use of 2 separate mutexes
ext/ffi/php_ffi.h
Outdated
#include <ffi.h> | ||
#include <pthread.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Windows build is broken. There are no <pthread.h> there.
ext/ffi/ffi.c
Outdated
static void zend_ffi_interrupt_function(zend_execute_data *execute_data){ /* {{{ */ | ||
pthread_mutex_lock(&FFI_G(vm_request_lock)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should check if this is FFI related interrupt. This may be a POSIX signal or something else...
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may a regular in-main-thread callback, that doesn't require any locks.
You should check FFI_G(callback_tid) == FFI_G(main_tid)
first.
Ideally, we should make distinct between regular and "thread" callback
$libc->pthread_create(
FFI::addr($tid), NULL,
FFI::thread_callback($thread_func), FFI::addr($arg)
);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a cooperative scheduler implemented on top of VM interrupts + pthread synchronisation for serialization of execution of callbacks in a single thread.
Is it possible to implement these ideas separately from FFI, provide some API, and extend FFI with new functionality using that API? This way we should achieve a better designed solution. I the current state, this looks like a hack that misses many things (e.g. exceptions and errors).
May be you should wrap "serialized" callbacks with fibers and then schedule fibers?
- skip locking in main thread
dc72df6
to
e2fce83
Compare
ca48818
to
40a6537
Compare
40a6537
to
dc5496f
Compare
Hello, I think we've just been bitten by this (spurious stack overflow exceptions off the main thread): libvips/php-vips#237 . As a workaround, it looks like we're going to have to ask users to disable all stack overflow checks. How about just disabling this check for execution off the main thread? I imagine it's a rare case, so it would only cause a small drop in the usefulness of this test. There are probably complications I'm unaware of, of course! |
Makes FFI callbacks work, regardless of the thread they are invoked from
Fixes #9214
$REVIEW, before merging: is there any additional handling that needs to be done for ZTS?