Skip to content

Make FFI callbacks thread safe #12823

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

Draft
wants to merge 28 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
16be72b
ffi: thread safe callbacks (preliminary)
smx-smx Nov 28, 2023
1de9669
ffi: make sure there are no in progress requests before posting a new…
smx-smx Nov 28, 2023
56c24d4
code style
smx-smx Nov 29, 2023
6423d3a
ffi: trace the requester and main thread IDs
smx-smx Nov 29, 2023
98eb079
ffi: fix deadlock when the callback invocation is from the main thread
smx-smx Nov 29, 2023
d762cfe
ffi: add tests/callback_threads
smx-smx Nov 29, 2023
29d6550
ffi: fix mutex unlock before zend_error_noreturn (fixes bug79177.phpt)
smx-smx Nov 29, 2023
ba483e6
ffi: remove wrongly placed restore of interrupt handler
smx-smx Nov 29, 2023
bce091d
ffi: have callbacks be handled by the thread that invoked them
smx-smx Nov 30, 2023
083cc9e
ffi: fix bug79177 once again
smx-smx Nov 30, 2023
34353aa
ffi: cleanup
smx-smx Nov 30, 2023
9164b16
ffi: initialize stack info for the new thread
smx-smx Dec 1, 2023
20dd9b1
ffi: fix vm_ack <-> vm_unlock deadlock
smx-smx Dec 1, 2023
e20a564
ffi: add missing includes for php_ffi.h
smx-smx Dec 2, 2023
af12a99
enable TSRM mutex APIs outside of ZTS
smx-smx Dec 2, 2023
261a1d3
tsrm: add cond API (POSIX only for now)
smx-smx Dec 2, 2023
2f71fa3
zend_globals_macros.h: add missing include
smx-smx Dec 3, 2023
391ca94
ffi: first version using fibers for callbacks
smx-smx Dec 3, 2023
e8b2b5f
ffi: fix tests
smx-smx Dec 3, 2023
e2fce83
tsrm: implement win32 cond API
smx-smx Dec 3, 2023
dc5496f
fix build errors
smx-smx Dec 3, 2023
37ee7de
Fix GH-13215 GCC 14 build
remicollet Jan 22, 2024
d2b2192
zend_ffi_callback_trampoline: fix build without ZEND_CHECK_STACK_LIMIT
smx-smx Apr 30, 2025
38f88d0
Merge commit '87d75328b2ac3cc6fc0360ede8f70d324a70819f' into ffi-ts-m…
smx-smx Apr 30, 2025
7e5697e
Merge remote-tracking branch 'origin/master' into ffi-ts-master2
smx-smx Apr 30, 2025
6f6a737
ffi: fix crash in callback (by using zend_fiber_resume_internal inste…
smx-smx May 5, 2025
9d2e24a
ffi: exceptions can now be thrown in PHP callbacks
smx-smx May 5, 2025
bd953ba
ffi: fix test gh16013
smx-smx May 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
288 changes: 166 additions & 122 deletions TSRM/TSRM.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,172 @@

#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
}/*}}}*/

TSRM_API COND_T tsrm_cond_alloc(void)
{
COND_T condp;
#ifdef TSRM_WIN32
condp = (PCONDITION_VARIABLE)malloc(sizeof(CONDITION_VARIABLE));
InitializeConditionVariable(condp);
#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
return SleepConditionVariableCS(condp, mutexp, INFINITE) ? 0 : -1;
#else
return pthread_cond_wait(condp, mutexp);
#endif
}

TSRM_API int tsrm_cond_broadcast(COND_T condp)
{
#ifdef TSRM_WIN32
WakeAllConditionVariable(condp);
return 0;
#else
return pthread_cond_broadcast(condp);
#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
}/*}}}*/

TSRM_API void tsrm_cond_free(COND_T condp)
{
#ifdef TSRM_WIN32
free(condp);
#else
if(condp){
pthread_cond_destroy(condp);
free(condp);
}
#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 <stdio.h>
Expand Down Expand Up @@ -74,33 +240,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;
Expand Down Expand Up @@ -598,101 +737,6 @@ TSRM_API void ts_apply_for_id(ts_rsrc_id id, void (*cb)(void *))
tsrm_mutex_unlock(tsmm_mutex);
}

/*
* 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;
Expand Down
Loading
Loading