From 8d8ece59d4e4c2d69440a56ae706375336d6a4fd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 2 Aug 2020 20:24:14 +0100 Subject: [PATCH 01/14] Introduced Thread::setAutoloadFile() This allows provisioning an autoloader that will be included by all new threads when they start. --- classes/thread.h | 20 ++++++++++++++++++ src/globals.c | 17 +++++++++++++++ src/globals.h | 8 +++++++ src/prepare.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+) diff --git a/classes/thread.h b/classes/thread.h index 74ca666f..305b7129 100644 --- a/classes/thread.h +++ b/classes/thread.h @@ -25,6 +25,7 @@ PHP_METHOD(Thread, getThreadId); PHP_METHOD(Thread, getCurrentThreadId); PHP_METHOD(Thread, getCurrentThread); PHP_METHOD(Thread, getCreatorId); +PHP_METHOD(Thread, setAutoloadFile); ZEND_BEGIN_ARG_INFO_EX(Thread_start, 0, 0, 0) ZEND_ARG_TYPE_INFO(0, options, IS_LONG, 0) @@ -62,6 +63,10 @@ ZEND_BEGIN_ARG_INFO_EX(Thread_merge, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, overwrite, _IS_BOOL, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Thread_setAutoloadFile, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, autoloadFile, IS_STRING, 0) +ZEND_END_ARG_INFO() + extern zend_function_entry pthreads_thread_methods[]; #else # ifndef HAVE_PTHREADS_CLASS_THREAD @@ -75,6 +80,7 @@ zend_function_entry pthreads_thread_methods[] = { PHP_ME(Thread, getCreatorId, Thread_getThreadId, ZEND_ACC_PUBLIC) PHP_ME(Thread, getCurrentThreadId, Thread_getCurrentThreadId, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME(Thread, getCurrentThread, Thread_getCurrentThread, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(Thread, setAutoloadFile, Thread_setAutoloadFile, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) {NULL, NULL, NULL} }; @@ -151,5 +157,19 @@ PHP_METHOD(Thread, getCreatorId) { ZVAL_LONG(return_value, (PTHREADS_FETCH_TS_FROM(Z_OBJ_P(getThis())))->creator.id); } /* }}} */ + +/* {{{ proto void Thread::setAutoloadFile(string|null $file) + Sets the path of the file used to set up new threads */ +PHP_METHOD(Thread, setAutoloadFile) +{ + zend_string* file; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) + Z_PARAM_STR(file) + ZEND_PARSE_PARAMETERS_END(); + + pthreads_globals_set_autoload_file(file); +} /* }}} */ + # endif #endif diff --git a/src/globals.c b/src/globals.c index e2f749a1..0589ea70 100644 --- a/src/globals.c +++ b/src/globals.c @@ -45,6 +45,8 @@ zend_bool pthreads_globals_init(){ &PTHREADS_G(objects), 64, NULL, (dtor_func_t) NULL, 1); } + PTHREADS_G(autoload_file) = NULL; + #define INIT_STRING(n, v) do { \ PTHREADS_G(strings).n = zend_new_interned_string(zend_string_init(v, 1)); \ } while(0) @@ -121,6 +123,21 @@ zend_bool pthreads_globals_object_delete(pthreads_zend_object_t *address) { return deleted; } /* }}} */ +/* {{{ */ +zend_bool pthreads_globals_set_autoload_file(const zend_string *path) { + if (pthreads_globals_lock()) { + zend_string *copy = path ? zend_string_init(ZSTR_VAL(path), ZSTR_LEN(path), 1) : NULL; + + if (PTHREADS_G(autoload_file)) { + zend_string_release(PTHREADS_G(autoload_file)); + } + PTHREADS_G(autoload_file) = copy; + pthreads_globals_unlock(); + return 1; + } + return 0; +} /* }}} */ + /* {{{ */ void pthreads_globals_shutdown() { if (PTHREADS_G(init)) { diff --git a/src/globals.h b/src/globals.h index 8cc52f4c..862d31dd 100644 --- a/src/globals.h +++ b/src/globals.h @@ -49,6 +49,11 @@ struct _pthreads_globals { */ HashTable objects; + /* + * File included on all new threads before any user code runs, usually an autoloader + */ + zend_string *autoload_file; + /* * High Frequency Strings */ @@ -88,6 +93,9 @@ zend_bool pthreads_globals_lock(); /* }}} */ /* {{{ release global lock */ void pthreads_globals_unlock(); /* }}} */ +/* {{{ set autoload file used to bootstrap new threads */ +zend_bool pthreads_globals_set_autoload_file(const zend_string *autoload_file); /* }}} */ + /* {{{ shutdown global structures */ void pthreads_globals_shutdown(); /* }}} */ diff --git a/src/prepare.c b/src/prepare.c index 600ffd38..0917493d 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -718,6 +718,53 @@ void pthreads_prepare_parent(pthreads_object_t *thread) { } } /* }}} */ +/* {{{ Includes the autoloader provided, if any. This code is borrowed from krakjoe/parallel. */ +static int pthreads_thread_bootstrap(zend_string *file) { + zend_file_handle fh; + zend_op_array *ops; + zval rv; + int result; + + if (!file) { + return SUCCESS; + } + + result = php_stream_open_for_zend_ex(ZSTR_VAL(file), &fh, USE_PATH|REPORT_ERRORS|STREAM_OPEN_FOR_INCLUDE); + + if (result != SUCCESS) { + return FAILURE; + } + + zend_hash_add_empty_element(&EG(included_files), + fh.opened_path ? + fh.opened_path : file); + + ops = zend_compile_file(&fh, ZEND_REQUIRE); + + zend_destroy_file_handle(&fh); + + if (ops) { + ZVAL_UNDEF(&rv); + zend_execute(ops, &rv); + destroy_op_array(ops); + efree(ops); + + if (EG(exception)) { + zend_clear_exception(); + return FAILURE; + } + + zval_ptr_dtor(&rv); + return SUCCESS; + } + + if (EG(exception)) { + zend_clear_exception(); + } + + return FAILURE; +} /* }}} */ + /* {{{ */ int pthreads_prepared_startup(pthreads_object_t* thread, pthreads_monitor_t *ready, zend_class_entry *thread_ce) { @@ -772,6 +819,13 @@ int pthreads_prepared_startup(pthreads_object_t* thread, pthreads_monitor_t *rea pthreads_prepare_exception_handler(thread); pthreads_prepare_resource_destructor(thread); + + if (PTHREADS_G(autoload_file)) { + if (pthreads_thread_bootstrap(PTHREADS_G(autoload_file)) == FAILURE) { + pthreads_monitor_add(ready, PTHREADS_MONITOR_ERROR); + return FAILURE; + } + } pthreads_monitor_add(ready, PTHREADS_MONITOR_READY); } PTHREADS_PREPARATION_END_CRITICAL(); From 3975bdb1c303c334f52cbf754e4834f60b9395b6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 2 Aug 2020 20:50:05 +0100 Subject: [PATCH 02/14] added a basic functionality test --- tests/autoload-file.phpt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/autoload-file.phpt diff --git a/tests/autoload-file.phpt b/tests/autoload-file.phpt new file mode 100644 index 00000000..598dbd9b --- /dev/null +++ b/tests/autoload-file.phpt @@ -0,0 +1,17 @@ +--TEST-- +Tests basic functionality of Thread::setAutoloadFile() +--FILE-- +hi(); + } +}; +$t->start(PTHREADS_INHERIT_NONE); +$t->join(); +?> +--EXPECT-- +string(20) "everything is great!" From 1f62771bf645a8543eb5b15c0e03d7125a27a6e6 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Thu, 7 Jan 2021 19:40:47 +0000 Subject: [PATCH 03/14] ... --- tests/assets/TestAutoloadFile.php | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/assets/TestAutoloadFile.php diff --git a/tests/assets/TestAutoloadFile.php b/tests/assets/TestAutoloadFile.php new file mode 100644 index 00000000..3be072d4 --- /dev/null +++ b/tests/assets/TestAutoloadFile.php @@ -0,0 +1,7 @@ + Date: Wed, 2 Jun 2021 21:09:20 +0100 Subject: [PATCH 04/14] =?UTF-8?q?Fixed=20potential=20deadlock=20during=20p?= =?UTF-8?q?threads=5Fprepared=5Fstartup()=C2=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/prepare.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/prepare.c b/src/prepare.c index 4ae386ba..59b70ef1 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -863,6 +863,7 @@ static int pthreads_thread_bootstrap(zend_string *file) { /* {{{ */ int pthreads_prepared_startup(pthreads_object_t* thread, pthreads_monitor_t *ready, zend_class_entry *thread_ce) { + zend_string *autoload_file = NULL; PTHREADS_PREPARATION_BEGIN_CRITICAL() { thread->local.id = pthreads_self(); @@ -919,15 +920,22 @@ int pthreads_prepared_startup(pthreads_object_t* thread, pthreads_monitor_t *rea pthreads_prepare_resource_destructor(thread); if (PTHREADS_G(autoload_file)) { - if (pthreads_thread_bootstrap(PTHREADS_G(autoload_file)) == FAILURE) { - pthreads_monitor_add(ready, PTHREADS_MONITOR_ERROR); - return FAILURE; - } + autoload_file = zend_string_init(ZSTR_VAL(PTHREADS_G(autoload_file)), ZSTR_LEN(PTHREADS_G(autoload_file)), 1); } pthreads_monitor_add(ready, PTHREADS_MONITOR_READY); } PTHREADS_PREPARATION_END_CRITICAL(); - return SUCCESS; + int result = SUCCESS; + + if (autoload_file != NULL) { + int result = FAILURE; + if (pthreads_thread_bootstrap(autoload_file) == FAILURE) { + pthreads_monitor_add(ready, PTHREADS_MONITOR_ERROR); + result = FAILURE; + } + zend_string_release(autoload_file); + } + return result; } /* }}} */ /* {{{ */ From 31f99866ecf9f16471d33786bc667bf97ac37194 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Nov 2024 16:32:23 +0000 Subject: [PATCH 05/14] fix --- src/prepare.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/prepare.c b/src/prepare.c index c32ec4a7..a31ec338 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -22,6 +22,7 @@ #include #include #include +#include #define PMMPTHREAD_PREPARATION_BEGIN_CRITICAL() pmmpthread_globals_lock(); #define PMMPTHREAD_PREPARATION_END_CRITICAL() pmmpthread_globals_unlock() @@ -836,7 +837,8 @@ static int pmmpthread_thread_bootstrap(zend_string* file) { return SUCCESS; } - result = php_stream_open_for_zend_ex(ZSTR_VAL(file), &fh, USE_PATH | REPORT_ERRORS | STREAM_OPEN_FOR_INCLUDE); + zend_stream_init_filename(&fh, file); + result = php_stream_open_for_zend_ex(&fh, USE_PATH | REPORT_ERRORS | STREAM_OPEN_FOR_INCLUDE); if (result != SUCCESS) { return FAILURE; From 5d8e79e8b50e4b7e30d26cb6afa6244968a976d6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Nov 2024 16:35:43 +0000 Subject: [PATCH 06/14] fix --- stubs/Thread.stub.php | 8 ++++++++ stubs/Thread_arginfo.h | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/stubs/Thread.stub.php b/stubs/Thread.stub.php index ab6ec7c7..8699ebfe 100644 --- a/stubs/Thread.stub.php +++ b/stubs/Thread.stub.php @@ -151,6 +151,14 @@ public static function getSharedGlobals() : ThreadSafeArray{} */ public static function getRunningCount() : int{} + /** + * Sets the file to be included when a new thread is started + * Typically this would be the path of your vendor/autoload.php + * + * @param string $file + */ + public static function setAutoloadFile(string $file) : void{} + /** * Will return the identity of the referenced Thread * diff --git a/stubs/Thread_arginfo.h b/stubs/Thread_arginfo.h index 42ccc63f..673d0b14 100644 --- a/stubs/Thread_arginfo.h +++ b/stubs/Thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f07c76a75d7bf70fbbcf7bfe841cc003d1ec6727 */ + * Stub hash: 48854b4be7c9443ff7d4eb5490f3fd79a6bc6118 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_pmmp_thread_Thread_getCreatorId, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -14,6 +14,10 @@ ZEND_END_ARG_INFO() #define arginfo_class_pmmp_thread_Thread_getRunningCount arginfo_class_pmmp_thread_Thread_getCreatorId +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_pmmp_thread_Thread_setAutoloadFile, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, file, IS_STRING, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_pmmp_thread_Thread_getThreadId arginfo_class_pmmp_thread_Thread_getCreatorId ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_pmmp_thread_Thread_isJoined, 0, 0, _IS_BOOL, 0) @@ -33,6 +37,7 @@ ZEND_METHOD(pmmp_thread_Thread, getCurrentThread); ZEND_METHOD(pmmp_thread_Thread, getCurrentThreadId); ZEND_METHOD(pmmp_thread_Thread, getSharedGlobals); ZEND_METHOD(pmmp_thread_Thread, getRunningCount); +ZEND_METHOD(pmmp_thread_Thread, setAutoloadFile); ZEND_METHOD(pmmp_thread_Thread, getThreadId); ZEND_METHOD(pmmp_thread_Thread, isJoined); ZEND_METHOD(pmmp_thread_Thread, isStarted); @@ -46,6 +51,7 @@ static const zend_function_entry class_pmmp_thread_Thread_methods[] = { ZEND_ME(pmmp_thread_Thread, getCurrentThreadId, arginfo_class_pmmp_thread_Thread_getCurrentThreadId, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(pmmp_thread_Thread, getSharedGlobals, arginfo_class_pmmp_thread_Thread_getSharedGlobals, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(pmmp_thread_Thread, getRunningCount, arginfo_class_pmmp_thread_Thread_getRunningCount, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_ME(pmmp_thread_Thread, setAutoloadFile, arginfo_class_pmmp_thread_Thread_setAutoloadFile, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(pmmp_thread_Thread, getThreadId, arginfo_class_pmmp_thread_Thread_getThreadId, ZEND_ACC_PUBLIC) ZEND_ME(pmmp_thread_Thread, isJoined, arginfo_class_pmmp_thread_Thread_isJoined, ZEND_ACC_PUBLIC) ZEND_ME(pmmp_thread_Thread, isStarted, arginfo_class_pmmp_thread_Thread_isStarted, ZEND_ACC_PUBLIC) From ea4176fbc6a86fdd939f373aa34631a3ba06e40d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Nov 2024 16:42:43 +0000 Subject: [PATCH 07/14] fix --- src/prepare.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prepare.c b/src/prepare.c index a31ec338..9cd9adfa 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -837,7 +837,7 @@ static int pmmpthread_thread_bootstrap(zend_string* file) { return SUCCESS; } - zend_stream_init_filename(&fh, file); + zend_stream_init_filename_ex(&fh, file); result = php_stream_open_for_zend_ex(&fh, USE_PATH | REPORT_ERRORS | STREAM_OPEN_FOR_INCLUDE); if (result != SUCCESS) { From 6dc2ffe9dbff70d939af011f4d8ec0100c0d1a15 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Nov 2024 17:15:31 +0000 Subject: [PATCH 08/14] Test for recursion problems inside a bootstrap file --- tests/assets/autoload-file-recursion.php | 5 +++++ tests/autoload-file-recursion.phpt | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/assets/autoload-file-recursion.php create mode 100644 tests/autoload-file-recursion.phpt diff --git a/tests/assets/autoload-file-recursion.php b/tests/assets/autoload-file-recursion.php new file mode 100644 index 00000000..4db578d4 --- /dev/null +++ b/tests/assets/autoload-file-recursion.php @@ -0,0 +1,5 @@ +start(Thread::INHERIT_NONE); +$thread->join(); +?> +--EXPECT-- +ok From dc72d1c812706e93860a37084b95995f45d4e701 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Nov 2024 17:46:52 +0000 Subject: [PATCH 09/14] Fix segfault on bootstrap error --- src/prepare.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/prepare.c b/src/prepare.c index 9cd9adfa..3fc53424 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -942,10 +942,13 @@ int pmmpthread_prepared_startup(pmmpthread_object_t* thread, pmmpthread_monitor_ int result = SUCCESS; + //TODO: we probably should put this code inside routine instead of prepare if (autoload_file != NULL) { int result = FAILURE; if (pmmpthread_thread_bootstrap(autoload_file) == FAILURE) { - pmmpthread_monitor_add(ready, PMMPTHREAD_MONITOR_ERROR); + //by this point the ready monitor has probably already been destroyed + //the main thread doesn't wait for user code to start running + pmmpthread_monitor_add(&thread->monitor, PMMPTHREAD_MONITOR_ERROR); result = FAILURE; } zend_string_release(autoload_file); From e3235cdf60be7aa38a3f66bf5cd00428a1bc6a4f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Nov 2024 17:48:02 +0000 Subject: [PATCH 10/14] Added error output and tests for said errors whose genius idea was it to suppress exception output from bootstrap files ????????????????? --- src/prepare.c | 7 +++-- tests/assets/autoload-file-syntax-error.php | 3 +++ .../autoload-file-uncaught-exception.php | 3 +++ tests/autoload-file-invalid-path.phpt | 26 +++++++++++++++++++ tests/autoload-file-invalid-script.phpt | 26 +++++++++++++++++++ tests/autoload-file-throws-exception.phpt | 25 ++++++++++++++++++ 6 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 tests/assets/autoload-file-syntax-error.php create mode 100644 tests/assets/autoload-file-uncaught-exception.php create mode 100644 tests/autoload-file-invalid-path.phpt create mode 100644 tests/autoload-file-invalid-script.phpt create mode 100644 tests/autoload-file-throws-exception.phpt diff --git a/src/prepare.c b/src/prepare.c index 3fc53424..51d2d95d 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -841,6 +841,7 @@ static int pmmpthread_thread_bootstrap(zend_string* file) { result = php_stream_open_for_zend_ex(&fh, USE_PATH | REPORT_ERRORS | STREAM_OPEN_FOR_INCLUDE); if (result != SUCCESS) { + zend_error(E_ERROR, "Unable to open thread autoload file %s", ZSTR_VAL(file)); return FAILURE; } @@ -859,7 +860,8 @@ static int pmmpthread_thread_bootstrap(zend_string* file) { efree(ops); if (EG(exception)) { - zend_clear_exception(); + zend_exception_error(EG(exception), E_ERROR); + zend_error(E_ERROR, "Uncaught exception thrown from thread autoload file %s", ZSTR_VAL(file)); return FAILURE; } @@ -868,7 +870,8 @@ static int pmmpthread_thread_bootstrap(zend_string* file) { } if (EG(exception)) { - zend_clear_exception(); + zend_exception_error(EG(exception), E_ERROR); + zend_error(E_ERROR, "Error compiling thread autoload file %s", ZSTR_VAL(file)); } return FAILURE; diff --git a/tests/assets/autoload-file-syntax-error.php b/tests/assets/autoload-file-syntax-error.php new file mode 100644 index 00000000..7fcd9265 --- /dev/null +++ b/tests/assets/autoload-file-syntax-error.php @@ -0,0 +1,3 @@ +start(Thread::INHERIT_NONE); +$thread->join(); +?> +--EXPECT-- +Warning: Unknown: Failed to open stream: No such file or directory in Unknown on line 0 + +Fatal error: Unable to open thread autoload file %si-dont-exist.php in Unknown on line 0 diff --git a/tests/autoload-file-invalid-script.phpt b/tests/autoload-file-invalid-script.phpt new file mode 100644 index 00000000..01956247 --- /dev/null +++ b/tests/autoload-file-invalid-script.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test that using Thread::setAutoloadFile() with a broken PHP file errors properly +--DESCRIPTION-- +Not sure if we can validate paths at the time of setting them. They might not exist +when set, or might be deleted before we can use them. This means it's ultimately +up to the thread itself to handle errors from wrong include paths correctly. +--FILE-- +start(Thread::INHERIT_NONE); +$thread->join(); +?> +--EXPECTF-- +Parse error: Unclosed '(' on line 3 in %s on line %d + +Fatal error: Error compiling thread autoload file %sautoload-file-syntax-error.php in Unknown on line 0 diff --git a/tests/autoload-file-throws-exception.phpt b/tests/autoload-file-throws-exception.phpt new file mode 100644 index 00000000..f3edee82 --- /dev/null +++ b/tests/autoload-file-throws-exception.phpt @@ -0,0 +1,25 @@ +--TEST-- +Test that using Thread::setAutoloadFile() behaves properly when a file throws errors +--FILE-- +start(Thread::INHERIT_NONE); +$thread->join(); +?> +--EXPECTF-- +Fatal error: Uncaught Exception: cya in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d + +Fatal error: Uncaught exception thrown from thread autoload file %sautoload-file-uncaught-exception.php in Unknown on line 0 From fd5c89ab517ab3b5b431772c443dfc3656bd2383 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Nov 2024 18:03:04 +0000 Subject: [PATCH 11/14] ... --- tests/autoload-file-invalid-path.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/autoload-file-invalid-path.phpt b/tests/autoload-file-invalid-path.phpt index cd9a5865..0d7fad92 100644 --- a/tests/autoload-file-invalid-path.phpt +++ b/tests/autoload-file-invalid-path.phpt @@ -20,7 +20,7 @@ $thread = new class extends Thread{ $thread->start(Thread::INHERIT_NONE); $thread->join(); ?> ---EXPECT-- +--EXPECTF-- Warning: Unknown: Failed to open stream: No such file or directory in Unknown on line 0 Fatal error: Unable to open thread autoload file %si-dont-exist.php in Unknown on line 0 From 0bb86cd58e2eebf1d29c867d8350c06d45ab24d1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Nov 2024 18:09:22 +0000 Subject: [PATCH 12/14] Wtf? --- src/prepare.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/prepare.c b/src/prepare.c index 51d2d95d..25f84925 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -947,7 +947,6 @@ int pmmpthread_prepared_startup(pmmpthread_object_t* thread, pmmpthread_monitor_ //TODO: we probably should put this code inside routine instead of prepare if (autoload_file != NULL) { - int result = FAILURE; if (pmmpthread_thread_bootstrap(autoload_file) == FAILURE) { //by this point the ready monitor has probably already been destroyed //the main thread doesn't wait for user code to start running From bc78680be5d21c86471224d1bf0d9a68233679d0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Nov 2024 18:11:55 +0000 Subject: [PATCH 13/14] fix bailout address errors --- src/prepare.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/prepare.c b/src/prepare.c index 25f84925..1c755a67 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -947,12 +947,14 @@ int pmmpthread_prepared_startup(pmmpthread_object_t* thread, pmmpthread_monitor_ //TODO: we probably should put this code inside routine instead of prepare if (autoload_file != NULL) { - if (pmmpthread_thread_bootstrap(autoload_file) == FAILURE) { - //by this point the ready monitor has probably already been destroyed - //the main thread doesn't wait for user code to start running - pmmpthread_monitor_add(&thread->monitor, PMMPTHREAD_MONITOR_ERROR); - result = FAILURE; - } + zend_first_try{ + if (pmmpthread_thread_bootstrap(autoload_file) == FAILURE) { + //by this point the ready monitor has probably already been destroyed + //the main thread doesn't wait for user code to start running + pmmpthread_monitor_add(&thread->monitor, PMMPTHREAD_MONITOR_ERROR); + result = FAILURE; + } + } zend_end_try(); zend_string_release(autoload_file); } return result; From 86e029b645a5d10ab953206fa5897cfb9e5d690b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Nov 2024 18:32:43 +0000 Subject: [PATCH 14/14] Fix --- src/prepare.c | 73 +---------------------- src/prepare.h | 2 +- src/routine.c | 67 ++++++++++++++++++++- tests/autoload-file-invalid-path.phpt | 2 +- tests/autoload-file-throws-exception.phpt | 2 +- 5 files changed, 72 insertions(+), 74 deletions(-) diff --git a/src/prepare.c b/src/prepare.c index 1c755a67..796de4d1 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -826,61 +826,8 @@ static inline void pmmpthread_prepare_sapi(const pmmpthread_ident_t* source) { } } /* }}} */ -/* {{{ Includes the autoloader provided, if any. This code is borrowed from krakjoe/parallel. */ -static int pmmpthread_thread_bootstrap(zend_string* file) { - zend_file_handle fh; - zend_op_array* ops; - zval rv; - int result; - - if (!file) { - return SUCCESS; - } - - zend_stream_init_filename_ex(&fh, file); - result = php_stream_open_for_zend_ex(&fh, USE_PATH | REPORT_ERRORS | STREAM_OPEN_FOR_INCLUDE); - - if (result != SUCCESS) { - zend_error(E_ERROR, "Unable to open thread autoload file %s", ZSTR_VAL(file)); - return FAILURE; - } - - zend_hash_add_empty_element(&EG(included_files), - fh.opened_path ? - fh.opened_path : file); - - ops = zend_compile_file(&fh, ZEND_REQUIRE); - - zend_destroy_file_handle(&fh); - - if (ops) { - ZVAL_UNDEF(&rv); - zend_execute(ops, &rv); - destroy_op_array(ops); - efree(ops); - - if (EG(exception)) { - zend_exception_error(EG(exception), E_ERROR); - zend_error(E_ERROR, "Uncaught exception thrown from thread autoload file %s", ZSTR_VAL(file)); - return FAILURE; - } - - zval_ptr_dtor(&rv); - return SUCCESS; - } - - if (EG(exception)) { - zend_exception_error(EG(exception), E_ERROR); - zend_error(E_ERROR, "Error compiling thread autoload file %s", ZSTR_VAL(file)); - } - - return FAILURE; -} /* }}} */ - /* {{{ */ -int pmmpthread_prepared_startup(pmmpthread_object_t* thread, pmmpthread_monitor_t *ready, zend_class_entry *thread_ce, zend_ulong thread_options) { - zend_string *autoload_file = NULL; - +int pmmpthread_prepared_startup(pmmpthread_object_t* thread, pmmpthread_monitor_t *ready, zend_class_entry *thread_ce, zend_ulong thread_options, zend_string **autoload_file) { PMMPTHREAD_PREPARATION_BEGIN_CRITICAL() { thread->local.id = pmmpthread_self(); thread->local.ls = ts_resource(0); @@ -936,28 +883,14 @@ int pmmpthread_prepared_startup(pmmpthread_object_t* thread, pmmpthread_monitor_ pmmpthread_prepare_includes(&thread->creator); if (PMMPTHREAD_G(autoload_file)) { - autoload_file = zend_string_init(ZSTR_VAL(PMMPTHREAD_G(autoload_file)), ZSTR_LEN(PMMPTHREAD_G(autoload_file)), 1); + *autoload_file = zend_string_init(ZSTR_VAL(PMMPTHREAD_G(autoload_file)), ZSTR_LEN(PMMPTHREAD_G(autoload_file)), 1); } pmmpthread_monitor_add(ready, PMMPTHREAD_MONITOR_READY); PMMPTHREAD_G(thread_count)++; } PMMPTHREAD_PREPARATION_END_CRITICAL(); - int result = SUCCESS; - - //TODO: we probably should put this code inside routine instead of prepare - if (autoload_file != NULL) { - zend_first_try{ - if (pmmpthread_thread_bootstrap(autoload_file) == FAILURE) { - //by this point the ready monitor has probably already been destroyed - //the main thread doesn't wait for user code to start running - pmmpthread_monitor_add(&thread->monitor, PMMPTHREAD_MONITOR_ERROR); - result = FAILURE; - } - } zend_end_try(); - zend_string_release(autoload_file); - } - return result; + return SUCCESS; } /* }}} */ /* {{{ Calls user shutdown functions before entering into the AWAIT_JOIN state. diff --git a/src/prepare.h b/src/prepare.h index 275f4c2a..834e27a6 100644 --- a/src/prepare.h +++ b/src/prepare.h @@ -30,7 +30,7 @@ void pmmpthread_prepared_entry_late_bindings(const pmmpthread_ident_t* source, z void pmmpthread_context_late_bindings(const pmmpthread_ident_t* source); /* }}} */ /* {{{ */ -int pmmpthread_prepared_startup(pmmpthread_object_t* thread, pmmpthread_monitor_t *ready, zend_class_entry *thread_ce, zend_ulong thread_options); /* }}} */ +int pmmpthread_prepared_startup(pmmpthread_object_t* thread, pmmpthread_monitor_t *ready, zend_class_entry *thread_ce, zend_ulong thread_options, zend_string **autoload_file); /* }}} */ /* {{{ */ void pmmpthread_call_shutdown_functions(void); /* }}} */ diff --git a/src/routine.c b/src/routine.c index 8cca1f9c..86a91d0b 100644 --- a/src/routine.c +++ b/src/routine.c @@ -35,6 +35,57 @@ static void pmmpthread_routine_free(pmmpthread_routine_arg_t* r) { pmmpthread_monitor_destroy(&r->ready); } /* }}} */ +/* {{{ Includes the autoloader provided, if any. This code is borrowed from krakjoe/parallel. */ +static int pmmpthread_routine_run_bootstrap(zend_string* file) { + zend_file_handle fh; + zend_op_array* ops; + zval rv; + int result; + + if (!file) { + return SUCCESS; + } + + zend_stream_init_filename_ex(&fh, file); + result = php_stream_open_for_zend_ex(&fh, USE_PATH | REPORT_ERRORS | STREAM_OPEN_FOR_INCLUDE); + + if (result != SUCCESS) { + zend_error(E_ERROR, "Unable to open thread autoload file %s", ZSTR_VAL(file)); + return FAILURE; + } + + zend_hash_add_empty_element(&EG(included_files), + fh.opened_path ? + fh.opened_path : file); + + ops = zend_compile_file(&fh, ZEND_REQUIRE); + + zend_destroy_file_handle(&fh); + + if (ops) { + ZVAL_UNDEF(&rv); + zend_execute(ops, &rv); + destroy_op_array(ops); + efree(ops); + + if (EG(exception)) { + zend_exception_error(EG(exception), E_ERROR); + zend_error(E_ERROR, "Uncaught exception thrown from thread autoload file %s", ZSTR_VAL(file)); + return FAILURE; + } + + zval_ptr_dtor(&rv); + return SUCCESS; + } + + if (EG(exception)) { + zend_exception_error(EG(exception), E_ERROR); + zend_error(E_ERROR, "Error compiling thread autoload file %s", ZSTR_VAL(file)); + } + + return FAILURE; +} /* }}} */ + /* {{{ */ static inline zend_result pmmpthread_routine_run_function(pmmpthread_zend_object_t* connection) { zend_function* run; @@ -106,12 +157,24 @@ static void* pmmpthread_routine(pmmpthread_routine_arg_t* routine) { zend_ulong thread_options = routine->options; pmmpthread_object_t* ts_obj = thread->ts_obj; pmmpthread_monitor_t* ready = &routine->ready; + zend_string* autoload_file = NULL; - if (pmmpthread_prepared_startup(ts_obj, ready, thread->std.ce, thread_options) == SUCCESS) { + if (pmmpthread_prepared_startup(ts_obj, ready, thread->std.ce, thread_options, &autoload_file) == SUCCESS) { pmmpthread_queue done_tasks_cache; memset(&done_tasks_cache, 0, sizeof(pmmpthread_queue)); zend_first_try{ + if (autoload_file != NULL) { + zend_try { + if (pmmpthread_routine_run_bootstrap(autoload_file) == FAILURE) { + zend_bailout(); + } + } zend_catch { + zend_string_release(autoload_file); + zend_bailout(); + } zend_end_try(); + } + ZVAL_UNDEF(&PMMPTHREAD_ZG(this)); pmmpthread_object_connect(thread, &PMMPTHREAD_ZG(this)); if (pmmpthread_routine_run_function(PMMPTHREAD_FETCH_FROM(Z_OBJ_P(&PMMPTHREAD_ZG(this)))) == FAILURE) { @@ -140,6 +203,8 @@ static void* pmmpthread_routine(pmmpthread_routine_arg_t* routine) { } } } + } zend_catch { + pmmpthread_monitor_add(&ts_obj->monitor, PMMPTHREAD_MONITOR_ERROR); } zend_end_try(); pmmpthread_call_shutdown_functions(); diff --git a/tests/autoload-file-invalid-path.phpt b/tests/autoload-file-invalid-path.phpt index 0d7fad92..bb51fa0b 100644 --- a/tests/autoload-file-invalid-path.phpt +++ b/tests/autoload-file-invalid-path.phpt @@ -13,7 +13,7 @@ Thread::setAutoloadFile(__DIR__ . '/assets/i-dont-exist.php'); $thread = new class extends Thread{ public function run() : void{ - + echo "unreachable\n"; } }; diff --git a/tests/autoload-file-throws-exception.phpt b/tests/autoload-file-throws-exception.phpt index f3edee82..a32bd993 100644 --- a/tests/autoload-file-throws-exception.phpt +++ b/tests/autoload-file-throws-exception.phpt @@ -9,7 +9,7 @@ Thread::setAutoloadFile(__DIR__ . '/assets/autoload-file-uncaught-exception.php' $thread = new class extends Thread{ public function run() : void{ - + echo "unreachable\n"; } };