diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 39b1db9b712d..089520041e33 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -10,7 +10,7 @@ +----------------------------------------------------------------------+ */ -#include "TSRM.h" +#include "TSRM_atexit.h" #ifdef ZTS @@ -68,6 +68,7 @@ static MUTEX_T tsrm_env_mutex; /* tsrm environ mutex */ static tsrm_thread_begin_func_t tsrm_new_thread_begin_handler = NULL; static tsrm_thread_end_func_t tsrm_new_thread_end_handler = NULL; static tsrm_shutdown_func_t tsrm_shutdown_handler = NULL; +static tsrm_shutdown_func_t tsrm_thread_free_handler = NULL; /* Debug support */ int tsrm_error(int level, const char *format, ...); @@ -105,7 +106,7 @@ static FILE *tsrm_error_file; #endif #ifdef TSRM_WIN32 -static DWORD tls_key; +static DWORD tls_key = TLS_OUT_OF_INDEXES; # define tsrm_tls_set(what) TlsSetValue(tls_key, (void*)(what)) # define tsrm_tls_get() TlsGetValue(tls_key) #else @@ -117,6 +118,16 @@ static pthread_key_t tls_key; TSRM_TLS bool in_main_thread = false; TSRM_TLS bool is_thread_shutdown = false; +void tsrm_free_thread_at_exit(tsrm_tls_entry *thread_resources) +{ + if (!thread_resources || thread_resources != TSRMLS_CACHE || in_main_thread || !tsrm_tls_table) { + return; + } + + tsrm_tls_set(thread_resources); + ts_free_thread(); +} + /* Startup TSRM (call once for the entire process) */ TSRM_API bool tsrm_startup(int expected_threads, int expected_resources, int debug_level, const char *debug_filename) {/*{{{*/ @@ -160,22 +171,22 @@ TSRM_API bool tsrm_startup(int expected_threads, int expected_resources, int deb tsrm_reserved_front = 0; tsrm_env_mutex = tsrm_mutex_alloc(); + tsrm_atexit_init(); return 1; }/*}}}*/ -static void ts_free_resources(tsrm_tls_entry *thread_resources) +static void ts_free_resources( + tsrm_tls_entry *thread_resources, bool destroy_tls_resources, bool run_dtors) { - bool own_thread = thread_resources->thread_id == tsrm_thread_id(); - /* Need to destroy in reverse order to respect dependencies. */ for (int i = thread_resources->count - 1; i >= 0; i--) { if (!resource_types_table[i].done) { - /* A __thread block of a foreign thread is inaccessible. */ - if (resource_types_table[i].tls_addr && !own_thread) { + /* Native TLS may only be accessed by its owning thread. */ + if (resource_types_table[i].tls_addr && !destroy_tls_resources) { continue; } - if (resource_types_table[i].dtor) { + if (run_dtors && resource_types_table[i].dtor) { resource_types_table[i].dtor(thread_resources->storage[i]); } @@ -189,14 +200,14 @@ static void ts_free_resources(tsrm_tls_entry *thread_resources) } /* Shutdown TSRM (call once for the entire process). Tears down every thread left - * in the table. For resources allocated with ts_allocate_tls_id(), only the dtor - * of the calling thread is invoked. */ + * in the table. Native TLS dtors only run for the calling thread. */ TSRM_API void tsrm_shutdown(void) {/*{{{*/ if (is_thread_shutdown) { /* shutdown must only occur once */ return; } + tsrm_tls_entry *current_thread_resources = tsrm_tls_get(); is_thread_shutdown = true; @@ -205,6 +216,8 @@ TSRM_API void tsrm_shutdown(void) return; } + tsrm_atexit_disarm(); + for (int i=0; inext; if (resource_types_table) { /* This call will already free p->storage for us */ - ts_free_resources(p); + ts_free_resources(p, p == current_thread_resources, true); } else { free(p->storage); } @@ -221,7 +234,9 @@ TSRM_API void tsrm_shutdown(void) } } free(tsrm_tls_table); + tsrm_tls_table = NULL; free(resource_types_table); + resource_types_table = NULL; tsrm_mutex_free(tsmm_mutex); tsrm_mutex_free(tsrm_env_mutex); TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Shutdown TSRM")); @@ -230,16 +245,19 @@ TSRM_API void tsrm_shutdown(void) } #ifdef TSRM_WIN32 TlsFree(tls_key); + tls_key = TLS_OUT_OF_INDEXES; #else pthread_setspecific(tls_key, 0); pthread_key_delete(tls_key); #endif + TSRMLS_CACHE = NULL; if (tsrm_shutdown_handler) { tsrm_shutdown_handler(); } tsrm_new_thread_begin_handler = NULL; tsrm_new_thread_end_handler = NULL; tsrm_shutdown_handler = NULL; + tsrm_thread_free_handler = NULL; tsrm_reserved_pos = 0; tsrm_reserved_size = 0; @@ -491,6 +509,11 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_ if (tsrm_new_thread_end_handler) { tsrm_new_thread_end_handler(thread_id); } + + /* A thread exiting from a resource constructor cannot safely clean up an + * entry that is still being built while tsmm_mutex is held. */ + tsrm_atexit_set_data(*thread_resources_ptr); + is_thread_shutdown = false; }/*}}}*/ /* fetches the requested resource for the current thread */ @@ -547,28 +570,23 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) /* It's possible that the current thread resources are requested, and that we get here. * This means that the TSRM key pointer and cached pointer are NULL, but there is still - * a thread resource associated with this ID in the hashtable. This can occur if a thread - * goes away, but its resources are never cleaned up, and then that thread ID is reused. - * Since we don't always have a way to know when a thread goes away, we can't clean up - * the thread's resources before the new thread spawns. - * To solve this issue, we'll free up the old thread resources gracefully (gracefully - * because there might still be resources open like database connection which need to - * be shut down cleanly). After freeing up, we'll create the new resources for this thread - * as if the stale resources never existed in the first place. From that point forward, - * it is as if that situation never occurred. + * a thread resource associated with this ID in the hashtable. Automatic thread cleanup + * may be unavailable or fail, leaving an entry behind after its thread exits. A new + * thread may then reuse that thread ID. + * The old native TLS is no longer accessible, so its destructors cannot run safely. + * Release the directly owned allocations and create fresh resources for the new thread. * The fact that this situation happens isn't that bad because a child process containing * threads will eventually be respawned anyway by the SAPI, so the stale threads won't last * forever. */ TSRM_ASSERT(thread_resources->thread_id == thread_id); if (thread_id == tsrm_thread_id() && !tsrm_tls_get()) { tsrm_tls_entry *next = thread_resources->next; - /* In case that extensions don't use the pointer passed from the dtor, but incorrectly - * use the global pointer, we need to setup the global pointer temporarily here. */ - set_thread_local_storage_resource_to(thread_resources); - /* Dead thread with a recycled id: its __thread blocks are gone, and this - * thread's blocks were never constructed, so keep tls dtors from running. */ - thread_resources->thread_id = 0; - ts_free_resources(thread_resources); + /* Keep signal handlers away from both the stale entry and the replacement + * until all of the replacement's resources have been constructed. */ + is_thread_shutdown = true; + /* The dead thread's native TLS is gone, and its remaining resource dtors + * may depend on those globals. Only release directly owned allocations. */ + ts_free_resources(thread_resources, false, false); free((char *) thread_resources - tsrm_reserved_front); /* Allocate a new resource at the same point in the linked list, and relink the next pointer */ allocate_new_resource(last_thread_resources, thread_id); @@ -591,33 +609,38 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) /* frees all resources allocated for the current thread */ void ts_free_thread(void) {/*{{{*/ - tsrm_tls_entry *thread_resources; - THREAD_T thread_id = tsrm_thread_id(); + tsrm_tls_entry *thread_resources = tsrm_tls_get(); + tsrm_tls_entry **p; int hash_value; - tsrm_tls_entry *last=NULL; TSRM_ASSERT(!in_main_thread); + if (!thread_resources) { + return; + } + + is_thread_shutdown = true; + /* Release resources that depend on TSRM before taking its lock. */ + if (tsrm_thread_free_handler) { + tsrm_thread_free_handler(); + } tsrm_mutex_lock(tsmm_mutex); - hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size); - thread_resources = tsrm_tls_table[hash_value]; + hash_value = THREAD_HASH_OF(thread_resources->thread_id, tsrm_tls_table_size); + p = &tsrm_tls_table[hash_value]; - while (thread_resources) { - if (thread_resources->thread_id == thread_id) { - ts_free_resources(thread_resources); - if (last) { - last->next = thread_resources->next; - } else { - tsrm_tls_table[hash_value] = thread_resources->next; - } - tsrm_tls_set(0); - free((char *) thread_resources - tsrm_reserved_front); - break; - } - if (thread_resources->next) { - last = thread_resources; - } - thread_resources = thread_resources->next; + while (*p && *p != thread_resources) { + p = &(*p)->next; + } + thread_resources = *p; + if (thread_resources) { + ts_free_resources(thread_resources, true, true); + *p = thread_resources->next; + } + tsrm_tls_set(0); + tsrm_atexit_set_data(NULL); + TSRMLS_CACHE = NULL; + if (thread_resources) { + free((char *) thread_resources - tsrm_reserved_front); } tsrm_mutex_unlock(tsmm_mutex); }/*}}}*/ @@ -626,6 +649,7 @@ void ts_free_thread(void) void ts_free_id(ts_rsrc_id id) {/*{{{*/ int rsrc_id = TSRM_UNSHUFFLE_RSRC_ID(id); + tsrm_tls_entry *current_thread_resources = tsrm_tls_get(); tsrm_mutex_lock(tsmm_mutex); @@ -637,7 +661,9 @@ void ts_free_id(ts_rsrc_id id) while (p) { if (p->count > rsrc_id && p->storage[rsrc_id]) { - if (resource_types_table) { + /* Native TLS may only be accessed by its owning thread. */ + if (resource_types_table + && (!resource_types_table[rsrc_id].tls_addr || p == current_thread_resources)) { if (resource_types_table[rsrc_id].dtor) { resource_types_table[rsrc_id].dtor(p->storage[rsrc_id]); } @@ -661,15 +687,20 @@ void ts_free_id(ts_rsrc_id id) TSRM_API void ts_apply_for_id(ts_rsrc_id id, void (*cb)(void *)) { int rsrc_id = TSRM_UNSHUFFLE_RSRC_ID(id); + tsrm_tls_entry *current_thread_resources = tsrm_tls_get(); tsrm_mutex_lock(tsmm_mutex); if (tsrm_tls_table && resource_types_table) { + bool tls_backed = resource_types_table[rsrc_id].tls_addr != NULL; + for (int i = 0; i < tsrm_tls_table_size; i++) { tsrm_tls_entry *p = tsrm_tls_table[i]; while (p) { - if (p->count > rsrc_id && p->storage[rsrc_id]) { + /* Native TLS may only be accessed by its owning thread. */ + if (p->count > rsrc_id && p->storage[rsrc_id] + && (!tls_backed || p == current_thread_resources)) { cb(p->storage[rsrc_id]); } p = p->next; @@ -802,6 +833,12 @@ TSRM_API void *tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler) }/*}}}*/ +void tsrm_set_thread_free_handler(tsrm_shutdown_func_t thread_free_handler) +{ + tsrm_thread_free_handler = thread_free_handler; +} + + /* * Debug support */ diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h index 5fe5fb1cf354..90e4b0720772 100644 --- a/TSRM/TSRM.h +++ b/TSRM/TSRM.h @@ -144,6 +144,8 @@ TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset); 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); +/* Internal Zend hook, run before any of the thread's resources are destroyed. */ +void tsrm_set_thread_free_handler(tsrm_shutdown_func_t thread_free_handler); TSRM_API void *tsrm_get_ls_cache(void); TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void); diff --git a/TSRM/TSRM_atexit.h b/TSRM/TSRM_atexit.h new file mode 100644 index 000000000000..ae73597e0e31 --- /dev/null +++ b/TSRM/TSRM_atexit.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | Thread Safe Resource Manager | + +----------------------------------------------------------------------+ + | Copyright (c) 1999-2011, Andi Gutmans, Sascha Schumann, Zeev Suraski | + | This source file is subject to the TSRM license, that is bundled | + | with this package in the file LICENSE | + +----------------------------------------------------------------------+ + | Authors: Marc Henderkes | + +----------------------------------------------------------------------+ +*/ + +#ifndef TSRM_ATEXIT_H +#define TSRM_ATEXIT_H + +#include "TSRM.h" + +#ifdef ZTS + +typedef struct _tsrm_tls_entry tsrm_tls_entry; + +void tsrm_atexit_init(void); +void tsrm_atexit_set_data(tsrm_tls_entry *thread_resources); +void tsrm_atexit_disarm(void); +void tsrm_free_thread_at_exit(tsrm_tls_entry *thread_resources); + +#endif /* ZTS */ + +#endif /* TSRM_ATEXIT_H */ diff --git a/TSRM/TSRM_atexit_darwin.c b/TSRM/TSRM_atexit_darwin.c new file mode 100644 index 000000000000..eb5678af7ef4 --- /dev/null +++ b/TSRM/TSRM_atexit_darwin.c @@ -0,0 +1,73 @@ +/* + +----------------------------------------------------------------------+ + | Thread Safe Resource Manager | + +----------------------------------------------------------------------+ + | Copyright (c) 1999-2011, Andi Gutmans, Sascha Schumann, Zeev Suraski | + | This source file is subject to the TSRM license, that is bundled | + | with this package in the file LICENSE | + +----------------------------------------------------------------------+ + | Authors: Marc Henderkes | + +----------------------------------------------------------------------+ +*/ + +#include "TSRM_atexit.h" + +#if defined(ZTS) && defined(__APPLE__) + +#include +#include + +static bool tsrm_thread_exit_armed = false; +static pthread_key_t tsrm_exit_key; +static TSRM_TLS bool tsrm_thread_exit_registered = false; + +static void tsrm_thread_exit_handler(void *arg) +{ + tsrm_thread_exit_registered = false; + if (!tsrm_thread_exit_armed) { + return; + } + tsrm_free_thread_at_exit(pthread_getspecific(tsrm_exit_key)); +} + +void tsrm_atexit_init(void) +{ + int error = pthread_key_create(&tsrm_exit_key, NULL); + tsrm_thread_exit_armed = error == 0; + if (!tsrm_thread_exit_armed) { + fprintf(stderr, "TSRM: pthread_key_create() failed (error %d); automatic thread cleanup is disabled\n", error); + } +} + +void tsrm_atexit_set_data(tsrm_tls_entry *thread_resources) +{ + if (!tsrm_thread_exit_armed) { + return; + } + + if (pthread_setspecific(tsrm_exit_key, thread_resources) != 0) { + return; + } + if (thread_resources && !tsrm_thread_exit_registered) { + tsrm_thread_exit_registered = true; + _tlv_atexit(tsrm_thread_exit_handler, NULL); + } +} + +void tsrm_atexit_disarm(void) +{ + if (!tsrm_thread_exit_armed) { + return; + } + tsrm_thread_exit_armed = false; + pthread_key_delete(tsrm_exit_key); +} + +#ifdef __GNUC__ +static void __attribute__((destructor)) tsrm_thread_exit_unload(void) +{ + tsrm_atexit_disarm(); +} +#endif + +#endif /* ZTS && __APPLE__ */ diff --git a/TSRM/TSRM_atexit_posix.c b/TSRM/TSRM_atexit_posix.c new file mode 100644 index 000000000000..cbbaafaedfe9 --- /dev/null +++ b/TSRM/TSRM_atexit_posix.c @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | Thread Safe Resource Manager | + +----------------------------------------------------------------------+ + | Copyright (c) 1999-2011, Andi Gutmans, Sascha Schumann, Zeev Suraski | + | This source file is subject to the TSRM license, that is bundled | + | with this package in the file LICENSE | + +----------------------------------------------------------------------+ + | Authors: Marc Henderkes | + +----------------------------------------------------------------------+ +*/ + +#include "TSRM_atexit.h" + +#if defined(ZTS) && !defined(TSRM_WIN32) && !defined(__APPLE__) + +#include + +static bool tsrm_thread_exit_armed = false; +static pthread_key_t tsrm_exit_key; + +static void tsrm_thread_exit_handler(void *arg) +{ + if (tsrm_thread_exit_armed) { + tsrm_free_thread_at_exit(arg); + } +} + +void tsrm_atexit_init(void) +{ + int error = pthread_key_create(&tsrm_exit_key, tsrm_thread_exit_handler); + tsrm_thread_exit_armed = error == 0; + if (!tsrm_thread_exit_armed) { + fprintf(stderr, "TSRM: pthread_key_create() failed (error %d); automatic thread cleanup is disabled\n", error); + } +} + +void tsrm_atexit_set_data(tsrm_tls_entry *thread_resources) +{ + if (!tsrm_thread_exit_armed) { + return; + } + + pthread_setspecific(tsrm_exit_key, thread_resources); +} + +void tsrm_atexit_disarm(void) +{ + if (!tsrm_thread_exit_armed) { + return; + } + tsrm_thread_exit_armed = false; + pthread_key_delete(tsrm_exit_key); +} + +#ifdef __GNUC__ +static void __attribute__((destructor)) tsrm_thread_exit_unload(void) +{ + tsrm_atexit_disarm(); +} +#endif + +#endif /* ZTS && !TSRM_WIN32 && !__APPLE__ */ diff --git a/TSRM/TSRM_atexit_win.c b/TSRM/TSRM_atexit_win.c new file mode 100644 index 000000000000..f7e554fc8053 --- /dev/null +++ b/TSRM/TSRM_atexit_win.c @@ -0,0 +1,76 @@ +/* + +----------------------------------------------------------------------+ + | Thread Safe Resource Manager | + +----------------------------------------------------------------------+ + | Copyright (c) 1999-2011, Andi Gutmans, Sascha Schumann, Zeev Suraski | + | This source file is subject to the TSRM license, that is bundled | + | with this package in the file LICENSE | + +----------------------------------------------------------------------+ + | Authors: Marc Henderkes | + +----------------------------------------------------------------------+ +*/ + +#include "TSRM_atexit.h" + +#if defined(ZTS) && defined(TSRM_WIN32) + +#include + +static bool tsrm_thread_exit_armed = false; +static DWORD tsrm_exit_key = FLS_OUT_OF_INDEXES; +static BOOLEAN (NTAPI *tsrm_dll_shutdown_in_progress)(void); + +static void WINAPI tsrm_thread_exit_handler(void *arg) +{ + if (!tsrm_thread_exit_armed || tsrm_dll_shutdown_in_progress()) { + return; + } + if (IsThreadAFiber()) { + fprintf(stderr, "TSRM: Native Windows fibers are not supported\n"); + return; + } + FlsSetValue(tsrm_exit_key, NULL); + tsrm_free_thread_at_exit(arg); +} + +void tsrm_atexit_init(void) +{ + tsrm_dll_shutdown_in_progress = (BOOLEAN (NTAPI *)(void)) (void *) GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlDllShutdownInProgress"); + /* Use FlsAlloc() because TlsAlloc() does not provide a thread-exit callback + * parameter. The callback calls ts_free_thread() if the SAPI has not done so + * explicitly, cleaning up EG(persistent_list) and other TSRM resources on the + * owning thread before its native TLS is released. */ + tsrm_exit_key = tsrm_dll_shutdown_in_progress ? FlsAlloc(tsrm_thread_exit_handler) : FLS_OUT_OF_INDEXES; + tsrm_thread_exit_armed = tsrm_exit_key != FLS_OUT_OF_INDEXES; + if (!tsrm_thread_exit_armed) { + fprintf(stderr, "TSRM: %s failed (error %lu); automatic thread cleanup is disabled\n", + tsrm_dll_shutdown_in_progress ? "FlsAlloc()" : "RtlDllShutdownInProgress lookup", + (unsigned long) GetLastError()); + } +} + +void tsrm_atexit_set_data(tsrm_tls_entry *thread_resources) +{ + if (IsThreadAFiber()) { + fprintf(stderr, "TSRM: Native Windows fibers are not supported\n"); + return; + } + if (!tsrm_thread_exit_armed) { + return; + } + + FlsSetValue(tsrm_exit_key, thread_resources); +} + +void tsrm_atexit_disarm(void) +{ + if (!tsrm_thread_exit_armed) { + return; + } + tsrm_thread_exit_armed = false; + DWORD key = tsrm_exit_key; + tsrm_exit_key = FLS_OUT_OF_INDEXES; + FlsFree(key); +} + +#endif /* ZTS && TSRM_WIN32 */ diff --git a/TSRM/config.w32 b/TSRM/config.w32 index fa7145ae4dc8..ecf7ed548114 100644 --- a/TSRM/config.w32 +++ b/TSRM/config.w32 @@ -1,4 +1,4 @@ // vim:ft=javascript -ADD_SOURCES("TSRM", "TSRM.c tsrm_win32.c"); +ADD_SOURCES("TSRM", "TSRM.c TSRM_atexit_win.c TSRM_atexit_darwin.c TSRM_atexit_posix.c tsrm_win32.c"); ADD_FLAG("CFLAGS_BD_TSRM", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 4c5d74b49e9c..51f8e399b830 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -91,3 +91,11 @@ PHP 8.7 INTERNALS UPGRADE NOTES . The force_http_10 method has been removed. . The get_fd method has been removed. . The get_target_uid and get_target_gid methods have been removed. + +- Per-thread TSRM resources are now released best-effort at normal thread exit, + because native TLS resource destructors can only run on their owning thread and + tsrm_shutdown() cannot release them afterwards. Prefer explicitly calling ts_free_thread(). + +- tsrm_shutdown() and the bundled library-unload hooks automatically disarm + thread-exit cleanup. Embedders must stop and join all PHP worker threads + before calling tsrm_shutdown() or unloading PHP. diff --git a/Zend/zend.c b/Zend/zend.c index 2df65654a4ef..1e9a59242346 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -61,7 +61,6 @@ static HashTable *global_function_table = NULL; static HashTable *global_class_table = NULL; static HashTable *global_constants_table = NULL; static HashTable *global_auto_globals_table = NULL; -static HashTable *global_persistent_list = NULL; TSRMLS_MAIN_CACHE_DEFINE() # define GLOBAL_FUNCTION_TABLE global_function_table # define GLOBAL_CLASS_TABLE global_class_table @@ -857,13 +856,17 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ } /* }}} */ -static void executor_globals_persistent_list_dtor(void *storage) +static void zend_thread_free_handler(void) { - zend_executor_globals *executor_globals = storage; + volatile bool completed = false; - if (&executor_globals->persistent_list != global_persistent_list) { - zend_destroy_rsrc_list(&executor_globals->persistent_list); - } + do { + zend_try { + zend_destroy_rsrc_list(&EG(persistent_list)); + completed = true; + } zend_end_try(); + } while (!completed); + zend_init_rsrc_plist(); } static void executor_globals_dtor(zend_executor_globals *executor_globals) /* {{{ */ @@ -1085,6 +1088,7 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */ #ifdef ZTS tsrm_set_new_thread_end_handler(zend_new_thread_end_handler); + tsrm_set_thread_free_handler(zend_thread_free_handler); tsrm_set_shutdown_handler(zend_interned_strings_dtor); #endif @@ -1155,7 +1159,6 @@ zend_result zend_post_startup(void) /* {{{ */ EG(zend_constants) = NULL; executor_globals_ctor(executor_globals); - global_persistent_list = &EG(persistent_list); zend_copy_ini_directives(); #else global_map_ptr_last = CG(map_ptr_last); @@ -1172,12 +1175,12 @@ zend_result zend_post_startup(void) /* {{{ */ void zend_shutdown(void) /* {{{ */ { +#ifdef ZTS + tsrm_set_thread_free_handler(NULL); +#endif zend_vm_dtor(); zend_destroy_rsrc_list(&EG(persistent_list)); -#ifdef ZTS - ts_apply_for_id(executor_globals_id, executor_globals_persistent_list_dtor); -#endif zend_destroy_modules(); virtual_cwd_deactivate(); diff --git a/configure.ac b/configure.ac index b5dcf0da17f8..d0321145e492 100644 --- a/configure.ac +++ b/configure.ac @@ -1658,7 +1658,12 @@ PHP_INSTALL_HEADERS([Zend/Optimizer], m4_normalize([ zend_worklist.h ])) -PHP_ADD_SOURCES([TSRM], [TSRM.c], [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) +PHP_ADD_SOURCES([TSRM], m4_normalize([ + TSRM.c + TSRM_atexit_win.c + TSRM_atexit_darwin.c + TSRM_atexit_posix.c + ]), [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) PHP_ADD_SOURCES([main], m4_normalize([ explicit_bzero.c diff --git a/win32/dllmain.c b/win32/dllmain.c index 168bef2baa22..b0567e29fa15 100644 --- a/win32/dllmain.c +++ b/win32/dllmain.c @@ -15,6 +15,7 @@ #include #include +#include #ifdef HAVE_LIBXML #include @@ -31,6 +32,12 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID dummy) { BOOL ret = TRUE; +#ifdef ZTS + if (reason == DLL_PROCESS_DETACH && dummy == NULL) { + tsrm_atexit_disarm(); + } +#endif + #ifdef HAVE_LIBXML /* This imply that only LIBXML_STATIC_FOR_DLL is supported ATM. If that changes, this place will need some rework.