diff --git a/frankenphp.c b/frankenphp.c index c25a3505f..f2e8f2b29 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -1033,32 +1033,108 @@ static void set_thread_name(char *thread_name) { #endif } +static inline void reset_sandboxed_environment() { + if (sandboxed_env != NULL) { + zend_hash_release(sandboxed_env); + sandboxed_env = NULL; + } +} + static void *php_thread(void *arg) { thread_index = (uintptr_t)arg; char thread_name[16] = {0}; snprintf(thread_name, 16, "php-%" PRIxPTR, thread_index); set_thread_name(thread_name); + /* Initial allocation of all global PHP memory for this thread */ #ifdef ZTS - /* initial resource fetch */ (void)ts_resource(0); #ifdef PHP_WIN32 ZEND_TSRMLS_CACHE_UPDATE(); #endif #endif - // loop until Go signals to stop - char *scriptName = NULL; - while ((scriptName = go_frankenphp_before_script_execution(thread_index))) { - go_frankenphp_after_script_execution(thread_index, - frankenphp_execute_script(scriptName)); + bool thread_is_healthy = true; + bool has_attempted_shutdown = false; + + /* Main loop of the PHP thread, execute a PHP script and repeat until Go + * signals to stop */ + zend_first_try { + char *scriptName = NULL; + while ((scriptName = go_frankenphp_before_script_execution(thread_index))) { + has_attempted_shutdown = false; + + frankenphp_update_request_context(); + + if (UNEXPECTED(php_request_startup() == FAILURE)) { + /* Request startup failed, bail out to zend_catch */ + frankenphp_log_message("Request startup failed, thread is unhealthy", + LOG_ERR); + zend_bailout(); + } + + zend_file_handle file_handle; + zend_stream_init_filename(&file_handle, scriptName); + + file_handle.primary_script = 1; + EG(exit_status) = 0; + + /* Execute the PHP script, potential bailout to zend_catch */ + php_execute_script(&file_handle); + zend_destroy_file_handle(&file_handle); + reset_sandboxed_environment(); + + has_attempted_shutdown = true; + + /* shutdown the request, potential bailout to zend_catch */ + php_request_shutdown((void *)0); + frankenphp_free_request_context(); + go_frankenphp_after_script_execution(thread_index, EG(exit_status)); + } + } + zend_catch { + /* Critical failure from php_execute_script or php_request_shutdown, mark + * the thread as unhealthy */ + thread_is_healthy = false; + if (!has_attempted_shutdown) { + /* php_request_shutdown() was not called, force a shutdown now */ + reset_sandboxed_environment(); + zend_try { php_request_shutdown((void *)0); } + zend_catch {} + zend_end_try(); + } + + /* Log the last error message, it must be cleared to prevent a crash when + * freeing execution globals */ + if (PG(last_error_message)) { + go_log_attrs(thread_index, PG(last_error_message), 8, NULL); + PG(last_error_message) = NULL; + PG(last_error_file) = NULL; + } + frankenphp_free_request_context(); + go_frankenphp_after_script_execution(thread_index, EG(exit_status)); } + zend_end_try(); #ifdef ZTS ts_free_thread(); #endif - go_frankenphp_on_thread_shutdown(thread_index); + /* Thread is healthy, signal to Go that the thread has shut down */ + if (thread_is_healthy) { + go_frankenphp_on_thread_shutdown(thread_index); + + return NULL; + } + + /* Thread is unhealthy, PHP globals might be in a bad state after a bailout, + * restart the entire thread */ + frankenphp_log_message("Restarting unhealthy thread", LOG_WARNING); + + if (!frankenphp_new_php_thread(thread_index)) { + /* probably unreachable */ + frankenphp_log_message("Failed to restart an unhealthy thread", LOG_ERR); + } return NULL; } @@ -1192,53 +1268,6 @@ bool frankenphp_new_php_thread(uintptr_t thread_index) { return true; } -static int frankenphp_request_startup() { - frankenphp_update_request_context(); - if (php_request_startup() == SUCCESS) { - return SUCCESS; - } - - php_request_shutdown((void *)0); - frankenphp_free_request_context(); - - return FAILURE; -} - -int frankenphp_execute_script(char *file_name) { - if (frankenphp_request_startup() == FAILURE) { - - return FAILURE; - } - - int status = SUCCESS; - - zend_file_handle file_handle; - zend_stream_init_filename(&file_handle, file_name); - - file_handle.primary_script = 1; - - zend_first_try { - EG(exit_status) = 0; - php_execute_script(&file_handle); - status = EG(exit_status); - } - zend_catch { status = EG(exit_status); } - zend_end_try(); - - zend_destroy_file_handle(&file_handle); - - /* Reset the sandboxed environment if it is in use */ - if (sandboxed_env != NULL) { - zend_hash_release(sandboxed_env); - sandboxed_env = NULL; - } - - php_request_shutdown((void *)0); - frankenphp_free_request_context(); - - return status; -} - /* Use global variables to store CLI arguments to prevent useless allocations */ static char *cli_script; static int cli_argc; diff --git a/frankenphp.h b/frankenphp.h index f25cb8512..7714c71a1 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -169,7 +169,6 @@ int frankenphp_new_main_thread(int num_threads); bool frankenphp_new_php_thread(uintptr_t thread_index); bool frankenphp_shutdown_dummy_request(void); -int frankenphp_execute_script(char *file_name); void frankenphp_update_local_thread_context(bool is_worker); int frankenphp_execute_script_cli(char *script, int argc, char **argv,