@@ -1038,32 +1038,113 @@ static void set_thread_name(char *thread_name) {
10381038#endif
10391039}
10401040
1041+ static inline void reset_sandboxed_environment () {
1042+ if (sandboxed_env != NULL ) {
1043+ zend_hash_release (sandboxed_env );
1044+ sandboxed_env = NULL ;
1045+ }
1046+ }
1047+
10411048static void * php_thread (void * arg ) {
10421049 thread_index = (uintptr_t )arg ;
10431050 char thread_name [16 ] = {0 };
10441051 snprintf (thread_name , 16 , "php-%" PRIxPTR , thread_index );
10451052 set_thread_name (thread_name );
10461053
1054+ /* Initial allocation of all global PHP memory for this thread */
10471055#ifdef ZTS
1048- /* initial resource fetch */
10491056 (void )ts_resource (0 );
10501057#ifdef PHP_WIN32
10511058 ZEND_TSRMLS_CACHE_UPDATE ();
10521059#endif
10531060#endif
10541061
1055- // loop until Go signals to stop
1056- char * scriptName = NULL ;
1057- while ((scriptName = go_frankenphp_before_script_execution (thread_index ))) {
1058- go_frankenphp_after_script_execution (thread_index ,
1059- frankenphp_execute_script (scriptName ));
1062+ bool thread_is_healthy = true;
1063+ bool has_attempted_shutdown = false;
1064+
1065+ /* Main loop of the PHP thread, execute a PHP script and repeat until Go
1066+ * signals to stop */
1067+ zend_first_try {
1068+ char * scriptName = NULL ;
1069+ while ((scriptName = go_frankenphp_before_script_execution (thread_index ))) {
1070+ has_attempted_shutdown = false;
1071+
1072+ frankenphp_update_request_context ();
1073+
1074+ if (UNEXPECTED (php_request_startup () == FAILURE )) {
1075+ /* Request startup failed, bail out to zend_catch */
1076+ frankenphp_log_message ("Request startup failed, thread is unhealthy" ,
1077+ LOG_ERR );
1078+ zend_bailout ();
1079+ }
1080+
1081+ zend_file_handle file_handle ;
1082+ zend_stream_init_filename (& file_handle , scriptName );
1083+
1084+ file_handle .primary_script = 1 ;
1085+ EG (exit_status ) = 0 ;
1086+
1087+ /* Execute the PHP script, potential bailout to zend_catch */
1088+ php_execute_script (& file_handle );
1089+ zend_destroy_file_handle (& file_handle );
1090+ reset_sandboxed_environment ();
1091+
1092+ /* Update the last memory usage for metrics */
1093+ __atomic_store_n (& thread_metrics [thread_index ].last_memory_usage ,
1094+ zend_memory_usage (0 ), __ATOMIC_RELAXED );
1095+
1096+ has_attempted_shutdown = true;
1097+
1098+ /* shutdown the request, potential bailout to zend_catch */
1099+ php_request_shutdown ((void * )0 );
1100+ frankenphp_free_request_context ();
1101+ go_frankenphp_after_script_execution (thread_index , EG (exit_status ));
1102+ }
10601103 }
1104+ zend_catch {
1105+ /* Critical failure from php_execute_script or php_request_shutdown, mark
1106+ * the thread as unhealthy */
1107+ thread_is_healthy = false;
1108+ if (!has_attempted_shutdown ) {
1109+ /* php_request_shutdown() was not called, force a shutdown now */
1110+ reset_sandboxed_environment ();
1111+ zend_try { php_request_shutdown ((void * )0 ); }
1112+ zend_catch {}
1113+ zend_end_try ();
1114+ }
10611115
1116+ /* Log the last error message, it must be cleared to prevent a crash when
1117+ * freeing execution globals */
1118+ if (PG (last_error_message )) {
1119+ go_log_attrs (thread_index , PG (last_error_message ), 8 , NULL );
1120+ PG (last_error_message ) = NULL ;
1121+ PG (last_error_file ) = NULL ;
1122+ }
1123+ frankenphp_free_request_context ();
1124+ go_frankenphp_after_script_execution (thread_index , EG (exit_status ));
1125+ }
1126+ zend_end_try ();
1127+
1128+ /* free all global PHP memory reserved for this thread */
10621129#ifdef ZTS
10631130 ts_free_thread ();
10641131#endif
10651132
1066- go_frankenphp_on_thread_shutdown (thread_index );
1133+ /* Thread is healthy, signal to Go that the thread has shut down */
1134+ if (thread_is_healthy ) {
1135+ go_frankenphp_on_thread_shutdown (thread_index );
1136+
1137+ return NULL ;
1138+ }
1139+
1140+ /* Thread is unhealthy, PHP globals might be in a bad state after a bailout,
1141+ * restart the entire thread */
1142+ frankenphp_log_message ("Restarting unhealthy thread" , LOG_WARNING );
1143+
1144+ if (!frankenphp_new_php_thread (thread_index )) {
1145+ /* probably unreachable */
1146+ frankenphp_log_message ("Failed to restart an unhealthy thread" , LOG_ERR );
1147+ }
10671148
10681149 return NULL ;
10691150}
@@ -1197,55 +1278,6 @@ bool frankenphp_new_php_thread(uintptr_t thread_index) {
11971278 return true;
11981279}
11991280
1200- static int frankenphp_request_startup () {
1201- frankenphp_update_request_context ();
1202- if (php_request_startup () == SUCCESS ) {
1203- return SUCCESS ;
1204- }
1205-
1206- php_request_shutdown ((void * )0 );
1207- frankenphp_free_request_context ();
1208-
1209- return FAILURE ;
1210- }
1211-
1212- int frankenphp_execute_script (char * file_name ) {
1213- if (frankenphp_request_startup () == FAILURE ) {
1214-
1215- return FAILURE ;
1216- }
1217-
1218- int status = SUCCESS ;
1219-
1220- zend_file_handle file_handle ;
1221- zend_stream_init_filename (& file_handle , file_name );
1222-
1223- file_handle .primary_script = 1 ;
1224-
1225- zend_first_try {
1226- EG (exit_status ) = 0 ;
1227- php_execute_script (& file_handle );
1228- status = EG (exit_status );
1229- }
1230- zend_catch { status = EG (exit_status ); }
1231- zend_end_try ();
1232-
1233- zend_destroy_file_handle (& file_handle );
1234-
1235- /* Reset the sandboxed environment if it is in use */
1236- if (sandboxed_env != NULL ) {
1237- zend_hash_release (sandboxed_env );
1238- sandboxed_env = NULL ;
1239- }
1240-
1241- __atomic_store_n (& thread_metrics [thread_index ].last_memory_usage ,
1242- zend_memory_usage (0 ), __ATOMIC_RELAXED );
1243- php_request_shutdown ((void * )0 );
1244- frankenphp_free_request_context ();
1245-
1246- return status ;
1247- }
1248-
12491281/* Use global variables to store CLI arguments to prevent useless allocations */
12501282static char * cli_script ;
12511283static int cli_argc ;
0 commit comments