From 3a17c3456f409c6b2c97eeaf54723a1406231ce1 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 15 Mar 2024 16:46:26 +0800 Subject: [PATCH 001/103] Optimize code --- ext-src/php_swoole.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 17046b423cf..59dd8d10f11 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -218,6 +218,11 @@ static void php_swoole_init_globals(zend_swoole_globals *swoole_globals) { swoole_globals->socket_buffer_size = SW_SOCKET_BUFFER_SIZE; swoole_globals->display_errors = 1; swoole_globals->use_shortname = 1; + swoole_globals->in_autoload = nullptr; + if (strcmp("cli", sapi_module.name) == 0 || strcmp("phpdbg", sapi_module.name) == 0 || + strcmp("embed", sapi_module.name) == 0) { + swoole_globals->cli = 1; + } } void php_swoole_register_shutdown_function(const char *function) { @@ -694,10 +699,6 @@ PHP_MINIT_FUNCTION(swoole) { // init bug report message bug_report_message_init(); - if (strcmp("cli", sapi_module.name) == 0 || strcmp("phpdbg", sapi_module.name) == 0 || - strcmp("embed", sapi_module.name) == 0) { - SWOOLE_G(cli) = 1; - } SW_INIT_CLASS_ENTRY_EX2( swoole_exception, "Swoole\\Exception", nullptr, nullptr, zend_ce_exception, zend_get_std_object_handlers()); From 9bc606b6bc55e1c24a69e06d4780ce07fed4d138 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 15 Mar 2024 18:01:29 +0800 Subject: [PATCH 002/103] thread local --- ext-src/php_swoole_coroutine.h | 12 ++++++------ ext-src/swoole_coroutine.cc | 12 ++++++------ ext-src/swoole_runtime.cc | 4 ++-- include/swoole_coroutine.h | 20 ++++++++++---------- src/coroutine/base.cc | 22 +++++++++++----------- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index aebb5c94429..5c6d4c3ed08 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -110,7 +110,7 @@ class PHPCoroutine { bool enable_deadlock_check; }; - static zend_array *options; + static thread_local zend_array *options; enum HookType { HOOK_NONE = 0, @@ -261,12 +261,12 @@ class PHPCoroutine { } protected: - static bool activated; - static PHPContext main_context; - static Config config; + static thread_local bool activated; + static thread_local PHPContext main_context; + static thread_local Config config; - static bool interrupt_thread_running; - static std::thread interrupt_thread; + static thread_local bool interrupt_thread_running; + static thread_local std::thread interrupt_thread; static void activate(); static void deactivate(void *ptr); diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index e0e9d27799b..47b409f8662 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -57,19 +57,19 @@ static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend enum sw_exit_flags { SW_EXIT_IN_COROUTINE = 1 << 1, SW_EXIT_IN_SERVER = 1 << 2 }; -bool PHPCoroutine::activated = false; -zend_array *PHPCoroutine::options = nullptr; +thread_local bool PHPCoroutine::activated = false; +thread_local zend_array *PHPCoroutine::options = nullptr; -PHPCoroutine::Config PHPCoroutine::config{ +thread_local PHPCoroutine::Config PHPCoroutine::config{ SW_DEFAULT_MAX_CORO_NUM, 0, false, true, }; -PHPContext PHPCoroutine::main_context{}; -std::thread PHPCoroutine::interrupt_thread; -bool PHPCoroutine::interrupt_thread_running = false; +thread_local PHPContext PHPCoroutine::main_context{}; +thread_local std::thread PHPCoroutine::interrupt_thread; +thread_local bool PHPCoroutine::interrupt_thread_running = false; extern void php_swoole_load_library(); diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index cbc6e7657b7..e8f2d88a59f 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -172,8 +172,8 @@ static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { #define SW_HOOK_LIBRARY_FE(name, arg_info) \ ZEND_RAW_FENTRY("swoole_hook_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) -static zend_array *tmp_function_table = nullptr; -static std::unordered_map child_class_entries; +static thread_local zend_array *tmp_function_table = nullptr; +static thread_local std::unordered_map child_class_entries; SW_EXTERN_C_BEGIN #include "ext/standard/file.h" diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index f83453005f7..04f4ca8aee5 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -135,7 +135,7 @@ class Coroutine { return ctx; } - static std::unordered_map coroutines; + static thread_local std::unordered_map coroutines; static void set_on_yield(SwapCallback func); static void set_on_resume(SwapCallback func); @@ -245,15 +245,15 @@ class Coroutine { static void print_list(); protected: - static Coroutine *current; - static long last_cid; - static uint64_t peak_num; - static size_t stack_size; - static SwapCallback on_yield; /* before yield */ - static SwapCallback on_resume; /* before resume */ - static SwapCallback on_close; /* before close */ - static BailoutCallback on_bailout; /* when bailout */ - static bool activated; + static thread_local Coroutine *current; + static thread_local long last_cid; + static thread_local uint64_t peak_num; + static thread_local size_t stack_size; + static thread_local SwapCallback on_yield; /* before yield */ + static thread_local SwapCallback on_resume; /* before resume */ + static thread_local SwapCallback on_close; /* before close */ + static thread_local BailoutCallback on_bailout; /* when bailout */ + static thread_local bool activated; enum State state = STATE_INIT; enum ResumeCode resume_code_ = RC_OK; diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index 330f9f6d634..ad034401ac1 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -19,17 +19,17 @@ namespace swoole { -Coroutine *Coroutine::current = nullptr; -long Coroutine::last_cid = 0; -std::unordered_map Coroutine::coroutines; -uint64_t Coroutine::peak_num = 0; -bool Coroutine::activated = false; - -size_t Coroutine::stack_size = SW_DEFAULT_C_STACK_SIZE; -Coroutine::SwapCallback Coroutine::on_yield = nullptr; -Coroutine::SwapCallback Coroutine::on_resume = nullptr; -Coroutine::SwapCallback Coroutine::on_close = nullptr; -Coroutine::BailoutCallback Coroutine::on_bailout = nullptr; +thread_local Coroutine *Coroutine::current = nullptr; +thread_local long Coroutine::last_cid = 0; +thread_local std::unordered_map Coroutine::coroutines; +thread_local uint64_t Coroutine::peak_num = 0; +thread_local bool Coroutine::activated = false; + +thread_local size_t Coroutine::stack_size = SW_DEFAULT_C_STACK_SIZE; +thread_local Coroutine::SwapCallback Coroutine::on_yield = nullptr; +thread_local Coroutine::SwapCallback Coroutine::on_resume = nullptr; +thread_local Coroutine::SwapCallback Coroutine::on_close = nullptr; +thread_local Coroutine::BailoutCallback Coroutine::on_bailout = nullptr; #ifdef SW_USE_THREAD_CONTEXT namespace coroutine { From 908644c72b2de0407e62a3d79f04f35fef09c98f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 19 Mar 2024 15:42:58 +0800 Subject: [PATCH 003/103] swoole thread --- config.m4 | 9 + examples/thread/co.php | 10 ++ examples/thread/mt.php | 13 ++ ext-src/php_swoole.cc | 10 +- ext-src/php_swoole_coroutine.h | 12 +- ext-src/php_swoole_private.h | 10 ++ ext-src/stubs/php_swoole_thread.stub.php | 12 ++ ext-src/stubs/php_swoole_thread_arginfo.h | 19 ++ ext-src/swoole_coroutine.cc | 12 +- ext-src/swoole_runtime.cc | 4 +- ext-src/swoole_thread.cc | 210 ++++++++++++++++++++++ include/swoole_coroutine.h | 24 +-- scripts/make.sh | 1 + src/coroutine/base.cc | 22 +-- 14 files changed, 332 insertions(+), 36 deletions(-) create mode 100644 examples/thread/co.php create mode 100644 examples/thread/mt.php create mode 100644 ext-src/stubs/php_swoole_thread.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_arginfo.h create mode 100644 ext-src/swoole_thread.cc diff --git a/config.m4 b/config.m4 index dd9cb803ff5..c3c0c3fb0ae 100644 --- a/config.m4 +++ b/config.m4 @@ -110,6 +110,11 @@ PHP_ARG_ENABLE([thread-context], [whether to enable thread context], [AS_HELP_STRING([--enable-thread-context], [Use thread context])], [no], [no]) + +PHP_ARG_ENABLE([swoole-thread], + [whether to enable swoole thread support], + [AS_HELP_STRING([--enable-swoole-thread], + [Enable swoole thread support])], [no], [no]) PHP_ARG_ENABLE([swoole-coro-time], [whether to enable coroutine execution time ], @@ -877,6 +882,10 @@ EOF AC_DEFINE(SW_LOG_TRACE_OPEN, 1, [enable trace log]) fi + if test "$PHP_SWOOLE_THREAD" != "no"; then + AC_DEFINE(SW_THREAD, 1, [enable swoole thread support]) + fi + if test "$PHP_SOCKETS" = "yes"; then AC_MSG_CHECKING([for php_sockets.h]) diff --git a/examples/thread/co.php b/examples/thread/co.php new file mode 100644 index 00000000000..e806ad76436 --- /dev/null +++ b/examples/thread/co.php @@ -0,0 +1,10 @@ +id); +var_dump($t2->id); + +$t1->join(); +$t2->join(); + diff --git a/examples/thread/mt.php b/examples/thread/mt.php new file mode 100644 index 00000000000..f76442532f1 --- /dev/null +++ b/examples/thread/mt.php @@ -0,0 +1,13 @@ +join(); +} + +sleep(5); +echo "end\n"; diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 59dd8d10f11..933da718ab4 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -746,7 +746,6 @@ PHP_MINIT_FUNCTION(swoole) { #ifdef SW_USE_ODBC php_swoole_odbc_minit(module_number); #endif - #ifdef SW_USE_ORACLE php_swoole_oracle_minit(module_number); #endif @@ -754,6 +753,9 @@ PHP_MINIT_FUNCTION(swoole) { #ifdef SW_USE_SQLITE php_swoole_sqlite_minit(module_number); #endif +#ifdef ZTS + php_swoole_thread_minit(module_number); +#endif SwooleG.fatal_error = fatal_error; Socket::default_buffer_size = SWOOLE_G(socket_buffer_size); @@ -827,6 +829,9 @@ PHP_MINFO_FUNCTION(swoole) { #ifdef HAVE_KQUEUE php_info_print_table_row(2, "kqueue", "enabled"); #endif +#ifdef ZTS + php_info_print_table_row(2, "thread", "enabled"); +#endif #ifdef HAVE_SIGNALFD php_info_print_table_row(2, "signalfd", "enabled"); #endif @@ -1055,6 +1060,9 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { php_swoole_coroutine_scheduler_rshutdown(); php_swoole_runtime_rshutdown(); php_swoole_process_rshutdown(); +#ifdef ZTS + php_swoole_thread_rshutdown(); +#endif SwooleG.running = 0; SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_END; diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 5c6d4c3ed08..6f6e1c98add 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -110,7 +110,7 @@ class PHPCoroutine { bool enable_deadlock_check; }; - static thread_local zend_array *options; + static SW_THREAD_LOCAL zend_array *options; enum HookType { HOOK_NONE = 0, @@ -261,12 +261,12 @@ class PHPCoroutine { } protected: - static thread_local bool activated; - static thread_local PHPContext main_context; - static thread_local Config config; + static SW_THREAD_LOCAL bool activated; + static SW_THREAD_LOCAL PHPContext main_context; + static SW_THREAD_LOCAL Config config; - static thread_local bool interrupt_thread_running; - static thread_local std::thread interrupt_thread; + static SW_THREAD_LOCAL bool interrupt_thread_running; + static SW_THREAD_LOCAL std::thread interrupt_thread; static void activate(); static void deactivate(void *ptr); diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 2b05b58b885..197dd9b01f6 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -112,6 +112,10 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #error "only linux support iouring" #endif +#if defined(SW_THREAD) && !defined(ZTS) +#error "swoole thread must be used with ZTS" +#endif + //-------------------------------------------------------- #define SW_MAX_FIND_COUNT 100 // for swoole_server::connection_list #define SW_PHP_CLIENT_BUFFER_SIZE 65535 @@ -266,6 +270,9 @@ void php_swoole_http_server_coro_minit(int module_number); void php_swoole_websocket_server_minit(int module_number); void php_swoole_redis_server_minit(int module_number); void php_swoole_name_resolver_minit(int module_number); +#ifdef ZTS +void php_swoole_thread_minit(int module_number); +#endif /** * RINIT @@ -290,6 +297,9 @@ void php_swoole_process_rshutdown(); void php_swoole_coroutine_scheduler_rshutdown(); void php_swoole_runtime_rshutdown(); void php_swoole_server_rshutdown(); +#ifdef ZTS +void php_swoole_thread_rshutdown(); +#endif int php_swoole_reactor_init(); void php_swoole_set_global_option(zend_array *vht); diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php new file mode 100644 index 00000000000..dca455904e8 --- /dev/null +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -0,0 +1,12 @@ + child_class_entries; +static SW_THREAD_LOCAL zend_array *tmp_function_table = nullptr; +static SW_THREAD_LOCAL std::unordered_map child_class_entries; SW_EXTERN_C_BEGIN #include "ext/standard/file.h" diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc new file mode 100644 index 00000000000..f67a5aaaf5b --- /dev/null +++ b/ext-src/swoole_thread.cc @@ -0,0 +1,210 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" + +#include +#include + +#include + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_thread_arginfo.h" +END_EXTERN_C() + +using namespace swoole; + +zend_class_entry *swoole_thread_ce; +static zend_object_handlers swoole_thread_handlers; +typedef std::thread Thread; +thread_local zval thread_argv; + +struct ThreadObject { + Thread *thread; + zend_object std; +}; + +static sw_inline ThreadObject *php_swoole_thread_fetch_object(zend_object *obj) { + return (ThreadObject *) ((char *) obj - swoole_thread_handlers.offset); +} + +static void php_swoole_thread_join(zend_object *object) { + ThreadObject *to = php_swoole_thread_fetch_object(object); + if (to->thread && to->thread->joinable()) { + to->thread->join(); + delete to->thread; + to->thread = nullptr; + } +} + +static void php_swoole_thread_free_object(zend_object *object) { + php_swoole_thread_join(object); + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_thread_create_object(zend_class_entry *ce) { + ThreadObject *to = (ThreadObject *) zend_object_alloc(sizeof(ThreadObject), ce); + zend_object_std_init(&to->std, ce); + object_properties_init(&to->std, ce); + to->std.handlers = &swoole_thread_handlers; + return &to->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread, __construct); +static PHP_METHOD(swoole_thread, join); +static PHP_METHOD(swoole_thread, run); +static PHP_METHOD(swoole_thread, getArguments); +static PHP_METHOD(swoole_thread, getId); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_methods[] = +{ + PHP_ME(swoole_thread, __construct, arginfo_class_Swoole_Thread___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, join, arginfo_class_Swoole_Thread_join, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, run, arginfo_class_Swoole_Thread_run, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getArguments, arginfo_class_Swoole_Thread_getArguments, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread, "Swoole\\Thread", nullptr, swoole_thread_methods); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_thread); + SW_SET_CLASS_CLONEABLE(swoole_thread, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread, php_swoole_thread_create_object, php_swoole_thread_free_object, ThreadObject, std); + + zend_declare_property_null(swoole_thread_ce, ZEND_STRL("id"), ZEND_ACC_PUBLIC); +} + +static PHP_METHOD(swoole_thread, __construct) {} + +static PHP_METHOD(swoole_thread, join) { + ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (to == nullptr || !to->thread->joinable()) { + RETURN_FALSE; + } + php_swoole_thread_join(Z_OBJ_P(ZEND_THIS)); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_thread, getArguments) { + RETURN_ZVAL(&thread_argv, 1, 0); +} + +static PHP_METHOD(swoole_thread, getId) { + RETURN_LONG(pthread_self()); +} + +std::string php_swoole_thread_serialize(zval *zdata) { + php_serialize_data_t var_hash; + smart_str serialized_data = {0}; + + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(&serialized_data, zdata, &var_hash); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + std::string result; + if (!EG(exception)) { + result = std::string(serialized_data.s->val, serialized_data.s->len); + } + smart_str_free(&serialized_data); + return result; +} + +bool php_swoole_thread_unserialize(const std::string &data, zval *zv) { + php_unserialize_data_t var_hash; + const char *p = data.c_str(); + size_t l = data.length(); + + PHP_VAR_UNSERIALIZE_INIT(var_hash); + zend_bool unserialized = php_var_unserialize(zv, (const uchar **) &p, (const uchar *) (p + l), &var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if (!unserialized) { + swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", + (zend_long) ((char *) p - data.c_str()), + l); + } + return unserialized; +} + +void php_swoole_thread_rshutdown() {} + +void php_swoole_thread_start(const std::string &file, const std::string &argv) { + ts_resource(0); +#if defined(COMPILE_DL_SWOOLE) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + zend_file_handle file_handle{}; + + if (php_request_startup() != SUCCESS) { + EG(exit_status) = 1; + goto _startup_error; + } + + zend_stream_init_filename(&file_handle, file.c_str()); + file_handle.primary_script = 1; + + zend_first_try { + if (argv.empty()) { + array_init(&thread_argv); + } else { + php_swoole_thread_unserialize(argv, &thread_argv); + } + php_execute_script(&file_handle); + } + zend_end_try(); + + zend_destroy_file_handle(&file_handle); + zval_dtor(&thread_argv); + + php_request_shutdown(NULL); + file_handle.filename = NULL; + +_startup_error: + ts_free_thread(); +} + +static PHP_METHOD(swoole_thread, run) { + char *execfile = nullptr; + size_t execfile_len = 0; + zval *args; + int argc; + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_STRING(execfile, execfile_len) + Z_PARAM_VARIADIC('+', args, argc) + ZEND_PARSE_PARAMETERS_END(); + + if (execfile_len < 1) { + php_swoole_fatal_error(E_WARNING, "exec file name is empty"); + RETURN_FALSE; + } + + object_init_ex(return_value, swoole_thread_ce); + ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(return_value)); + std::string file(execfile, execfile_len); + std::string argv = php_swoole_thread_serialize(args); + (void) argc; + + to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); + zend_update_property_long( + swoole_thread_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("id"), to->thread->native_handle()); +} diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 04f4ca8aee5..6df226241b5 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -41,6 +41,10 @@ typedef std::chrono::microseconds seconds_type; #define CALC_EXECUTE_USEC(yield_coroutine, resume_coroutine) #endif +#ifdef SW_THREAD +#define SW_THREAD_LOCAL thread_local +#endif + namespace swoole { class Coroutine { public: @@ -135,7 +139,7 @@ class Coroutine { return ctx; } - static thread_local std::unordered_map coroutines; + static SW_THREAD_LOCAL std::unordered_map coroutines; static void set_on_yield(SwapCallback func); static void set_on_resume(SwapCallback func); @@ -245,15 +249,15 @@ class Coroutine { static void print_list(); protected: - static thread_local Coroutine *current; - static thread_local long last_cid; - static thread_local uint64_t peak_num; - static thread_local size_t stack_size; - static thread_local SwapCallback on_yield; /* before yield */ - static thread_local SwapCallback on_resume; /* before resume */ - static thread_local SwapCallback on_close; /* before close */ - static thread_local BailoutCallback on_bailout; /* when bailout */ - static thread_local bool activated; + static SW_THREAD_LOCAL Coroutine *current; + static SW_THREAD_LOCAL long last_cid; + static SW_THREAD_LOCAL uint64_t peak_num; + static SW_THREAD_LOCAL size_t stack_size; + static SW_THREAD_LOCAL SwapCallback on_yield; /* before yield */ + static SW_THREAD_LOCAL SwapCallback on_resume; /* before resume */ + static SW_THREAD_LOCAL SwapCallback on_close; /* before close */ + static SW_THREAD_LOCAL BailoutCallback on_bailout; /* when bailout */ + static SW_THREAD_LOCAL bool activated; enum State state = STATE_INIT; enum ResumeCode resume_code_ = RC_OK; diff --git a/scripts/make.sh b/scripts/make.sh index d72285c3617..4a478151554 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -8,6 +8,7 @@ COMPILE_PARAMS="--enable-openssl \ --enable-cares \ --enable-swoole-pgsql \ --enable-iouring \ +--enable-swoole-thread \ --with-swoole-odbc=unixODBC,/usr \ --enable-swoole-sqlite" diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index ad034401ac1..c479ce9d742 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -19,17 +19,17 @@ namespace swoole { -thread_local Coroutine *Coroutine::current = nullptr; -thread_local long Coroutine::last_cid = 0; -thread_local std::unordered_map Coroutine::coroutines; -thread_local uint64_t Coroutine::peak_num = 0; -thread_local bool Coroutine::activated = false; - -thread_local size_t Coroutine::stack_size = SW_DEFAULT_C_STACK_SIZE; -thread_local Coroutine::SwapCallback Coroutine::on_yield = nullptr; -thread_local Coroutine::SwapCallback Coroutine::on_resume = nullptr; -thread_local Coroutine::SwapCallback Coroutine::on_close = nullptr; -thread_local Coroutine::BailoutCallback Coroutine::on_bailout = nullptr; +SW_THREAD_LOCAL Coroutine *Coroutine::current = nullptr; +SW_THREAD_LOCAL long Coroutine::last_cid = 0; +SW_THREAD_LOCAL std::unordered_map Coroutine::coroutines; +SW_THREAD_LOCAL uint64_t Coroutine::peak_num = 0; +SW_THREAD_LOCAL bool Coroutine::activated = false; + +SW_THREAD_LOCAL size_t Coroutine::stack_size = SW_DEFAULT_C_STACK_SIZE; +SW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_yield = nullptr; +SW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_resume = nullptr; +SW_THREAD_LOCAL Coroutine::SwapCallback Coroutine::on_close = nullptr; +SW_THREAD_LOCAL Coroutine::BailoutCallback Coroutine::on_bailout = nullptr; #ifdef SW_USE_THREAD_CONTEXT namespace coroutine { From 7c3cb35069c11eb14af77430ee781ecfdd3b311f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 19 Mar 2024 16:04:35 +0800 Subject: [PATCH 004/103] optimize argv --- ext-src/swoole_thread.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index f67a5aaaf5b..44bb7a2be70 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -201,8 +201,14 @@ static PHP_METHOD(swoole_thread, run) { object_init_ex(return_value, swoole_thread_ce); ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(return_value)); std::string file(execfile, execfile_len); - std::string argv = php_swoole_thread_serialize(args); - (void) argc; + + zval zargv; + array_init(&zargv); + for (int i = 0; i < argc; i++) { + zend::array_add(&zargv, &args[i]); + } + std::string argv = php_swoole_thread_serialize(&zargv); + zval_dtor(&zargv); to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); zend_update_property_long( From c190fc8eb8b5c661be03399eb6cef8c6f0bd725f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 20 Mar 2024 10:36:06 +0800 Subject: [PATCH 005/103] optimize code, add more methods for thread --- examples/thread/co.php | 4 +- examples/thread/mt.php | 4 +- ext-src/stubs/php_swoole_thread.stub.php | 5 ++- ext-src/stubs/php_swoole_thread_arginfo.h | 8 +++- ext-src/swoole_thread.cc | 45 ++++++++++++++++++----- 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/examples/thread/co.php b/examples/thread/co.php index e806ad76436..8fcb5f0c0f9 100644 --- a/examples/thread/co.php +++ b/examples/thread/co.php @@ -1,6 +1,6 @@ id); var_dump($t2->id); diff --git a/examples/thread/mt.php b/examples/thread/mt.php index f76442532f1..45c788032c7 100644 --- a/examples/thread/mt.php +++ b/examples/thread/mt.php @@ -1,11 +1,13 @@ join(); } diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index dca455904e8..f0309258259 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -5,7 +5,10 @@ class Thread { private function __construct() {} public function join(): bool {} - public static function run(string $script_file, mixed ...$args): Thread {} + public function joinable(): bool {} + public function detach(): bool {} + + public static function exec(string $script_file, mixed ...$args): Thread {} public static function getArguments(): array {} public static function getId(): int {} } diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index a7b1d53edfd..f53c21d7540 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 557f0cd02152d61e4b5b697d3c95e3ae9bf494f0 */ + * Stub hash: d728269b4bc73add11a02b17484d4cdb1d5cb368 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -7,7 +7,11 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_join, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Thread_run, 0, 1, Swoole\\Thread, 0) +#define arginfo_class_Swoole_Thread_joinable arginfo_class_Swoole_Thread_join + +#define arginfo_class_Swoole_Thread_detach arginfo_class_Swoole_Thread_join + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Swoole_Thread_exec, 0, 1, Swoole\\Thread, 0) ZEND_ARG_TYPE_INFO(0, script_file, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 44bb7a2be70..0e2942313da 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -16,6 +16,8 @@ #include "php_swoole_cxx.h" +#ifdef SW_THREAD + #include #include @@ -66,7 +68,9 @@ static zend_object *php_swoole_thread_create_object(zend_class_entry *ce) { SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_thread, __construct); static PHP_METHOD(swoole_thread, join); -static PHP_METHOD(swoole_thread, run); +static PHP_METHOD(swoole_thread, joinable); +static PHP_METHOD(swoole_thread, detach); +static PHP_METHOD(swoole_thread, exec); static PHP_METHOD(swoole_thread, getArguments); static PHP_METHOD(swoole_thread, getId); SW_EXTERN_C_END @@ -76,7 +80,9 @@ static const zend_function_entry swoole_thread_methods[] = { PHP_ME(swoole_thread, __construct, arginfo_class_Swoole_Thread___construct, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread, join, arginfo_class_Swoole_Thread_join, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread, run, arginfo_class_Swoole_Thread_run, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, joinable, arginfo_class_Swoole_Thread_joinable, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, detach, arginfo_class_Swoole_Thread_detach, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread, exec, arginfo_class_Swoole_Thread_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getArguments, arginfo_class_Swoole_Thread_getArguments, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END @@ -98,13 +104,32 @@ static PHP_METHOD(swoole_thread, __construct) {} static PHP_METHOD(swoole_thread, join) { ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(ZEND_THIS)); - if (to == nullptr || !to->thread->joinable()) { + if (!to || !to->thread || !to->thread->joinable()) { RETURN_FALSE; } php_swoole_thread_join(Z_OBJ_P(ZEND_THIS)); RETURN_TRUE; } +static PHP_METHOD(swoole_thread, joinable) { + ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (to == nullptr || !to->thread) { + RETURN_FALSE; + } + RETURN_BOOL(to->thread->joinable()); +} + +static PHP_METHOD(swoole_thread, detach) { + ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (to == nullptr || !to->thread) { + RETURN_FALSE; + } + to->thread->detach(); + delete to->thread; + to->thread = nullptr; + RETURN_TRUE; +} + static PHP_METHOD(swoole_thread, getArguments) { RETURN_ZVAL(&thread_argv, 1, 0); } @@ -182,25 +207,25 @@ void php_swoole_thread_start(const std::string &file, const std::string &argv) { ts_free_thread(); } -static PHP_METHOD(swoole_thread, run) { - char *execfile = nullptr; - size_t execfile_len = 0; +static PHP_METHOD(swoole_thread, exec) { + char *script_file = nullptr; + size_t l_script_file = 0; zval *args; int argc; ZEND_PARSE_PARAMETERS_START(1, -1) - Z_PARAM_STRING(execfile, execfile_len) + Z_PARAM_STRING(script_file, l_script_file) Z_PARAM_VARIADIC('+', args, argc) ZEND_PARSE_PARAMETERS_END(); - if (execfile_len < 1) { + if (l_script_file < 1) { php_swoole_fatal_error(E_WARNING, "exec file name is empty"); RETURN_FALSE; } object_init_ex(return_value, swoole_thread_ce); ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(return_value)); - std::string file(execfile, execfile_len); + std::string file(script_file, l_script_file); zval zargv; array_init(&zargv); @@ -214,3 +239,5 @@ static PHP_METHOD(swoole_thread, run) { zend_update_property_long( swoole_thread_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("id"), to->thread->native_handle()); } + +#endif From 4ad6b361c765b2a744aa27a5256c668337cbd695 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 20 Mar 2024 16:35:15 +0800 Subject: [PATCH 006/103] Swoole\Thread\Map --- examples/thread/co.php | 13 +- examples/thread/mt.php | 18 +- ext-src/stubs/php_swoole_thread.stub.php | 21 ++ ext-src/stubs/php_swoole_thread_arginfo.h | 42 ++- ext-src/swoole_process.cc | 2 +- ext-src/swoole_thread.cc | 438 +++++++++++++++++++++- 6 files changed, 500 insertions(+), 34 deletions(-) diff --git a/examples/thread/co.php b/examples/thread/co.php index 8fcb5f0c0f9..41b5b571ace 100644 --- a/examples/thread/co.php +++ b/examples/thread/co.php @@ -1,10 +1,15 @@ id); -var_dump($t2->id); +$t1 = Swoole\Thread::exec('mt.php', 'thread-1', PHP_OS, $map); +$t2 = Swoole\Thread::exec('mt.php', 'thread-2', PHP_OS, $map); + +//var_dump($t1->id); +//var_dump($t2->id); +echo Swoole\Thread::getId() . "\t" . 'gmap[uuid]' . "\t" . $map['uuid'] . "\n"; $t1->join(); $t2->join(); + diff --git a/examples/thread/mt.php b/examples/thread/mt.php index 45c788032c7..c62da2ed28b 100644 --- a/examples/thread/mt.php +++ b/examples/thread/mt.php @@ -1,15 +1,13 @@ join(); -} +//if ($args[0] == 'thread-2') { +// $t3 = Swoole\Thread::exec('mt.php', 'thread-3', PHP_OS); +// $t3->join(); +//} sleep(5); -echo "end\n"; +//echo "end\n"; diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index f0309258259..8ffd75cadfe 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -13,3 +13,24 @@ public static function getArguments(): array {} public static function getId(): int {} } } + +namespace Swoole\Thread { + class Map implements ArrayAccess, Countable { + public function __construct(int $key_type) {} + public function offsetGet(mixed $key): mixed {} + public function offsetExists(mixed $key): bool {} + public function offsetSet(mixed $key, mixed $value): void {} + public function offsetUnset(mixed $key): void {} + public function count(): int {} + public function __wakeup(): void {} + } + class ArrayList implements ArrayAccess, Countable { + public function __construct() {} + public function offsetGet(mixed $key): mixed {} + public function offsetExists(mixed $key): bool {} + public function offsetSet(mixed $key, mixed $value): void {} + public function offsetUnset(mixed $key): void {} + public function count(): int {} + public function __wakeup(): void {} + } +} diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index f53c21d7540..45e8bda2989 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d728269b4bc73add11a02b17484d4cdb1d5cb368 */ + * Stub hash: 741bd3e100efcd9ae4660772b142f1fbd7d5da80 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -21,3 +21,43 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getId, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_Map___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, key_type, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_offsetGet, 0, 1, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_offsetExists, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_offsetSet, 0, 2, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_offsetUnset, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Swoole_Thread_Map_count arginfo_class_Swoole_Thread_getId + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map___wakeup, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Swoole_Thread_ArrayList___construct arginfo_class_Swoole_Thread___construct + +#define arginfo_class_Swoole_Thread_ArrayList_offsetGet arginfo_class_Swoole_Thread_Map_offsetGet + +#define arginfo_class_Swoole_Thread_ArrayList_offsetExists arginfo_class_Swoole_Thread_Map_offsetExists + +#define arginfo_class_Swoole_Thread_ArrayList_offsetSet arginfo_class_Swoole_Thread_Map_offsetSet + +#define arginfo_class_Swoole_Thread_ArrayList_offsetUnset arginfo_class_Swoole_Thread_Map_offsetUnset + +#define arginfo_class_Swoole_Thread_ArrayList_count arginfo_class_Swoole_Thread_getId + +#define arginfo_class_Swoole_Thread_ArrayList___wakeup arginfo_class_Swoole_Thread_Map___wakeup diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 77543fc84a7..830ad8f9ab5 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -52,7 +52,7 @@ Worker *php_swoole_process_get_worker(zval *zobject) { Worker *php_swoole_process_get_and_check_worker(zval *zobject) { Worker *worker = php_swoole_process_get_worker(zobject); if (!worker) { - php_swoole_fatal_error(E_ERROR, "you must call Process constructor first"); + php_swoole_fatal_error(E_ERROR, "must call constructor first"); } return worker; } diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 0e2942313da..08d7fb76187 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -23,26 +23,181 @@ #include +#include "swoole_lock.h" + BEGIN_EXTERN_C() #include "stubs/php_swoole_thread_arginfo.h" END_EXTERN_C() -using namespace swoole; +using swoole::RWLock; zend_class_entry *swoole_thread_ce; static zend_object_handlers swoole_thread_handlers; + +zend_class_entry *swoole_thread_map_ce; +static zend_object_handlers swoole_thread_map_handlers; + +struct ThreadValue { + uint32_t type; + uint32_t length; + union { + char *strval; + zend_long lval; + double dval; + }; + + ThreadValue(zval *zvalue) { + store(zvalue); + } + + void store(zval *zvalue) { + type = Z_TYPE_P(zvalue); + switch (type) { + case IS_LONG: + lval = zval_get_long(zvalue); + break; + case IS_DOUBLE: + dval = zval_get_double(zvalue); + break; + case IS_STRING: { + strval = swoole_strndup(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); + length = Z_STRLEN_P(zvalue); + break; + } + default: + type = IS_LONG; + lval = 0; + break; + } + } + + void fetch(zval *return_value) { + switch (type) { + case IS_LONG: + RETVAL_LONG(lval); + break; + case IS_DOUBLE: + RETVAL_LONG(dval); + break; + case IS_STRING: + RETVAL_STRINGL(strval, length); + break; + default: + RETVAL_NULL(); + break; + } + } + + void release() { + if (type == IS_STRING) { + sw_free(strval); + } + } +}; + +struct Map { + RWLock lock_; + uint32_t ref_count; + uint8_t type_; + + enum KeyType { + KEY_INT = 1, + KEY_STR = 2, + }; + + Map(uint8_t type) : lock_(0) { + ref_count = 1; + type_ = type; + } + + uint32_t add_ref() { + return ++ref_count; + } + + uint32_t del_ref() { + return --ref_count; + } +}; + +struct MapIntKey : public Map { + std::unordered_map map_; + + MapIntKey() : Map(KEY_INT) {} +}; + +struct MapStrKey : public Map { + std::unordered_map map_; + + MapStrKey() : Map(KEY_STR) {} +}; + typedef std::thread Thread; -thread_local zval thread_argv; + +struct MapHandlers { + void (*offsetGet)(void *resource, zval *zkey, zval *return_value); + void (*offsetExists)(void *resource, zval *zkey, zval *return_value); + void (*offsetUnset)(void *resource, zval *zkey); + void (*offsetSet)(void *resource, zval *zkey, zval *zvalue); + void (*count)(void *resource, zval *return_value); +}; struct ThreadObject { Thread *thread; zend_object std; }; +struct ThreadMapObject { + MapHandlers *handlers; + void *resource; + zend_object std; +}; + +static void php_swoole_thread_join(zend_object *object); +static void thread_map_strkey_offsetGet(void *resource, zval *zkey, zval *return_value); +static void thread_map_strkey_offsetExists(void *resource, zval *zkey, zval *return_value); +static void thread_map_strkey_offsetUnset(void *resource, zval *zkey); +static void thread_map_strkey_offsetSet(void *resource, zval *zkey, zval *zvalue); +static void thread_map_intkey_offsetGet(void *resource, zval *zkey, zval *return_value); +static void thread_map_intkey_offsetExists(void *resource, zval *zkey, zval *return_value); +static void thread_map_intkey_offsetUnset(void *resource, zval *zkey); +static void thread_map_intkey_offsetSet(void *resource, zval *zkey, zval *zvalue); + +thread_local zval thread_argv; +static std::mutex thread_lock; +static zend_long thread_resource_id = 0; +static std::unordered_map thread_resources; + +MapHandlers map_strkey_handlers = { + thread_map_strkey_offsetGet, + thread_map_strkey_offsetExists, + thread_map_strkey_offsetUnset, + thread_map_strkey_offsetSet, +}; + +MapHandlers map_intkey_handlers = { + thread_map_intkey_offsetGet, + thread_map_intkey_offsetExists, + thread_map_intkey_offsetUnset, + thread_map_intkey_offsetSet, +}; + static sw_inline ThreadObject *php_swoole_thread_fetch_object(zend_object *obj) { return (ThreadObject *) ((char *) obj - swoole_thread_handlers.offset); } +static void php_swoole_thread_free_object(zend_object *object) { + php_swoole_thread_join(object); + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_thread_create_object(zend_class_entry *ce) { + ThreadObject *to = (ThreadObject *) zend_object_alloc(sizeof(ThreadObject), ce); + zend_object_std_init(&to->std, ce); + object_properties_init(&to->std, ce); + to->std.handlers = &swoole_thread_handlers; + return &to->std; +} + static void php_swoole_thread_join(zend_object *object) { ThreadObject *to = php_swoole_thread_fetch_object(object); if (to->thread && to->thread->joinable()) { @@ -52,17 +207,127 @@ static void php_swoole_thread_join(zend_object *object) { } } -static void php_swoole_thread_free_object(zend_object *object) { - php_swoole_thread_join(object); +static sw_inline ThreadMapObject *thread_map_fetch_object(zend_object *obj) { + return (ThreadMapObject *) ((char *) obj - swoole_thread_map_handlers.offset); +} + +static sw_inline zend_long thread_map_get_resource_id(zend_object *obj) { + zval rv, *property = zend_read_property(swoole_thread_map_ce, obj, ZEND_STRL("id"), 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static sw_inline zend_long thread_map_get_resource_id(zval *zobject) { + return thread_map_get_resource_id(Z_OBJ_P(zobject)); +} + +static void thread_map_free_object(zend_object *object) { + zend_long resource_id = thread_map_get_resource_id(object); + ThreadMapObject *mo = thread_map_fetch_object(object); + if (mo->resource) { + Map *map = (Map *) mo->resource; + thread_lock.lock(); + if (map->del_ref() == 0) { + thread_resources.erase(resource_id); + delete map; + mo->resource = nullptr; + } + thread_lock.unlock(); + } zend_object_std_dtor(object); } -static zend_object *php_swoole_thread_create_object(zend_class_entry *ce) { - ThreadObject *to = (ThreadObject *) zend_object_alloc(sizeof(ThreadObject), ce); - zend_object_std_init(&to->std, ce); - object_properties_init(&to->std, ce); - to->std.handlers = &swoole_thread_handlers; - return &to->std; +static zend_object *thread_map_create_object(zend_class_entry *ce) { + ThreadMapObject *mo = (ThreadMapObject *) zend_object_alloc(sizeof(ThreadMapObject), ce); + zend_object_std_init(&mo->std, ce); + object_properties_init(&mo->std, ce); + mo->std.handlers = &swoole_thread_map_handlers; + return &mo->std; +} + +ThreadMapObject *thread_map_fetch_object_check(zval *zobject) { + ThreadMapObject *map = thread_map_fetch_object(Z_OBJ_P(zobject)); + if (!map->resource) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return map; +} + +static void thread_map_strkey_offsetGet(void *resource, zval *zkey, zval *return_value) { + MapStrKey *map = (MapStrKey *) resource; + zend::String skey(zkey); + map->lock_.lock_rd(); + auto iter = map->map_.find(skey.to_std_string()); + if (iter != map->map_.end()) { + iter->second.fetch(return_value); + } + map->lock_.unlock(); +} + +static void thread_map_strkey_offsetExists(void *resource, zval *zkey, zval *return_value) { + MapStrKey *map = (MapStrKey *) resource; + zend::String skey(zkey); + map->lock_.lock_rd(); + RETVAL_BOOL(map->map_.find(skey.to_std_string()) != map->map_.end()); + map->lock_.unlock(); +} + +static void thread_map_strkey_offsetUnset(void *resource, zval *zkey) { + MapStrKey *map = (MapStrKey *) resource; + zend::String skey(zkey); + map->lock_.lock(); + auto iter = map->map_.find(skey.to_std_string()); + if (iter != map->map_.end()) { + iter->second.release(); + map->map_.erase(iter); + } + map->lock_.unlock(); +} + +static void thread_map_strkey_offsetSet(void *resource, zval *zkey, zval *zvalue) { + MapStrKey *map = (MapStrKey *) resource; + zend::String skey(zkey); + map->lock_.lock(); + map->map_.emplace(skey.to_std_string(), ThreadValue(zvalue)); + map->lock_.unlock(); +} + +static void thread_map_intkey_offsetGet(void *resource, zval *zkey, zval *return_value) { + MapIntKey *map = (MapIntKey *) resource; + zend_long lkey = zval_get_long(zkey); + map->lock_.lock_rd(); + auto iter = map->map_.find(lkey); + if (iter != map->map_.end()) { + iter->second.fetch(return_value); + } + map->lock_.unlock(); +} + +static void thread_map_intkey_offsetExists(void *resource, zval *zkey, zval *return_value) { + MapIntKey *map = (MapIntKey *) resource; + zend_long lkey = zval_get_long(zkey); + map->lock_.lock_rd(); + RETVAL_BOOL(map->map_.find(lkey) != map->map_.end()); + map->lock_.unlock(); +} + +static void thread_map_intkey_offsetUnset(void *resource, zval *zkey) { + MapIntKey *map = (MapIntKey *) resource; + zend_long lkey = zval_get_long(zkey); + map->lock_.lock(); + auto iter = map->map_.find(lkey); + if (iter != map->map_.end()) { + iter->second.release(); + map->map_.erase(iter); + } + map->lock_.unlock(); +} + +static void thread_map_intkey_offsetSet(void *resource, zval *zkey, zval *zvalue) { + MapIntKey *map = (MapIntKey *) resource; + zend_long lkey = zval_get_long(zkey); + map->lock_.lock(); + map->map_.emplace(lkey, ThreadValue(zvalue)); + map->lock_.unlock(); } SW_EXTERN_C_BEGIN @@ -73,11 +338,19 @@ static PHP_METHOD(swoole_thread, detach); static PHP_METHOD(swoole_thread, exec); static PHP_METHOD(swoole_thread, getArguments); static PHP_METHOD(swoole_thread, getId); + +static PHP_METHOD(swoole_thread_map, __construct); +static PHP_METHOD(swoole_thread_map, offsetGet); +static PHP_METHOD(swoole_thread_map, offsetExists); +static PHP_METHOD(swoole_thread_map, offsetSet); +static PHP_METHOD(swoole_thread_map, offsetUnset); +static PHP_METHOD(swoole_thread_map, count); +static PHP_METHOD(swoole_thread_map, __wakeup); + SW_EXTERN_C_END // clang-format off -static const zend_function_entry swoole_thread_methods[] = -{ +static const zend_function_entry swoole_thread_methods[] = { PHP_ME(swoole_thread, __construct, arginfo_class_Swoole_Thread___construct, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread, join, arginfo_class_Swoole_Thread_join, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread, joinable, arginfo_class_Swoole_Thread_joinable, ZEND_ACC_PUBLIC) @@ -87,6 +360,17 @@ static const zend_function_entry swoole_thread_methods[] = PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; + +static const zend_function_entry swoole_thread_map_methods[] = { + PHP_ME(swoole_thread_map, __construct, arginfo_class_Swoole_Thread_Map___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetGet, arginfo_class_Swoole_Thread_Map_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetExists, arginfo_class_Swoole_Thread_Map_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetSet, arginfo_class_Swoole_Thread_Map_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetUnset, arginfo_class_Swoole_Thread_Map_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, count, arginfo_class_Swoole_Thread_Map_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, __wakeup, arginfo_class_Swoole_Thread_Map___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; // clang-format on void php_swoole_thread_minit(int module_number) { @@ -97,7 +381,22 @@ void php_swoole_thread_minit(int module_number) { SW_SET_CLASS_CUSTOM_OBJECT( swoole_thread, php_swoole_thread_create_object, php_swoole_thread_free_object, ThreadObject, std); - zend_declare_property_null(swoole_thread_ce, ZEND_STRL("id"), ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_thread_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + zend_declare_class_constant_long( + swoole_thread_ce, ZEND_STRL("HARDWARE_CONCURRENCY"), std::thread::hardware_concurrency()); + + SW_INIT_CLASS_ENTRY(swoole_thread_map, "Swoole\\Thread\\Map", nullptr, swoole_thread_map_methods); + SW_SET_CLASS_CLONEABLE(swoole_thread_map, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_map, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_map, thread_map_create_object, thread_map_free_object, ThreadMapObject, std); + + zend_class_implements(swoole_thread_map_ce, 2, zend_ce_arrayaccess, zend_ce_countable); + + zend_declare_class_constant_long(swoole_thread_map_ce, ZEND_STRL("KEY_INT"), 1); + zend_declare_class_constant_long(swoole_thread_map_ce, ZEND_STRL("KEY_STRING"), 1); + + zend_declare_property_long(swoole_thread_map_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); } static PHP_METHOD(swoole_thread, __construct) {} @@ -131,6 +430,9 @@ static PHP_METHOD(swoole_thread, detach) { } static PHP_METHOD(swoole_thread, getArguments) { + if (!ZVAL_IS_ARRAY(&thread_argv)) { + array_init(&thread_argv); + } RETURN_ZVAL(&thread_argv, 1, 0); } @@ -170,7 +472,9 @@ bool php_swoole_thread_unserialize(const std::string &data, zval *zv) { return unserialized; } -void php_swoole_thread_rshutdown() {} +void php_swoole_thread_rshutdown() { + zval_dtor(&thread_argv); +} void php_swoole_thread_start(const std::string &file, const std::string &argv) { ts_resource(0); @@ -198,8 +502,6 @@ void php_swoole_thread_start(const std::string &file, const std::string &argv) { zend_end_try(); zend_destroy_file_handle(&file_handle); - zval_dtor(&thread_argv); - php_request_shutdown(NULL); file_handle.filename = NULL; @@ -208,8 +510,8 @@ void php_swoole_thread_start(const std::string &file, const std::string &argv) { } static PHP_METHOD(swoole_thread, exec) { - char *script_file = nullptr; - size_t l_script_file = 0; + char *script_file; + size_t l_script_file; zval *args; int argc; @@ -240,4 +542,104 @@ static PHP_METHOD(swoole_thread, exec) { swoole_thread_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("id"), to->thread->native_handle()); } +static PHP_METHOD(swoole_thread_map, __construct) { + zend_long key_type; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(key_type) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (key_type == Map::KEY_INT) { + mo->resource = new MapIntKey(); + mo->handlers = &map_intkey_handlers; + } else { + mo->resource = new MapStrKey(); + mo->handlers = &map_strkey_handlers; + } + + thread_lock.lock(); + zend_long resource_id = ++thread_resource_id; + thread_resources[resource_id] = mo->resource; + thread_lock.unlock(); + + zend_update_property_long(swoole_thread_map_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +} + +static PHP_METHOD(swoole_thread_map, offsetGet) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->handlers->offsetGet(mo->resource, zkey, return_value); +} + +static PHP_METHOD(swoole_thread_map, offsetExists) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->handlers->offsetExists(mo->resource, zkey, return_value); +} + +static PHP_METHOD(swoole_thread_map, offsetSet) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->handlers->offsetSet(mo->resource, zkey, zvalue); +} + +static PHP_METHOD(swoole_thread_map, offsetUnset) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->handlers->offsetUnset(mo->resource, zkey); +} + +static PHP_METHOD(swoole_thread_map, count) { + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->handlers->count(mo->resource, return_value); +} + +static PHP_METHOD(swoole_thread_map, __wakeup) { + auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); + bool success = false; + zend_long resource_id = thread_map_get_resource_id(ZEND_THIS); + + thread_lock.lock(); + auto iter = thread_resources.find(resource_id); + if (iter != thread_resources.end()) { + Map *map = (Map *) iter->second; + map->add_ref(); + if (map->type_ == Map::KEY_INT) { + mo->handlers = &map_intkey_handlers; + } else { + mo->handlers = &map_strkey_handlers; + } + mo->resource = map; + success = true; + } + thread_lock.unlock(); + + if (!success) { + zend_throw_exception(swoole_exception_ce, "resource not found", -2); + } +} + #endif From ef5fb06c62fd72aa7294fbea8e96330cfc807827 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 20 Mar 2024 17:54:08 +0800 Subject: [PATCH 007/103] Swoole\Thread\ArrayList --- examples/thread/co.php | 20 ++- examples/thread/mt.php | 10 +- ext-src/php_swoole.cc | 2 +- ext-src/php_swoole_coroutine.h | 5 + ext-src/php_swoole_private.h | 4 +- ext-src/swoole_coroutine.cc | 1 + ext-src/swoole_thread.cc | 296 ++++++++++++++++++++++++++++++--- 7 files changed, 313 insertions(+), 25 deletions(-) diff --git a/examples/thread/co.php b/examples/thread/co.php index 41b5b571ace..f02b949a427 100644 --- a/examples/thread/co.php +++ b/examples/thread/co.php @@ -2,13 +2,29 @@ $map = new Swoole\Thread\Map(2); $map['uuid'] = uniqid(); -$t1 = Swoole\Thread::exec('mt.php', 'thread-1', PHP_OS, $map); -$t2 = Swoole\Thread::exec('mt.php', 'thread-2', PHP_OS, $map); +$list = new Swoole\Thread\ArrayList(); +$list[] = base64_encode(random_bytes(32)); +$list[1] = uniqid(); + +$t1 = Swoole\Thread::exec('mt.php', 'thread-1', PHP_OS, $map, $list); +$t2 = Swoole\Thread::exec('mt.php', 'thread-2', PHP_OS, $map, $list); //var_dump($t1->id); //var_dump($t2->id); echo Swoole\Thread::getId() . "\t" . 'gmap[uuid]' . "\t" . $map['uuid'] . "\n"; +try { + var_dump($list[999]); +} catch (Swoole\Exception $e) { + assert(str_contains($e->getMessage(), 'out of range')); +} + +try { + unset($list[0]); +} catch (Swoole\Exception $e) { + assert(str_contains($e->getMessage(), 'unsupported')); +} + $t1->join(); $t2->join(); diff --git a/examples/thread/mt.php b/examples/thread/mt.php index c62da2ed28b..63fbe67e51e 100644 --- a/examples/thread/mt.php +++ b/examples/thread/mt.php @@ -3,11 +3,19 @@ $args = Swoole\Thread::getArguments(); echo Swoole\Thread::getId() . "\t" . 'gmap[uuid]' . "\t" . $args[2]['uuid'] . "\n"; +$args[2]['hello'] = uniqid('swoole'); +var_dump(count($args[2])); + +$args[3][] = uniqid('swoole'); +$args[3][count($args[3])] = uniqid('php'); + +echo Swoole\Thread::getId() . "\t" . 'glist[0]' . "\t" . $args[3][0] . "\n"; +var_dump(count($args[3])); //if ($args[0] == 'thread-2') { // $t3 = Swoole\Thread::exec('mt.php', 'thread-3', PHP_OS); // $t3->join(); //} -sleep(5); +//sleep(5); //echo "end\n"; diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 933da718ab4..1c08779119a 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -829,7 +829,7 @@ PHP_MINFO_FUNCTION(swoole) { #ifdef HAVE_KQUEUE php_info_print_table_row(2, "kqueue", "enabled"); #endif -#ifdef ZTS +#ifdef SW_THREAD php_info_print_table_row(2, "thread", "enabled"); #endif #ifdef HAVE_SIGNALFD diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index 6f6e1c98add..a0aa09bcec3 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -260,6 +260,11 @@ class PHPCoroutine { save_context(&main_context); } + static inline void free_main_context() { + delete main_context.co; + main_context = {}; + } + protected: static SW_THREAD_LOCAL bool activated; static SW_THREAD_LOCAL PHPContext main_context; diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 197dd9b01f6..f5fb192eae2 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -270,7 +270,7 @@ void php_swoole_http_server_coro_minit(int module_number); void php_swoole_websocket_server_minit(int module_number); void php_swoole_redis_server_minit(int module_number); void php_swoole_name_resolver_minit(int module_number); -#ifdef ZTS +#ifdef SW_THREAD void php_swoole_thread_minit(int module_number); #endif @@ -297,7 +297,7 @@ void php_swoole_process_rshutdown(); void php_swoole_coroutine_scheduler_rshutdown(); void php_swoole_runtime_rshutdown(); void php_swoole_server_rshutdown(); -#ifdef ZTS +#ifdef SW_THREAD void php_swoole_thread_rshutdown(); #endif diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 6d1d6b9ac8e..a8a1dfbfdf2 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -413,6 +413,7 @@ void PHPCoroutine::shutdown() { zend_array_destroy(options); options = nullptr; } + free_main_context(); } void PHPCoroutine::deadlock_check() { diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 08d7fb76187..9598112d71e 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -37,6 +37,9 @@ static zend_object_handlers swoole_thread_handlers; zend_class_entry *swoole_thread_map_ce; static zend_object_handlers swoole_thread_map_handlers; +zend_class_entry *swoole_thread_arraylist_ce; +static zend_object_handlers swoole_thread_arraylist_handlers; + struct ThreadValue { uint32_t type; uint32_t length; @@ -71,6 +74,11 @@ struct ThreadValue { } } + void assign(zval *zvalue) { + release(); + store(zvalue); + } + void fetch(zval *return_value) { switch (type) { case IS_LONG: @@ -95,19 +103,12 @@ struct ThreadValue { } }; -struct Map { +struct Resource { RWLock lock_; uint32_t ref_count; - uint8_t type_; - - enum KeyType { - KEY_INT = 1, - KEY_STR = 2, - }; - Map(uint8_t type) : lock_(0) { + Resource() : lock_(0) { ref_count = 1; - type_ = type; } uint32_t add_ref() { @@ -119,16 +120,57 @@ struct Map { } }; +struct Map : public Resource { + uint8_t type_; + + enum KeyType { + KEY_INT = 1, + KEY_STR = 2, + }; + + Map(uint8_t type) : Resource() { + type_ = type; + } +}; + struct MapIntKey : public Map { std::unordered_map map_; MapIntKey() : Map(KEY_INT) {} + + ~MapIntKey() { + for (auto &iter : map_) { + iter.second.release(); + } + } }; struct MapStrKey : public Map { std::unordered_map map_; MapStrKey() : Map(KEY_STR) {} + + ~MapStrKey() { + for (auto &iter : map_) { + iter.second.release(); + } + } +}; + +struct ArrayList : public Resource { + std::vector list_; + + ArrayList() : Resource() {} + + ~ArrayList() { + for (auto &value : list_) { + value.release(); + } + } + + bool exists(zend_long index) { + return index < (zend_long) list_.size(); + } }; typedef std::thread Thread; @@ -152,33 +194,43 @@ struct ThreadMapObject { zend_object std; }; +struct ThreadArrayListObject { + ArrayList *resource; + zend_object std; +}; + static void php_swoole_thread_join(zend_object *object); static void thread_map_strkey_offsetGet(void *resource, zval *zkey, zval *return_value); static void thread_map_strkey_offsetExists(void *resource, zval *zkey, zval *return_value); static void thread_map_strkey_offsetUnset(void *resource, zval *zkey); static void thread_map_strkey_offsetSet(void *resource, zval *zkey, zval *zvalue); +static void thread_map_strkey_count(void *resource, zval *return_value); + static void thread_map_intkey_offsetGet(void *resource, zval *zkey, zval *return_value); static void thread_map_intkey_offsetExists(void *resource, zval *zkey, zval *return_value); static void thread_map_intkey_offsetUnset(void *resource, zval *zkey); static void thread_map_intkey_offsetSet(void *resource, zval *zkey, zval *zvalue); +static void thread_map_intkey_count(void *resource, zval *return_value); thread_local zval thread_argv; static std::mutex thread_lock; static zend_long thread_resource_id = 0; static std::unordered_map thread_resources; -MapHandlers map_strkey_handlers = { - thread_map_strkey_offsetGet, - thread_map_strkey_offsetExists, - thread_map_strkey_offsetUnset, - thread_map_strkey_offsetSet, -}; - MapHandlers map_intkey_handlers = { thread_map_intkey_offsetGet, thread_map_intkey_offsetExists, thread_map_intkey_offsetUnset, thread_map_intkey_offsetSet, + thread_map_intkey_count, +}; + +MapHandlers map_strkey_handlers = { + thread_map_strkey_offsetGet, + thread_map_strkey_offsetExists, + thread_map_strkey_offsetUnset, + thread_map_strkey_offsetSet, + thread_map_strkey_count, }; static sw_inline ThreadObject *php_swoole_thread_fetch_object(zend_object *obj) { @@ -228,7 +280,13 @@ static void thread_map_free_object(zend_object *object) { thread_lock.lock(); if (map->del_ref() == 0) { thread_resources.erase(resource_id); - delete map; + if (map->type_ == Map::KEY_INT) { + auto map_intkey = (MapIntKey *) map; + delete map_intkey; + } else { + auto map_strkey = (MapStrKey *) map; + delete map_strkey; + } mo->resource = nullptr; } thread_lock.unlock(); @@ -287,7 +345,19 @@ static void thread_map_strkey_offsetSet(void *resource, zval *zkey, zval *zvalue MapStrKey *map = (MapStrKey *) resource; zend::String skey(zkey); map->lock_.lock(); - map->map_.emplace(skey.to_std_string(), ThreadValue(zvalue)); + auto iter = map->map_.find(skey.to_std_string()); + if (iter != map->map_.end()) { + iter->second.assign(zvalue); + } else { + map->map_.emplace(skey.to_std_string(), ThreadValue(zvalue)); + } + map->lock_.unlock(); +} + +static void thread_map_strkey_count(void *resource, zval *return_value) { + MapStrKey *map = (MapStrKey *) resource; + map->lock_.lock_rd(); + RETVAL_LONG(map->map_.size()); map->lock_.unlock(); } @@ -326,10 +396,66 @@ static void thread_map_intkey_offsetSet(void *resource, zval *zkey, zval *zvalue MapIntKey *map = (MapIntKey *) resource; zend_long lkey = zval_get_long(zkey); map->lock_.lock(); - map->map_.emplace(lkey, ThreadValue(zvalue)); + auto iter = map->map_.find(lkey); + if (iter != map->map_.end()) { + iter->second.assign(zvalue); + } else { + map->map_.emplace(lkey, ThreadValue(zvalue)); + } map->lock_.unlock(); } +static void thread_map_intkey_count(void *resource, zval *return_value) { + MapIntKey *map = (MapIntKey *) resource; + map->lock_.lock_rd(); + RETVAL_LONG(map->map_.size()); + map->lock_.unlock(); +} + +static sw_inline ThreadArrayListObject *thread_arraylist_fetch_object(zend_object *obj) { + return (ThreadArrayListObject *) ((char *) obj - swoole_thread_arraylist_handlers.offset); +} + +static sw_inline zend_long thread_arraylist_get_resource_id(zend_object *obj) { + zval rv, *property = zend_read_property(swoole_thread_arraylist_ce, obj, ZEND_STRL("id"), 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static sw_inline zend_long thread_arraylist_get_resource_id(zval *zobject) { + return thread_arraylist_get_resource_id(Z_OBJ_P(zobject)); +} + +static void thread_arraylist_free_object(zend_object *object) { + zend_long resource_id = thread_arraylist_get_resource_id(object); + ThreadArrayListObject *ao = thread_arraylist_fetch_object(object); + if (ao->resource) { + thread_lock.lock(); + if (ao->resource->del_ref() == 0) { + thread_resources.erase(resource_id); + delete ao->resource; + ao->resource = nullptr; + } + thread_lock.unlock(); + } + zend_object_std_dtor(object); +} + +static zend_object *thread_arraylist_create_object(zend_class_entry *ce) { + ThreadArrayListObject *ao = (ThreadArrayListObject *) zend_object_alloc(sizeof(ThreadArrayListObject), ce); + zend_object_std_init(&ao->std, ce); + object_properties_init(&ao->std, ce); + ao->std.handlers = &swoole_thread_arraylist_handlers; + return &ao->std; +} + +ThreadArrayListObject *thread_arraylist_fetch_object_check(zval *zobject) { + ThreadArrayListObject *ao = thread_arraylist_fetch_object(Z_OBJ_P(zobject)); + if (!ao->resource) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return ao; +} + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_thread, __construct); static PHP_METHOD(swoole_thread, join); @@ -347,6 +473,14 @@ static PHP_METHOD(swoole_thread_map, offsetUnset); static PHP_METHOD(swoole_thread_map, count); static PHP_METHOD(swoole_thread_map, __wakeup); +static PHP_METHOD(swoole_thread_arraylist, __construct); +static PHP_METHOD(swoole_thread_arraylist, offsetGet); +static PHP_METHOD(swoole_thread_arraylist, offsetExists); +static PHP_METHOD(swoole_thread_arraylist, offsetSet); +static PHP_METHOD(swoole_thread_arraylist, offsetUnset); +static PHP_METHOD(swoole_thread_arraylist, count); +static PHP_METHOD(swoole_thread_arraylist, __wakeup); + SW_EXTERN_C_END // clang-format off @@ -371,6 +505,17 @@ static const zend_function_entry swoole_thread_map_methods[] = { PHP_ME(swoole_thread_map, __wakeup, arginfo_class_Swoole_Thread_Map___wakeup, ZEND_ACC_PUBLIC) PHP_FE_END }; + +static const zend_function_entry swoole_thread_arraylist_methods[] = { + PHP_ME(swoole_thread_arraylist, __construct, arginfo_class_Swoole_Thread_ArrayList___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetGet, arginfo_class_Swoole_Thread_ArrayList_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetExists, arginfo_class_Swoole_Thread_ArrayList_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetSet, arginfo_class_Swoole_Thread_ArrayList_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetUnset, arginfo_class_Swoole_Thread_ArrayList_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, count, arginfo_class_Swoole_Thread_ArrayList_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, __wakeup, arginfo_class_Swoole_Thread_ArrayList___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; // clang-format on void php_swoole_thread_minit(int module_number) { @@ -397,6 +542,18 @@ void php_swoole_thread_minit(int module_number) { zend_declare_class_constant_long(swoole_thread_map_ce, ZEND_STRL("KEY_STRING"), 1); zend_declare_property_long(swoole_thread_map_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + + SW_INIT_CLASS_ENTRY(swoole_thread_arraylist, "Swoole\\Thread\\ArrayList", nullptr, swoole_thread_arraylist_methods); + SW_SET_CLASS_CLONEABLE(swoole_thread_arraylist, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_arraylist, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_arraylist, + thread_arraylist_create_object, + thread_arraylist_free_object, + ThreadArrayListObject, + std); + + zend_class_implements(swoole_thread_arraylist_ce, 2, zend_ce_arrayaccess, zend_ce_countable); + zend_declare_property_long(swoole_thread_arraylist_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); } static PHP_METHOD(swoole_thread, __construct) {} @@ -642,4 +799,105 @@ static PHP_METHOD(swoole_thread_map, __wakeup) { } } +static PHP_METHOD(swoole_thread_arraylist, __construct) { + ZEND_PARSE_PARAMETERS_NONE(); + + auto ao = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); + ao->resource = new ArrayList(); + + thread_lock.lock(); + zend_long resource_id = ++thread_resource_id; + thread_resources[resource_id] = ao->resource; + thread_lock.unlock(); + + zend_update_property_long(swoole_thread_arraylist_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +} + +static PHP_METHOD(swoole_thread_arraylist, offsetGet) { + zend_long index; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(index) + ZEND_PARSE_PARAMETERS_END(); + + bool out_of_range = true; + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + ao->resource->lock_.lock_rd(); + if (ao->resource->exists(index)) { + out_of_range = false; + ao->resource->list_.at(index).fetch(return_value); + } + ao->resource->lock_.unlock(); + + if (out_of_range) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); + } +} + +static PHP_METHOD(swoole_thread_arraylist, offsetExists) { + zend_long index; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(index) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + ao->resource->lock_.lock_rd(); + RETVAL_BOOL(ao->resource->exists(index)); + ao->resource->lock_.unlock(); +} + +static PHP_METHOD(swoole_thread_arraylist, offsetSet) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + ArrayList *array = ao->resource; + + array->lock_.lock(); + if (ZVAL_IS_NULL(zkey) || (ZVAL_IS_LONG(zkey) && Z_LVAL_P(zkey) == (zend_long) array->list_.size())) { + array->list_.emplace_back(ThreadValue(zvalue)); + } else { + zend_long index = zval_get_long(zkey); + array->list_.at(index).assign(zvalue); + } + array->lock_.unlock(); +} + +static PHP_METHOD(swoole_thread_arraylist, offsetUnset) { + zend_throw_exception(swoole_exception_ce, "unsupported", -3); +} + +static PHP_METHOD(swoole_thread_arraylist, count) { + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + ao->resource->lock_.lock_rd(); + RETVAL_LONG(ao->resource->list_.size()); + ao->resource->lock_.unlock(); +} + +static PHP_METHOD(swoole_thread_arraylist, __wakeup) { + auto mo = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); + bool success = false; + zend_long resource_id = thread_arraylist_get_resource_id(ZEND_THIS); + + thread_lock.lock(); + auto iter = thread_resources.find(resource_id); + if (iter != thread_resources.end()) { + ArrayList *array = (ArrayList *) iter->second; + array->add_ref(); + mo->resource = array; + success = true; + } + thread_lock.unlock(); + + if (!success) { + zend_throw_exception(swoole_exception_ce, "resource not found", -2); + } +} + #endif From a721e6541ff14d0c7df5de9e37db6b480a840de9 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 20 Mar 2024 18:34:26 +0800 Subject: [PATCH 008/103] Optimize --- examples/thread/array.php | 20 ++++++++++++++++++++ examples/thread/benchmark.php | 21 +++++++++++++++++++++ examples/thread/run_test.php | 16 ++++++++++++++++ ext-src/stubs/php_swoole_thread.stub.php | 2 +- ext-src/stubs/php_swoole_thread_arginfo.h | 2 +- ext-src/swoole_thread.cc | 4 ++-- include/swoole_coroutine.h | 2 ++ 7 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 examples/thread/array.php create mode 100644 examples/thread/benchmark.php create mode 100644 examples/thread/run_test.php diff --git a/examples/thread/array.php b/examples/thread/array.php new file mode 100644 index 00000000000..1df20fb66c7 --- /dev/null +++ b/examples/thread/array.php @@ -0,0 +1,20 @@ +join(); +} + diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index 8ffd75cadfe..b3ca4fb9445 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -7,7 +7,7 @@ private function __construct() {} public function join(): bool {} public function joinable(): bool {} public function detach(): bool {} - + public static function exec(string $script_file, mixed ...$args): Thread {} public static function getArguments(): array {} public static function getId(): int {} diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index 45e8bda2989..fdfaab5aac7 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 741bd3e100efcd9ae4660772b142f1fbd7d5da80 */ + * Stub hash: 446102efcc4c1f8abfe4fdcb2be9bc9f2621f749 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 9598112d71e..438b330a728 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -538,8 +538,8 @@ void php_swoole_thread_minit(int module_number) { zend_class_implements(swoole_thread_map_ce, 2, zend_ce_arrayaccess, zend_ce_countable); - zend_declare_class_constant_long(swoole_thread_map_ce, ZEND_STRL("KEY_INT"), 1); - zend_declare_class_constant_long(swoole_thread_map_ce, ZEND_STRL("KEY_STRING"), 1); + zend_declare_class_constant_long(swoole_thread_map_ce, ZEND_STRL("KEY_INT"), Map::KEY_INT); + zend_declare_class_constant_long(swoole_thread_map_ce, ZEND_STRL("KEY_STR"), Map::KEY_STR); zend_declare_property_long(swoole_thread_map_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 6df226241b5..b1ab5fb3dfb 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -43,6 +43,8 @@ typedef std::chrono::microseconds seconds_type; #ifdef SW_THREAD #define SW_THREAD_LOCAL thread_local +#else +#define SW_THREAD_LOCAL #endif namespace swoole { From cc478338d950191ee6c7c2f452ed3b7dde763821 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 20 Mar 2024 20:59:39 +0800 Subject: [PATCH 009/103] zend_array --- examples/thread/test.php | 8 ++ ext-src/swoole_thread.cc | 222 ++++++++++++++++----------------------- 2 files changed, 101 insertions(+), 129 deletions(-) create mode 100644 examples/thread/test.php diff --git a/examples/thread/test.php b/examples/thread/test.php new file mode 100644 index 00000000000..f03a4985f18 --- /dev/null +++ b/examples/thread/test.php @@ -0,0 +1,8 @@ + map_; - - MapIntKey() : Map(KEY_INT) {} - ~MapIntKey() { - for (auto &iter : map_) { - iter.second.release(); - } - } -}; - -struct MapStrKey : public Map { - std::unordered_map map_; - - MapStrKey() : Map(KEY_STR) {} - - ~MapStrKey() { - for (auto &iter : map_) { - iter.second.release(); - } + ~Map() { + zend_hash_destroy(&ht); } }; struct ArrayList : public Resource { - std::vector list_; + std::vector list_; ArrayList() : Resource() {} ~ArrayList() { for (auto &value : list_) { - value.release(); + delete value; } } @@ -176,11 +173,11 @@ struct ArrayList : public Resource { typedef std::thread Thread; struct MapHandlers { - void (*offsetGet)(void *resource, zval *zkey, zval *return_value); - void (*offsetExists)(void *resource, zval *zkey, zval *return_value); - void (*offsetUnset)(void *resource, zval *zkey); - void (*offsetSet)(void *resource, zval *zkey, zval *zvalue); - void (*count)(void *resource, zval *return_value); + void (*offsetGet)(Map *map, zval *zkey, zval *return_value); + void (*offsetExists)(Map *map, zval *zkey, zval *return_value); + void (*offsetUnset)(Map *map, zval *zkey); + void (*offsetSet)(Map *map, zval *zkey, zval *zvalue); + void (*count)(Map *map, zval *return_value); }; struct ThreadObject { @@ -190,7 +187,7 @@ struct ThreadObject { struct ThreadMapObject { MapHandlers *handlers; - void *resource; + Map *map; zend_object std; }; @@ -200,17 +197,17 @@ struct ThreadArrayListObject { }; static void php_swoole_thread_join(zend_object *object); -static void thread_map_strkey_offsetGet(void *resource, zval *zkey, zval *return_value); -static void thread_map_strkey_offsetExists(void *resource, zval *zkey, zval *return_value); -static void thread_map_strkey_offsetUnset(void *resource, zval *zkey); -static void thread_map_strkey_offsetSet(void *resource, zval *zkey, zval *zvalue); -static void thread_map_strkey_count(void *resource, zval *return_value); - -static void thread_map_intkey_offsetGet(void *resource, zval *zkey, zval *return_value); -static void thread_map_intkey_offsetExists(void *resource, zval *zkey, zval *return_value); -static void thread_map_intkey_offsetUnset(void *resource, zval *zkey); -static void thread_map_intkey_offsetSet(void *resource, zval *zkey, zval *zvalue); -static void thread_map_intkey_count(void *resource, zval *return_value); +static void thread_map_strkey_offsetGet(Map *map, zval *zkey, zval *return_value); +static void thread_map_strkey_offsetExists(Map *map, zval *zkey, zval *return_value); +static void thread_map_strkey_offsetUnset(Map *map, zval *zkey); +static void thread_map_strkey_offsetSet(Map *map, zval *zkey, zval *zvalue); + +static void thread_map_intkey_offsetGet(Map *map, zval *zkey, zval *return_value); +static void thread_map_intkey_offsetExists(Map *map, zval *zkey, zval *return_value); +static void thread_map_intkey_offsetUnset(Map *map, zval *zkey); +static void thread_map_intkey_offsetSet(Map *map, zval *zkey, zval *zvalue); + +static void thread_map_count(Map *map, zval *return_value); thread_local zval thread_argv; static std::mutex thread_lock; @@ -222,7 +219,7 @@ MapHandlers map_intkey_handlers = { thread_map_intkey_offsetExists, thread_map_intkey_offsetUnset, thread_map_intkey_offsetSet, - thread_map_intkey_count, + thread_map_count, }; MapHandlers map_strkey_handlers = { @@ -230,7 +227,7 @@ MapHandlers map_strkey_handlers = { thread_map_strkey_offsetExists, thread_map_strkey_offsetUnset, thread_map_strkey_offsetSet, - thread_map_strkey_count, + thread_map_count, }; static sw_inline ThreadObject *php_swoole_thread_fetch_object(zend_object *obj) { @@ -275,19 +272,12 @@ static sw_inline zend_long thread_map_get_resource_id(zval *zobject) { static void thread_map_free_object(zend_object *object) { zend_long resource_id = thread_map_get_resource_id(object); ThreadMapObject *mo = thread_map_fetch_object(object); - if (mo->resource) { - Map *map = (Map *) mo->resource; + if (mo->map) { thread_lock.lock(); - if (map->del_ref() == 0) { + if (mo->map->del_ref() == 0) { thread_resources.erase(resource_id); - if (map->type_ == Map::KEY_INT) { - auto map_intkey = (MapIntKey *) map; - delete map_intkey; - } else { - auto map_strkey = (MapStrKey *) map; - delete map_strkey; - } - mo->resource = nullptr; + delete mo->map; + mo->map = nullptr; } thread_lock.unlock(); } @@ -304,111 +294,80 @@ static zend_object *thread_map_create_object(zend_class_entry *ce) { ThreadMapObject *thread_map_fetch_object_check(zval *zobject) { ThreadMapObject *map = thread_map_fetch_object(Z_OBJ_P(zobject)); - if (!map->resource) { + if (!map->map) { php_swoole_fatal_error(E_ERROR, "must call constructor first"); } return map; } -static void thread_map_strkey_offsetGet(void *resource, zval *zkey, zval *return_value) { - MapStrKey *map = (MapStrKey *) resource; +static void thread_map_strkey_offsetGet(Map *map, zval *zkey, zval *return_value) { zend::String skey(zkey); map->lock_.lock_rd(); - auto iter = map->map_.find(skey.to_std_string()); - if (iter != map->map_.end()) { - iter->second.fetch(return_value); + MapItem *tv = (MapItem *) zend_hash_find_ptr(&map->ht, skey.get()); + if (tv) { + tv->fetch(return_value); } map->lock_.unlock(); } -static void thread_map_strkey_offsetExists(void *resource, zval *zkey, zval *return_value) { - MapStrKey *map = (MapStrKey *) resource; +static void thread_map_strkey_offsetExists(Map *map, zval *zkey, zval *return_value) { zend::String skey(zkey); map->lock_.lock_rd(); - RETVAL_BOOL(map->map_.find(skey.to_std_string()) != map->map_.end()); + RETVAL_BOOL(zend_hash_find_ptr(&map->ht, skey.get()) != NULL); map->lock_.unlock(); } -static void thread_map_strkey_offsetUnset(void *resource, zval *zkey) { - MapStrKey *map = (MapStrKey *) resource; +static void thread_map_strkey_offsetUnset(Map *map, zval *zkey) { zend::String skey(zkey); map->lock_.lock(); - auto iter = map->map_.find(skey.to_std_string()); - if (iter != map->map_.end()) { - iter->second.release(); - map->map_.erase(iter); - } + zend_hash_del(&map->ht, skey.get()); map->lock_.unlock(); } -static void thread_map_strkey_offsetSet(void *resource, zval *zkey, zval *zvalue) { - MapStrKey *map = (MapStrKey *) resource; +static void thread_map_strkey_offsetSet(Map *map, zval *zkey, zval *zvalue) { zend::String skey(zkey); + auto item = new MapItem(zvalue); + item->key = zend_string_init(skey.val(), skey.len(), 1); map->lock_.lock(); - auto iter = map->map_.find(skey.to_std_string()); - if (iter != map->map_.end()) { - iter->second.assign(zvalue); - } else { - map->map_.emplace(skey.to_std_string(), ThreadValue(zvalue)); - } + zend_hash_update_ptr(&map->ht, item->key, item); map->lock_.unlock(); } -static void thread_map_strkey_count(void *resource, zval *return_value) { - MapStrKey *map = (MapStrKey *) resource; +static void thread_map_count(Map *map, zval *return_value) { map->lock_.lock_rd(); - RETVAL_LONG(map->map_.size()); + RETVAL_LONG(zend_array_count(&map->ht)); map->lock_.unlock(); } -static void thread_map_intkey_offsetGet(void *resource, zval *zkey, zval *return_value) { - MapIntKey *map = (MapIntKey *) resource; - zend_long lkey = zval_get_long(zkey); +static void thread_map_intkey_offsetGet(Map *map, zval *zkey, zval *return_value) { + zend_long index = zval_get_long(zkey); map->lock_.lock_rd(); - auto iter = map->map_.find(lkey); - if (iter != map->map_.end()) { - iter->second.fetch(return_value); + MapItem *tv = (MapItem *) zend_hash_index_find(&map->ht, index); + if (tv) { + tv->fetch(return_value); } map->lock_.unlock(); } -static void thread_map_intkey_offsetExists(void *resource, zval *zkey, zval *return_value) { - MapIntKey *map = (MapIntKey *) resource; - zend_long lkey = zval_get_long(zkey); +static void thread_map_intkey_offsetExists(Map *map, zval *zkey, zval *return_value) { + zend_long index = zval_get_long(zkey); map->lock_.lock_rd(); - RETVAL_BOOL(map->map_.find(lkey) != map->map_.end()); + RETVAL_BOOL(zend_hash_index_find_ptr(&map->ht, index) != NULL); map->lock_.unlock(); } -static void thread_map_intkey_offsetUnset(void *resource, zval *zkey) { - MapIntKey *map = (MapIntKey *) resource; - zend_long lkey = zval_get_long(zkey); +static void thread_map_intkey_offsetUnset(Map *map, zval *zkey) { + zend_long index = zval_get_long(zkey); map->lock_.lock(); - auto iter = map->map_.find(lkey); - if (iter != map->map_.end()) { - iter->second.release(); - map->map_.erase(iter); - } + zend_hash_index_del(&map->ht, index); map->lock_.unlock(); } -static void thread_map_intkey_offsetSet(void *resource, zval *zkey, zval *zvalue) { - MapIntKey *map = (MapIntKey *) resource; - zend_long lkey = zval_get_long(zkey); +static void thread_map_intkey_offsetSet(Map *map, zval *zkey, zval *zvalue) { + zend_long index = zval_get_long(zkey); + auto item = new MapItem(zvalue); map->lock_.lock(); - auto iter = map->map_.find(lkey); - if (iter != map->map_.end()) { - iter->second.assign(zvalue); - } else { - map->map_.emplace(lkey, ThreadValue(zvalue)); - } - map->lock_.unlock(); -} - -static void thread_map_intkey_count(void *resource, zval *return_value) { - MapIntKey *map = (MapIntKey *) resource; - map->lock_.lock_rd(); - RETVAL_LONG(map->map_.size()); + zend_hash_index_update_ptr(&map->ht, index, item); map->lock_.unlock(); } @@ -699,6 +658,11 @@ static PHP_METHOD(swoole_thread, exec) { swoole_thread_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("id"), to->thread->native_handle()); } +static void thread_map_value_dtor(zval *pDest) { + MapItem *item = (MapItem *) Z_PTR_P(pDest); + delete item; +} + static PHP_METHOD(swoole_thread_map, __construct) { zend_long key_type; @@ -707,17 +671,17 @@ static PHP_METHOD(swoole_thread_map, __construct) { ZEND_PARSE_PARAMETERS_END(); auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); + mo->map = new Map(key_type); + if (key_type == Map::KEY_INT) { - mo->resource = new MapIntKey(); mo->handlers = &map_intkey_handlers; } else { - mo->resource = new MapStrKey(); mo->handlers = &map_strkey_handlers; } thread_lock.lock(); zend_long resource_id = ++thread_resource_id; - thread_resources[resource_id] = mo->resource; + thread_resources[resource_id] = mo->map; thread_lock.unlock(); zend_update_property_long(swoole_thread_map_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); @@ -731,7 +695,7 @@ static PHP_METHOD(swoole_thread_map, offsetGet) { ZEND_PARSE_PARAMETERS_END(); auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->handlers->offsetGet(mo->resource, zkey, return_value); + mo->handlers->offsetGet(mo->map, zkey, return_value); } static PHP_METHOD(swoole_thread_map, offsetExists) { @@ -742,7 +706,7 @@ static PHP_METHOD(swoole_thread_map, offsetExists) { ZEND_PARSE_PARAMETERS_END(); auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->handlers->offsetExists(mo->resource, zkey, return_value); + mo->handlers->offsetExists(mo->map, zkey, return_value); } static PHP_METHOD(swoole_thread_map, offsetSet) { @@ -755,7 +719,7 @@ static PHP_METHOD(swoole_thread_map, offsetSet) { ZEND_PARSE_PARAMETERS_END(); auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->handlers->offsetSet(mo->resource, zkey, zvalue); + mo->handlers->offsetSet(mo->map, zkey, zvalue); } static PHP_METHOD(swoole_thread_map, offsetUnset) { @@ -766,12 +730,12 @@ static PHP_METHOD(swoole_thread_map, offsetUnset) { ZEND_PARSE_PARAMETERS_END(); auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->handlers->offsetUnset(mo->resource, zkey); + mo->handlers->offsetUnset(mo->map, zkey); } static PHP_METHOD(swoole_thread_map, count) { auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->handlers->count(mo->resource, return_value); + mo->handlers->count(mo->map, return_value); } static PHP_METHOD(swoole_thread_map, __wakeup) { @@ -789,7 +753,7 @@ static PHP_METHOD(swoole_thread_map, __wakeup) { } else { mo->handlers = &map_strkey_handlers; } - mo->resource = map; + mo->map = map; success = true; } thread_lock.unlock(); @@ -825,7 +789,7 @@ static PHP_METHOD(swoole_thread_arraylist, offsetGet) { ao->resource->lock_.lock_rd(); if (ao->resource->exists(index)) { out_of_range = false; - ao->resource->list_.at(index).fetch(return_value); + ao->resource->list_.at(index)->fetch(return_value); } ao->resource->lock_.unlock(); @@ -861,10 +825,10 @@ static PHP_METHOD(swoole_thread_arraylist, offsetSet) { array->lock_.lock(); if (ZVAL_IS_NULL(zkey) || (ZVAL_IS_LONG(zkey) && Z_LVAL_P(zkey) == (zend_long) array->list_.size())) { - array->list_.emplace_back(ThreadValue(zvalue)); + array->list_.emplace_back(new MapItem(zvalue)); } else { zend_long index = zval_get_long(zkey); - array->list_.at(index).assign(zvalue); + array->list_.at(index)->assign(zvalue); } array->lock_.unlock(); } From 604b35e6d8e8445a18a98d920efa494d7becc572 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 21 Mar 2024 11:08:32 +0800 Subject: [PATCH 010/103] Optimize --- examples/thread/co.php | 1 + examples/thread/run_test.php | 2 +- ext-src/stubs/php_swoole_thread.stub.php | 2 +- ext-src/stubs/php_swoole_thread_arginfo.h | 6 +- ext-src/swoole_thread.cc | 363 +++++++++------------- 5 files changed, 156 insertions(+), 218 deletions(-) diff --git a/examples/thread/co.php b/examples/thread/co.php index f02b949a427..e3fc99539b7 100644 --- a/examples/thread/co.php +++ b/examples/thread/co.php @@ -5,6 +5,7 @@ $list = new Swoole\Thread\ArrayList(); $list[] = base64_encode(random_bytes(32)); $list[1] = uniqid(); +var_dump(count($list)); $t1 = Swoole\Thread::exec('mt.php', 'thread-1', PHP_OS, $map, $list); $t2 = Swoole\Thread::exec('mt.php', 'thread-2', PHP_OS, $map, $list); diff --git a/examples/thread/run_test.php b/examples/thread/run_test.php index db988a4d4e9..4f4bec2686e 100644 --- a/examples/thread/run_test.php +++ b/examples/thread/run_test.php @@ -2,7 +2,7 @@ use Swoole\Thread\Map; -$map = new Map(Map::KEY_STR); +$map = new Map; $c = 4; $threads = []; diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index b3ca4fb9445..4d95361c241 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -16,7 +16,7 @@ public static function getId(): int {} namespace Swoole\Thread { class Map implements ArrayAccess, Countable { - public function __construct(int $key_type) {} + public function __construct() {} public function offsetGet(mixed $key): mixed {} public function offsetExists(mixed $key): bool {} public function offsetSet(mixed $key, mixed $value): void {} diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index fdfaab5aac7..dc468dd001c 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 446102efcc4c1f8abfe4fdcb2be9bc9f2621f749 */ + * Stub hash: 8278b4b79d73cc176a240b37d2dffe837f349eeb */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -22,9 +22,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_getId, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread_Map___construct, 0, 0, 1) - ZEND_ARG_TYPE_INFO(0, key_type, IS_LONG, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_Swoole_Thread_Map___construct arginfo_class_Swoole_Thread___construct ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_offsetGet, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 4b7880e1793..35da63c4b40 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -40,8 +40,6 @@ static zend_object_handlers swoole_thread_map_handlers; zend_class_entry *swoole_thread_arraylist_ce; static zend_object_handlers swoole_thread_arraylist_handlers; -static void thread_map_value_dtor(zval *pDest); - struct MapItem { uint32_t type; uint32_t length; @@ -79,11 +77,6 @@ struct MapItem { } } - void assign(zval *zvalue) { - release(); - store(zvalue); - } - void fetch(zval *return_value) { switch (type) { case IS_LONG: @@ -118,12 +111,23 @@ struct MapItem { } }; -struct Resource { +struct ZendArray { RWLock lock_; uint32_t ref_count; + zend_array ht; - Resource() : lock_(0) { + static void item_dtor(zval *pDest) { + MapItem *item = (MapItem *) Z_PTR_P(pDest); + delete item; + } + + ZendArray() : lock_(0) { ref_count = 1; + zend_hash_init(&ht, 0, NULL, item_dtor, 1); + } + + ~ZendArray() { + zend_hash_destroy(&ht); } uint32_t add_ref() { @@ -133,102 +137,146 @@ struct Resource { uint32_t del_ref() { return --ref_count; } -}; -struct Map : public Resource { - uint8_t type_; - zend_array ht; + bool index_exists(zend_long index) { + return index < (zend_long) zend_hash_num_elements(&ht); + } - enum KeyType { - KEY_INT = 1, - KEY_STR = 2, - }; + void strkey_offsetGet(zval *zkey, zval *return_value) { + zend::String skey(zkey); + lock_.lock_rd(); + MapItem *item = (MapItem *) zend_hash_find_ptr(&ht, skey.get()); + if (item) { + item->fetch(return_value); + } + lock_.unlock(); + } - Map(uint8_t type) : Resource() { - type_ = type; - zend_hash_init(&ht, 0, NULL, thread_map_value_dtor, 1); + void strkey_offsetExists(zval *zkey, zval *return_value) { + zend::String skey(zkey); + lock_.lock_rd(); + RETVAL_BOOL(zend_hash_find_ptr(&ht, skey.get()) != NULL); + lock_.unlock(); } - ~Map() { - zend_hash_destroy(&ht); + void strkey_offsetUnset(zval *zkey) { + zend::String skey(zkey); + lock_.lock(); + zend_hash_del(&ht, skey.get()); + lock_.unlock(); } -}; -struct ArrayList : public Resource { - std::vector list_; + void strkey_offsetSet(zval *zkey, zval *zvalue) { + zend::String skey(zkey); + auto item = new MapItem(zvalue); + item->key = zend_string_init(skey.val(), skey.len(), 1); + lock_.lock(); + zend_hash_update_ptr(&ht, item->key, item); + lock_.unlock(); + } - ArrayList() : Resource() {} + void count(zval *return_value) { + lock_.lock_rd(); + RETVAL_LONG(zend_hash_num_elements(&ht)); + lock_.unlock(); + } - ~ArrayList() { - for (auto &value : list_) { - delete value; + void intkey_offsetGet(zend_long index, zval *return_value) { + lock_.lock_rd(); + MapItem *item = (MapItem *) zend_hash_index_find(&ht, index); + if (item) { + item->fetch(return_value); } + lock_.unlock(); } - bool exists(zend_long index) { - return index < (zend_long) list_.size(); + void intkey_offsetGet(zval *zkey, zval *return_value) { + intkey_offsetGet(zval_get_long(zkey), return_value); } -}; -typedef std::thread Thread; + void intkey_offsetExists(zval *zkey, zval *return_value) { + zend_long index = zval_get_long(zkey); + lock_.lock_rd(); + RETVAL_BOOL(zend_hash_index_find_ptr(&ht, index) != NULL); + lock_.unlock(); + } + + void intkey_offsetUnset(zval *zkey) { + zend_long index = zval_get_long(zkey); + lock_.lock(); + zend_hash_index_del(&ht, index); + lock_.unlock(); + } + + void intkey_offsetSet(zval *zkey, zval *zvalue) { + zend_long index = zval_get_long(zkey); + auto item = new MapItem(zvalue); + lock_.lock(); + zend_hash_index_update_ptr(&ht, index, item); + lock_.unlock(); + } + + bool index_offsetGet(zval *zkey, zval *return_value) { + zend_long index = zval_get_long(zkey); + bool out_of_range = true; + lock_.lock_rd(); + if (index_exists(index)) { + out_of_range = false; + MapItem *item = (MapItem *) zend_hash_index_find_ptr(&ht, index); + if (item) { + item->fetch(return_value); + } + } + lock_.unlock(); + return !out_of_range; + } + + bool index_offsetSet(zval *zkey, zval *zvalue) { + zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); + bool success = true; + lock_.lock(); + if (index > zend_hash_num_elements(&ht)) { + success = false; + } else if (index == -1 || index == zend_hash_num_elements(&ht)) { + zend_hash_next_index_insert_ptr(&ht, new MapItem(zvalue)); + } else { + zend_hash_index_update_ptr(&ht, index, new MapItem(zvalue)); + } + lock_.unlock(); + return success; + } -struct MapHandlers { - void (*offsetGet)(Map *map, zval *zkey, zval *return_value); - void (*offsetExists)(Map *map, zval *zkey, zval *return_value); - void (*offsetUnset)(Map *map, zval *zkey); - void (*offsetSet)(Map *map, zval *zkey, zval *zvalue); - void (*count)(Map *map, zval *return_value); + void index_offsetExists(zval *zkey, zval *return_value) { + zend_long index = zval_get_long(zkey); + lock_.lock_rd(); + RETVAL_BOOL(index_exists(index)); + lock_.unlock(); + } }; +typedef std::thread Thread; + struct ThreadObject { Thread *thread; zend_object std; }; struct ThreadMapObject { - MapHandlers *handlers; - Map *map; + ZendArray *map; zend_object std; }; struct ThreadArrayListObject { - ArrayList *resource; + ZendArray *list; zend_object std; }; static void php_swoole_thread_join(zend_object *object); -static void thread_map_strkey_offsetGet(Map *map, zval *zkey, zval *return_value); -static void thread_map_strkey_offsetExists(Map *map, zval *zkey, zval *return_value); -static void thread_map_strkey_offsetUnset(Map *map, zval *zkey); -static void thread_map_strkey_offsetSet(Map *map, zval *zkey, zval *zvalue); - -static void thread_map_intkey_offsetGet(Map *map, zval *zkey, zval *return_value); -static void thread_map_intkey_offsetExists(Map *map, zval *zkey, zval *return_value); -static void thread_map_intkey_offsetUnset(Map *map, zval *zkey); -static void thread_map_intkey_offsetSet(Map *map, zval *zkey, zval *zvalue); - -static void thread_map_count(Map *map, zval *return_value); thread_local zval thread_argv; static std::mutex thread_lock; static zend_long thread_resource_id = 0; -static std::unordered_map thread_resources; - -MapHandlers map_intkey_handlers = { - thread_map_intkey_offsetGet, - thread_map_intkey_offsetExists, - thread_map_intkey_offsetUnset, - thread_map_intkey_offsetSet, - thread_map_count, -}; - -MapHandlers map_strkey_handlers = { - thread_map_strkey_offsetGet, - thread_map_strkey_offsetExists, - thread_map_strkey_offsetUnset, - thread_map_strkey_offsetSet, - thread_map_count, -}; +static std::unordered_map thread_resources; static sw_inline ThreadObject *php_swoole_thread_fetch_object(zend_object *obj) { return (ThreadObject *) ((char *) obj - swoole_thread_handlers.offset); @@ -300,77 +348,6 @@ ThreadMapObject *thread_map_fetch_object_check(zval *zobject) { return map; } -static void thread_map_strkey_offsetGet(Map *map, zval *zkey, zval *return_value) { - zend::String skey(zkey); - map->lock_.lock_rd(); - MapItem *tv = (MapItem *) zend_hash_find_ptr(&map->ht, skey.get()); - if (tv) { - tv->fetch(return_value); - } - map->lock_.unlock(); -} - -static void thread_map_strkey_offsetExists(Map *map, zval *zkey, zval *return_value) { - zend::String skey(zkey); - map->lock_.lock_rd(); - RETVAL_BOOL(zend_hash_find_ptr(&map->ht, skey.get()) != NULL); - map->lock_.unlock(); -} - -static void thread_map_strkey_offsetUnset(Map *map, zval *zkey) { - zend::String skey(zkey); - map->lock_.lock(); - zend_hash_del(&map->ht, skey.get()); - map->lock_.unlock(); -} - -static void thread_map_strkey_offsetSet(Map *map, zval *zkey, zval *zvalue) { - zend::String skey(zkey); - auto item = new MapItem(zvalue); - item->key = zend_string_init(skey.val(), skey.len(), 1); - map->lock_.lock(); - zend_hash_update_ptr(&map->ht, item->key, item); - map->lock_.unlock(); -} - -static void thread_map_count(Map *map, zval *return_value) { - map->lock_.lock_rd(); - RETVAL_LONG(zend_array_count(&map->ht)); - map->lock_.unlock(); -} - -static void thread_map_intkey_offsetGet(Map *map, zval *zkey, zval *return_value) { - zend_long index = zval_get_long(zkey); - map->lock_.lock_rd(); - MapItem *tv = (MapItem *) zend_hash_index_find(&map->ht, index); - if (tv) { - tv->fetch(return_value); - } - map->lock_.unlock(); -} - -static void thread_map_intkey_offsetExists(Map *map, zval *zkey, zval *return_value) { - zend_long index = zval_get_long(zkey); - map->lock_.lock_rd(); - RETVAL_BOOL(zend_hash_index_find_ptr(&map->ht, index) != NULL); - map->lock_.unlock(); -} - -static void thread_map_intkey_offsetUnset(Map *map, zval *zkey) { - zend_long index = zval_get_long(zkey); - map->lock_.lock(); - zend_hash_index_del(&map->ht, index); - map->lock_.unlock(); -} - -static void thread_map_intkey_offsetSet(Map *map, zval *zkey, zval *zvalue) { - zend_long index = zval_get_long(zkey); - auto item = new MapItem(zvalue); - map->lock_.lock(); - zend_hash_index_update_ptr(&map->ht, index, item); - map->lock_.unlock(); -} - static sw_inline ThreadArrayListObject *thread_arraylist_fetch_object(zend_object *obj) { return (ThreadArrayListObject *) ((char *) obj - swoole_thread_arraylist_handlers.offset); } @@ -387,12 +364,12 @@ static sw_inline zend_long thread_arraylist_get_resource_id(zval *zobject) { static void thread_arraylist_free_object(zend_object *object) { zend_long resource_id = thread_arraylist_get_resource_id(object); ThreadArrayListObject *ao = thread_arraylist_fetch_object(object); - if (ao->resource) { + if (ao->list) { thread_lock.lock(); - if (ao->resource->del_ref() == 0) { + if (ao->list->del_ref() == 0) { thread_resources.erase(resource_id); - delete ao->resource; - ao->resource = nullptr; + delete ao->list; + ao->list = nullptr; } thread_lock.unlock(); } @@ -409,7 +386,7 @@ static zend_object *thread_arraylist_create_object(zend_class_entry *ce) { ThreadArrayListObject *thread_arraylist_fetch_object_check(zval *zobject) { ThreadArrayListObject *ao = thread_arraylist_fetch_object(Z_OBJ_P(zobject)); - if (!ao->resource) { + if (!ao->list) { php_swoole_fatal_error(E_ERROR, "must call constructor first"); } return ao; @@ -496,10 +473,6 @@ void php_swoole_thread_minit(int module_number) { swoole_thread_map, thread_map_create_object, thread_map_free_object, ThreadMapObject, std); zend_class_implements(swoole_thread_map_ce, 2, zend_ce_arrayaccess, zend_ce_countable); - - zend_declare_class_constant_long(swoole_thread_map_ce, ZEND_STRL("KEY_INT"), Map::KEY_INT); - zend_declare_class_constant_long(swoole_thread_map_ce, ZEND_STRL("KEY_STR"), Map::KEY_STR); - zend_declare_property_long(swoole_thread_map_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); SW_INIT_CLASS_ENTRY(swoole_thread_arraylist, "Swoole\\Thread\\ArrayList", nullptr, swoole_thread_arraylist_methods); @@ -658,26 +631,9 @@ static PHP_METHOD(swoole_thread, exec) { swoole_thread_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("id"), to->thread->native_handle()); } -static void thread_map_value_dtor(zval *pDest) { - MapItem *item = (MapItem *) Z_PTR_P(pDest); - delete item; -} - static PHP_METHOD(swoole_thread_map, __construct) { - zend_long key_type; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(key_type) - ZEND_PARSE_PARAMETERS_END(); - auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); - mo->map = new Map(key_type); - - if (key_type == Map::KEY_INT) { - mo->handlers = &map_intkey_handlers; - } else { - mo->handlers = &map_strkey_handlers; - } + mo->map = new ZendArray(); thread_lock.lock(); zend_long resource_id = ++thread_resource_id; @@ -687,6 +643,13 @@ static PHP_METHOD(swoole_thread_map, __construct) { zend_update_property_long(swoole_thread_map_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); } +#define ZEND_ARRAY_CALL_METHOD(array, method, zkey, ...) \ + if (ZVAL_IS_LONG(zkey)) { \ + array->intkey_##method(zkey, ##__VA_ARGS__); \ + } else { \ + array->strkey_##method(zkey, ##__VA_ARGS__); \ + } + static PHP_METHOD(swoole_thread_map, offsetGet) { zval *zkey; @@ -695,7 +658,7 @@ static PHP_METHOD(swoole_thread_map, offsetGet) { ZEND_PARSE_PARAMETERS_END(); auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->handlers->offsetGet(mo->map, zkey, return_value); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetGet, zkey, return_value); } static PHP_METHOD(swoole_thread_map, offsetExists) { @@ -706,7 +669,7 @@ static PHP_METHOD(swoole_thread_map, offsetExists) { ZEND_PARSE_PARAMETERS_END(); auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->handlers->offsetExists(mo->map, zkey, return_value); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetExists, zkey, return_value); } static PHP_METHOD(swoole_thread_map, offsetSet) { @@ -719,7 +682,7 @@ static PHP_METHOD(swoole_thread_map, offsetSet) { ZEND_PARSE_PARAMETERS_END(); auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->handlers->offsetSet(mo->map, zkey, zvalue); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetSet, zkey, zvalue); } static PHP_METHOD(swoole_thread_map, offsetUnset) { @@ -730,12 +693,12 @@ static PHP_METHOD(swoole_thread_map, offsetUnset) { ZEND_PARSE_PARAMETERS_END(); auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->handlers->offsetUnset(mo->map, zkey); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetUnset, zkey); } static PHP_METHOD(swoole_thread_map, count) { auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->handlers->count(mo->map, return_value); + mo->map->count(return_value); } static PHP_METHOD(swoole_thread_map, __wakeup) { @@ -746,13 +709,8 @@ static PHP_METHOD(swoole_thread_map, __wakeup) { thread_lock.lock(); auto iter = thread_resources.find(resource_id); if (iter != thread_resources.end()) { - Map *map = (Map *) iter->second; + ZendArray *map = iter->second; map->add_ref(); - if (map->type_ == Map::KEY_INT) { - mo->handlers = &map_intkey_handlers; - } else { - mo->handlers = &map_strkey_handlers; - } mo->map = map; success = true; } @@ -767,48 +725,38 @@ static PHP_METHOD(swoole_thread_arraylist, __construct) { ZEND_PARSE_PARAMETERS_NONE(); auto ao = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); - ao->resource = new ArrayList(); + ao->list = new ZendArray(); thread_lock.lock(); zend_long resource_id = ++thread_resource_id; - thread_resources[resource_id] = ao->resource; + thread_resources[resource_id] = ao->list; thread_lock.unlock(); zend_update_property_long(swoole_thread_arraylist_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); } static PHP_METHOD(swoole_thread_arraylist, offsetGet) { - zend_long index; + zval *zkey; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(index) + Z_PARAM_ZVAL(zkey) ZEND_PARSE_PARAMETERS_END(); - bool out_of_range = true; auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); - ao->resource->lock_.lock_rd(); - if (ao->resource->exists(index)) { - out_of_range = false; - ao->resource->list_.at(index)->fetch(return_value); - } - ao->resource->lock_.unlock(); - - if (out_of_range) { + if (!ao->list->index_offsetGet(zkey, return_value)) { zend_throw_exception(swoole_exception_ce, "out of range", -1); } } static PHP_METHOD(swoole_thread_arraylist, offsetExists) { - zend_long index; + zval *zkey; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(index) + Z_PARAM_ZVAL(zkey) ZEND_PARSE_PARAMETERS_END(); auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); - ao->resource->lock_.lock_rd(); - RETVAL_BOOL(ao->resource->exists(index)); - ao->resource->lock_.unlock(); + ao->list->index_offsetExists(zkey, return_value); } static PHP_METHOD(swoole_thread_arraylist, offsetSet) { @@ -821,16 +769,9 @@ static PHP_METHOD(swoole_thread_arraylist, offsetSet) { ZEND_PARSE_PARAMETERS_END(); auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); - ArrayList *array = ao->resource; - - array->lock_.lock(); - if (ZVAL_IS_NULL(zkey) || (ZVAL_IS_LONG(zkey) && Z_LVAL_P(zkey) == (zend_long) array->list_.size())) { - array->list_.emplace_back(new MapItem(zvalue)); - } else { - zend_long index = zval_get_long(zkey); - array->list_.at(index)->assign(zvalue); + if (!ao->list->index_offsetSet(zkey, zvalue)) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); } - array->lock_.unlock(); } static PHP_METHOD(swoole_thread_arraylist, offsetUnset) { @@ -839,9 +780,7 @@ static PHP_METHOD(swoole_thread_arraylist, offsetUnset) { static PHP_METHOD(swoole_thread_arraylist, count) { auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); - ao->resource->lock_.lock_rd(); - RETVAL_LONG(ao->resource->list_.size()); - ao->resource->lock_.unlock(); + ao->list->count(return_value); } static PHP_METHOD(swoole_thread_arraylist, __wakeup) { @@ -852,9 +791,9 @@ static PHP_METHOD(swoole_thread_arraylist, __wakeup) { thread_lock.lock(); auto iter = thread_resources.find(resource_id); if (iter != thread_resources.end()) { - ArrayList *array = (ArrayList *) iter->second; - array->add_ref(); - mo->resource = array; + ZendArray *list = iter->second; + list->add_ref(); + mo->list = list; success = true; } thread_lock.unlock(); From f1dbc6a7054224b779cd914874a326cafc61f2de Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 21 Mar 2024 12:20:28 +0800 Subject: [PATCH 011/103] Optimize --- examples/thread/co.php | 1 + examples/thread/test.php | 9 ++++ ext-src/swoole_thread.cc | 99 +++++++++++++++++++++++++++------------- 3 files changed, 77 insertions(+), 32 deletions(-) diff --git a/examples/thread/co.php b/examples/thread/co.php index e3fc99539b7..a657688e8e3 100644 --- a/examples/thread/co.php +++ b/examples/thread/co.php @@ -1,6 +1,7 @@ uuid = uniqid(); +$map['obj'] = $o; + +var_dump($map['obj']); + +$s = serialize($map); +var_dump(unserialize($s)); + diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 35da63c4b40..e3e29c7f607 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -40,19 +40,24 @@ static zend_object_handlers swoole_thread_map_handlers; zend_class_entry *swoole_thread_arraylist_ce; static zend_object_handlers swoole_thread_arraylist_handlers; +zend_string *php_swoole_thread_serialize(zval *zdata); +bool php_swoole_thread_unserialize(zend_string *data, zval *zv); + +#define IS_SERIALIZED_OBJECT 99 + struct MapItem { uint32_t type; - uint32_t length; zend_string *key; union { - char *strval; + zend_string *str; zend_long lval; double dval; - }; + zend_string *serialized_object; + } value; MapItem(zval *zvalue) { key = nullptr; - strval = nullptr; + value = {}; store(zvalue); } @@ -60,49 +65,70 @@ struct MapItem { type = Z_TYPE_P(zvalue); switch (type) { case IS_LONG: - lval = zval_get_long(zvalue); + value.lval = zval_get_long(zvalue); break; case IS_DOUBLE: - dval = zval_get_double(zvalue); + value.dval = zval_get_double(zvalue); break; case IS_STRING: { - strval = swoole_strndup(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); - length = Z_STRLEN_P(zvalue); + value.str = zend_string_init(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), 1); + break; + case IS_TRUE: + case IS_FALSE: + case IS_NULL: break; } - default: - type = IS_LONG; - lval = 0; + default: { + auto _serialized_object = php_swoole_thread_serialize(zvalue); + if (!_serialized_object) { + type = IS_UNDEF; + break; + } else { + type = IS_SERIALIZED_OBJECT; + value.serialized_object = _serialized_object; + } break; } + } } void fetch(zval *return_value) { switch (type) { case IS_LONG: - RETVAL_LONG(lval); + RETVAL_LONG(value.lval); break; case IS_DOUBLE: - RETVAL_LONG(dval); + RETVAL_LONG(value.dval); + break; + case IS_TRUE: + RETVAL_TRUE; + break; + case IS_FALSE: + RETVAL_FALSE; break; case IS_STRING: - RETVAL_STRINGL(strval, length); + RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); + break; + case IS_SERIALIZED_OBJECT: + php_swoole_thread_unserialize(value.serialized_object, return_value); break; default: - RETVAL_NULL(); break; } } void release() { if (type == IS_STRING) { - sw_free(strval); - strval = nullptr; + zend_string_release(value.str); + value.str = nullptr; + } else if (type == IS_SERIALIZED_OBJECT) { + zend_string_release(value.serialized_object); + value.serialized_object = nullptr; } } ~MapItem() { - if (strval) { + if (value.str) { release(); } if (key) { @@ -233,14 +259,16 @@ struct ZendArray { bool index_offsetSet(zval *zkey, zval *zvalue) { zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); + auto item = new MapItem(zvalue); bool success = true; lock_.lock(); if (index > zend_hash_num_elements(&ht)) { success = false; + delete item; } else if (index == -1 || index == zend_hash_num_elements(&ht)) { - zend_hash_next_index_insert_ptr(&ht, new MapItem(zvalue)); + zend_hash_next_index_insert_ptr(&ht, item); } else { - zend_hash_index_update_ptr(&ht, index, new MapItem(zvalue)); + zend_hash_index_update_ptr(&ht, index, item); } lock_.unlock(); return success; @@ -529,7 +557,7 @@ static PHP_METHOD(swoole_thread, getId) { RETURN_LONG(pthread_self()); } -std::string php_swoole_thread_serialize(zval *zdata) { +zend_string *php_swoole_thread_serialize(zval *zdata) { php_serialize_data_t var_hash; smart_str serialized_data = {0}; @@ -537,25 +565,25 @@ std::string php_swoole_thread_serialize(zval *zdata) { php_var_serialize(&serialized_data, zdata, &var_hash); PHP_VAR_SERIALIZE_DESTROY(var_hash); - std::string result; + zend_string *result = nullptr; if (!EG(exception)) { - result = std::string(serialized_data.s->val, serialized_data.s->len); + result = zend_string_init(serialized_data.s->val, serialized_data.s->len, 1); } smart_str_free(&serialized_data); return result; } -bool php_swoole_thread_unserialize(const std::string &data, zval *zv) { +bool php_swoole_thread_unserialize(zend_string *data, zval *zv) { php_unserialize_data_t var_hash; - const char *p = data.c_str(); - size_t l = data.length(); + const char *p = ZSTR_VAL(data); + size_t l = ZSTR_LEN(data); PHP_VAR_UNSERIALIZE_INIT(var_hash); zend_bool unserialized = php_var_unserialize(zv, (const uchar **) &p, (const uchar *) (p + l), &var_hash); PHP_VAR_UNSERIALIZE_DESTROY(var_hash); if (!unserialized) { swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", - (zend_long) ((char *) p - data.c_str()), + (zend_long) ((char *) p - ZSTR_VAL(data)), l); } return unserialized; @@ -565,7 +593,7 @@ void php_swoole_thread_rshutdown() { zval_dtor(&thread_argv); } -void php_swoole_thread_start(const std::string &file, const std::string &argv) { +void php_swoole_thread_start(zend_string *file, zend_string *argv) { ts_resource(0); #if defined(COMPILE_DL_SWOOLE) ZEND_TSRMLS_CACHE_UPDATE(); @@ -577,11 +605,11 @@ void php_swoole_thread_start(const std::string &file, const std::string &argv) { goto _startup_error; } - zend_stream_init_filename(&file_handle, file.c_str()); + zend_stream_init_filename(&file_handle, ZSTR_VAL(file)); file_handle.primary_script = 1; zend_first_try { - if (argv.empty()) { + if (ZSTR_LEN(file) == 0) { array_init(&thread_argv); } else { php_swoole_thread_unserialize(argv, &thread_argv); @@ -595,6 +623,8 @@ void php_swoole_thread_start(const std::string &file, const std::string &argv) { file_handle.filename = NULL; _startup_error: + zend_string_release(file); + zend_string_release(argv); ts_free_thread(); } @@ -616,16 +646,21 @@ static PHP_METHOD(swoole_thread, exec) { object_init_ex(return_value, swoole_thread_ce); ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(return_value)); - std::string file(script_file, l_script_file); + zend_string *file = zend_string_init(script_file, l_script_file, 1); zval zargv; array_init(&zargv); for (int i = 0; i < argc; i++) { zend::array_add(&zargv, &args[i]); } - std::string argv = php_swoole_thread_serialize(&zargv); + zend_string *argv = php_swoole_thread_serialize(&zargv); zval_dtor(&zargv); + if (!argv) { + zend_string_release(file); + return; + } + to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); zend_update_property_long( swoole_thread_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("id"), to->thread->native_handle()); From 55d2c570a66c102619bb5901715451354ef829b9 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 21 Mar 2024 13:01:59 +0800 Subject: [PATCH 012/103] Optimize --- examples/thread/mt.php | 20 ++++-- examples/thread/run_test.php | 2 +- ext-src/stubs/php_swoole_thread.stub.php | 3 + ext-src/stubs/php_swoole_thread_arginfo.h | 12 +++- ext-src/swoole_thread.cc | 82 ++++++++++++++++++++--- 5 files changed, 98 insertions(+), 21 deletions(-) diff --git a/examples/thread/mt.php b/examples/thread/mt.php index 63fbe67e51e..eb63d199e7d 100644 --- a/examples/thread/mt.php +++ b/examples/thread/mt.php @@ -2,15 +2,21 @@ //echo "begin\n"; $args = Swoole\Thread::getArguments(); -echo Swoole\Thread::getId() . "\t" . 'gmap[uuid]' . "\t" . $args[2]['uuid'] . "\n"; -$args[2]['hello'] = uniqid('swoole'); -var_dump(count($args[2])); -$args[3][] = uniqid('swoole'); -$args[3][count($args[3])] = uniqid('php'); +$map = $args[2]; +$list = $args[3]; -echo Swoole\Thread::getId() . "\t" . 'glist[0]' . "\t" . $args[3][0] . "\n"; -var_dump(count($args[3])); +echo Swoole\Thread::getId() . "\t" . 'gmap[uuid]' . "\t" . $map['uuid'] . "\n"; +$map['hello'] = uniqid('swoole'); +var_dump($map->keys()); + +$list[] = uniqid('swoole'); +$list[count($list)] = uniqid('php'); + +var_dump($args); + +echo Swoole\Thread::getId() . "\t" . 'glist[0]' . "\t" . $list[0] . "\n"; +var_dump(count($list)); //if ($args[0] == 'thread-2') { // $t3 = Swoole\Thread::exec('mt.php', 'thread-3', PHP_OS); diff --git a/examples/thread/run_test.php b/examples/thread/run_test.php index 4f4bec2686e..5ff3f9713d5 100644 --- a/examples/thread/run_test.php +++ b/examples/thread/run_test.php @@ -3,7 +3,7 @@ use Swoole\Thread\Map; $map = new Map; -$c = 4; +$c = 1; $threads = []; for ($i = 0; $i < $c; $i++) { diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index 4d95361c241..5d0e241b0b8 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -22,6 +22,8 @@ public function offsetExists(mixed $key): bool {} public function offsetSet(mixed $key, mixed $value): void {} public function offsetUnset(mixed $key): void {} public function count(): int {} + public function keys(): array {} + public function clean(): void {} public function __wakeup(): void {} } class ArrayList implements ArrayAccess, Countable { @@ -31,6 +33,7 @@ public function offsetExists(mixed $key): bool {} public function offsetSet(mixed $key, mixed $value): void {} public function offsetUnset(mixed $key): void {} public function count(): int {} + public function clean(): void {} public function __wakeup(): void {} } } diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index dc468dd001c..04a137e70a3 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8278b4b79d73cc176a240b37d2dffe837f349eeb */ + * Stub hash: 86ee0e408295738c52a0f9944e4ed83b6f4fadf3 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -43,9 +43,13 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Thread_Map_count arginfo_class_Swoole_Thread_getId -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map___wakeup, 0, 0, IS_VOID, 0) +#define arginfo_class_Swoole_Thread_Map_keys arginfo_class_Swoole_Thread_getArguments + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_Map_clean, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() +#define arginfo_class_Swoole_Thread_Map___wakeup arginfo_class_Swoole_Thread_Map_clean + #define arginfo_class_Swoole_Thread_ArrayList___construct arginfo_class_Swoole_Thread___construct #define arginfo_class_Swoole_Thread_ArrayList_offsetGet arginfo_class_Swoole_Thread_Map_offsetGet @@ -58,4 +62,6 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Thread_ArrayList_count arginfo_class_Swoole_Thread_getId -#define arginfo_class_Swoole_Thread_ArrayList___wakeup arginfo_class_Swoole_Thread_Map___wakeup +#define arginfo_class_Swoole_Thread_ArrayList_clean arginfo_class_Swoole_Thread_Map_clean + +#define arginfo_class_Swoole_Thread_ArrayList___wakeup arginfo_class_Swoole_Thread_Map_clean diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index e3e29c7f607..b5fdd91263c 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -45,7 +45,7 @@ bool php_swoole_thread_unserialize(zend_string *data, zval *zv); #define IS_SERIALIZED_OBJECT 99 -struct MapItem { +struct ArrayItem { uint32_t type; zend_string *key; union { @@ -55,7 +55,7 @@ struct MapItem { zend_string *serialized_object; } value; - MapItem(zval *zvalue) { + ArrayItem(zval *zvalue) { key = nullptr; value = {}; store(zvalue); @@ -127,7 +127,7 @@ struct MapItem { } } - ~MapItem() { + ~ArrayItem() { if (value.str) { release(); } @@ -143,7 +143,7 @@ struct ZendArray { zend_array ht; static void item_dtor(zval *pDest) { - MapItem *item = (MapItem *) Z_PTR_P(pDest); + ArrayItem *item = (ArrayItem *) Z_PTR_P(pDest); delete item; } @@ -164,6 +164,12 @@ struct ZendArray { return --ref_count; } + void clean() { + lock_.lock(); + zend_hash_clean(&ht); + lock_.unlock(); + } + bool index_exists(zend_long index) { return index < (zend_long) zend_hash_num_elements(&ht); } @@ -171,7 +177,7 @@ struct ZendArray { void strkey_offsetGet(zval *zkey, zval *return_value) { zend::String skey(zkey); lock_.lock_rd(); - MapItem *item = (MapItem *) zend_hash_find_ptr(&ht, skey.get()); + ArrayItem *item = (ArrayItem *) zend_hash_find_ptr(&ht, skey.get()); if (item) { item->fetch(return_value); } @@ -194,7 +200,7 @@ struct ZendArray { void strkey_offsetSet(zval *zkey, zval *zvalue) { zend::String skey(zkey); - auto item = new MapItem(zvalue); + auto item = new ArrayItem(zvalue); item->key = zend_string_init(skey.val(), skey.len(), 1); lock_.lock(); zend_hash_update_ptr(&ht, item->key, item); @@ -207,9 +213,44 @@ struct ZendArray { lock_.unlock(); } + void keys(zval *return_value) { + lock_.lock_rd(); + zend_ulong elem_count = zend_hash_num_elements(&ht); + array_init_size(return_value, elem_count); + zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); + zend_ulong num_idx; + zend_string *str_idx; + zval *entry; + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + if (HT_IS_PACKED(&ht) && HT_IS_WITHOUT_HOLES(&ht)) { + /* Optimistic case: range(0..n-1) for vector-like packed array */ + zend_ulong lval = 0; + + for (; lval < elem_count; ++lval) { + ZEND_HASH_FILL_SET_LONG(lval); + ZEND_HASH_FILL_NEXT(); + } + } else { + /* Go through input array and add keys to the return array */ + ZEND_HASH_FOREACH_KEY_VAL(&ht, num_idx, str_idx, entry) { + if (str_idx) { + ZEND_HASH_FILL_SET_STR_COPY(str_idx); + } else { + ZEND_HASH_FILL_SET_LONG(num_idx); + } + ZEND_HASH_FILL_NEXT(); + } + ZEND_HASH_FOREACH_END(); + } + (void) entry; + } + ZEND_HASH_FILL_END(); + lock_.unlock(); + } + void intkey_offsetGet(zend_long index, zval *return_value) { lock_.lock_rd(); - MapItem *item = (MapItem *) zend_hash_index_find(&ht, index); + ArrayItem *item = (ArrayItem *) zend_hash_index_find(&ht, index); if (item) { item->fetch(return_value); } @@ -236,7 +277,7 @@ struct ZendArray { void intkey_offsetSet(zval *zkey, zval *zvalue) { zend_long index = zval_get_long(zkey); - auto item = new MapItem(zvalue); + auto item = new ArrayItem(zvalue); lock_.lock(); zend_hash_index_update_ptr(&ht, index, item); lock_.unlock(); @@ -248,7 +289,7 @@ struct ZendArray { lock_.lock_rd(); if (index_exists(index)) { out_of_range = false; - MapItem *item = (MapItem *) zend_hash_index_find_ptr(&ht, index); + ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); if (item) { item->fetch(return_value); } @@ -259,7 +300,7 @@ struct ZendArray { bool index_offsetSet(zval *zkey, zval *zvalue) { zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); - auto item = new MapItem(zvalue); + auto item = new ArrayItem(zvalue); bool success = true; lock_.lock(); if (index > zend_hash_num_elements(&ht)) { @@ -435,6 +476,8 @@ static PHP_METHOD(swoole_thread_map, offsetExists); static PHP_METHOD(swoole_thread_map, offsetSet); static PHP_METHOD(swoole_thread_map, offsetUnset); static PHP_METHOD(swoole_thread_map, count); +static PHP_METHOD(swoole_thread_map, keys); +static PHP_METHOD(swoole_thread_map, clean); static PHP_METHOD(swoole_thread_map, __wakeup); static PHP_METHOD(swoole_thread_arraylist, __construct); @@ -443,6 +486,7 @@ static PHP_METHOD(swoole_thread_arraylist, offsetExists); static PHP_METHOD(swoole_thread_arraylist, offsetSet); static PHP_METHOD(swoole_thread_arraylist, offsetUnset); static PHP_METHOD(swoole_thread_arraylist, count); +static PHP_METHOD(swoole_thread_arraylist, clean); static PHP_METHOD(swoole_thread_arraylist, __wakeup); SW_EXTERN_C_END @@ -466,6 +510,8 @@ static const zend_function_entry swoole_thread_map_methods[] = { PHP_ME(swoole_thread_map, offsetSet, arginfo_class_Swoole_Thread_Map_offsetSet, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, offsetUnset, arginfo_class_Swoole_Thread_Map_offsetUnset, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, count, arginfo_class_Swoole_Thread_Map_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, clean, arginfo_class_Swoole_Thread_Map_clean, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, keys, arginfo_class_Swoole_Thread_Map_keys, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_map, __wakeup, arginfo_class_Swoole_Thread_Map___wakeup, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -476,6 +522,7 @@ static const zend_function_entry swoole_thread_arraylist_methods[] = { PHP_ME(swoole_thread_arraylist, offsetExists, arginfo_class_Swoole_Thread_ArrayList_offsetExists, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, offsetSet, arginfo_class_Swoole_Thread_ArrayList_offsetSet, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, offsetUnset, arginfo_class_Swoole_Thread_ArrayList_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, clean, arginfo_class_Swoole_Thread_ArrayList_clean, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, count, arginfo_class_Swoole_Thread_ArrayList_count, ZEND_ACC_PUBLIC) PHP_ME(swoole_thread_arraylist, __wakeup, arginfo_class_Swoole_Thread_ArrayList___wakeup, ZEND_ACC_PUBLIC) PHP_FE_END @@ -736,6 +783,16 @@ static PHP_METHOD(swoole_thread_map, count) { mo->map->count(return_value); } +static PHP_METHOD(swoole_thread_map, keys) { + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->map->keys(return_value); +} + +static PHP_METHOD(swoole_thread_map, clean) { + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->map->clean(); +} + static PHP_METHOD(swoole_thread_map, __wakeup) { auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); bool success = false; @@ -818,6 +875,11 @@ static PHP_METHOD(swoole_thread_arraylist, count) { ao->list->count(return_value); } +static PHP_METHOD(swoole_thread_arraylist, clean) { + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + ao->list->clean(); +} + static PHP_METHOD(swoole_thread_arraylist, __wakeup) { auto mo = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); bool success = false; From 44f1c4c77ed883898b2056fbaadc4f6515e6fa7a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 21 Mar 2024 13:22:00 +0800 Subject: [PATCH 013/103] fix --- ext-src/swoole_thread.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index b5fdd91263c..0bb6abc0c2b 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -234,7 +234,7 @@ struct ZendArray { /* Go through input array and add keys to the return array */ ZEND_HASH_FOREACH_KEY_VAL(&ht, num_idx, str_idx, entry) { if (str_idx) { - ZEND_HASH_FILL_SET_STR_COPY(str_idx); + ZEND_HASH_FILL_SET_STR(zend_string_init(str_idx->val, str_idx->len, 0)); } else { ZEND_HASH_FILL_SET_LONG(num_idx); } From c366385c920c5c6fb258a8b6ad344bf9df100da4 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 25 Mar 2024 19:42:41 +0800 Subject: [PATCH 014/103] remove co redis/mysql/pgsql client --- ext-src/php_swoole_mysql_proto.h | 1017 ------ ext-src/swoole_mysql_coro.cc | 2279 ------------ ext-src/swoole_mysql_proto.cc | 744 ---- ext-src/swoole_postgresql_coro.cc | 1945 ---------- ext-src/swoole_redis_coro.cc | 5545 ----------------------------- 5 files changed, 11530 deletions(-) delete mode 100644 ext-src/php_swoole_mysql_proto.h delete mode 100644 ext-src/swoole_mysql_coro.cc delete mode 100644 ext-src/swoole_mysql_proto.cc delete mode 100644 ext-src/swoole_postgresql_coro.cc delete mode 100644 ext-src/swoole_redis_coro.cc diff --git a/ext-src/php_swoole_mysql_proto.h b/ext-src/php_swoole_mysql_proto.h deleted file mode 100644 index 62b7b19a6ed..00000000000 --- a/ext-src/php_swoole_mysql_proto.h +++ /dev/null @@ -1,1017 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Twosee | - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "php_swoole_cxx.h" -#include "swoole_util.h" - -#ifdef SW_USE_OPENSSL -#ifndef OPENSSL_NO_RSA -#define SW_MYSQL_RSA_SUPPORT -#include -#include -#include -#endif -#endif - -enum sw_mysql_command -{ - SW_MYSQL_COM_NULL = -1, - SW_MYSQL_COM_SLEEP = 0, - SW_MYSQL_COM_QUIT, - SW_MYSQL_COM_INIT_DB, - SW_MYSQL_COM_QUERY = 3, - SW_MYSQL_COM_FIELD_LIST, - SW_MYSQL_COM_CREATE_DB, - SW_MYSQL_COM_DROP_DB, - SW_MYSQL_COM_REFRESH, - SW_MYSQL_COM_SHUTDOWN, - SW_MYSQL_COM_STATISTICS, - SW_MYSQL_COM_PROCESS_INFO, - SW_MYSQL_COM_CONNECT, - SW_MYSQL_COM_PROCESS_KILL, - SW_MYSQL_COM_DEBUG, - SW_MYSQL_COM_PING, - SW_MYSQL_COM_TIME, - SW_MYSQL_COM_DELAYED_INSERT, - SW_MYSQL_COM_CHANGE_USER, - SW_MYSQL_COM_BINLOG_DUMP, - SW_MYSQL_COM_TABLE_DUMP, - SW_MYSQL_COM_CONNECT_OUT, - SW_MYSQL_COM_REGISTER_SLAVE, - SW_MYSQL_COM_STMT_PREPARE, - SW_MYSQL_COM_STMT_EXECUTE, - SW_MYSQL_COM_STMT_SEND_LONG_DATA, - SW_MYSQL_COM_STMT_CLOSE, - SW_MYSQL_COM_STMT_RESET, - SW_MYSQL_COM_SET_OPTION, - SW_MYSQL_COM_STMT_FETCH, - SW_MYSQL_COM_DAEMON, - SW_MYSQL_COM_END -}; - -enum sw_mysql_handshake_state -{ - SW_MYSQL_HANDSHAKE_WAIT_REQUEST, - SW_MYSQL_HANDSHAKE_WAIT_SWITCH, - SW_MYSQL_HANDSHAKE_WAIT_SIGNATURE, - SW_MYSQL_HANDSHAKE_WAIT_RSA, - SW_MYSQL_HANDSHAKE_WAIT_RESULT, - SW_MYSQL_HANDSHAKE_COMPLETED, -}; - -#define SW_MYSQL_AUTH_SIGNATRUE_PACKET_LENGTH 2 - -enum sw_mysql_auth_signature -{ - SW_MYSQL_AUTH_SIGNATURE_ERROR = 0x00, // get signature failed - SW_MYSQL_AUTH_SIGNATURE = 0x01, - SW_MYSQL_AUTH_SIGNATURE_RSA_PREPARED = 0x02, - SW_MYSQL_AUTH_SIGNATURE_SUCCESS = 0x03, - SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED = 0x04, // rsa required -}; - -enum sw_mysql_command_flag -{ - SW_MYSQL_COMMAND_FLAG_QUERY = 1 << 4, - SW_MYSQL_COMMAND_FLAG_EXECUTE = 1 << 5, -}; - -enum sw_mysql_state -{ - SW_MYSQL_STATE_CLOSED = 0, - SW_MYSQL_STATE_IDLE = 1, - SW_MYSQL_STATE_QUERY = 2 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_QUERY_FETCH = 3 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_QUERY_MORE_RESULTS = 4 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_PREPARE = 5 | SW_MYSQL_COMMAND_FLAG_QUERY, - SW_MYSQL_STATE_EXECUTE = 6 | SW_MYSQL_COMMAND_FLAG_EXECUTE, - SW_MYSQL_STATE_EXECUTE_FETCH = 7 | SW_MYSQL_COMMAND_FLAG_EXECUTE, - SW_MYSQL_STATE_EXECUTE_MORE_RESULTS = 8 | SW_MYSQL_COMMAND_FLAG_EXECUTE, -}; - -enum sw_mysql_packet_types -{ - SW_MYSQL_PACKET_OK = 0x0, - SW_MYSQL_PACKET_AUTH_SIGNATURE_REQUEST = 0x01, - - /* not defined in protocol */ - SW_MYSQL_PACKET_RAW_DATA, - SW_MYSQL_PACKET_GREETING, - SW_MYSQL_PACKET_LOGIN, - SW_MYSQL_PACKET_AUTH_SWITCH_RESPONSE, - SW_MYSQL_PACKET_AUTH_SIGNATURE_RESPONSE, - SW_MYSQL_PACKET_LCB, // length coded binary - SW_MYSQL_PACKET_FIELD, - SW_MYSQL_PACKET_ROW_DATA, - SW_MYSQL_PACKET_PREPARE_STATEMENT, - /* ======================= */ - - SW_MYSQL_PACKET_NULL = 0xfb, - SW_MYSQL_PACKET_EOF = 0xfe, - SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST = 0xfe, - SW_MYSQL_PACKET_ERR = 0xff -}; - -enum sw_mysql_field_types -{ - SW_MYSQL_TYPE_DECIMAL, - SW_MYSQL_TYPE_TINY, - SW_MYSQL_TYPE_SHORT, - SW_MYSQL_TYPE_LONG, - SW_MYSQL_TYPE_FLOAT, - SW_MYSQL_TYPE_DOUBLE, - SW_MYSQL_TYPE_NULL, - SW_MYSQL_TYPE_TIMESTAMP, - SW_MYSQL_TYPE_LONGLONG, - SW_MYSQL_TYPE_INT24, - SW_MYSQL_TYPE_DATE, - SW_MYSQL_TYPE_TIME, - SW_MYSQL_TYPE_DATETIME, - SW_MYSQL_TYPE_YEAR, - SW_MYSQL_TYPE_NEWDATE, - SW_MYSQL_TYPE_VARCHAR, - SW_MYSQL_TYPE_BIT, - SW_MYSQL_TYPE_JSON = 245, - SW_MYSQL_TYPE_NEWDECIMAL, - SW_MYSQL_TYPE_ENUM, - SW_MYSQL_TYPE_SET, - SW_MYSQL_TYPE_TINY_BLOB, - SW_MYSQL_TYPE_MEDIUM_BLOB, - SW_MYSQL_TYPE_LONG_BLOB, - SW_MYSQL_TYPE_BLOB, - SW_MYSQL_TYPE_VAR_STRING, - SW_MYSQL_TYPE_STRING, - SW_MYSQL_TYPE_GEOMETRY -}; - -// ref: https://dev.mysql.com/doc/dev/mysql-server/8.0.0/group__group__cs__capabilities__flags.html -// use regex: "\#define[ ]+(CLIENT_[A-Z_\d]+)[ ]+(\(?[\dA-Z <]+\)?)\n[ ]+?[ ]+([\s\S ]+?\.) More\.\.\.\n?" -// to "SW_MYSQL_$1 = $2, /* $3 */" -enum sw_mysql_client_capability_flags -{ - SW_MYSQL_CLIENT_LONG_PASSWORD = 1, /* Use the improved version of Old Password Authentication. */ - SW_MYSQL_CLIENT_FOUND_ROWS = 2, /* Send found rows instead of affected rows in EOF_Packet. */ - SW_MYSQL_CLIENT_LONG_FLAG = 4, /* Get all column flags. */ - SW_MYSQL_CLIENT_CONNECT_WITH_DB = 8, /* Database (schema) name can be specified on connect in Handshake Response Packet. */ - SW_MYSQL_CLIENT_NO_SCHEMA = 16, /* Don't allow database.table.column. */ - SW_MYSQL_CLIENT_COMPRESS = 32, /* Compression protocol supported. */ - SW_MYSQL_CLIENT_ODBC = 64, /* Special handling of ODBC behavior. */ - SW_MYSQL_CLIENT_LOCAL_FILES = 128, /* Can use LOAD DATA LOCAL. */ - SW_MYSQL_CLIENT_IGNORE_SPACE = 256, /* Ignore spaces before '('. */ - SW_MYSQL_CLIENT_PROTOCOL_41 = 512, /* New 4.1 protocol. */ - SW_MYSQL_CLIENT_INTERACTIVE = 1024, /* This is an interactive client. */ - SW_MYSQL_CLIENT_SSL = 2048, /* Use SSL encryption for the session. */ - SW_MYSQL_CLIENT_IGNORE_SIGPIPE = 4096, /* Client only flag. */ - SW_MYSQL_CLIENT_TRANSACTIONS = 8192, /* Client knows about transactions. */ - SW_MYSQL_CLIENT_RESERVED = 16384, /* flag for 4.1 protocol. */ - SW_MYSQL_CLIENT_SECURE_CONNECTION = 32768, /* swoole custom name for RESERVED2. */ - SW_MYSQL_CLIENT_RESERVED2 = 32768, /* flag for 4.1 authentication. */ - SW_MYSQL_CLIENT_MULTI_STATEMENTS = (1UL << 16), /* Enable/disable multi-stmt support. */ - SW_MYSQL_CLIENT_MULTI_RESULTS = (1UL << 17), /* Enable/disable multi-results. */ - SW_MYSQL_CLIENT_PS_MULTI_RESULTS = (1UL << 18), /* Multi-results and OUT parameters in PS-protocol. */ - SW_MYSQL_CLIENT_PLUGIN_AUTH = (1UL << 19), /* Client supports plugin authentication. */ - SW_MYSQL_CLIENT_CONNECT_ATTRS = (1UL << 20), /* Client supports connection attributes. */ - SW_MYSQL_CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = (1UL << 21), /* Enable authentication response packet to be larger than 255 bytes. */ - SW_MYSQL_CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS = (1UL << 22), /* Don't close the connection for a user account with expired password. */ - SW_MYSQL_CLIENT_SESSION_TRACK = (1UL << 23), /* Capable of handling server state change information. */ - SW_MYSQL_CLIENT_DEPRECATE_EOF = (1UL << 24), /* Client no longer needs EOF_Packet and will use OK_Packet instead. */ - SW_MYSQL_CLIENT_SSL_VERIFY_SERVER_CERT = (1UL << 30), /* Verify server certificate. */ - SW_MYSQL_CLIENT_REMEMBER_OPTIONS = (1UL << 31) /* Don't reset the options after an unsuccessful connect. */ -}; - -// ref: https://dev.mysql.com/doc/internals/en/status-flags.html -enum sw_mysql_server_status_flags -{ - SW_MYSQL_SERVER_STATUS_IN_TRANS = 0x0001, // a transaction is active - SW_MYSQL_SERVER_STATUS_AUTOCOMMIT = 0x0002, //auto-commit is enabled - SW_MYSQL_SERVER_MORE_RESULTS_EXISTS = 0x0008, - SW_MYSQL_SERVER_STATUS_NO_GOOD_INDEX_USED = 0x0010, - SW_MYSQL_SERVER_STATUS_NO_INDEX_USED = 0x0020, - SW_MYSQL_SERVER_STATUS_CURSOR_EXISTS = 0x0040, // Used by Binary Protocol Resultset to signal that COM_STMT_FETCH must be used to fetch the row-data. - SW_MYSQL_SERVER_STATUS_LAST_ROW_SENT = 0x0080, - SW_MYSQL_SERVER_STATUS_DB_DROPPED = 0x0100, - SW_MYSQL_SERVER_STATUS_NO_BACKSLASH_ESCAPES = 0x0200, - SW_MYSQL_SERVER_STATUS_METADATA_CHANGED = 0x0400, - SW_MYSQL_SERVER_QUERY_WAS_SLOW = 0x0800, - SW_MYSQL_SERVER_PS_OUT_PARAMS = 0x1000, - SW_MYSQL_SERVER_STATUS_IN_TRANS_READONLY = 0x2000, // in a read-only transaction - SW_MYSQL_SERVER_SESSION_STATE_CHANGED = 0x4000 // connection state information has changed -}; - -#define SW_MYSQL_NO_RSA_ERROR "MySQL8 caching_sha2_password authentication plugin need enable OpenSSL support" - -#define SW_MYSQL_NOT_NULL_FLAG 1 -#define SW_MYSQL_PRI_KEY_FLAG 2 -#define SW_MYSQL_UNIQUE_KEY_FLAG 4 -#define SW_MYSQL_MULTIPLE_KEY_FLAG 8 -#define SW_MYSQL_BLOB_FLAG 16 -#define SW_MYSQL_UNSIGNED_FLAG 32 -#define SW_MYSQL_ZEROFILL_FLAG 64 -#define SW_MYSQL_BINARY_FLAG 128 -#define SW_MYSQL_ENUM_FLAG 256 -#define SW_MYSQL_AUTO_INCREMENT_FLAG 512 -#define SW_MYSQL_TIMESTAMP_FLAG 1024 -#define SW_MYSQL_SET_FLAG 2048 -#define SW_MYSQL_NO_DEFAULT_VALUE_FLAG 4096 -#define SW_MYSQL_ON_UPDATE_NOW_FLAG 8192 -#define SW_MYSQL_PART_KEY_FLAG 16384 -#define SW_MYSQL_GROUP_FLAG 32768 -#define SW_MYSQL_NUM_FLAG 32768 - -/* int<3> payload_length + int<1> sequence_id */ -#define SW_MYSQL_PACKET_HEADER_SIZE 4 -#define SW_MYSQL_PACKET_TYPE_OFFSET 5 -#define SW_MYSQL_PACKET_EOF_MAX_SIZE 9 -#define SW_MYSQL_PACKET_PREPARED_OK_SIZE 12 -#define SW_MYSQL_MAX_PACKET_BODY_SIZE 0x00ffffff -#define SW_MYSQL_MAX_PACKET_SIZE (SW_MYSQL_PACKET_HEADER_SIZE + SW_MYSQL_MAX_PACKET_BODY_SIZE) - -// nonce: a number or bit string used only once, in security engineering -// other names on doc: challenge/scramble/salt -#define SW_MYSQL_NONCE_LENGTH 20 - -// clang-format off -#define sw_mysql_uint2korr2korr(A) (uint16_t) (((uint16_t) ((uchar) (A)[0])) +\ - ((uint16_t) ((uchar) (A)[1]) << 8)) -#define sw_mysql_uint2korr3korr(A) (uint32_t) (((uint32_t) ((uchar) (A)[0])) +\ - (((uint32_t) ((uchar) (A)[1])) << 8) +\ - (((uint32_t) ((uchar) (A)[2])) << 16)) -#define sw_mysql_uint2korr4korr(A) (uint32_t) (((uint32_t) ((uchar) (A)[0])) +\ - (((uint32_t) ((uchar) (A)[1])) << 8) +\ - (((uint32_t) ((uchar) (A)[2])) << 16) +\ - (((uint32_t) ((uchar) (A)[3])) << 24)) -#define sw_mysql_uint2korr8korr(A) ((uint64_t)(((uint32_t) ((uchar) (A)[0])) +\ - (((uint32_t) ((uchar) (A)[1])) << 8) +\ - (((uint32_t) ((uchar) (A)[2])) << 16) +\ - (((uint32_t) ((uchar) (A)[3])) << 24)) +\ - (((uint64_t) (((uint32_t) ((uchar) (A)[4])) +\ - (((uint32_t) ((uchar) (A)[5])) << 8) +\ - (((uint32_t) ((uchar) (A)[6])) << 16) +\ - (((uint32_t) ((uchar) (A)[7])) << 24))) << 32)) - -#define sw_mysql_int1store(T,A) do { *((int8_t*) (T)) = (int8_t)(A); } while(0) -#define sw_mysql_int2store(T,A) do { uint32_t def_temp= (uint32_t) (A) ;\ - *((uchar*) (T)) = (uchar)(def_temp); \ - *((uchar*) (T+1)) = (uchar)((def_temp >> 8)); } while (0) -#define sw_mysql_int3store(T,A) do { /*lint -save -e734 */\ - *(((char *)(T))) = (char) ((A));\ - *(((char *)(T))+1) = (char) (((A) >> 8));\ - *(((char *)(T))+2) = (char) (((A) >> 16)); \ - /*lint -restore */} while (0) -#define sw_mysql_int4store(T,A) do { \ - *(((char *)(T))) = (char) ((A));\ - *(((char *)(T))+1) = (char) (((A) >> 8));\ - *(((char *)(T))+2) = (char) (((A) >> 16));\ - *(((char *)(T))+3) = (char) (((A) >> 24)); } while (0) -#define sw_mysql_int5store(T,A) do { \ - *(((char *)(T))) = (char)((A));\ - *(((char *)(T))+1) = (char)(((A) >> 8));\ - *(((char *)(T))+2) = (char)(((A) >> 16));\ - *(((char *)(T))+3) = (char)(((A) >> 24)); \ - *(((char *)(T))+4) = (char)(((A) >> 32)); } while (0) -/* Based on int5store() from Andrey Hristov */ -#define sw_mysql_int6store(T,A) do { \ - *(((char *)(T))) = (char)((A));\ - *(((char *)(T))+1) = (char)(((A) >> 8));\ - *(((char *)(T))+2) = (char)(((A) >> 16));\ - *(((char *)(T))+3) = (char)(((A) >> 24)); \ - *(((char *)(T))+4) = (char)(((A) >> 32)); \ - *(((char *)(T))+5) = (char)(((A) >> 40)); } while (0) - -// clang-format on - -#define sw_mysql_int8store(T,A) do { \ - uint32_t def_temp= (uint32_t) (A), def_temp2= (uint32_t) ((A) >> 32); \ - sw_mysql_int4store((T),def_temp); \ - sw_mysql_int4store((T+4),def_temp2); } while (0) - -#define sw_mysql_doublestore(T,A) do { \ - double def_temp = (double) A; \ - memcpy(T, &def_temp, sizeof(double)); \ - } while (0) - -#if defined(SW_DEBUG) && defined(SW_LOG_TRACE_OPEN) -#define swMysqlPacketDump(length, number, data, title) \ - if (SW_LOG_TRACE >= sw_logger()->get_level() && (SW_TRACE_MYSQL_CLIENT & SwooleG.trace_flags)) \ - { \ - swoole_debug("+----------+------------+-------------------------------------------------------+"); \ - swoole_debug("| P#%-6u | L%-9u | %-10u %42s |", number, SW_MYSQL_PACKET_HEADER_SIZE + length, length, title); \ - swoole_hex_dump(data, length); \ - } -#else -#define swMysqlPacketDump(length, number, data, title) -#endif - -namespace swoole { namespace mysql { -//-----------------------------------namespace begin-------------------------------------------- -char get_charset(const char *name); -uint8_t get_static_type_size(uint8_t type); - -inline uint8_t read_lcb_size(const char *p) -{ - switch ((uchar) p[0]) - { - case 251: - return 1; - case 252: - return 3; - case 253: - return 4; - case 254: - return 9; - default: - return 1; - } -} - -inline uint8_t read_lcb(const char *p, uint64_t *length, bool *nul) -{ - switch ((uchar) p[0]) - { - case 251: /* fb : 1 octet */ - *length = 0; - *nul = true; - return 1; - case 252: /* fc : 2 octets */ - *length = sw_mysql_uint2korr2korr(p + 1); - *nul = false; - return 3; - case 253: /* fd : 3 octets */ - *length = sw_mysql_uint2korr3korr(p + 1); - *nul = false; - return 4; - case 254: /* fe : 8 octets */ - *length = sw_mysql_uint2korr8korr(p + 1); - *nul = false; - return 9; - default: - *length = (uchar) p[0]; - *nul = false; - return 1; - } -} - -inline uint8_t read_lcb(const char *p, uint32_t *length, bool *nul) -{ - uint64_t _r; - uint8_t ret = read_lcb(p, &_r, nul); - *length = _r; - return ret; -} - -inline uint8_t write_lcb(char *p, uint64_t length, bool nul = false) -{ - if (nul) - { - sw_mysql_int1store(p++, 251); - return 1; - } - if (length <= 250) - { - sw_mysql_int1store(p, length); - return 1; - } - else if (length <= 0xffff) - { - sw_mysql_int1store(p++, 252); - sw_mysql_int2store(p, length); - return 3; - } - else if (length <= 0xffffff) - { - sw_mysql_int1store(p++, 253); - sw_mysql_int3store(p, length); - return 4; - } - else - { - sw_mysql_int1store(p++, 254); - sw_mysql_int8store(p, length); - return 9; - } -} - -class packet -{ -public: - static inline uint32_t get_length(const char *data) - { - return sw_mysql_uint2korr3korr(data); - } - static inline uint32_t get_number(const char *data) - { - return (uint8_t) data[3]; - } - static inline void set_length(char *buffer, uint32_t length) - { - buffer[0] = length; - buffer[1] = length >> 8; - buffer[2] = length >> 16; - } - static inline void set_number(char *buffer, uint8_t number) - { - buffer[3] = number; - } - static inline void set_header(char *buffer, uint32_t length, uint8_t number) - { - set_length(buffer, length); - set_number(buffer, number); - } -}; - -class server_packet : public packet -{ -public: - struct header { - uint32_t length :24; - uint32_t number :8; - header() : length(0), number(0) { } - } header; - server_packet() { } - server_packet(const char *data) - { - parse(data); - } - inline void parse(const char *data) - { - header.length = packet::get_length(data); - header.number = packet::get_number(data); - } - static inline uint8_t parse_type(const char *data) - { - if (sw_unlikely(!data)) - { - return SW_MYSQL_PACKET_NULL; - } - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE]; - } - static inline bool is_eof(const char *data) - { - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_EOF; - } - static inline bool is_ok(const char *data) - { - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_OK; - } - static inline bool is_err(const char *data) - { - return (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE] == SW_MYSQL_PACKET_ERR; - } -}; - -class server_status -{ -public: - int16_t status = 0; - void operator =(uint16_t status) - { - this->status = status; - } - inline bool more_results_exists() - { - bool b = !!(status & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "More results exist = %u", b); - return b; - } -}; - -class client_packet : public packet -{ -public: - client_packet(size_t body_size = 1024 - SW_MYSQL_PACKET_HEADER_SIZE) - { - SW_ASSERT(body_size > 0); - if (body_size <= 4) - { - data.header = stack_buffer; - } - else - { - data.header = new char[SW_MEM_ALIGNED_SIZE(SW_MYSQL_PACKET_HEADER_SIZE + body_size)](); - } - data.body = data.header + SW_MYSQL_PACKET_HEADER_SIZE; - } - inline const char* get_data() - { - return data.header; - } - inline uint32_t get_data_length() - { - return SW_MYSQL_PACKET_HEADER_SIZE + get_length(); - } - inline uint32_t get_length() - { - return sw_mysql_uint2korr3korr(data.header); - } - inline uint8_t get_number() - { - return (uint8_t) data.header[3]; - } - inline const char* get_body() - { - return data.body; - } - inline void set_header(uint32_t length, uint8_t number) - { - packet::set_header(data.header, length, number); - } - ~client_packet() - { - if (data.header != stack_buffer) - { - delete[] data.header; - } - } -protected: - struct { - char *header = nullptr; - char *body = nullptr; - } data; - char stack_buffer[SW_MYSQL_PACKET_HEADER_SIZE + 4] = {}; -}; - -class command_packet : public client_packet -{ -public: - command_packet(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0) : client_packet(1 + length) - { - set_command(command); - set_header(1 + length, 0); - if (length > 0) - { - memcpy(data.body + 1, sql, length); - } - }; - inline void set_command(enum sw_mysql_command command) - { - data.body[0] = (char) command; - } -}; - -class err_packet : public server_packet -{ -public: - uint16_t code; - std::string msg; - char sql_state[5 + 1]; - err_packet(const char *data); -}; - -class ok_packet : public server_packet -{ -public: - uint64_t affected_rows = 0; - uint64_t last_insert_id = 0; - mysql::server_status server_status; - unsigned int warning_count = 0; - ok_packet() { } - ok_packet(const char *data); -}; - -class eof_packet : public server_packet -{ -public: - uint16_t warning_count; - mysql::server_status server_status; - eof_packet(const char *data); -}; - -class raw_data_packet : public server_packet -{ -public: - const char *body; - raw_data_packet(const char *data) : server_packet(data), body(data + SW_MYSQL_PACKET_HEADER_SIZE) - { - swMysqlPacketDump(header.length, header.number, data, "Protocol::RawData"); - } -}; - -class greeting_packet : public server_packet -{ -public: - uint8_t protocol_version = 0; - std::string server_version = ""; - int connection_id = 0; - char auth_plugin_data[SW_MYSQL_NONCE_LENGTH + 1] = {}; // nonce + '\0' - uint8_t auth_plugin_data_length = 0; - char filler = 0; - int capability_flags = 0; - char charset = SW_MYSQL_DEFAULT_CHARSET; - mysql::server_status status_flags; - char reserved[10] = {}; - std::string auth_plugin_name = ""; - greeting_packet(const char *data); -}; - -class login_packet : public client_packet -{ -public: - login_packet( - greeting_packet *greeting_packet, - const std::string &user, - const std::string &password, - std::string database, - char charset - ); -}; - -class auth_switch_request_packet : public server_packet -{ -public: - std::string auth_method_name = "mysql_native_password"; - char auth_method_data[SW_MYSQL_NONCE_LENGTH + 1] = {}; - auth_switch_request_packet(const char *data); -}; - -class auth_switch_response_packet : public client_packet -{ -public: - auth_switch_response_packet(auth_switch_request_packet *req, const std::string &password); -}; - -class auth_signature_request_packet : public server_packet -{ -public: - char data[2] = {}; - auth_signature_request_packet(const char *data) :server_packet(data) - { - swMysqlPacketDump(header.length, header.number, data, "Protocol::AuthSignatureRequest"); - memcpy(&this->data, data + SW_MYSQL_PACKET_HEADER_SIZE, 2); - } - inline bool is_full_auth_required() - { - return data[1] == SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED; - } - inline bool is_vaild() - { - return data[0] == SW_MYSQL_AUTH_SIGNATURE && (data[1] == SW_MYSQL_AUTH_SIGNATURE_SUCCESS || data[1] == SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED); - } -}; - -class auth_signature_prepared_packet : public client_packet -{ -public: - auth_signature_prepared_packet(uint8_t number) : client_packet(1) - { - set_header(1, number); - data.body[0] = SW_MYSQL_AUTH_SIGNATURE_RSA_PREPARED; - } -}; - -class auth_signature_response_packet : public client_packet -{ -public: - auth_signature_response_packet(raw_data_packet *raw_data_pakcet, const std::string &password, const char *auth_plugin_data); -}; - -class lcb_packet : public server_packet -{ -public: - uint32_t length = 0; - bool nul = 0; - lcb_packet(const char *data) : server_packet(data) - { - swMysqlPacketDump(header.length, header.number, data, "Protocol::LengthCodedBinary"); - bytes_length = read_lcb(data + SW_MYSQL_PACKET_HEADER_SIZE, &length, &nul); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "binary_length=%u, nul=%u", header.length, nul); - } - bool is_vaild() - { - return header.length == bytes_length; - } -private: - uint8_t bytes_length; -}; - -class field_packet : public server_packet -{ -public: - char *catalog = nullptr; /* Catalog for table */ - uint32_t catalog_length = 0; - char *database = nullptr; /* Database for table */ - uint32_t database_length = 0; - char *table = nullptr; /* Table of column if column was a field */ - uint32_t table_length = 0; - char *org_table = nullptr; /* Org table name, if table was an alias */ - uint32_t org_table_length = 0; - char *name = nullptr; /* Name of column */ - uint32_t name_length = 0; - char *org_name = nullptr; /* Original column name, if an alias */ - uint32_t org_name_length = 0; - char charset = 0; - uint64_t length = 0; /* Width of column (create length) */ - uint8_t type = 0; /* Type of field. See mysql_com.h for types */ - uint32_t flags = 0; /* Div flags */ - uint32_t decimals = 0; /* Number of decimals in field */ - char *def = nullptr; /* Default value (set by mysql_list_fields) */ - uint32_t def_length = 0; - void *extension = nullptr; - field_packet() { } - field_packet(const char *data) { - parse(data); - } - void parse(const char *data); - ~field_packet() - { - if (body) - { - delete[] body; - } - } -protected: - char *body = nullptr; -}; - -typedef field_packet param_packet; - -class row_data -{ -public: - char stack_buffer[32]; - struct { - uint64_t length; // binary code length - bool nul; // is nul? - } text; - row_data(const char *data) - { - next_packet(data); - } - inline void next_packet(const char *data) - { - read_ptr = packet_body = data + SW_MYSQL_PACKET_HEADER_SIZE; - packet_eof = packet_body + packet::get_length(data); - } - inline bool eof() - { - return read_ptr == packet_eof; - } - inline const char* read(size_t length) - { - if (sw_likely(read_ptr + length <= packet_eof)) - { - const char *p = read_ptr; - read_ptr += length; - return p; - } - return nullptr; - } - inline uint32_t recv(char *buf, size_t size) - { - uint32_t readable_length = packet_eof - read_ptr; - uint32_t read_bytes = SW_MIN(readable_length, size); - if (sw_likely(read_bytes > 0)) - { - memcpy(buf, read_ptr, read_bytes); - read_ptr += read_bytes; - } - return read_bytes; - } -protected: - const char *packet_body; - const char *packet_eof; - const char *read_ptr; -}; - -class row_data_text -{ -public: - uint64_t length = 0; - bool nul = false; - const char *body = nullptr; - row_data_text(const char **pp) - { - body = *pp + read_lcb(*pp, &length, &nul); - *pp = body + length; - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, - "text[%" PRIu64 "]: %.*s%s", - length, (int) SW_MIN(64, length), body, - nul ? "null" : ((length > 64 /*|| length > readable_length*/) ? "..." : "") - ); - } -}; - -inline std::string datetime(const char *p, uint8_t length, uint32_t decimals) -{ - uint16_t y = 0; - uint8_t m = 0, d = 0, h = 0, i = 0, s = 0; - uint32_t sp = 0; - if (length != 0) - { - y = sw_mysql_uint2korr2korr(p); - m = *(uint8_t *) (p + 2); - d = *(uint8_t *) (p + 3); - if (length > 4) - { - h = *(uint8_t *) (p + 4); - i = *(uint8_t *) (p + 5); - s = *(uint8_t *) (p + 6); - } - if (length > 7) - { - sp = sw_mysql_uint2korr4korr(p + 7); - } - } - if (decimals > 0 && decimals < 7) { - return swoole::std_string::format( - "%04u-%02u-%02u %02u:%02u:%02u.%0*u", - y, m, d, h, i, s, decimals, (uint32_t) (sp / ::pow(10, (double) (6 - decimals))) - ); - } else { - return swoole::std_string::format( - "%04u-%02u-%02u %02u:%02u:%02u", - y, m, d, h, i, s - ); - } -} - -inline std::string time(const char *p, uint8_t length, uint32_t decimals) -{ - bool neg = false; - uint32_t d = 0, sp = 0; - uint8_t h = 0, m = 0, s = 0; - if (length != 0) - { - neg = (bool) *((uint8_t *) p); - d = sw_mysql_uint2korr4korr(p + 1); - h = *(uint8_t *) (p + 5); - m = *(uint8_t *) (p + 6); - s = *(uint8_t *) (p + 7); - if (length > 8) - { - sp = sw_mysql_uint2korr4korr(p + 8); - } - if (d != 0) { - /* Convert days to hours at once */ - h += d * 24; - } - } - if (decimals > 0 && decimals < 7) { - return swoole::std_string::format( - "%s%02u:%02u:%02u.%0*u", - (neg ? "-" : ""), h, m, s, decimals, (uint32_t) (sp / ::pow(10, (double) (6 - decimals))) - ); - } else { - return swoole::std_string::format( - "%s%02u:%02u:%02u", - (neg ? "-" : ""), h, m, s - ); - } -} - -inline std::string date(const char *p, uint8_t length) -{ - uint16_t y = 0; - uint8_t m = 0, d = 0; - if (length != 0) - { - y = sw_mysql_uint2korr2korr(p); - m = *(uint8_t *) (p + 2); - d = *(uint8_t *) (p + 3); - } - return swoole::std_string::format("%04u-%02u-%02u", y, m, d); -} - -inline std::string year(const char *p, uint8_t length) -{ - uint16_t y = 0; - if (length != 0) - { - y = sw_mysql_uint2korr2korr(p); - } - return swoole::std_string::format("%04u", y); -} - -class result_info -{ -public: - ok_packet ok; - - inline void alloc_fields(uint32_t length) - { - clear_fields(); - if (sw_likely(length != 0)) - { - fields.info = new field_packet[length]; - fields.length = length; - } - else - { - fields.length = 0; - fields.info = nullptr; - } - } - inline uint32_t get_fields_length() - { - return fields.length; - } - inline field_packet* get_fields(uint32_t index) - { - return fields.info; - } - inline field_packet* get_field(uint32_t index) - { - return &fields.info[index]; - } - inline void set_field(uint32_t index, const char *data) - { - fields.info[index].parse(data); - } - inline void clear_fields() - { - if (fields.length > 0) - { - delete[] fields.info; - } - } - ~result_info() - { - clear_fields(); - } -protected: - struct { - uint32_t length = 0; - field_packet *info = nullptr; - } fields; -}; - -class statement : public server_packet -{ -public: - uint32_t id = 0; - uint16_t field_count = 0; - uint16_t param_count = 0; - uint16_t warning_count = 0; - statement() { } - statement(const char* data) : server_packet(data) - { - swMysqlPacketDump(header.length, header.number, data, "COM_STMT_PREPARE_OK_Packet"); - // skip the packet header - data += SW_MYSQL_PACKET_HEADER_SIZE; - // status (1) -- [00] OK - SW_ASSERT(data[0] == SW_MYSQL_PACKET_OK); - data += 1; - // statement_id (4) -- statement-id - id = sw_mysql_uint2korr4korr(data); - data += 4; - // num_columns (2) -- number of columns - field_count = sw_mysql_uint2korr2korr(data); - data += 2; - // num_params (2) -- number of params - param_count = sw_mysql_uint2korr2korr(data); - data += 2; - // reserved_1 (1) -- [00] filler - data += 1; - // warning_count (2) -- number of warnings - warning_count = sw_mysql_uint2korr2korr(data); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "statement_id=%u, field_count=%u, param_count=%u, warning_count=%u", - id, field_count, param_count, warning_count - ); - } -}; - -class null_bitmap -{ -public: - static uint32_t get_size(uint32_t field_length) - { - return ((field_length + 9) / 8) + 1; - } - null_bitmap(const char *p, uint32_t size) : - size(size) - { - map = new char[size]; - memcpy(map, p, size); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "null_count=%u", size); - } - inline bool is_null(size_t i) - { - return ((map + 1)[((i + 2) / 8)] & (0x01 << ((i + 2) % 8))) != 0; - } - ~null_bitmap() - { - delete[] map; - } -protected: - uint32_t size; - char *map; -}; -//-----------------------------------namespace end-------------------------------------------- -}} diff --git a/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc deleted file mode 100644 index c388de802b1..00000000000 --- a/ext-src/swoole_mysql_coro.cc +++ /dev/null @@ -1,2279 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | Copyright (c) 2012-2018 The Swoole Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Twosee | - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" -#include "php_swoole_mysql_proto.h" - -#include "swoole_string.h" - -// see mysqlnd 'L64' macro redefined -#undef L64 - -SW_EXTERN_C_BEGIN -#include "ext/hash/php_hash.h" -#include "ext/hash/php_hash_sha.h" -#include "ext/standard/php_math.h" -#ifdef SW_USE_MYSQLND -#include "ext/mysqlnd/mysqlnd.h" -#include "ext/mysqlnd/mysqlnd_charset.h" -#endif -SW_EXTERN_C_END - -#include - -/* keep same with pdo and mysqli */ -#define MYSQLND_UNKNOWN_SQLSTATE "HY000" -#define MYSQLND_SERVER_GONE "MySQL server has gone away" -#define MYSQLND_CR_UNKNOWN_ERROR 2000 -#define MYSQLND_CR_CONNECTION_ERROR 2002 -#define MYSQLND_CR_SERVER_GONE_ERROR 2006 -#define MYSQLND_CR_OUT_OF_MEMORY 2008 -#define MYSQLND_CR_SERVER_LOST 2013 -#define MYSQLND_CR_COMMANDS_OUT_OF_SYNC 2014 -#define MYSQLND_CR_CANT_FIND_CHARSET 2019 -#define MYSQLND_CR_MALFORMED_PACKET 2027 -#define MYSQLND_CR_NOT_IMPLEMENTED 2054 -#define MYSQLND_CR_NO_PREPARE_STMT 2030 -#define MYSQLND_CR_PARAMS_NOT_BOUND 2031 -#define MYSQLND_CR_INVALID_PARAMETER_NO 2034 -#define MYSQLND_CR_INVALID_BUFFER_USE 2035 - -using swoole::coroutine::Socket; - -namespace swoole { -class MysqlStatement; -class MysqlClient { - public: - /* session related {{{ */ - Socket *socket = nullptr; - zval zsocket; - zval zobject; - Socket::timeout_controller *tc = nullptr; - - enum sw_mysql_state state = SW_MYSQL_STATE_CLOSED; - bool quit = false; - mysql::result_info result; - - std::unordered_map statements; - MysqlStatement *statement = nullptr; - /* }}} */ - - std::string host = SW_MYSQL_DEFAULT_HOST; - uint16_t port = SW_MYSQL_DEFAULT_PORT; - bool ssl = false; - - std::string user = "root"; - std::string password = "root"; - std::string database = "test"; - char charset = SW_MYSQL_DEFAULT_CHARSET; - - double connect_timeout = network::Socket::default_connect_timeout; - bool strict_type = false; - - inline int get_error_code() { - return error_code; - } - - inline const char *get_error_msg() { - return error_msg.c_str(); - } - - inline void non_sql_error(int code, const char *msg) { - error_code = code; - error_msg = std_string::format("SQLSTATE[" MYSQLND_UNKNOWN_SQLSTATE "] [%d] %s", code, msg); - } - - template - inline void non_sql_error(int code, const char *format, Args... args) { - error_code = code; - error_msg = std_string::format( - "SQLSTATE[" MYSQLND_UNKNOWN_SQLSTATE "] [%d] %s", code, std_string::format(format, args...).c_str()); - } - - void io_error() { - if (state == SW_MYSQL_STATE_CLOSED) { - non_sql_error(MYSQLND_CR_CONNECTION_ERROR, socket->errMsg); - } else { - non_sql_error(MYSQLND_CR_SERVER_GONE_ERROR, - MYSQLND_SERVER_GONE "%s%s", - socket->errCode ? " due to " : "", - socket->errCode ? socket->errMsg : ""); - } - /* don't send QUIT after IO error */ - quit = true; - close(); - } - - void proto_error(const char *data, const enum sw_mysql_packet_types expected_type) { - mysql::server_packet packet(data); - non_sql_error(MYSQLND_CR_MALFORMED_PACKET, - "Unexpected mysql packet length=%u, number=%u, type=%u, expected_type=%u", - packet.header.length, - packet.header.number, - (uint8_t) data[SW_MYSQL_PACKET_HEADER_SIZE], - expected_type); - close(); - } - - void server_error(const char *data) { - mysql::err_packet err_packet(data); - error_code = err_packet.code; - error_msg = - std_string::format("SQLSTATE[%s] [%d] %s", err_packet.sql_state, err_packet.code, err_packet.msg.c_str()); - state = SW_MYSQL_STATE_IDLE; - } - - inline bool get_fetch_mode() { - return fetch_mode; - } - - inline bool set_fetch_mode(bool v) { - if (sw_unlikely(socket && v)) { - non_sql_error(ENOTSUP, "Can not use fetch mode after the connection is established"); - return false; - } - fetch_mode = v; - return true; - } - - inline bool get_defer() { - return defer; - } - - inline bool set_defer(bool v) { - // if (sw_unlikely(fetch_mode && v)) - // { - // non_sql_error(ENOTSUP, "Can not use defer mode when fetch mode is on"); - // return false; - // } - defer = v; - return true; - } - - void add_timeout_controller(double timeout, const enum Socket::TimeoutType type) { - if (sw_unlikely(!socket)) { - return; - } - // Notice: `timeout > 0` is wrong, maybe -1 - if (timeout != 0) { - SW_ASSERT(!tc); - tc = new Socket::timeout_controller(socket, timeout, type); - } - } - - inline bool has_timedout(enum Socket::TimeoutType type) { - return tc && tc->has_timedout(type); - } - - void del_timeout_controller() { - if (tc) { - delete tc; - tc = nullptr; - } - } - - bool connect(std::string host, uint16_t port, bool ssl); - - inline bool connect() { - return connect(host, port, ssl); - } - - inline bool is_connected() { - return socket && socket->is_connected(); - } - - inline int get_fd() { - return socket ? socket->get_fd() : -1; - } - - inline bool check_connection() { - if (sw_unlikely(!is_connected())) { - non_sql_error(MYSQLND_CR_CONNECTION_ERROR, "%s or %s", strerror(ECONNRESET), strerror(ENOTCONN)); - return false; - } - return true; - } - - inline bool check_liveness() { - if (sw_unlikely(!check_connection())) { - return false; - } - if (sw_unlikely(!socket->check_liveness())) { - non_sql_error(MYSQLND_CR_SERVER_GONE_ERROR, MYSQLND_SERVER_GONE); - close(); - return false; - } - return true; - } - - inline bool is_writable() { - return is_connected() && !socket->has_bound(SW_EVENT_WRITE); - } - - bool is_available_for_new_request() { - if (sw_unlikely(state != SW_MYSQL_STATE_IDLE && state != SW_MYSQL_STATE_CLOSED)) { - if (socket) { - socket->check_bound_co(SW_EVENT_RDWR); - } - non_sql_error(EINPROGRESS, - "MySQL client is busy now on state#%d, " - "please use recv/fetchAll/nextResult to get all unread data " - "and wait for response then try again", - state); - return false; - } - if (sw_unlikely(!check_liveness())) { - return false; - } else { - /* without unread data */ - String *buffer = socket->get_read_buffer(); - SW_ASSERT(buffer->length == (size_t) buffer->offset); - buffer->clear(); - return true; - } - } - - const char *recv_packet(); - - inline const char *recv_none_error_packet() { - const char *data = recv_packet(); - if (sw_unlikely(data && mysql::server_packet::is_err(data))) { - server_error(data); - return nullptr; - } - return data; - } - - inline const char *recv_eof_packet() { - const char *data = recv_packet(); - if (sw_unlikely(data && !mysql::server_packet::is_eof(data))) { - proto_error(data, SW_MYSQL_PACKET_EOF); - return nullptr; - } -#ifdef SW_LOG_TRACE_OPEN - mysql::eof_packet eof_packet(data); -#endif - return data; - } - - inline bool send_raw(const char *data, size_t length) { - if (sw_unlikely(!check_connection())) { - return false; - } else { - if (sw_unlikely(has_timedout(Socket::TIMEOUT_WRITE))) { - io_error(); - return false; - } - if (sw_unlikely(socket->send_all(data, length) != (ssize_t) length)) { - io_error(); - return false; - } - return true; - } - } - - bool send_packet(mysql::client_packet *packet); - bool send_command(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0); - // just for internal - void send_command_without_check(enum sw_mysql_command command, const char *sql = nullptr, size_t length = 0); - - void query(zval *return_value, const char *statement, size_t statement_length); - void send_query_request(zval *return_value, const char *statement, size_t statement_length); - void recv_query_response(zval *return_value); - const char *handle_row_data_size(mysql::row_data *row_data, uint8_t size); - bool handle_row_data_lcb(mysql::row_data *row_data); - void handle_row_data_text(zval *return_value, mysql::row_data *row_data, mysql::field_packet *field); - void handle_strict_type(zval *ztext, mysql::field_packet *field); - void fetch(zval *return_value); - void fetch_all(zval *return_value); - void next_result(zval *return_value); - bool recv(); - - bool send_prepare_request(const char *statement, size_t statement_length); - MysqlStatement *recv_prepare_response(); - - void close(); - void socket_dtor(); - - ~MysqlClient() { - SW_ASSERT(statements.empty()); - close(); - } - - private: - int error_code = 0; - std::string error_msg = ""; - - /* unable to support both features at the same time, so we have to set them by method {{{ */ - bool fetch_mode = false; - bool defer = false; - /* }}} */ - - // recv data of specified length - const char *recv_length(size_t need_length, const bool try_to_recycle = false); - // usually mysql->connect = connect(TCP) + handshake - bool handshake(); -}; - -class MysqlStatement { - public: - std::string statement; - mysql::statement info; - mysql::result_info result; - - MysqlStatement(MysqlClient *client, const char *statement, size_t statement_length) : client(client) { - this->statement = std::string(statement, statement_length); - } - - inline MysqlClient *get_client() { - return client; - } - - inline int get_error_code() { - return sw_likely(client) ? client->get_error_code() : error_code; - } - - inline const char *get_error_msg() { - return sw_likely(client) ? client->get_error_msg() : error_msg.c_str(); - } - - inline bool is_available() { - if (sw_unlikely(!client)) { - error_code = ECONNRESET; - error_msg = "statement must to be recompiled after the connection is broken"; - return false; - } - return true; - } - - inline bool is_available_for_new_request() { - if (sw_unlikely(!is_available())) { - return false; - } - if (sw_unlikely(!client->is_available_for_new_request())) { - return false; - } - return true; - } - - inline void add_timeout_controller(double timeout, const enum Socket::TimeoutType type) { - if (sw_likely(client)) { - client->add_timeout_controller(timeout, type); - } - } - - inline void del_timeout_controller() { - if (sw_likely(client)) { - client->del_timeout_controller(); - } - } - - // [notify = false]: Client actively close - inline void close(const bool notify = true) { - if (client) { - // if client point exists, socket is always available - if (notify) { - if (sw_likely(client->is_writable())) { - char id[4]; - sw_mysql_int4store(id, info.id); - client->send_command_without_check(SW_MYSQL_COM_STMT_CLOSE, id, sizeof(id)); - } - client->statements.erase(info.id); - } else { - error_code = client->get_error_code(); - error_msg = client->get_error_msg(); - } - client = nullptr; - } - } - - ~MysqlStatement() { - close(); - } - - bool send_prepare_request(); - bool recv_prepare_response(); - - void execute(zval *return_value, zval *params); - void send_execute_request(zval *return_value, zval *params); - void recv_execute_response(zval *return_value); - - void fetch(zval *return_value); - void fetch_all(zval *return_value); - void next_result(zval *return_value); - - private: - MysqlClient *client = nullptr; - int error_code = 0; - std::string error_msg; -}; -} // namespace swoole - -using Client = swoole::MysqlClient; -using Statement = swoole::MysqlStatement; -namespace mysql = swoole::mysql; - -static zend_class_entry *swoole_mysql_coro_ce; -static zend_object_handlers swoole_mysql_coro_handlers; - -static zend_class_entry *swoole_mysql_coro_exception_ce; -static zend_object_handlers swoole_mysql_coro_exception_handlers; - -static zend_class_entry *swoole_mysql_coro_statement_ce; -static zend_object_handlers swoole_mysql_coro_statement_handlers; - -struct MysqlClientObject { - Client *client; - zend_object std; -}; - -struct MysqlStatementObject { - Statement *statement; - zend_object *zclient; - zend_object std; -}; - -SW_EXTERN_C_BEGIN -static PHP_METHOD(swoole_mysql_coro, __construct); -static PHP_METHOD(swoole_mysql_coro, __destruct); -static PHP_METHOD(swoole_mysql_coro, connect); -static PHP_METHOD(swoole_mysql_coro, getDefer); -static PHP_METHOD(swoole_mysql_coro, setDefer); -static PHP_METHOD(swoole_mysql_coro, query); -static PHP_METHOD(swoole_mysql_coro, fetch); -static PHP_METHOD(swoole_mysql_coro, fetchAll); -static PHP_METHOD(swoole_mysql_coro, nextResult); -static PHP_METHOD(swoole_mysql_coro, prepare); -static PHP_METHOD(swoole_mysql_coro, recv); -static PHP_METHOD(swoole_mysql_coro, begin); -static PHP_METHOD(swoole_mysql_coro, commit); -static PHP_METHOD(swoole_mysql_coro, rollback); -#ifdef SW_USE_MYSQLND -static PHP_METHOD(swoole_mysql_coro, escape); -#endif -static PHP_METHOD(swoole_mysql_coro, close); - -static PHP_METHOD(swoole_mysql_coro_statement, execute); -static PHP_METHOD(swoole_mysql_coro_statement, fetch); -static PHP_METHOD(swoole_mysql_coro_statement, fetchAll); -static PHP_METHOD(swoole_mysql_coro_statement, nextResult); -static PHP_METHOD(swoole_mysql_coro_statement, recv); -static PHP_METHOD(swoole_mysql_coro_statement, close); -SW_EXTERN_C_END - -// clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_optional_timeout, 0, 0, 0) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_connect, 0, 0, 0) - ZEND_ARG_ARRAY_INFO(0, server_config, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_query, 0, 0, 1) - ZEND_ARG_INFO(0, sql) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_prepare, 0, 0, 1) - ZEND_ARG_INFO(0, query) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_setDefer, 0, 0, 0) - ZEND_ARG_INFO(0, defer) -ZEND_END_ARG_INFO() - -#ifdef SW_USE_MYSQLND -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_escape, 0, 0, 1) - ZEND_ARG_INFO(0, string) - ZEND_ARG_INFO(0, flags) -ZEND_END_ARG_INFO() -#endif - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_coro_statement_execute, 0, 0, 0) - ZEND_ARG_INFO(0, params) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -static const zend_function_entry swoole_mysql_coro_methods[] = -{ - PHP_ME(swoole_mysql_coro, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_mysql_coro, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, getDefer, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, setDefer, arginfo_swoole_mysql_coro_setDefer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, connect, arginfo_swoole_mysql_coro_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, query, arginfo_swoole_mysql_coro_query, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, fetch, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, fetchAll, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, nextResult, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, prepare, arginfo_swoole_mysql_coro_prepare, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, recv, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, begin, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, commit, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro, rollback, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) -#ifdef SW_USE_MYSQLND - PHP_ME(swoole_mysql_coro, escape, arginfo_swoole_mysql_coro_escape, ZEND_ACC_PUBLIC) -#endif - PHP_ME(swoole_mysql_coro, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_FE_END -}; - -static const zend_function_entry swoole_mysql_coro_statement_methods[] = -{ - PHP_ME(swoole_mysql_coro_statement, execute, arginfo_swoole_mysql_coro_statement_execute, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, fetch, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, fetchAll, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, nextResult, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, recv, arginfo_swoole_optional_timeout, ZEND_ACC_PUBLIC) - PHP_ME(swoole_mysql_coro_statement, close, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_FE_END -}; -// clang-format on - -void php_swoole_sha256(const char *str, int len, unsigned char *digest) { - PHP_SHA256_CTX context; - PHP_SHA256Init(&context); - PHP_SHA256Update(&context, (unsigned char *) str, len); - PHP_SHA256Final(digest, &context); -} - -bool Client::connect(std::string host, uint16_t port, bool ssl) { - if (socket && (host != this->host || port != this->port || ssl != this->ssl)) { - close(); - } - if (socket) { - return true; - } - enum swSocketType socket_type; - if (host.compare(0, 6, "unix:/", 0, 6) == 0) { - host = host.substr(sizeof("unix:") - 1); - host.erase(0, host.find_first_not_of('/') - 1); - socket_type = SW_SOCK_UNIX_STREAM; - } else if (host.find(':') != std::string::npos) { - socket_type = SW_SOCK_TCP6; - } else { - socket_type = SW_SOCK_TCP; - } - auto object = php_swoole_create_socket(socket_type); - if (UNEXPECTED(!object)) { - non_sql_error(MYSQLND_CR_CONNECTION_ERROR, strerror(errno)); - return false; - } - ZVAL_OBJ(&zsocket, object); - zend_update_property(Z_OBJCE_P(&zobject), SW_Z8_OBJ_P(&zobject), ZEND_STRL("socket"), &zsocket); - - socket = php_swoole_get_socket(&zsocket); - socket->set_zero_copy(true); - socket->set_dtor([this](Socket *) { socket_dtor(); }); -#ifdef SW_USE_OPENSSL - if (ssl) { - socket->enable_ssl_encrypt(); - } -#endif - socket->set_timeout(connect_timeout, Socket::TIMEOUT_CONNECT); - add_timeout_controller(connect_timeout, Socket::TIMEOUT_ALL); - if (!socket->connect(host, port)) { - io_error(); - return false; - } - this->host = host; - this->port = port; -#ifdef SW_USE_OPENSSL - this->ssl = ssl; -#endif - if (!handshake()) { - close(); - return false; - } - state = SW_MYSQL_STATE_IDLE; - quit = false; - del_timeout_controller(); - return true; -} - -const char *Client::recv_length(size_t need_length, const bool try_to_recycle) { - if (sw_likely(check_connection())) { - ssize_t retval; - String *buffer = socket->get_read_buffer(); - off_t offset = buffer->offset; // save offset instead of buffer point (due to realloc) - size_t read_n = buffer->length - buffer->offset; // readable bytes - if (try_to_recycle && read_n == 0) { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "mysql buffer will be recycled, length=%zu, offset=%jd", - buffer->length, - (intmax_t) offset); - buffer->clear(); - offset = 0; - } - while (read_n < need_length) { - if (sw_unlikely(has_timedout(Socket::TIMEOUT_READ))) { - io_error(); - return nullptr; - } - if (sw_unlikely(buffer->length == buffer->size)) { - /* offset + need_length = new size (min) */ - if (!buffer->extend(SW_MEM_ALIGNED_SIZE_EX(offset + need_length, swoole_pagesize()))) { - non_sql_error(MYSQLND_CR_OUT_OF_MEMORY, strerror(ENOMEM)); - return nullptr; - } else { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "mysql buffer extend to %zu", buffer->size); - } - } - retval = socket->recv(buffer->str + buffer->length, buffer->size - buffer->length); - if (sw_unlikely(retval <= 0)) { - io_error(); - return nullptr; - } - read_n += retval; - buffer->length += retval; - } - buffer->offset += need_length; - return buffer->str + offset; - } - return nullptr; -} - -const char *Client::recv_packet() { - const char *p; - uint32_t length; - p = recv_length(SW_MYSQL_PACKET_HEADER_SIZE, true); - if (sw_unlikely(!p)) { - return nullptr; - } - length = mysql::packet::get_length(p); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "recv packet length=%u, number=%u", length, mysql::packet::get_number(p)); - p = recv_length(length); - if (sw_unlikely(!p)) { - return nullptr; - } - /* Notice: why we do this? because buffer maybe reallocated when recv data */ - return p - SW_MYSQL_PACKET_HEADER_SIZE; -} - -bool Client::send_packet(mysql::client_packet *packet) { - const char *data = packet->get_data(); - uint32_t length = SW_MYSQL_PACKET_HEADER_SIZE + packet->get_length(); - if (sw_likely(send_raw(data, length))) { - return true; - } - return false; -} - -bool Client::send_command(enum sw_mysql_command command, const char *sql, size_t length) { - if (sw_likely(SW_MYSQL_PACKET_HEADER_SIZE + 1 + length <= swoole_pagesize())) { - mysql::command_packet command_packet(command, sql, length); - return send_raw(command_packet.get_data(), command_packet.get_data_length()); - } else { - /* if the data is larger than page_size, copy memory to the kernel buffer multiple times is much faster */ - size_t send_s = SW_MIN(length, SW_MYSQL_MAX_PACKET_BODY_SIZE - 1), send_n = send_s, number = 0; - mysql::command_packet command_packet(command); - command_packet.set_header(1 + send_s, number++); - - if (sw_unlikely(!send_raw(command_packet.get_data(), SW_MYSQL_PACKET_HEADER_SIZE + 1)) || - !send_raw(sql, send_s)) { - return false; - } - /* MySQL single packet size is 16M, we must subpackage */ - while (send_n < length) { - send_s = length - send_n; - send_s = SW_MIN(send_s, SW_MYSQL_MAX_PACKET_BODY_SIZE); - command_packet.set_header(send_s, number++); - if (sw_unlikely(!send_raw(command_packet.get_data(), SW_MYSQL_PACKET_HEADER_SIZE)) || - !send_raw(sql + send_n, send_s)) { - return false; - } - send_n += send_s; - } - return true; - } -} - -void Client::send_command_without_check(enum sw_mysql_command command, const char *sql, size_t length) { - mysql::command_packet command_packet(command, sql, length); - (void) (socket && socket->send(command_packet.get_data(), command_packet.get_data_length())); -} - -bool Client::handshake() { - const char *data; - // recv greeting pakcet - if (sw_unlikely(!(data = recv_none_error_packet()))) { - return false; - } - mysql::greeting_packet greeting_packet(data); - // generate login packet - do { - mysql::login_packet login_packet(&greeting_packet, user, password, database, charset); - if (sw_unlikely(!send_packet(&login_packet))) { - return false; - } - } while (0); - // recv auth switch request packet, 4 possible packet types - switch (mysql::server_packet::parse_type(data = recv_packet())) { - case SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST: { - mysql::auth_switch_request_packet request(data); - mysql::auth_switch_response_packet response(&request, password); - if (sw_unlikely(!send_packet(&response))) { - return false; - } - break; - } - case SW_MYSQL_PACKET_AUTH_SIGNATURE_REQUEST: { - mysql::auth_signature_request_packet request(data); - if (sw_unlikely(!request.is_vaild())) { - goto _proto_error; - } - if (sw_likely(!request.is_full_auth_required())) { - break; - } - // no cache, need full auth with rsa key (openssl required) -#ifdef SW_MYSQL_RSA_SUPPORT - // tell the server we are prepared - do { - mysql::auth_signature_prepared_packet prepared(request.header.number + 1); - if (sw_unlikely(!send_packet(&prepared))) { - return false; - } - } while (0); - // recv rsa key and encode the password - do { - if (sw_unlikely(!(data = recv_none_error_packet()))) { - return false; - } - mysql::raw_data_packet raw_data_packet(data); - mysql::auth_signature_response_packet response( - &raw_data_packet, password, greeting_packet.auth_plugin_data); - if (sw_unlikely(!send_packet(&response))) { - return false; - } - } while (0); - break; -#else - error_code = EPROTONOSUPPORT; - error_msg = SW_MYSQL_NO_RSA_ERROR; - return false; -#endif - } - case SW_MYSQL_PACKET_OK: { -#ifdef SW_LOG_TRACE_OPEN - mysql::ok_packet ok_packet(data); -#endif - return true; - } - case SW_MYSQL_PACKET_ERR: - server_error(data); - return false; - case SW_MYSQL_PACKET_NULL: - // io_error - return false; - default: - _proto_error: - proto_error(data, SW_MYSQL_PACKET_AUTH_SWITCH_REQUEST); - return false; - } - // maybe ok packet or err packet - if (sw_unlikely(!(data = recv_none_error_packet()))) { - return false; - } -#ifdef SW_LOG_TRACE_OPEN - mysql::ok_packet ok_packet(data); -#endif - return true; -} - -void Client::query(zval *return_value, const char *statement, size_t statement_length) { - send_query_request(return_value, statement, statement_length); - if (EXPECTED(!defer && Z_TYPE_P(return_value) == IS_TRUE)) { - recv_query_response(return_value); - } -} - -void Client::send_query_request(zval *return_value, const char *statement, size_t statement_length) { - if (sw_unlikely(!is_available_for_new_request())) { - RETURN_FALSE; - } - if (sw_unlikely(!send_command(SW_MYSQL_COM_QUERY, statement, statement_length))) { - RETURN_FALSE; - } - state = SW_MYSQL_STATE_QUERY; - RETURN_TRUE; -}; - -void Client::recv_query_response(zval *return_value) { - const char *data; - if (sw_unlikely(!(data = recv_none_error_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_ok(data)) { - mysql::ok_packet ok_packet(data); - result.ok = ok_packet; - state = ok_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_QUERY_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_TRUE; - } - do { - mysql::lcb_packet lcb_packet(data); - if (sw_unlikely(lcb_packet.length == 0)) { - // is it possible? - proto_error(data, SW_MYSQL_PACKET_FIELD); - RETURN_FALSE; - } - result.alloc_fields(lcb_packet.length); - for (uint32_t i = 0; i < lcb_packet.length; i++) { - if (sw_unlikely(!(data = recv_packet()))) { - RETURN_FALSE; - } - result.set_field(i, data); - } - } while (0); - // expect eof - if (sw_unlikely(!(data = recv_eof_packet()))) { - RETURN_FALSE; - } - state = SW_MYSQL_STATE_QUERY_FETCH; - if (get_fetch_mode()) { - RETURN_TRUE; - } - fetch_all(return_value); -} - -const char *Client::handle_row_data_size(mysql::row_data *row_data, uint8_t size) { - const char *p, *data; - SW_ASSERT(size < sizeof(row_data->stack_buffer)); - if (sw_unlikely(!(p = row_data->read(size)))) { - uint8_t received = row_data->recv(row_data->stack_buffer, size); - if (sw_unlikely(!(data = recv_packet()))) { - return nullptr; - } - row_data->next_packet(data); - received += row_data->recv(row_data->stack_buffer + received, size - received); - if (sw_unlikely(received != size)) { - proto_error(data, SW_MYSQL_PACKET_ROW_DATA); - return nullptr; - } - p = row_data->stack_buffer; - } - return p; -} - -bool Client::handle_row_data_lcb(mysql::row_data *row_data) { - const char *p, *data; - // recv 1 byte to get binary code size - if (sw_unlikely(row_data->eof())) { - if (sw_unlikely(!(data = recv_packet()))) { - return false; - } - row_data->next_packet(data); - if (sw_unlikely(row_data->eof())) { - proto_error(data, SW_MYSQL_PACKET_ROW_DATA); - return false; - } - } - // decode lcb (use 0 to prevent read_ptr from moving) - // recv "size" bytes to get binary code length - p = handle_row_data_size(row_data, mysql::read_lcb_size(row_data->read(0))); - if (sw_unlikely(!p)) { - return false; - } - mysql::read_lcb(p, &row_data->text.length, &row_data->text.nul); - return true; -} - -void Client::handle_row_data_text(zval *return_value, mysql::row_data *row_data, mysql::field_packet *field) { - const char *p, *data; - if (sw_unlikely(!handle_row_data_lcb(row_data))) { - RETURN_FALSE; - } - if (sw_unlikely(!(p = row_data->read(row_data->text.length)))) { - size_t received = 0, required = row_data->text.length; - if (required < sizeof(row_data->stack_buffer)) { - p = handle_row_data_size(row_data, required); - if (sw_unlikely(!p)) { - RETURN_FALSE; - } - } else { - zend_string *zstring = zend_string_alloc(required, 0); - do { - received += row_data->recv(ZSTR_VAL(zstring) + received, required - received); - if (received == required) { - break; - } - if (row_data->eof()) { - if (sw_unlikely(!(data = recv_packet()))) { - RETURN_FALSE; - } - row_data->next_packet(data); - } - } while (true); - ZSTR_VAL(zstring)[ZSTR_LEN(zstring)] = '\0'; - RETVAL_STR(zstring); - goto _return; - } - } - if (row_data->text.nul || field->type == SW_MYSQL_TYPE_NULL) { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s is null", field->name_length, field->name); - RETURN_NULL(); - } else { - RETVAL_STRINGL(p, row_data->text.length); - _return: - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "%.*s=[%lu]%.*s%s", - field->name_length, - field->name, - Z_STRLEN_P(return_value), - (int) SW_MIN(32, Z_STRLEN_P(return_value)), - Z_STRVAL_P(return_value), - (Z_STRLEN_P(return_value) > 32 ? "..." : "")); - } -} - -void Client::handle_strict_type(zval *ztext, mysql::field_packet *field) { - if (sw_likely(Z_TYPE_P(ztext) == IS_STRING)) { - char *error; - switch (field->type) { - /* String */ - case SW_MYSQL_TYPE_TINY_BLOB: - case SW_MYSQL_TYPE_MEDIUM_BLOB: - case SW_MYSQL_TYPE_LONG_BLOB: - case SW_MYSQL_TYPE_BLOB: - case SW_MYSQL_TYPE_DECIMAL: - case SW_MYSQL_TYPE_NEWDECIMAL: - case SW_MYSQL_TYPE_BIT: - case SW_MYSQL_TYPE_STRING: - case SW_MYSQL_TYPE_VAR_STRING: - case SW_MYSQL_TYPE_VARCHAR: - case SW_MYSQL_TYPE_NEWDATE: - case SW_MYSQL_TYPE_GEOMETRY: - /* Date Time */ - case SW_MYSQL_TYPE_TIME: - case SW_MYSQL_TYPE_YEAR: - case SW_MYSQL_TYPE_TIMESTAMP: - case SW_MYSQL_TYPE_DATETIME: - case SW_MYSQL_TYPE_DATE: - case SW_MYSQL_TYPE_JSON: - return; - /* Integer */ - case SW_MYSQL_TYPE_TINY: - case SW_MYSQL_TYPE_SHORT: - case SW_MYSQL_TYPE_INT24: - case SW_MYSQL_TYPE_LONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - ulong_t uint = strtoul(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, uint); - } - } else { - long sint = strtol(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, sint); - } - } - break; - case SW_MYSQL_TYPE_LONGLONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - unsigned long long ubigint = strtoull(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0' && ubigint <= ZEND_LONG_MAX)) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, ubigint); - } - } else { - long long sbigint = strtoll(Z_STRVAL_P(ztext), &error, 10); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_LONG(ztext, sbigint); - } - } - break; - case SW_MYSQL_TYPE_FLOAT: - case SW_MYSQL_TYPE_DOUBLE: { - double mdouble = strtod(Z_STRVAL_P(ztext), &error); - if (sw_likely(*error == '\0')) { - zend_string_release(Z_STR_P(ztext)); - ZVAL_DOUBLE(ztext, mdouble); - } - break; - } - default: { - swoole_warning("unknown type[%d] for field [%.*s].", field->type, field->name_length, field->name); - break; - } - } - } -} - -void Client::fetch(zval *return_value) { - if (sw_unlikely(!is_connected())) { - RETURN_FALSE; - } - if (sw_unlikely(state != SW_MYSQL_STATE_QUERY_FETCH)) { - RETURN_NULL(); - } - const char *data; - if (sw_unlikely(!(data = recv_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_eof(data)) { - mysql::eof_packet eof_packet(data); - state = - eof_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_QUERY_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_NULL(); - } - do { - mysql::row_data row_data(data); - array_init_size(return_value, result.get_fields_length()); - for (uint32_t i = 0; i < result.get_fields_length(); i++) { - mysql::field_packet *field = result.get_field(i); - zval ztext; - handle_row_data_text(&ztext, &row_data, field); - if (sw_unlikely(Z_TYPE_P(&ztext) == IS_FALSE)) { - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - if (strict_type) { - handle_strict_type(&ztext, field); - } - add_assoc_zval_ex(return_value, field->name, field->name_length, &ztext); - } - } while (0); -} - -void Client::fetch_all(zval *return_value) { - array_init(return_value); - while (true) { - zval zrow; - fetch(&zrow); - if (sw_unlikely(ZVAL_IS_NULL(&zrow))) { - // eof - return; - } - if (sw_unlikely(Z_TYPE_P(&zrow) == IS_FALSE)) { - // error - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - (void) add_next_index_zval(return_value, &zrow); - } -} - -void Client::next_result(zval *return_value) { - if (sw_unlikely(state == SW_MYSQL_STATE_QUERY_FETCH)) { - // skip unread data - fetch_all(return_value); - zval_ptr_dtor(return_value); - next_result(return_value); - } else if (sw_likely(state == SW_MYSQL_STATE_QUERY_MORE_RESULTS)) { - recv_query_response(return_value); - } else if (state == SW_MYSQL_STATE_IDLE) { - RETURN_NULL(); - } else { - RETURN_FALSE; - } -} - -bool Client::send_prepare_request(const char *statement, size_t statement_length) { - this->statement = new Statement(this, statement, statement_length); - if (sw_unlikely(!this->statement->send_prepare_request())) { - delete this->statement; - this->statement = nullptr; - return false; - } - return true; -} - -void Client::socket_dtor() { - zend_update_property_null(Z_OBJCE_P(&zobject), SW_Z8_OBJ_P(&zobject), ZEND_STRL("socket")); - socket = nullptr; - zval_ptr_dtor(&zsocket); - ZVAL_NULL(&zsocket); -} - -Statement *Client::recv_prepare_response() { - if (sw_likely(state == SW_MYSQL_STATE_PREPARE)) { - Statement *statement = this->statement; - SW_ASSERT(statement != nullptr); - this->statement = nullptr; - if (sw_unlikely(!statement->recv_prepare_response())) { - delete statement; - return nullptr; - } - statements[statement->info.id] = statement; - return statement; - } - return nullptr; -} - -void Client::close() { - state = SW_MYSQL_STATE_CLOSED; - Socket *_socket = socket; - if (_socket) { - del_timeout_controller(); - if (!quit && is_writable()) { - send_command_without_check(SW_MYSQL_COM_QUIT); - quit = true; - } - // make statements non-available - while (!statements.empty()) { - auto i = statements.begin(); - i->second->close(false); - statements.erase(i); - } - _socket->close(); - } -} - -bool Statement::send_prepare_request() { - if (sw_unlikely(!is_available_for_new_request())) { - return false; - } - if (sw_unlikely(!client->send_command(SW_MYSQL_COM_STMT_PREPARE, statement.c_str(), statement.length()))) { - return false; - } - client->state = SW_MYSQL_STATE_PREPARE; - return true; -} - -bool Statement::recv_prepare_response() { - if (sw_unlikely(!is_available())) { - return false; - } else { - client->state = SW_MYSQL_STATE_IDLE; - } - const char *data; - if (sw_unlikely(!(data = client->recv_none_error_packet()))) { - return false; - } - info = mysql::statement(data); - if (sw_likely(info.param_count != 0)) { - for (uint16_t i = info.param_count; i--;) { - if (sw_unlikely(!(data = client->recv_packet()))) { - return false; - } -#ifdef SW_LOG_TRACE_OPEN - mysql::param_packet param_packet(data); -#endif - } - if (sw_unlikely(!(data = client->recv_eof_packet()))) { - return false; - } - } - if (info.field_count != 0) { - result.alloc_fields(info.field_count); - for (uint16_t i = 0; i < info.field_count; i++) { - if (sw_unlikely(!(data = client->recv_packet()))) { - return false; - } - result.set_field(i, data); - } - if (sw_unlikely(!(data = client->recv_eof_packet()))) { - return false; - } - } - return true; -} - -void Statement::execute(zval *return_value, zval *params) { - send_execute_request(return_value, params); - /* Notice: must check return_value first */ - if (EXPECTED(Z_TYPE_P(return_value) == IS_TRUE && !client->get_defer())) { - recv_execute_response(return_value); - } -} - -void Statement::send_execute_request(zval *return_value, zval *params) { - if (sw_unlikely(!is_available_for_new_request())) { - RETURN_FALSE; - } - - uint32_t param_count = params ? php_swoole_array_length(params) : 0; - - if (sw_unlikely(param_count != info.param_count)) { - client->non_sql_error(MYSQLND_CR_INVALID_PARAMETER_NO, - "Statement#%u expects %u parameter, %u given.", - info.id, - info.param_count, - param_count); - RETURN_FALSE; - } - - String *buffer = client->socket->get_write_buffer(); - char *p = buffer->str; - - memset(p, 0, 5); - // command - buffer->str[4] = SW_MYSQL_COM_STMT_EXECUTE; - buffer->length = 5; - p += 5; - - // stmt.id - sw_mysql_int4store(p, info.id); - p += 4; - // flags = CURSOR_TYPE_NO_CURSOR - sw_mysql_int1store(p, 0); - p += 1; - // iteration_count - sw_mysql_int4store(p, 1); - p += 4; - buffer->length += 9; - - // TODO: support more types - if (param_count != 0) { - // null bitmap - size_t null_start_offset = p - buffer->str; - unsigned int map_size = (param_count + 7) / 8; - memset(p, 0, map_size); - p += map_size; - buffer->length += map_size; - - // rebind - sw_mysql_int1store(p, 1); - p += 1; - buffer->length += 1; - - size_t type_start_offset = p - buffer->str; - p += param_count * 2; - buffer->length += param_count * 2; - - char stack_buffer[10]; - zend_ulong index = 0; - zval *value; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(params), value) { - switch (client->strict_type ? Z_TYPE_P(value) : (IS_NULL == Z_TYPE_P(value) ? IS_NULL : IS_STRING)) { - case IS_NULL: - *((buffer->str + null_start_offset) + (index / 8)) |= (1UL << (index % 8)); - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_NULL); - break; - case IS_TRUE: - case IS_FALSE: - case IS_LONG: - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_LONGLONG); - sw_mysql_int8store(stack_buffer, zval_get_long(value)); - if (buffer->append(stack_buffer, mysql::get_static_type_size(SW_MYSQL_TYPE_LONGLONG)) < 0) { - RETURN_FALSE; - } - break; - case IS_DOUBLE: - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_DOUBLE); - sw_mysql_doublestore(stack_buffer, zval_get_double(value)); - if (buffer->append(stack_buffer, mysql::get_static_type_size(SW_MYSQL_TYPE_DOUBLE)) < 0) { - RETURN_FALSE; - } - break; - default: - zend::String str_value(value); - uint8_t lcb_size = mysql::write_lcb(stack_buffer, str_value.len()); - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_VAR_STRING); - if (buffer->append(stack_buffer, lcb_size) < 0) { - RETURN_FALSE; - } - if (buffer->append(str_value.val(), str_value.len()) < 0) { - RETURN_FALSE; - } - } - index++; - } - ZEND_HASH_FOREACH_END(); - } - do { - size_t length = buffer->length - SW_MYSQL_PACKET_HEADER_SIZE; - size_t send_s = SW_MIN(length, SW_MYSQL_MAX_PACKET_BODY_SIZE); - mysql::packet::set_header(buffer->str, send_s, 0); - if (sw_unlikely(!client->send_raw(buffer->str, SW_MYSQL_PACKET_HEADER_SIZE + send_s))) { - RETURN_FALSE; - } - if (sw_unlikely(length > SW_MYSQL_MAX_PACKET_BODY_SIZE)) { - size_t send_n = SW_MYSQL_MAX_PACKET_BODY_SIZE, number = 1; - /* MySQL single packet size is 16M, we must subpackage */ - while (send_n < length) { - send_s = length - send_n; - send_s = SW_MIN(send_s, SW_MYSQL_MAX_PACKET_BODY_SIZE); - mysql::packet::set_header(buffer->str, send_s, number++); - if (sw_unlikely(!client->send_raw(buffer->str, SW_MYSQL_PACKET_HEADER_SIZE)) || - !client->send_raw(buffer->str + SW_MYSQL_PACKET_HEADER_SIZE + send_n, send_s)) { - RETURN_FALSE; - } - send_n += send_s; - } - } - } while (0); - client->state = SW_MYSQL_STATE_EXECUTE; - RETURN_TRUE; -} - -void Statement::recv_execute_response(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - const char *data; - if (sw_unlikely(!(data = client->recv_none_error_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_ok(data)) { - mysql::ok_packet ok_packet(data); - result.ok = ok_packet; - client->state = - ok_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_EXECUTE_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_TRUE; - } - do { - mysql::lcb_packet lcb_packet(data); - if (sw_unlikely(lcb_packet.length == 0)) { - // is it possible? - client->proto_error(data, SW_MYSQL_PACKET_FIELD); - RETURN_FALSE; - } - // although we have already known the field data when we prepared the statement, - // we don't know if the data is always reliable, such as when we using stored procedure... - // so we should not optimize here for the time being for stability - result.alloc_fields(lcb_packet.length); - for (size_t i = 0; i < result.get_fields_length(); i++) { - if (sw_unlikely(!(data = client->recv_packet()))) { - RETURN_FALSE; - } - result.set_field(i, data); - } - } while (0); - // expect eof - if (sw_unlikely(!(data = client->recv_eof_packet()))) { - RETURN_FALSE; - } - client->state = SW_MYSQL_STATE_EXECUTE_FETCH; - if (client->get_fetch_mode()) { - RETURN_TRUE; - } - fetch_all(return_value); -} - -void Statement::fetch(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - if (sw_unlikely(client->state != SW_MYSQL_STATE_EXECUTE_FETCH)) { - RETURN_NULL(); - } - const char *data; - if (sw_unlikely(!(data = client->recv_packet()))) { - RETURN_FALSE; - } - if (mysql::server_packet::is_eof(data)) { - mysql::eof_packet eof_packet(data); - client->state = - eof_packet.server_status.more_results_exists() ? SW_MYSQL_STATE_EXECUTE_MORE_RESULTS : SW_MYSQL_STATE_IDLE; - RETURN_NULL(); - } - do { - mysql::row_data row_data(data); - uint32_t null_bitmap_size = mysql::null_bitmap::get_size(result.get_fields_length()); - mysql::null_bitmap null_bitmap(row_data.read(null_bitmap_size), null_bitmap_size); - - array_init_size(return_value, result.get_fields_length()); - for (uint32_t i = 0; i < result.get_fields_length(); i++) { - mysql::field_packet *field = result.get_field(i); - - /* to check Null-Bitmap @see https://dev.mysql.com/doc/internals/en/null-bitmap.html */ - if (null_bitmap.is_null(i) || field->type == SW_MYSQL_TYPE_NULL) { - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s is null", field->name_length, field->name); - add_assoc_null_ex(return_value, field->name, field->name_length); - continue; - } - - switch (field->type) { - /* String */ - case SW_MYSQL_TYPE_TINY_BLOB: - case SW_MYSQL_TYPE_MEDIUM_BLOB: - case SW_MYSQL_TYPE_LONG_BLOB: - case SW_MYSQL_TYPE_BLOB: - case SW_MYSQL_TYPE_DECIMAL: - case SW_MYSQL_TYPE_NEWDECIMAL: - case SW_MYSQL_TYPE_BIT: - case SW_MYSQL_TYPE_JSON: - case SW_MYSQL_TYPE_STRING: - case SW_MYSQL_TYPE_VAR_STRING: - case SW_MYSQL_TYPE_VARCHAR: - case SW_MYSQL_TYPE_NEWDATE: - case SW_MYSQL_TYPE_GEOMETRY: { - _add_string: - zval ztext; - client->handle_row_data_text(&ztext, &row_data, field); - if (sw_unlikely(Z_TYPE_P(&ztext) == IS_FALSE)) { - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - add_assoc_zval_ex(return_value, field->name, field->name_length, &ztext); - break; - } - default: { - const char *p = nullptr; - uint8_t lcb = mysql::get_static_type_size(field->type); - if (lcb == 0) { - client->handle_row_data_lcb(&row_data); - lcb = row_data.text.length; - } - p = client->handle_row_data_size(&row_data, lcb); - if (sw_unlikely(!p)) { - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - /* Date Time */ - switch (field->type) { - case SW_MYSQL_TYPE_TIMESTAMP: - case SW_MYSQL_TYPE_DATETIME: { - std::string datetime = mysql::datetime(p, row_data.text.length, field->decimals); - add_assoc_stringl_ex( - return_value, field->name, field->name_length, (char *) datetime.c_str(), datetime.length()); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, datetime.c_str()); - break; - } - case SW_MYSQL_TYPE_TIME: { - std::string time = mysql::time(p, row_data.text.length, field->decimals); - add_assoc_stringl_ex( - return_value, field->name, field->name_length, (char *) time.c_str(), time.length()); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, time.c_str()); - break; - } - case SW_MYSQL_TYPE_DATE: { - std::string date = mysql::date(p, row_data.text.length); - add_assoc_stringl_ex( - return_value, field->name, field->name_length, (char *) date.c_str(), date.length()); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%s", field->name_length, field->name, date.c_str()); - break; - } - case SW_MYSQL_TYPE_YEAR: { -#if PHP_VERSION_ID >= 80100 - std::string year = mysql::year(p, row_data.text.length); - add_assoc_stringl_ex( - return_value, field->name, field->name_length, (char *) year.c_str(), year.length()); -#else - add_assoc_long_ex(return_value, field->name, field->name_length, sw_mysql_uint2korr2korr(p)); -#endif - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, sw_mysql_uint2korr2korr(p)); - break; - } - /* Number */ - case SW_MYSQL_TYPE_TINY: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_long_ex(return_value, field->name, field->name_length, *(uint8_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint8_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int8_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int8_t *) p); - } - break; - case SW_MYSQL_TYPE_SHORT: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_long_ex(return_value, field->name, field->name_length, *(uint16_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint16_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int16_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int16_t *) p); - } - break; - case SW_MYSQL_TYPE_INT24: - case SW_MYSQL_TYPE_LONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_long_ex(return_value, field->name, field->name_length, *(uint32_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%u", field->name_length, field->name, *(uint32_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int32_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%d", field->name_length, field->name, *(int32_t *) p); - } - break; - case SW_MYSQL_TYPE_LONGLONG: - if (field->flags & SW_MYSQL_UNSIGNED_FLAG) { - add_assoc_ulong_safe_ex(return_value, field->name, field->name_length, *(uint64_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%" PRIu64, field->name_length, field->name, *(uint64_t *) p); - } else { - add_assoc_long_ex(return_value, field->name, field->name_length, *(int64_t *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%" PRId64, field->name_length, field->name, *(int64_t *) p); - } - break; - case SW_MYSQL_TYPE_FLOAT: { - double dv = sw_php_math_round(*(float *) p, 7, PHP_ROUND_HALF_DOWN); - add_assoc_double_ex(return_value, field->name, field->name_length, dv); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "%.*s=%.7f", field->name_length, field->name, dv); - } break; - case SW_MYSQL_TYPE_DOUBLE: { - add_assoc_double_ex(return_value, field->name, field->name_length, *(double *) p); - swoole_trace_log( - SW_TRACE_MYSQL_CLIENT, "%.*s=%.16f", field->name_length, field->name, *(double *) p); - } break; - default: - swoole_warning("unknown type[%d] for field [%.*s].", field->type, field->name_length, field->name); - goto _add_string; - } - } - } - } - } while (0); -} - -void Statement::fetch_all(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - - zval zrow; - array_init(return_value); - while (true) { - fetch(&zrow); - if (sw_unlikely(ZVAL_IS_NULL(&zrow))) { - // eof - return; - } - if (sw_unlikely(Z_TYPE_P(&zrow) == IS_FALSE)) { - // error - zval_ptr_dtor(return_value); - RETURN_FALSE; - } - (void) add_next_index_zval(return_value, &zrow); - } -} - -void Statement::next_result(zval *return_value) { - if (sw_unlikely(!is_available())) { - RETURN_FALSE; - } - if (sw_unlikely(client->state == SW_MYSQL_STATE_EXECUTE_FETCH)) { - // skip unread data - fetch_all(return_value); - zval_ptr_dtor(return_value); - next_result(return_value); - } else if (sw_likely(client->state == SW_MYSQL_STATE_EXECUTE_MORE_RESULTS)) { - recv_execute_response(return_value); - } else if (client->state == SW_MYSQL_STATE_IDLE) { - RETURN_NULL(); - } else { - RETURN_FALSE; - } -} - -static sw_inline MysqlClientObject *mysql_coro_fetch_object(zend_object *obj) { - return (MysqlClientObject *) ((char *) obj - swoole_mysql_coro_handlers.offset); -} - -static sw_inline Client *mysql_coro_get_client(zval *zobject) { - return mysql_coro_fetch_object(Z_OBJ_P(zobject))->client; -} - -static void mysql_coro_free_object(zend_object *object) { - MysqlClientObject *zmc = mysql_coro_fetch_object(object); - delete zmc->client; - zend_object_std_dtor(&zmc->std); -} - -static zend_object *mysql_coro_create_object(zend_class_entry *ce) { - MysqlClientObject *zmc = (MysqlClientObject *) zend_object_alloc(sizeof(MysqlClientObject), ce); - zend_object_std_init(&zmc->std, ce); - object_properties_init(&zmc->std, ce); - zmc->std.handlers = &swoole_mysql_coro_handlers; - zmc->client = new Client; - ZVAL_OBJ(&zmc->client->zobject, &zmc->std); - return &zmc->std; -} - -static sw_inline MysqlStatementObject *mysql_coro_statement_fetch_object(zend_object *obj) { - return (MysqlStatementObject *) ((char *) obj - swoole_mysql_coro_statement_handlers.offset); -} - -static sw_inline Statement *mysql_coro_get_statement(zval *zobject) { - return mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->statement; -} - -static void mysql_coro_statement_free_object(zend_object *object) { - MysqlStatementObject *zms = mysql_coro_statement_fetch_object(object); - delete zms->statement; - OBJ_RELEASE(zms->zclient); - zend_object_std_dtor(&zms->std); -} - -static sw_inline zend_object *mysql_coro_statement_create_object(zend_class_entry *ce, - Statement *statement, - zend_object *client) { - zval zobject; - MysqlStatementObject *zms = (MysqlStatementObject *) zend_object_alloc(sizeof(MysqlStatementObject), ce); - zend_object_std_init(&zms->std, ce); - object_properties_init(&zms->std, ce); - zms->std.handlers = &swoole_mysql_coro_statement_handlers; - ZVAL_OBJ(&zobject, &zms->std); - zend_update_property_long(ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("id"), statement->info.id); - zms->statement = statement; - zms->zclient = client; - GC_ADDREF(client); - return &zms->std; -} - -static sw_inline zend_object *mysql_coro_statement_create_object(Statement *statement, zend_object *client) { - return mysql_coro_statement_create_object(swoole_mysql_coro_statement_ce, statement, client); -} - -static zend_object *mysql_coro_statement_create_object(zend_class_entry *ce) { - php_swoole_fatal_error(E_ERROR, "you must create mysql statement object by prepare method"); - return nullptr; -} - -static sw_inline void mysql_coro_sync_error_properties(zval *zobject, - int error_code, - const char *error_msg, - const bool connected = true) { - SW_ASSERT(instanceof_function(Z_OBJCE_P(zobject), swoole_mysql_coro_ce) || - instanceof_function(Z_OBJCE_P(zobject), swoole_mysql_coro_statement_ce)); - zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("errno"), error_code); - zend_update_property_string(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("error"), error_msg); - if (!connected) { - zend_update_property_bool(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), connected); - } -} - -static sw_inline void swoole_mysql_coro_sync_query_result_properties(zval *zobject, Client *mc, zval *return_value) { - switch (Z_TYPE_P(return_value)) { - case IS_TRUE: { - mysql::ok_packet *ok_packet = &mc->result.ok; - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("insert_id"), ok_packet->last_insert_id); - break; - } - case IS_FALSE: { - mysql_coro_sync_error_properties(zobject, mc->get_error_code(), mc->get_error_msg()); - break; - } - default: - break; - } -} - -static sw_inline void swoole_mysql_coro_sync_execute_error_properties(zval *zobject, - int error_code, - const char *error_msg, - const bool connected = true) { - mysql_coro_sync_error_properties(zobject, error_code, error_msg, connected); - - /* backward compatibility (sync error info to client) */ - zval zclient; - ZVAL_OBJ(&zclient, mysql_coro_statement_fetch_object(Z_OBJ_P(zobject))->zclient); - mysql_coro_sync_error_properties(&zclient, error_code, error_msg, connected); -} - -static sw_inline void swoole_mysql_coro_sync_execute_result_properties(zval *zobject, zval *return_value) { - MysqlStatementObject *zms = mysql_coro_statement_fetch_object(Z_OBJ_P(zobject)); - Statement *ms = zms->statement; - - switch (Z_TYPE_P(return_value)) { - case IS_TRUE: { - mysql::ok_packet *ok_packet = &ms->result.ok; - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long( - Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("insert_id"), ok_packet->last_insert_id); - - /* backward compatibility (sync result info to client) */ - zval zclient; - ZVAL_OBJ(&zclient, zms->zclient); - zend_update_property_long( - Z_OBJCE_P(&zclient), SW_Z8_OBJ_P(&zclient), ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long( - Z_OBJCE_P(&zclient), SW_Z8_OBJ_P(&zclient), ZEND_STRL("insert_id"), ok_packet->last_insert_id); - break; - } - case IS_FALSE: { - swoole_mysql_coro_sync_execute_error_properties(zobject, ms->get_error_code(), ms->get_error_msg()); - break; - } - default: - break; - } -} - -void php_swoole_mysql_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_mysql_coro, "Swoole\\Coroutine\\MySQL", "Co\\MySQL", swoole_mysql_coro_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro); - SW_SET_CLASS_CLONEABLE(swoole_mysql_coro, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_mysql_coro, mysql_coro_create_object, mysql_coro_free_object, MysqlClientObject, std); - -#if PHP_VERSION_ID >= 80200 - zend_add_parameter_attribute( - (zend_function *) zend_hash_str_find_ptr(&swoole_mysql_coro_ce->function_table, SW_STRL("connect")), - 0, - ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), - 0); -#endif - - SW_INIT_CLASS_ENTRY(swoole_mysql_coro_statement, - "Swoole\\Coroutine\\MySQL\\Statement", - "Co\\MySQL\\Statement", - swoole_mysql_coro_statement_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro_statement); - SW_SET_CLASS_CLONEABLE(swoole_mysql_coro_statement, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro_statement, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_mysql_coro_statement, - mysql_coro_statement_create_object, - mysql_coro_statement_free_object, - MysqlStatementObject, - std); - - SW_INIT_CLASS_ENTRY_EX(swoole_mysql_coro_exception, - "Swoole\\Coroutine\\MySQL\\Exception", - "Co\\MySQL\\Exception", - nullptr, - swoole_exception); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_mysql_coro_exception); - SW_SET_CLASS_CLONEABLE(swoole_mysql_coro_exception, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_mysql_coro_exception, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_mysql_coro_exception); - - zend_declare_property_null(swoole_mysql_coro_ce, ZEND_STRL("socket"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_mysql_coro_ce, ZEND_STRL("serverInfo"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_bool(swoole_mysql_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("connect_errno"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_mysql_coro_ce, ZEND_STRL("connect_error"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("affected_rows"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("insert_id"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_mysql_coro_ce, ZEND_STRL("error"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_ce, ZEND_STRL("errno"), 0, ZEND_ACC_PUBLIC); - - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("affected_rows"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("insert_id"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_mysql_coro_statement_ce, ZEND_STRL("error"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_mysql_coro_statement_ce, ZEND_STRL("errno"), 0, ZEND_ACC_PUBLIC); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_UNKNOWN_ERROR", MYSQLND_CR_UNKNOWN_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_CONNECTION_ERROR", MYSQLND_CR_CONNECTION_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR", MYSQLND_CR_SERVER_GONE_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_OUT_OF_MEMORY", MYSQLND_CR_OUT_OF_MEMORY); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_SERVER_LOST", MYSQLND_CR_SERVER_LOST); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_COMMANDS_OUT_OF_SYNC", MYSQLND_CR_COMMANDS_OUT_OF_SYNC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_CANT_FIND_CHARSET", MYSQLND_CR_CANT_FIND_CHARSET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_MALFORMED_PACKET", MYSQLND_CR_MALFORMED_PACKET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_NOT_IMPLEMENTED", MYSQLND_CR_NOT_IMPLEMENTED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_NO_PREPARE_STMT", MYSQLND_CR_NO_PREPARE_STMT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_PARAMS_NOT_BOUND", MYSQLND_CR_PARAMS_NOT_BOUND); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_INVALID_PARAMETER_NO", MYSQLND_CR_INVALID_PARAMETER_NO); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MYSQLND_CR_INVALID_BUFFER_USE", MYSQLND_CR_INVALID_BUFFER_USE); -} - -static PHP_METHOD(swoole_mysql_coro, __construct) {} -static PHP_METHOD(swoole_mysql_coro, __destruct) {} - -static PHP_METHOD(swoole_mysql_coro, connect) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - zval *zserver_info = nullptr; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ARRAY_EX(zserver_info, 1, 0) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (zserver_info) { - HashTable *ht = Z_ARRVAL_P(zserver_info); - zval *ztmp; - - if (php_swoole_array_get_value(ht, "host", ztmp)) { - mc->host = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [host] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "port", ztmp)) { - mc->port = zval_get_long(ztmp); - } - if (php_swoole_array_get_value(ht, "ssl", ztmp)) { - mc->ssl = zval_is_true(ztmp); -#ifndef SW_USE_OPENSSL - if (sw_unlikely(mc->ssl)) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, - EPROTONOSUPPORT, - "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole"); - RETURN_FALSE; - } -#endif - } - if (php_swoole_array_get_value(ht, "user", ztmp)) { - mc->user = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [user] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "password", ztmp)) { - mc->password = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [password] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "database", ztmp)) { - mc->database = std::string(zend::String(ztmp).val()); - } else { - zend_throw_exception(swoole_mysql_coro_exception_ce, "Parameter [database] is required", EINVAL); - RETURN_FALSE; - } - if (php_swoole_array_get_value(ht, "timeout", ztmp)) { - mc->connect_timeout = zval_get_double(ztmp); - } - if (php_swoole_array_get_value(ht, "charset", ztmp)) { - zend::String zstr_charset(ztmp); - char charset = mysql::get_charset(zstr_charset.val()); - if (UNEXPECTED(charset < 0)) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, EINVAL, "Unknown charset [%s]", zstr_charset.val()); - RETURN_FALSE; - } - mc->charset = charset; - } - if (php_swoole_array_get_value(ht, "strict_type", ztmp)) { - mc->strict_type = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(ht, "fetch_mode", ztmp)) { - if (UNEXPECTED(!mc->set_fetch_mode(zval_is_true(ztmp)))) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, mc->get_error_code(), "%s", mc->get_error_msg()); - RETURN_FALSE; - } - } - } - if (!mc->connect()) { - zend_update_property_long( - swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connect_errno"), mc->get_error_code()); - zend_update_property_string( - swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connect_error"), mc->get_error_msg()); - RETURN_FALSE; - } - if (zserver_info && php_swoole_array_length(zserver_info) > 0) { - php_array_merge(Z_ARRVAL_P(sw_zend_read_and_convert_property_array( - swoole_mysql_coro_ce, ZEND_THIS, ZEND_STRL("serverInfo"), 0)), - Z_ARRVAL_P(zserver_info)); - } - zend_update_property_long(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("sock"), mc->get_fd()); - zend_update_property_bool(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 1); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_mysql_coro, getDefer) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - RETURN_BOOL(mc->get_defer()); -} - -static PHP_METHOD(swoole_mysql_coro, setDefer) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - zend_bool defer = 1; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_BOOL(defer) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - bool ret = mc->set_defer(defer); - if (UNEXPECTED(!ret)) { - zend_throw_exception_ex(swoole_mysql_coro_exception_ce, mc->get_error_code(), "%s", mc->get_error_msg()); - } - RETURN_BOOL(ret); -} - -static PHP_METHOD(swoole_mysql_coro, query) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - char *sql; - size_t sql_length; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(sql, sql_length) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->query(return_value, sql, sql_length); - mc->del_timeout_controller(); - swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value); -} - -static PHP_METHOD(swoole_mysql_coro, fetch) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->fetch(return_value); - mc->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); - } -} - -static PHP_METHOD(swoole_mysql_coro, fetchAll) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->fetch_all(return_value); - mc->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); - } -} - -static PHP_METHOD(swoole_mysql_coro, nextResult) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->next_result(return_value); - mc->del_timeout_controller(); - swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value); - if (Z_TYPE_P(return_value) == IS_TRUE) { - if (mc->state == SW_MYSQL_STATE_IDLE) { - // the end of procedure - Z_TYPE_INFO_P(return_value) = mc->get_fetch_mode() ? IS_FALSE : IS_NULL; - } - } -} - -static PHP_METHOD(swoole_mysql_coro, prepare) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - char *statement; - size_t statement_length; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(statement, statement_length) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - if (UNEXPECTED(!mc->send_prepare_request(statement, statement_length))) { - _failed: - mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), mc->is_connected()); - RETVAL_FALSE; - } else if (UNEXPECTED(mc->get_defer())) { - RETVAL_TRUE; - } else { - Statement *statement = mc->recv_prepare_response(); - if (UNEXPECTED(!statement)) { - goto _failed; - } - RETVAL_OBJ(mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS))); - } - mc->del_timeout_controller(); -} - -static PHP_METHOD(swoole_mysql_coro, recv) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!mc->check_connection())) { - mysql_coro_sync_error_properties(ZEND_THIS, mc->get_error_code(), mc->get_error_msg(), false); - RETURN_FALSE; - } - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_READ); - switch (mc->state) { - case SW_MYSQL_STATE_IDLE: - mysql_coro_sync_error_properties(ZEND_THIS, ENOMSG, "no message to receive"); - RETVAL_FALSE; - break; - case SW_MYSQL_STATE_QUERY: - mc->recv_query_response(return_value); - break; - case SW_MYSQL_STATE_PREPARE: { - Statement *statement = mc->recv_prepare_response(); - if (UNEXPECTED(!statement)) { - RETVAL_FALSE; - } else { - RETVAL_OBJ(mysql_coro_statement_create_object(statement, Z_OBJ_P(ZEND_THIS))); - } - break; - } - default: - if (UNEXPECTED(mc->state & SW_MYSQL_COMMAND_FLAG_EXECUTE)) { - mysql_coro_sync_error_properties(ZEND_THIS, EPERM, "please use statement to receive data"); - } else { - mysql_coro_sync_error_properties(ZEND_THIS, EPERM, "please use fetch/fetchAll/nextResult to get result"); - } - RETVAL_FALSE; - } - mc->del_timeout_controller(); -} - -static void swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAMETERS, - const char *command, - size_t command_length) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(mc->get_defer())) { - zend_throw_exception_ex( - swoole_mysql_coro_exception_ce, - EPERM, - "you should not query transaction when defer mode is on, if you want, please use `query('%s')` instead", - command); - RETURN_FALSE; - } - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - mc->query(return_value, command, command_length); - mc->del_timeout_controller(); - swoole_mysql_coro_sync_query_result_properties(ZEND_THIS, mc, return_value); -} - -static PHP_METHOD(swoole_mysql_coro, begin) { - swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("BEGIN")); -} - -static PHP_METHOD(swoole_mysql_coro, commit) { - swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("COMMIT")); -} - -static PHP_METHOD(swoole_mysql_coro, rollback) { - swoole_mysql_coro_query_transcation(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ROLLBACK")); -} - -#ifdef SW_USE_MYSQLND -static PHP_METHOD(swoole_mysql_coro, escape) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - char *str; - size_t str_length; - zend_long flags = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(str, str_length) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - char *newstr = (char *) safe_emalloc(2, str_length + 1, 1); - const MYSQLND_CHARSET *cset = mysqlnd_find_charset_nr(mc->charset); - if (!cset) { - php_swoole_fatal_error(E_ERROR, "unknown mysql charset[%d]", mc->charset); - RETURN_FALSE; - } - zend_ulong newstr_len = mysqlnd_cset_escape_slashes(cset, newstr, str, str_length); - if (newstr_len == (zend_ulong) ~0) { - php_swoole_fatal_error(E_ERROR, "mysqlnd_cset_escape_slashes() failed"); - RETURN_FALSE; - } - RETVAL_STRINGL(newstr, newstr_len); - efree(newstr); - return; -} -#endif - -static PHP_METHOD(swoole_mysql_coro, close) { - Client *mc = mysql_coro_get_client(ZEND_THIS); - SW_CLIENT_PRESERVE_SOCKET(&mc->zsocket); - mc->close(); - zend_update_property_bool(swoole_mysql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("connected"), 0); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_mysql_coro_statement, execute) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - zval *params = nullptr; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_ARRAY_EX(params, 1, 0) - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->execute(return_value, params); - ms->del_timeout_controller(); - swoole_mysql_coro_sync_execute_result_properties(ZEND_THIS, return_value); -} - -static PHP_METHOD(swoole_mysql_coro_statement, fetch) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->fetch(return_value); - ms->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg()); - } -} - -static PHP_METHOD(swoole_mysql_coro_statement, fetchAll) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->fetch_all(return_value); - ms->del_timeout_controller(); - if (sw_unlikely(Z_TYPE_P(return_value) == IS_FALSE)) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg()); - } -} - -static PHP_METHOD(swoole_mysql_coro_statement, nextResult) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_RDWR); - ms->next_result(return_value); - ms->del_timeout_controller(); - swoole_mysql_coro_sync_execute_result_properties(ZEND_THIS, return_value); - if (Z_TYPE_P(return_value) == IS_TRUE) { - Client *mc = ms->get_client(); - if (mc->state == SW_MYSQL_STATE_IDLE) { - // the end of procedure - Z_TYPE_INFO_P(return_value) = mc->get_fetch_mode() ? IS_FALSE : IS_NULL; - } - } -} - -static PHP_METHOD(swoole_mysql_coro_statement, recv) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - double timeout = 0; - enum sw_mysql_state state; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - - ms->add_timeout_controller(timeout, Socket::TIMEOUT_READ); - switch ((state = ms->get_client()->state)) { - case SW_MYSQL_STATE_IDLE: - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ENOMSG, "no message to receive"); - RETVAL_FALSE; - break; - case SW_MYSQL_STATE_EXECUTE: - ms->recv_execute_response(return_value); - break; - default: - if (UNEXPECTED(state & SW_MYSQL_COMMAND_FLAG_QUERY)) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, EPERM, "please use client to receive data"); - } else { - swoole_mysql_coro_sync_execute_error_properties( - ZEND_THIS, EPERM, "please use fetch/fetchAll/nextResult to get result"); - } - RETVAL_FALSE; - } - ms->del_timeout_controller(); -} - -static PHP_METHOD(swoole_mysql_coro_statement, close) { - Statement *ms = mysql_coro_get_statement(ZEND_THIS); - if (UNEXPECTED(!ms->is_available())) { - swoole_mysql_coro_sync_execute_error_properties(ZEND_THIS, ms->get_error_code(), ms->get_error_msg(), false); - RETURN_FALSE; - } - SW_CLIENT_PRESERVE_SOCKET(&ms->get_client()->zsocket); - ms->close(); - RETURN_TRUE; -} diff --git a/ext-src/swoole_mysql_proto.cc b/ext-src/swoole_mysql_proto.cc deleted file mode 100644 index 61978817ddb..00000000000 --- a/ext-src/swoole_mysql_proto.cc +++ /dev/null @@ -1,744 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | Copyright (c) 2012-2015 The Swoole Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http:// www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Twosee | - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_mysql_proto.h" - -using namespace swoole::mysql; - -namespace swoole { -namespace mysql { -struct charset_t { - uint nr; - const char *name; - const char *collation; -}; - -char get_charset(const char *name) { - static const charset_t charsets[] = { - {1, "big5", "big5_chinese_ci"}, - {3, "dec8", "dec8_swedish_ci"}, - {4, "cp850", "cp850_general_ci"}, - {6, "hp8", "hp8_english_ci"}, - {7, "koi8r", "koi8r_general_ci"}, - {8, "latin1", "latin1_swedish_ci"}, - {5, "latin1", "latin1_german1_ci"}, - {9, "latin2", "latin2_general_ci"}, - {2, "latin2", "latin2_czech_cs"}, - {10, "swe7", "swe7_swedish_ci"}, - {11, "ascii", "ascii_general_ci"}, - {12, "ujis", "ujis_japanese_ci"}, - {13, "sjis", "sjis_japanese_ci"}, - {16, "hebrew", "hebrew_general_ci"}, - {17, "filename", "filename"}, - {18, "tis620", "tis620_thai_ci"}, - {19, "euckr", "euckr_korean_ci"}, - {21, "latin2", "latin2_hungarian_ci"}, - {27, "latin2", "latin2_croatian_ci"}, - {22, "koi8u", "koi8u_general_ci"}, - {24, "gb2312", "gb2312_chinese_ci"}, - {25, "greek", "greek_general_ci"}, - {26, "cp1250", "cp1250_general_ci"}, - {28, "gbk", "gbk_chinese_ci"}, - {30, "latin5", "latin5_turkish_ci"}, - {31, "latin1", "latin1_german2_ci"}, - {15, "latin1", "latin1_danish_ci"}, - {32, "armscii8", "armscii8_general_ci"}, - {33, "utf8", "utf8_general_ci"}, - {35, "ucs2", "ucs2_general_ci"}, - {36, "cp866", "cp866_general_ci"}, - {37, "keybcs2", "keybcs2_general_ci"}, - {38, "macce", "macce_general_ci"}, - {39, "macroman", "macroman_general_ci"}, - {40, "cp852", "cp852_general_ci"}, - {41, "latin7", "latin7_general_ci"}, - {20, "latin7", "latin7_estonian_cs"}, - {57, "cp1256", "cp1256_general_ci"}, - {59, "cp1257", "cp1257_general_ci"}, - {63, "binary", "binary"}, - {97, "eucjpms", "eucjpms_japanese_ci"}, - {29, "cp1257", "cp1257_lithuanian_ci"}, - {31, "latin1", "latin1_german2_ci"}, - {34, "cp1250", "cp1250_czech_cs"}, - {42, "latin7", "latin7_general_cs"}, - {43, "macce", "macce_bin"}, - {44, "cp1250", "cp1250_croatian_ci"}, - {45, "utf8mb4", "utf8mb4_general_ci"}, - {46, "utf8mb4", "utf8mb4_bin"}, - {47, "latin1", "latin1_bin"}, - {48, "latin1", "latin1_general_ci"}, - {49, "latin1", "latin1_general_cs"}, - {51, "cp1251", "cp1251_general_ci"}, - {14, "cp1251", "cp1251_bulgarian_ci"}, - {23, "cp1251", "cp1251_ukrainian_ci"}, - {50, "cp1251", "cp1251_bin"}, - {52, "cp1251", "cp1251_general_cs"}, - {53, "macroman", "macroman_bin"}, - {54, "utf16", "utf16_general_ci"}, - {55, "utf16", "utf16_bin"}, - {56, "utf16le", "utf16le_general_ci"}, - {58, "cp1257", "cp1257_bin"}, - {60, "utf32", "utf32_general_ci"}, - {61, "utf32", "utf32_bin"}, - {62, "utf16le", "utf16le_bin"}, - {64, "armscii8", "armscii8_bin"}, - {65, "ascii", "ascii_bin"}, - {66, "cp1250", "cp1250_bin"}, - {67, "cp1256", "cp1256_bin"}, - {68, "cp866", "cp866_bin"}, - {69, "dec8", "dec8_bin"}, - {70, "greek", "greek_bin"}, - {71, "hebrew", "hebrew_bin"}, - {72, "hp8", "hp8_bin"}, - {73, "keybcs2", "keybcs2_bin"}, - {74, "koi8r", "koi8r_bin"}, - {75, "koi8u", "koi8u_bin"}, - {77, "latin2", "latin2_bin"}, - {78, "latin5", "latin5_bin"}, - {79, "latin7", "latin7_bin"}, - {80, "cp850", "cp850_bin"}, - {81, "cp852", "cp852_bin"}, - {82, "swe7", "swe7_bin"}, - {83, "utf8", "utf8_bin"}, - {84, "big5", "big5_bin"}, - {85, "euckr", "euckr_bin"}, - {86, "gb2312", "gb2312_bin"}, - {87, "gbk", "gbk_bin"}, - {88, "sjis", "sjis_bin"}, - {89, "tis620", "tis620_bin"}, - {90, "ucs2", "ucs2_bin"}, - {91, "ujis", "ujis_bin"}, - {92, "geostd8", "geostd8_general_ci"}, - {93, "geostd8", "geostd8_bin"}, - {94, "latin1", "latin1_spanish_ci"}, - {95, "cp932", "cp932_japanese_ci"}, - {96, "cp932", "cp932_bin"}, - {97, "eucjpms", "eucjpms_japanese_ci"}, - {98, "eucjpms", "eucjpms_bin"}, - {99, "cp1250", "cp1250_polish_ci"}, - {128, "ucs2", "ucs2_unicode_ci"}, - {129, "ucs2", "ucs2_icelandic_ci"}, - {130, "ucs2", "ucs2_latvian_ci"}, - {131, "ucs2", "ucs2_romanian_ci"}, - {132, "ucs2", "ucs2_slovenian_ci"}, - {133, "ucs2", "ucs2_polish_ci"}, - {134, "ucs2", "ucs2_estonian_ci"}, - {135, "ucs2", "ucs2_spanish_ci"}, - {136, "ucs2", "ucs2_swedish_ci"}, - {137, "ucs2", "ucs2_turkish_ci"}, - {138, "ucs2", "ucs2_czech_ci"}, - {139, "ucs2", "ucs2_danish_ci"}, - {140, "ucs2", "ucs2_lithuanian_ci"}, - {141, "ucs2", "ucs2_slovak_ci"}, - {142, "ucs2", "ucs2_spanish2_ci"}, - {143, "ucs2", "ucs2_roman_ci"}, - {144, "ucs2", "ucs2_persian_ci"}, - {145, "ucs2", "ucs2_esperanto_ci"}, - {146, "ucs2", "ucs2_hungarian_ci"}, - {147, "ucs2", "ucs2_sinhala_ci"}, - {148, "ucs2", "ucs2_german2_ci"}, - {149, "ucs2", "ucs2_croatian_ci"}, - {150, "ucs2", "ucs2_unicode_520_ci"}, - {151, "ucs2", "ucs2_vietnamese_ci"}, - {160, "utf32", "utf32_unicode_ci"}, - {161, "utf32", "utf32_icelandic_ci"}, - {162, "utf32", "utf32_latvian_ci"}, - {163, "utf32", "utf32_romanian_ci"}, - {164, "utf32", "utf32_slovenian_ci"}, - {165, "utf32", "utf32_polish_ci"}, - {166, "utf32", "utf32_estonian_ci"}, - {167, "utf32", "utf32_spanish_ci"}, - {168, "utf32", "utf32_swedish_ci"}, - {169, "utf32", "utf32_turkish_ci"}, - {170, "utf32", "utf32_czech_ci"}, - {171, "utf32", "utf32_danish_ci"}, - {172, "utf32", "utf32_lithuanian_ci"}, - {173, "utf32", "utf32_slovak_ci"}, - {174, "utf32", "utf32_spanish2_ci"}, - {175, "utf32", "utf32_roman_ci"}, - {176, "utf32", "utf32_persian_ci"}, - {177, "utf32", "utf32_esperanto_ci"}, - {178, "utf32", "utf32_hungarian_ci"}, - {179, "utf32", "utf32_sinhala_ci"}, - {180, "utf32", "utf32_german2_ci"}, - {181, "utf32", "utf32_croatian_ci"}, - {182, "utf32", "utf32_unicode_520_ci"}, - {183, "utf32", "utf32_vietnamese_ci"}, - {192, "utf8", "utf8_unicode_ci"}, - {193, "utf8", "utf8_icelandic_ci"}, - {194, "utf8", "utf8_latvian_ci"}, - {195, "utf8", "utf8_romanian_ci"}, - {196, "utf8", "utf8_slovenian_ci"}, - {197, "utf8", "utf8_polish_ci"}, - {198, "utf8", "utf8_estonian_ci"}, - {199, "utf8", "utf8_spanish_ci"}, - {200, "utf8", "utf8_swedish_ci"}, - {201, "utf8", "utf8_turkish_ci"}, - {202, "utf8", "utf8_czech_ci"}, - {203, "utf8", "utf8_danish_ci"}, - {204, "utf8", "utf8_lithuanian_ci"}, - {205, "utf8", "utf8_slovak_ci"}, - {206, "utf8", "utf8_spanish2_ci"}, - {207, "utf8", "utf8_roman_ci"}, - {208, "utf8", "utf8_persian_ci"}, - {209, "utf8", "utf8_esperanto_ci"}, - {210, "utf8", "utf8_hungarian_ci"}, - {211, "utf8", "utf8_sinhala_ci"}, - {212, "utf8", "utf8_german2_ci"}, - {213, "utf8", "utf8_croatian_ci"}, - {214, "utf8", "utf8_unicode_520_ci"}, - {215, "utf8", "utf8_vietnamese_ci"}, - {224, "utf8mb4", "utf8mb4_unicode_ci"}, - {225, "utf8mb4", "utf8mb4_icelandic_ci"}, - {226, "utf8mb4", "utf8mb4_latvian_ci"}, - {227, "utf8mb4", "utf8mb4_romanian_ci"}, - {228, "utf8mb4", "utf8mb4_slovenian_ci"}, - {229, "utf8mb4", "utf8mb4_polish_ci"}, - {230, "utf8mb4", "utf8mb4_estonian_ci"}, - {231, "utf8mb4", "utf8mb4_spanish_ci"}, - {232, "utf8mb4", "utf8mb4_swedish_ci"}, - {233, "utf8mb4", "utf8mb4_turkish_ci"}, - {234, "utf8mb4", "utf8mb4_czech_ci"}, - {235, "utf8mb4", "utf8mb4_danish_ci"}, - {236, "utf8mb4", "utf8mb4_lithuanian_ci"}, - {237, "utf8mb4", "utf8mb4_slovak_ci"}, - {238, "utf8mb4", "utf8mb4_spanish2_ci"}, - {239, "utf8mb4", "utf8mb4_roman_ci"}, - {240, "utf8mb4", "utf8mb4_persian_ci"}, - {241, "utf8mb4", "utf8mb4_esperanto_ci"}, - {242, "utf8mb4", "utf8mb4_hungarian_ci"}, - {243, "utf8mb4", "utf8mb4_sinhala_ci"}, - {244, "utf8mb4", "utf8mb4_german2_ci"}, - {245, "utf8mb4", "utf8mb4_croatian_ci"}, - {246, "utf8mb4", "utf8mb4_unicode_520_ci"}, - {247, "utf8mb4", "utf8mb4_vietnamese_ci"}, - {248, "gb18030", "gb18030_chinese_ci"}, - {249, "gb18030", "gb18030_bin"}, - {254, "utf8", "utf8_general_cs"}, - {0, nullptr, nullptr}, - }; - const charset_t *c = charsets; - while (c[0].nr) { - if (!strcasecmp(c->name, name)) { - return c->nr; - } - ++c; - } - return -1; -} - -// clang-format off -uint8_t get_static_type_size(uint8_t type) -{ - static const uint8_t map[] = - { - 0, // SW_MYSQL_TYPE_DECIMAL 0 - sizeof(int8_t), // SW_MYSQL_TYPE_TINY 1 - sizeof(int16_t), // SW_MYSQL_TYPE_SHORT 2 - sizeof(int32_t), // SW_MYSQL_TYPE_LONG 3 - sizeof(float), // SW_MYSQL_TYPE_FLOAT 4 - sizeof(double), // SW_MYSQL_TYPE_DOUBLE 5 - 0, // SW_MYSQL_TYPE_NULL 6 - 0, // SW_MYSQL_TYPE_TIMESTAMP 7 - sizeof(int64_t), // SW_MYSQL_TYPE_LONGLONG 8 - sizeof(int32_t), // SW_MYSQL_TYPE_INT24 9 - 0, // SW_MYSQL_TYPE_DATE 10 - 0, // SW_MYSQL_TYPE_TIME 11 - 0, // SW_MYSQL_TYPE_DATETIME 12 - sizeof(int16_t), // SW_MYSQL_TYPE_YEAR 13 - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0 - }; - SW_ASSERT(sizeof(map) == UINT8_MAX + 1); - return map[type]; -} -// clang-format on - -static uint32_t sha1_password_with_nonce(char *buf, const char *nonce, const char *password) { - char hash_0[20] = {}; - php_swoole_sha1(password, strlen(password), (uchar *) hash_0); - - char hash_1[20] = {}; - php_swoole_sha1(hash_0, sizeof(hash_0), (uchar *) hash_1); - - char str[40]; - memcpy(str, nonce, 20); - memcpy(str + 20, hash_1, 20); - - char hash_2[20]; - php_swoole_sha1(str, sizeof(str), (uchar *) hash_2); - - char hash_3[20]; - - int *a = (int *) hash_2; - int *b = (int *) hash_0; - int *c = (int *) hash_3; - - int i; - for (i = 0; i < 5; i++) { - c[i] = a[i] ^ b[i]; - } - memcpy(buf, hash_3, 20); - return 20; -} - -static uint32_t sha256_password_with_nonce(char *buf, const char *nonce, const char *password) { - // XOR(SHA256(password), SHA256(SHA256(SHA256(password)), nonce)) - char hashed[32], double_hashed[32]; - php_swoole_sha256(password, strlen(password), (unsigned char *) hashed); - php_swoole_sha256(hashed, 32, (unsigned char *) double_hashed); - char combined[32 + SW_MYSQL_NONCE_LENGTH]; // double-hashed + nonce - memcpy(combined, double_hashed, 32); - memcpy(combined + 32, nonce, SW_MYSQL_NONCE_LENGTH); - char xor_bytes[32]; - php_swoole_sha256(combined, 32 + SW_MYSQL_NONCE_LENGTH, (unsigned char *) xor_bytes); - int i; - for (i = 0; i < 32; i++) { - hashed[i] ^= xor_bytes[i]; - } - memcpy(buf, hashed, 32); - return 32; -} - -/** @return: password length */ -static sw_inline uint32_t mysql_auth_encrypt_dispatch(char *buf, - const std::string auth_plugin_name, - const char *nonce, - const char *password) { - if (auth_plugin_name.length() == 0 || auth_plugin_name == "mysql_native_password") { - // mysql_native_password is default - return sha1_password_with_nonce(buf, nonce, password); - } else if (auth_plugin_name == "caching_sha2_password") { - return sha256_password_with_nonce(buf, nonce, password); - } else { - swoole_warning("Unknown auth plugin: %s", auth_plugin_name.c_str()); - return 0; - } -} - -eof_packet::eof_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "EOF_Packet"); - // EOF_Packet = Packet header (4 bytes) + 0xFE + warning(2byte) + status(2byte) - data += SW_MYSQL_PACKET_HEADER_SIZE; - // int<1> header [fe] EOF header - data += 1; - // int<2> warnings number of warnings - warning_count = sw_mysql_uint2korr2korr(data); - data += 2; - // int<2> status_flags Status Flags - server_status = sw_mysql_uint2korr2korr(data); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "EOF_Packet, warnings=%u, status_code=%u", warning_count, server_status.status); -} - -ok_packet::ok_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "OK_Packet"); - bool nul; - data += SW_MYSQL_PACKET_HEADER_SIZE; - // int<1> header [00] or [fe] the OK packet header - data += 1; - // int affected_rows affected rows - data += read_lcb(data, &affected_rows, &nul); - // int last_insert_id last insert id - data += read_lcb(data, &last_insert_id, &nul); - // int<2> status_flags status Flags - server_status = sw_mysql_uint2korr2korr(data); - data += 2; - // int<2> warnings number of warnings - warning_count = sw_mysql_uint2korr2korr(data); - // p += 2; - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "OK_Packet, affected_rows=%" PRIu64 ", insert_id=%" PRIu64 ", status_flags=0x%08x, warnings=%u", - affected_rows, - last_insert_id, - server_status.status, - warning_count); -} - -err_packet::err_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "ERR_Packet"); - // ERR Packet = Packet header (4 bytes) + ERR Payload - data += SW_MYSQL_PACKET_HEADER_SIZE; - // int<1> header [ff] header of the ERR packet - data += 1; - // int<2> error_code error-code - code = sw_mysql_uint2korr2korr(data); - data += 2; - // string[1] sql_state_marker # marker of the SQL State - data += 1; - // string[5] sql_state SQL State - memcpy(sql_state, data, 5); - sql_state[5] = '\0'; - data += 5; - // string error_message human readable error message - msg = std::string(data, header.length - 9); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "ERR_Packet, error_code=%u, sql_state=%s, status_msg=[%s]", - code, - sql_state, - msg.c_str()); -}; - -greeting_packet::greeting_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "Protocol::HandshakeGreeting"); - /** - 1 [0a] protocol version - string[NUL] server version - 4 connection id - string[8] auth-plugin-data-part-1 - 1 [00] filler - 2 capability flags (lower 2 bytes) - if more data in the packet: - 1 character set - 2 status flags - 2 capability flags (upper 2 bytes) - if capabilities & CLIENT_PLUGIN_AUTH { - 1 length of auth-plugin-data - } else { - 1 [00] - } - string[10] reserved (all [00]) - if capabilities & CLIENT_SECURE_CONNECTION { - string[$len] auth-plugin-data-part-2 ($len=MAX(13, length of auth-plugin-data - 8)) - if capabilities & CLIENT_PLUGIN_AUTH { - string[NUL] auth-plugin name - } - */ - const char *p = data + SW_MYSQL_PACKET_HEADER_SIZE; - // 1 [0a] protocol version - protocol_version = *p; - p++; - // x server version - server_version = std::string(p); - p += server_version.length() + 1; - // 4 connection id - connection_id = *((int *) p); - p += 4; - // string[8] auth-plugin-data-part-1 - memcpy(auth_plugin_data, p, 8); - p += 8; - // 1 [00] filler - filler = *p; - p += 1; - // 2 capability flags (lower 2 bytes) - memcpy(((char *) (&capability_flags)), p, 2); - p += 2; - - if (p < data + header.length) { - // 1 character set - charset = *p; - p += 1; - // 2 status flags - memcpy(&status_flags, p, 2); - p += 2; - // 2 capability flags (upper 2 bytes) - memcpy(((char *) (&capability_flags) + 2), p, 2); - p += 2; - // 1 auth plugin data length - auth_plugin_data_length = (uint8_t) *p; - p += 1; - // x reserved - memcpy(&reserved, p, sizeof(reserved)); - p += sizeof(reserved); - if (capability_flags & SW_MYSQL_CLIENT_SECURE_CONNECTION) { - uint8_t len = SW_MAX(13, auth_plugin_data_length - 8); - memcpy(auth_plugin_data + 8, p, len); - p += len; - } - if (capability_flags & SW_MYSQL_CLIENT_PLUGIN_AUTH) { - auth_plugin_name = std::string(p, strlen(p)); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "use %s auth plugin", auth_plugin_name.c_str()); - } - } - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "Server protocol=%d, version=%s, connection_id=%d, capabilites=0x%08x, status=%u, auth_plugin_name=%s, " - "auth_plugin_data=L%u[%s]", - protocol_version, - server_version.c_str(), - connection_id, - capability_flags, - status_flags.status, - auth_plugin_name.c_str(), - auth_plugin_data_length, - auth_plugin_data); -}; - -login_packet::login_packet(greeting_packet *greeting_packet, - const std::string &user, - const std::string &password, - std::string database, - char charset) { - char *p = data.body; - uint32_t tint; - // capability flags, CLIENT_PROTOCOL_41 always set - tint = SW_MYSQL_CLIENT_LONG_PASSWORD | SW_MYSQL_CLIENT_PROTOCOL_41 | SW_MYSQL_CLIENT_SECURE_CONNECTION | - SW_MYSQL_CLIENT_CONNECT_WITH_DB | SW_MYSQL_CLIENT_PLUGIN_AUTH | SW_MYSQL_CLIENT_MULTI_RESULTS; - memcpy(p, &tint, sizeof(tint)); - p += sizeof(tint); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "Client capabilites=0x%08x", tint); - // max-packet size - tint = 300; - memcpy(p, &tint, sizeof(tint)); - p += sizeof(tint); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "Client max packet=%u", tint); - // use the server character_set when the character_set is not set. - *p = charset ? charset : greeting_packet->charset; - p += 1; - // string[23] reserved (all [0]) - p += 23; - // string[NUL] username - strcpy(p, user.c_str()); - p += (user.length() + 1); - // string[NUL] password - if (password.length() > 0) { - *p = mysql_auth_encrypt_dispatch( - p + 1, greeting_packet->auth_plugin_name, greeting_packet->auth_plugin_data, password.c_str()); - } else { - *p = 0; - } - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "Client charset=%u, user=%s, password=%s, hased=L%d[%.*s], database=%s, auth_plugin_name=%s", - charset, - user.c_str(), - password.c_str(), - (int) *p, - (int) *p, - p + 1, - database.c_str(), - greeting_packet->auth_plugin_name.c_str()); - p += (((uint32_t) *p) + 1); - // string[NUL] database - strcpy(p, database.c_str()); - p += (database.length() + 1); - // string[NUL] auth plugin name - strcpy(p, greeting_packet->auth_plugin_name.c_str()); - p += (greeting_packet->auth_plugin_name.length() + 1); - // packet header - set_header(p - data.body, greeting_packet->header.number + 1); - swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::HandshakeLogin"); -} - -auth_switch_request_packet::auth_switch_request_packet(const char *data) : server_packet(data) { - swMysqlPacketDump(header.length, header.number, data, "Protocol::AuthSwitchRequest"); - // 4 header - data += SW_MYSQL_PACKET_HEADER_SIZE; - // 1 type - data += 1; - // string[NUL] auth_method_name - auth_method_name = std::string(data); - data += (auth_method_name.length() + 1); - // string[NUL] auth_method_data - strlcpy(auth_method_data, data, sizeof(auth_method_data)); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "auth switch plugin name=%s", auth_method_name.c_str()); -} - -auth_switch_response_packet::auth_switch_response_packet(auth_switch_request_packet *req, const std::string &password) { - // if auth switch is triggered, password can't be empty - // create auth switch response packet - set_header(mysql_auth_encrypt_dispatch(data.body, req->auth_method_name, req->auth_method_data, password.c_str()), - req->header.number + 1); - swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::AuthSignatureResponse"); -} - -// Caching sha2 authentication. Public key request and send encrypted password -// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse -auth_signature_response_packet::auth_signature_response_packet(raw_data_packet *raw_data_pakcet, - const std::string &password, - const char *auth_plugin_data) { -#ifndef SW_MYSQL_RSA_SUPPORT - { - swoole_warning(SW_MYSQL_NO_RSA_ERROR); -#else - if (0) { - _error: -#endif - data.body[0] = SW_MYSQL_AUTH_SIGNATURE_ERROR; - set_header(1, raw_data_pakcet->header.number + 1); - return; - } -#ifdef SW_MYSQL_RSA_SUPPORT - const char *tmp = raw_data_pakcet->body; - uint32_t rsa_public_key_length = raw_data_pakcet->header.length; - while (tmp[0] != 0x2d) { - tmp++; // ltrim - rsa_public_key_length--; - } - char rsa_public_key[rsa_public_key_length + 1]; // rsa + '\0' - memcpy((char *) rsa_public_key, tmp, rsa_public_key_length); - rsa_public_key[rsa_public_key_length] = '\0'; - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "rsa_public_key_length=%d;\nrsa_public_key=[%.*s]", - rsa_public_key_length, - rsa_public_key_length, - rsa_public_key); - - size_t password_bytes_length = password.length() + 1; - unsigned char password_bytes[password_bytes_length]; - // copy NUL terminator to password to stack - strcpy((char *) password_bytes, password.c_str()); - // XOR the password bytes with the challenge - for (size_t i = 0; i < password_bytes_length; i++) // include '\0' byte - { - password_bytes[i] ^= auth_plugin_data[i % SW_MYSQL_NONCE_LENGTH]; - } - - // prepare RSA public key - BIO *bio = nullptr; - RSA *public_rsa = nullptr; - if (sw_unlikely((bio = BIO_new_mem_buf((void *) rsa_public_key, -1)) == nullptr)) { - swoole_warning("BIO_new_mem_buf publicKey error!"); - goto _error; - } - // PEM_read_bio_RSA_PUBKEY - ERR_clear_error(); - if (sw_unlikely((public_rsa = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr)) == nullptr)) { - char err_buf[512]; - ERR_load_crypto_strings(); - ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); - swoole_warning("[PEM_read_bio_RSA_PUBKEY ERROR]: %s", err_buf); - goto _error; - } - BIO_free_all(bio); - // encrypt with RSA public key - int rsa_len = RSA_size(public_rsa); - unsigned char encrypt_msg[rsa_len]; - // RSA_public_encrypt - ERR_clear_error(); - size_t flen = rsa_len - 42; - flen = password_bytes_length > flen ? flen : password_bytes_length; - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, "rsa_len=%d", rsa_len); - if (sw_unlikely(RSA_public_encrypt(flen, - (const unsigned char *) password_bytes, - (unsigned char *) encrypt_msg, - public_rsa, - RSA_PKCS1_OAEP_PADDING) < 0)) { - char err_buf[512]; - ERR_load_crypto_strings(); - ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf)); - swoole_warning("[RSA_public_encrypt ERROR]: %s", err_buf); - goto _error; - } - RSA_free(public_rsa); - memcpy(data.body, (char *) encrypt_msg, rsa_len); // copy rsa to buf - set_header(rsa_len, raw_data_pakcet->header.number + 1); - swMysqlPacketDump(get_length(), get_number(), get_data(), "Protocol::AuthSignatureResponse"); -#endif -} - -void field_packet::parse(const char *data) { - server_packet::parse(data); - bool nul = false; - char *p = body = new char[header.length]; - memcpy(body, data + SW_MYSQL_PACKET_HEADER_SIZE, header.length); - // catalog - p += read_lcb(p, &catalog_length, &nul); - catalog = p; - p += catalog_length; - // database - p += read_lcb(p, &database_length, &nul); - database = p; - p += database_length; - // table - p += read_lcb(p, &table_length, &nul); - table = p; - p += table_length; - // origin table - p += read_lcb(p, &org_table_length, &nul); - org_table = p; - p += org_table_length; - // name - p += read_lcb(p, &name_length, &nul); - name = p; - p += name_length; - // origin table - p += read_lcb(p, &org_name_length, &nul); - org_name = p; - p += org_name_length; - // filler - p += 1; - // charset - charset = sw_mysql_uint2korr2korr(p); - p += 2; - // binary length - length = sw_mysql_uint2korr4korr(p); - p += 4; - // field type - type = (uint8_t) *p; - p += 1; - // flags - flags = sw_mysql_uint2korr2korr(p); - p += 2; - /* decimals */ - decimals = *p; - p += 1; - /* filler */ - p += 2; - /* default - a priori facultatif */ - if (p < body + header.length) { - p += read_lcb(p, &def_length, &nul); - def = p; - p += def_length; - } - swMysqlPacketDump(header.length, header.number, data, (*name == '?' ? "Protocol::Param" : "Protocol::Field")); - swoole_trace_log(SW_TRACE_MYSQL_CLIENT, - "catalog=%.*s, database=%.*s, table=%.*s, org_table=%.*s, name=%.*s, org_name=%.*s," - "charset=%u, binary_length=%" PRIu64 ", type=%u, flags=0x%08x, decimals=%u, def=[%.*s]", - catalog_length, - catalog, - database_length, - database, - table_length, - table, - org_table_length, - org_table, - name_length, - name, - org_name_length, - org_name, - charset, - length, - type, - flags, - decimals, - def_length, - def); -} -} // namespace mysql -} // namespace swoole diff --git a/ext-src/swoole_postgresql_coro.cc b/ext-src/swoole_postgresql_coro.cc deleted file mode 100644 index e07d601b815..00000000000 --- a/ext-src/swoole_postgresql_coro.cc +++ /dev/null @@ -1,1945 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Zhenyu Wu <936321732@qq.com> | - | Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" -#include "swoole_reactor.h" -#include "swoole_socket.h" - -#include - -#ifdef SW_USE_PGSQL - -#include -#include - -BEGIN_EXTERN_C() -#include "stubs/php_swoole_postgresql_coro_arginfo.h" -END_EXTERN_C() - -namespace swoole { -namespace postgresql { - -enum QueryType { NORMAL_QUERY, META_DATA, PREPARE }; - -class Statement; - -class Object { - public: - PGconn *conn; - network::Socket *socket; - Coroutine *co; - PGresult *result; - zval *return_value; - zval *object; - zval _object; - ConnStatusType status; - Statement *statement; - enum QueryType request_type; - bool connected; - bool ignore_notices; - bool log_notices; - size_t stmt_counter; - bool request_success; - HashTable *lob_streams; - - bool yield(zval *_return_value, EventType event, double timeout); - bool wait_write_ready(); -}; - -class Statement { - public: - zval *object; - zval _object; - Object *pg_object; - PGresult *result; - char *name; - char *query; - int row; -}; -} // namespace postgresql -} // namespace swoole - -#define PGSQL_ASSOC 1 << 0 -#define PGSQL_NUM 1 << 1 -#define PGSQL_BOTH (PGSQL_ASSOC | PGSQL_NUM) - -/* from postgresql/src/include/catalog/pg_type.h */ -#define BOOLOID 16 -#define BYTEAOID 17 -#define INT2OID 21 -#define INT4OID 23 -#define INT8OID 20 -#define TEXTOID 25 -#define OIDOID 26 -#define FLOAT4OID 700 -#define FLOAT8OID 701 - -// extension part - -using swoole::Coroutine; -using swoole::Event; -using swoole::Reactor; -using swoole::coroutine::System; -using swoole::network::Socket; -using PGObject = swoole::postgresql::Object; -using PGStatement = swoole::postgresql::Statement; -using PGQueryType = swoole::postgresql::QueryType; - -static zend_class_entry *swoole_postgresql_coro_ce, *swoole_postgresql_coro_statement_ce; -static zend_object_handlers swoole_postgresql_coro_handlers, swoole_postgresql_coro_statement_handlers; - -struct PostgreSQLObject { - PGObject *object; - zend_object std; -}; - -static sw_inline PostgreSQLObject *php_swoole_postgresql_coro_fetch_object(zend_object *obj) { - return (PostgreSQLObject *) ((char *) obj - swoole_postgresql_coro_handlers.offset); -} - -static sw_inline PGObject *php_swoole_postgresql_coro_get_object(zval *zobject) { - return php_swoole_postgresql_coro_fetch_object(Z_OBJ_P(zobject))->object; -} - -static sw_inline zend_object *php_swoole_postgresql_coro_get_zend_object(PostgreSQLObject *obj) { - return (zend_object *) ((char *) obj + swoole_postgresql_coro_handlers.offset); -} - -struct PostgreSQLStatementObject { - PGStatement *object; - zend_object std; -}; - -static sw_inline PostgreSQLStatementObject *php_swoole_postgresql_coro_statement_fetch_object(zend_object *obj) { - return (PostgreSQLStatementObject *) ((char *) obj - swoole_postgresql_coro_statement_handlers.offset); -} - -static sw_inline PGStatement *php_swoole_postgresql_coro_statement_get_object(zval *zobject) { - return php_swoole_postgresql_coro_statement_fetch_object(Z_OBJ_P(zobject))->object; -} - -static int swoole_postgresql_coro_close(zval *zobject); - -static void php_swoole_postgresql_coro_free_object(zend_object *object) { - PostgreSQLObject *postgresql_coro = php_swoole_postgresql_coro_fetch_object(object); - if (postgresql_coro->object->conn) { - zval zobject; - ZVAL_OBJ(&zobject, object); - swoole_postgresql_coro_close(&zobject); - } - delete postgresql_coro->object; - zend_object_std_dtor(&postgresql_coro->std); -} - -static zend_object *php_swoole_postgresql_coro_create_object(zend_class_entry *ce) { - PostgreSQLObject *postgresql_coro = (PostgreSQLObject *) zend_object_alloc(sizeof(*postgresql_coro), ce); - zend_object_std_init(&postgresql_coro->std, ce); - object_properties_init(&postgresql_coro->std, ce); - postgresql_coro->std.handlers = &swoole_postgresql_coro_handlers; - - Coroutine::get_current_safe(); - - do { - postgresql_coro->object = new PGObject(); - PGObject *object = postgresql_coro->object; - object->object = &object->_object; - ZVAL_OBJ(object->object, &postgresql_coro->std); - } while (0); - - return &postgresql_coro->std; -} - -static void php_swoole_postgresql_coro_statement_dtor_object(zend_object *object) { - PGresult *pgsql_result; - PostgreSQLStatementObject *postgresql_coro_statement = php_swoole_postgresql_coro_statement_fetch_object(object); - PGStatement *statement = postgresql_coro_statement->object; - if (statement->result) { - PQclear(statement->result); - statement->result = nullptr; - } - - if (swoole_coroutine_is_in() && statement->pg_object->conn && statement->pg_object->connected && statement->name) { - while ((pgsql_result = PQgetResult(statement->pg_object->conn))) { - PQclear(pgsql_result); - } - - statement->pg_object->request_type = PGQueryType::NORMAL_QUERY; - if (0 == PQsendQuery(statement->pg_object->conn, - swoole::std_string::format("DEALLOCATE %s", statement->name).c_str())) { - char *err_msg = PQerrorMessage(statement->pg_object->conn); - swoole_warning("error:[%s]", err_msg); - } - zval zv; - if (statement->pg_object->wait_write_ready() && - statement->pg_object->yield(&zv, SW_EVENT_READ, Socket::default_read_timeout) && - statement->pg_object->result) { - PQclear(statement->pg_object->result); - statement->pg_object->result = nullptr; - } - } -} - -static void php_swoole_postgresql_coro_statement_free_object(zend_object *object) { - PostgreSQLStatementObject *postgresql_coro_statement = php_swoole_postgresql_coro_statement_fetch_object(object); - PGStatement *statement = postgresql_coro_statement->object; - - if (statement->name) { - efree(statement->name); - statement->name = nullptr; - } - if (statement->query) { - efree(statement->query); - statement->query = nullptr; - } - OBJ_RELEASE(SW_Z8_OBJ_P(statement->pg_object->object)); - delete statement; - zend_object_std_dtor(&postgresql_coro_statement->std); -} - -static zend_object *php_swoole_postgresql_coro_statement_create_object(zend_class_entry *ce) { - php_swoole_fatal_error(E_ERROR, "you must create postgresql statement object by prepare method"); - return nullptr; -} - -static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object) { - PostgreSQLStatementObject *postgresql_coro_statement = (PostgreSQLStatementObject *) zend_object_alloc( - sizeof(*postgresql_coro_statement), swoole_postgresql_coro_statement_ce); - zend_object_std_init(&postgresql_coro_statement->std, swoole_postgresql_coro_statement_ce); - object_properties_init(&postgresql_coro_statement->std, swoole_postgresql_coro_statement_ce); - postgresql_coro_statement->std.handlers = &swoole_postgresql_coro_statement_handlers; - - Coroutine::get_current_safe(); - - do { - postgresql_coro_statement->object = new PGStatement(); - PGStatement *object = postgresql_coro_statement->object; - object->pg_object = pg_object; - object->object = &object->_object; - ZVAL_OBJ(object->object, &postgresql_coro_statement->std); - } while (0); - - GC_ADDREF(SW_Z8_OBJ_P(pg_object->object)); - return &postgresql_coro_statement->std; -} - -static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object, const char *query) { - zend_object *zobject = php_swoole_postgresql_coro_statement_create_object(pg_object); - PGStatement *stmt = php_swoole_postgresql_coro_statement_fetch_object(zobject)->object; - stmt->query = estrdup(query); - stmt->result = stmt->pg_object->result; - return zobject; -} - -static zend_object *php_swoole_postgresql_coro_statement_create_object(PGObject *pg_object, - const char *stmtname, - const char *query) { - zend_object *zobject = php_swoole_postgresql_coro_statement_create_object(pg_object); - PGStatement *stmt = php_swoole_postgresql_coro_statement_fetch_object(zobject)->object; - stmt->name = estrdup(stmtname); - stmt->query = estrdup(query); - return zobject; -} - -/* {{{ pdo_pgsql_create_lob_stream */ -struct swoole_pgsql_lob_self { - zval zobject; - PGconn *conn; - int lfd; - Oid oid; -}; - -static ssize_t pgsql_lob_write(php_stream *stream, const char *buf, size_t count) { - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; - int result = 0; - swoole::coroutine::async([&]() { result = lo_write(self->conn, self->lfd, (char *) buf, count); }); - if (result < 0) { - php_swoole_error(E_WARNING, "lo_write() failed. %s", PQerrorMessage(self->conn)); - } - return result; -} - -static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) { - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; - int result = 0; - swoole::coroutine::async([&]() { result = lo_read(self->conn, self->lfd, buf, count); }); - if (result < 0) { - php_swoole_error(E_WARNING, "lo_read() failed. %s", PQerrorMessage(self->conn)); - } - return result; -} - -static int pgsql_lob_close(php_stream *stream, int close_handle) { - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; - PGObject *object = php_swoole_postgresql_coro_get_object(&self->zobject); - - if (close_handle) { - swoole::coroutine::async([&]() { lo_close(self->conn, self->lfd); }); - } - zend_hash_index_del(object->lob_streams, php_stream_get_resource_id(stream)); - zval_ptr_dtor(&self->zobject); - efree(self); - return 0; -} - -static int pgsql_lob_flush(php_stream *stream) { - return 0; -} - -static int pgsql_lob_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) { - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stream->abstract; - zend_off_t pos = 0; - swoole::coroutine::async([&]() { -#if defined(HAVE_PG_LO64) && defined(ZEND_ENABLE_ZVAL_LONG64) - pos = lo_lseek64(self->conn, self->lfd, offset, whence); -#else - pos = lo_lseek(self->conn, self->lfd, offset, whence); -#endif - }); - *newoffset = pos; - return pos >= 0 ? 0 : -1; -} - -const php_stream_ops swoole_pgsql_lob_stream_ops = {pgsql_lob_write, - pgsql_lob_read, - pgsql_lob_close, - pgsql_lob_flush, - "swoole pgsql lob stream", - pgsql_lob_seek, - NULL, - NULL, - NULL}; - -php_stream *swoole_pgsql_create_lob_stream(zval *zobject, int lfd, Oid oid) { - php_stream *stm; - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) ecalloc(1, sizeof(swoole_pgsql_lob_self)); - PGObject *object = php_swoole_postgresql_coro_get_object(zobject); - - ZVAL_COPY_VALUE(&self->zobject, object->object); - self->lfd = lfd; - self->oid = oid; - self->conn = object->conn; - - stm = php_stream_alloc(&swoole_pgsql_lob_stream_ops, self, 0, "r+b"); - - if (stm) { - Z_ADDREF_P(&self->zobject); - zend_hash_index_add_ptr(object->lob_streams, php_stream_get_resource_id(stm), stm->res); - return stm; - } - - efree(self); - return NULL; -} -/* }}} */ - -static PHP_METHOD(swoole_postgresql_coro, __construct); -static PHP_METHOD(swoole_postgresql_coro, __destruct); -static PHP_METHOD(swoole_postgresql_coro, connect); -static PHP_METHOD(swoole_postgresql_coro, escape); -static PHP_METHOD(swoole_postgresql_coro, escapeLiteral); -static PHP_METHOD(swoole_postgresql_coro, escapeIdentifier); -static PHP_METHOD(swoole_postgresql_coro, query); -static PHP_METHOD(swoole_postgresql_coro, prepare); -static PHP_METHOD(swoole_postgresql_coro, metaData); -static PHP_METHOD(swoole_postgresql_coro, createLOB); -static PHP_METHOD(swoole_postgresql_coro, openLOB); -static PHP_METHOD(swoole_postgresql_coro, unlinkLOB); - -static PHP_METHOD(swoole_postgresql_coro_statement, execute); -static PHP_METHOD(swoole_postgresql_coro_statement, fetchAll); -static PHP_METHOD(swoole_postgresql_coro_statement, affectedRows); -static PHP_METHOD(swoole_postgresql_coro_statement, numRows); -static PHP_METHOD(swoole_postgresql_coro_statement, fieldCount); -static PHP_METHOD(swoole_postgresql_coro_statement, fetchObject); -static PHP_METHOD(swoole_postgresql_coro_statement, fetchAssoc); -static PHP_METHOD(swoole_postgresql_coro_statement, fetchArray); -static PHP_METHOD(swoole_postgresql_coro_statement, fetchRow); - -static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object); - -static int swoole_pgsql_coro_onReadable(Reactor *reactor, Event *event); -static int swoole_pgsql_coro_onWritable(Reactor *reactor, Event *event); -static int swoole_pgsql_coro_onError(Reactor *reactor, Event *event); -static int swoole_postgresql_coro_close(zval *zobject); -static int query_result_parse(PGObject *object); -static int prepare_result_parse(PGObject *object); -static int meta_data_result_parse(PGObject *object); -static void _php_pgsql_free_params(char **params, int num_params); - -static void swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type); -static PGresult *swoole_pgsql_get_result(PGObject *object); -static void swoole_pgsql_close_lob_streams(PGObject *object); -static inline bool swoole_pgsql_in_transaction(PGObject *object); - -// clang-format off -static const zend_function_entry swoole_postgresql_coro_methods[] = -{ - PHP_ME(swoole_postgresql_coro, __construct, arginfo_class_Swoole_Coroutine_PostgreSQL___construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, connect, arginfo_class_Swoole_Coroutine_PostgreSQL_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, query, arginfo_class_Swoole_Coroutine_PostgreSQL_query, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, prepare, arginfo_class_Swoole_Coroutine_PostgreSQL_prepare, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, metaData, arginfo_class_Swoole_Coroutine_PostgreSQL_metaData, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, escape, arginfo_class_Swoole_Coroutine_PostgreSQL_escape, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, escapeLiteral, arginfo_class_Swoole_Coroutine_PostgreSQL_escapeLiteral, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, escapeIdentifier, arginfo_class_Swoole_Coroutine_PostgreSQL_escapeIdentifier, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, createLOB, arginfo_class_Swoole_Coroutine_PostgreSQL_createLOB, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, openLOB, arginfo_class_Swoole_Coroutine_PostgreSQL_openLOB, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, unlinkLOB, arginfo_class_Swoole_Coroutine_PostgreSQL_unlinkLOB, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro, __destruct, arginfo_class_Swoole_Coroutine_PostgreSQL___destruct, ZEND_ACC_PUBLIC) - PHP_FE_END -}; -// clang-format on - -// clang-format off -static const zend_function_entry swoole_postgresql_coro_statement_methods[] = -{ - PHP_ME(swoole_postgresql_coro_statement, execute, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_execute, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fetchAll, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchAll, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, affectedRows, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_affectedRows, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, numRows, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_numRows, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fieldCount, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fieldCount, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fetchObject, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchObject, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fetchAssoc, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchAssoc, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fetchArray, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchArray, ZEND_ACC_PUBLIC) - PHP_ME(swoole_postgresql_coro_statement, fetchRow, arginfo_class_Swoole_Coroutine_PostgreSQLStatement_fetchRow, ZEND_ACC_PUBLIC) - PHP_FE_END -}; -// clang-format on - -void php_swoole_postgresql_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY( - swoole_postgresql_coro, "Swoole\\Coroutine\\PostgreSQL", "Co\\PostgreSQL", swoole_postgresql_coro_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_postgresql_coro); - SW_SET_CLASS_CLONEABLE(swoole_postgresql_coro, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_postgresql_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_postgresql_coro, - php_swoole_postgresql_coro_create_object, - php_swoole_postgresql_coro_free_object, - PostgreSQLObject, - std); - - zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("error"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_postgresql_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_postgresql_coro_ce, ZEND_STRL("resultStatus"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("resultDiag"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_postgresql_coro_ce, ZEND_STRL("notices"), ZEND_ACC_PUBLIC); - - SW_INIT_CLASS_ENTRY(swoole_postgresql_coro_statement, - "Swoole\\Coroutine\\PostgreSQLStatement", - nullptr, - swoole_postgresql_coro_statement_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_postgresql_coro_statement); - SW_SET_CLASS_CLONEABLE(swoole_postgresql_coro_statement, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_postgresql_coro_statement, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_postgresql_coro_statement, - php_swoole_postgresql_coro_statement_create_object, - php_swoole_postgresql_coro_statement_free_object, - PostgreSQLStatementObject, - std); - SW_SET_CLASS_DTOR(swoole_postgresql_coro_statement, php_swoole_postgresql_coro_statement_dtor_object); - - zend_declare_property_null(swoole_postgresql_coro_statement_ce, ZEND_STRL("error"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_postgresql_coro_statement_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_postgresql_coro_statement_ce, ZEND_STRL("resultStatus"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_postgresql_coro_statement_ce, ZEND_STRL("resultDiag"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_postgresql_coro_statement_ce, ZEND_STRL("notices"), ZEND_ACC_PUBLIC); - - SW_REGISTER_LONG_CONSTANT("SW_PGSQL_ASSOC", PGSQL_ASSOC); - SW_REGISTER_LONG_CONSTANT("SW_PGSQL_NUM", PGSQL_NUM); - SW_REGISTER_LONG_CONSTANT("SW_PGSQL_BOTH", PGSQL_BOTH); -} - -static char *_php_pgsql_trim_message(const char *message, size_t *len) { - size_t i = strlen(message); - if (i > 2 && (message[i - 2] == '\r' || message[i - 2] == '\n') && message[i - 1] == '.') { - --i; - } - while (i > 1 && (message[i - 1] == '\r' || message[i - 1] == '\n')) { - --i; - } - if (len) { - *len = i; - } - return estrndup(message, i); -} - -static void _php_pgsql_notice_handler(void *resource_id, const char *message) { - zval *notices; - char *trimed_message; - size_t trimed_message_len; - PGObject *object = (PGObject *) resource_id; - - if (!object->ignore_notices) { - notices = sw_zend_read_and_convert_property_array( - swoole_postgresql_coro_ce, &object->_object, ZEND_STRL("notices"), 0); - - trimed_message = _php_pgsql_trim_message(message, &trimed_message_len); - if (object->log_notices) { - php_error_docref(nullptr, E_NOTICE, "%s", trimed_message); - } - add_next_index_stringl(notices, trimed_message, trimed_message_len); - efree(trimed_message); - } -} - -static PHP_METHOD(swoole_postgresql_coro, __construct) {} - -static PHP_METHOD(swoole_postgresql_coro, connect) { - zval *conninfo; - double timeout = Socket::default_connect_timeout; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ZVAL(conninfo) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (object->conn) { - RETURN_FALSE; - } - - zend::String dsn(conninfo); - char *p = dsn.val(); - for (size_t i = 0; i < dsn.len(); i++) { - if (*p == ';') { - *p = ' '; - } - p++; - } - - PGconn *pgsql = PQconnectStart(dsn.val()); - if (!pgsql) { - RETURN_FALSE; - } - - int fd = PQsocket(pgsql); - if (sw_unlikely(fd < 0)) { - RETURN_FALSE; - } - - php_swoole_check_reactor(); - - if (!swoole_event_isset_handler(PHP_SWOOLE_FD_POSTGRESQL)) { - swoole_event_set_handler(PHP_SWOOLE_FD_POSTGRESQL | SW_EVENT_READ, swoole_pgsql_coro_onReadable); - swoole_event_set_handler(PHP_SWOOLE_FD_POSTGRESQL | SW_EVENT_WRITE, swoole_pgsql_coro_onWritable); - swoole_event_set_handler(PHP_SWOOLE_FD_POSTGRESQL | SW_EVENT_ERROR, swoole_pgsql_coro_onError); - } - - object->socket = swoole::make_socket(fd, (enum swFdType) PHP_SWOOLE_FD_POSTGRESQL); - object->socket->object = object; - object->conn = pgsql; - object->status = CONNECTION_STARTED; - object->connected = false; - - ON_SCOPE_EXIT { - if (!object->connected) { - object->conn = nullptr; - object->socket->fd = -1; - object->socket->free(); - } - }; - - PQsetnonblocking(pgsql, 1); - PQsetNoticeProcessor(pgsql, _php_pgsql_notice_handler, object); - - if (pgsql == nullptr || PQstatus(pgsql) == CONNECTION_BAD) { - swoole_warning("Unable to connect to PostgreSQL server: [%s]", PQhost(pgsql)); - if (pgsql) { - PQfinish(pgsql); - } - RETURN_FALSE; - } - - if (!object->yield(return_value, SW_EVENT_WRITE, timeout)) { - const char *feedback; - - switch (PQstatus(pgsql)) { - case CONNECTION_STARTED: - feedback = "connection time out...please make sure your host,dbname,user and password is correct "; - break; - case CONNECTION_MADE: - feedback = "Connected to server.."; - break; - default: - feedback = " time out.."; - break; - } - - char *err_msg = PQerrorMessage(object->conn); - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), err_msg); - - if (pgsql == nullptr || PQstatus(pgsql) == CONNECTION_STARTED) { - swoole_warning(" [%s, %s] ", feedback, err_msg); - } else { - PQfinish(pgsql); - } - - RETURN_FALSE; - } - - ZVAL_BOOL(return_value, object->connected); -} - -static void connect_callback(PGObject *object, Reactor *reactor, Event *event) { - PGconn *conn = object->conn; - ConnStatusType status = PQstatus(conn); - int events = 0; - char *err_msg; - - swoole_event_del(object->socket); - - if (status != CONNECTION_OK) { - PostgresPollingStatusType flag = PQconnectPoll(conn); - switch (flag) { - case PGRES_POLLING_READING: - events = SW_EVENT_READ; - break; - case PGRES_POLLING_WRITING: - events = SW_EVENT_WRITE; - break; - case PGRES_POLLING_OK: - object->connected = true; - object->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), 1); - zend_hash_init(object->lob_streams, 0, NULL, NULL, 1); - events = 0; - break; - case PGRES_POLLING_FAILED: - events = 0; - err_msg = PQerrorMessage(conn); - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); - if (object->statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("error"), - err_msg); - } - break; - default: - swoole_warning("PQconnectPoll unexpected status"); - break; - } - - if (events) { - event->socket->fd = PQsocket(conn); - swoole_event_add(event->socket, events); - return; - } - } - - if (object->connected == 1) { - object->request_success = true; - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); - if (object->statement) { - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); - } - } else { - object->request_success = false; - } - object->co->resume(); -} - -static int swoole_pgsql_coro_onWritable(Reactor *reactor, Event *event) { - PGObject *object = (PGObject *) event->socket->object; - - if (!object->connected) { - connect_callback(object, reactor, event); - return SW_OK; - } - - if (object->co) { - object->co->resume(); - return SW_OK; - } else { - return reactor->default_write_handler(reactor, event); - } -} - -static int swoole_pgsql_coro_onReadable(Reactor *reactor, Event *event) { - PGObject *object = (PGObject *) (event->socket->object); - - if (!object->connected) { - connect_callback(object, reactor, event); - return SW_OK; - } - - switch (object->request_type) { - case PGQueryType::NORMAL_QUERY: - query_result_parse(object); - break; - case PGQueryType::META_DATA: - meta_data_result_parse(object); - break; - case PGQueryType::PREPARE: - prepare_result_parse(object); - break; - } - - return SW_OK; -} - -static int meta_data_result_parse(PGObject *object) { - int i, num_rows; - zval elem; - PGresult *pg_result; - zend_bool extended = 0; - pg_result = swoole_pgsql_get_result(object); - - if (PQresultStatus(pg_result) != PGRES_TUPLES_OK || (num_rows = PQntuples(pg_result)) == 0) { - php_swoole_fatal_error(E_WARNING, "Table doesn't exists"); - return 0; - } - - array_init(object->return_value); - - object->result = pg_result; - for (i = 0; i < num_rows; i++) { - char *name; - array_init(&elem); - /* pg_attribute.attnum */ - add_assoc_long_ex(&elem, "num", sizeof("num") - 1, atoi(PQgetvalue(pg_result, i, 1))); - /* pg_type.typname */ - add_assoc_string_ex(&elem, "type", sizeof("type") - 1, PQgetvalue(pg_result, i, 2)); - /* pg_attribute.attlen */ - add_assoc_long_ex(&elem, "len", sizeof("len") - 1, atoi(PQgetvalue(pg_result, i, 3))); - /* pg_attribute.attnonull */ - add_assoc_bool_ex(&elem, "not null", sizeof("not null") - 1, !strcmp(PQgetvalue(pg_result, i, 4), "t")); - /* pg_attribute.atthasdef */ - add_assoc_bool_ex(&elem, "has default", sizeof("has default") - 1, !strcmp(PQgetvalue(pg_result, i, 5), "t")); - /* pg_attribute.attndims */ - add_assoc_long_ex(&elem, "array dims", sizeof("array dims") - 1, atoi(PQgetvalue(pg_result, i, 6))); - /* pg_type.typtype */ - add_assoc_bool_ex(&elem, "is enum", sizeof("is enum") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "e")); - if (extended) { - /* pg_type.typtype */ - add_assoc_bool_ex(&elem, "is base", sizeof("is base") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "b")); - add_assoc_bool_ex( - &elem, "is composite", sizeof("is composite") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "c")); - add_assoc_bool_ex(&elem, "is pesudo", sizeof("is pesudo") - 1, !strcmp(PQgetvalue(pg_result, i, 7), "p")); - /* pg_description.description */ - add_assoc_string_ex(&elem, "description", sizeof("description") - 1, PQgetvalue(pg_result, i, 8)); - } - /* pg_attribute.attname */ - name = PQgetvalue(pg_result, i, 0); - add_assoc_zval(object->return_value, name, &elem); - } - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); - if (object->statement) { - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); - } - object->co->resume(); - return SW_OK; -} - -static void set_error_diag(const PGObject *object, const PGresult *pgsql_result) { - const unsigned int error_codes[] = {PG_DIAG_SEVERITY, - PG_DIAG_SQLSTATE, - PG_DIAG_MESSAGE_PRIMARY, - PG_DIAG_MESSAGE_DETAIL, - PG_DIAG_MESSAGE_HINT, - PG_DIAG_STATEMENT_POSITION, - PG_DIAG_INTERNAL_POSITION, - PG_DIAG_INTERNAL_QUERY, - PG_DIAG_CONTEXT, - PG_DIAG_SCHEMA_NAME, - PG_DIAG_TABLE_NAME, - PG_DIAG_COLUMN_NAME, - PG_DIAG_DATATYPE_NAME, - PG_DIAG_CONSTRAINT_NAME, - PG_DIAG_SOURCE_FILE, - PG_DIAG_SOURCE_LINE, - PG_DIAG_SOURCE_FUNCTION}; - - const char *error_names[] = {"severity", - "sqlstate", - "message_primary", - "message_detail", - "message_hint", - "statement_position", - "internal_position", - "internal_query", - "content", - "schema_name", - "table_name", - "column_name", - "datatype_name", - "constraint_name", - "source_file", - "source_line", - "source_function"}; - - long unsigned int i; - char *error_result; - - zval result_diag; - array_init_size(&result_diag, sizeof(error_codes) / sizeof(int)); - - for (i = 0; i < sizeof(error_codes) / sizeof(int); i++) { - error_result = PQresultErrorField(pgsql_result, error_codes[i]); - - if (error_result != nullptr) { - add_assoc_string(&result_diag, error_names[i], error_result); - } else { - add_assoc_null(&result_diag, error_names[i]); - } - } - - zend_update_property(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag"), &result_diag); - zval_dtor(&result_diag); -} - -static int query_result_parse(PGObject *object) { - PGresult *pgsql_result; - ExecStatusType status; - - int error = 0; - char *err_msg; - int res; - - pgsql_result = swoole_pgsql_get_result(object); - status = PQresultStatus(pgsql_result); - - zend_update_property_long( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultStatus"), status); - if (object->statement) { - zend_update_property_long(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("resultStatus"), - status); - } - - object->request_success = (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK); - - switch (status) { - case PGRES_EMPTY_QUERY: - case PGRES_BAD_RESPONSE: - case PGRES_NONFATAL_ERROR: - case PGRES_FATAL_ERROR: - err_msg = PQerrorMessage(object->conn); - set_error_diag(object, pgsql_result); - PQclear(pgsql_result); - ZVAL_FALSE(object->return_value); - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); - if (object->statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("error"), - err_msg); - } - object->co->resume(); - break; - case PGRES_COMMAND_OK: /* successful command that did not return rows */ - default: - object->result = pgsql_result; - /* Wait to finish sending buffer */ - res = PQflush(object->conn); - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); - if (object->statement) { - object->statement->row = 0; - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); - } - object->co->resume(); - if (error != 0) { - php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); - } - break; - } - (void) res; - - return SW_OK; -} - -static int prepare_result_parse(PGObject *object) { - int error = 0; - char *err_msg; - int res; - - PGresult *pgsql_result = swoole_pgsql_get_result(object); - ExecStatusType status = PQresultStatus(pgsql_result); - - zend_update_property_long( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultStatus"), status); - if (object->statement) { - zend_update_property_long(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("resultStatus"), - status); - } - - object->request_success = (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK); - - switch (status) { - case PGRES_EMPTY_QUERY: - case PGRES_BAD_RESPONSE: - case PGRES_NONFATAL_ERROR: - case PGRES_FATAL_ERROR: - err_msg = PQerrorMessage(object->conn); - set_error_diag(object, pgsql_result); - PQclear(pgsql_result); - ZVAL_FALSE(object->return_value); - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), err_msg); - if (object->statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("error"), - err_msg); - } - object->co->resume(); - if (error != 0) { - php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); - } - break; - case PGRES_COMMAND_OK: /* successful command that did not return rows */ - /* Wait to finish sending buffer */ - PQclear(pgsql_result); - ZVAL_TRUE(object->return_value); - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error")); - zend_update_property_null(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("resultDiag")); - if (object->statement) { - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error")); - zend_update_property_null( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("resultDiag")); - } - object->co->resume(); - if (error != 0) { - php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); - } - break; - default: - PQclear(pgsql_result); - ZVAL_FALSE(object->return_value); - zend_update_property_string(swoole_postgresql_coro_ce, - SW_Z8_OBJ_P(object->object), - ZEND_STRL("error"), - "Bad result returned to prepare"); - if (object->statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(object->statement->object), - ZEND_STRL("error"), - "Bad result returned to prepare"); - } - object->co->resume(); - if (error != 0) { - php_swoole_fatal_error(E_WARNING, "socket error. Error: %s [%d]", strerror(error), error); - } - break; - } - (void) res; - - return SW_OK; -} - -bool PGObject::wait_write_ready() { - int retval = 0; - while ((retval = PQflush(conn)) == 1) { - zval return_value; - if (!yield(&return_value, SW_EVENT_WRITE, Socket::default_write_timeout)) { - return false; - } - } - - if (retval == -1) { - char *err_msg = PQerrorMessage(conn); - zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object), ZEND_STRL("error"), err_msg); - if (statement) { - zend_update_property_string( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(statement->object), ZEND_STRL("error"), err_msg); - } - return false; - } - - return true; -} - -bool PGObject::yield(zval *_return_value, EventType event, double timeout) { - co = swoole::Coroutine::get_current_safe(); - if (swoole_event_add(socket, event) < 0) { - php_swoole_fatal_error(E_WARNING, "swoole_event_add failed"); - RETVAL_FALSE; - return false; - } - - ON_SCOPE_EXIT { - co = nullptr; - if (!socket->removed && swoole_event_del(socket) < 0) { - php_swoole_fatal_error(E_WARNING, "swoole_event_del failed"); - } - }; - - return_value = _return_value; - - if (!co->yield_ex(timeout)) { - ZVAL_FALSE(_return_value); - - if (co->is_canceled()) { - zend_update_property_string(swoole_postgresql_coro_ce, - SW_Z8_OBJ_P(object), - ZEND_STRL("error"), - swoole_strerror(SW_ERROR_CO_CANCELED)); - if (statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(statement->object), - ZEND_STRL("error"), - swoole_strerror(SW_ERROR_CO_CANCELED)); - } - } else if (co->is_timedout()) { - zend_update_property_string(swoole_postgresql_coro_ce, - SW_Z8_OBJ_P(object), - ZEND_STRL("error"), - swoole_strerror(SW_ERROR_CO_TIMEDOUT)); - if (statement) { - zend_update_property_string(swoole_postgresql_coro_statement_ce, - SW_Z8_OBJ_P(statement->object), - ZEND_STRL("error"), - swoole_strerror(SW_ERROR_CO_TIMEDOUT)); - } - } - - return false; - } else if (!request_success) { - ZVAL_FALSE(_return_value); - return false; - } - - return true; -} - -static PHP_METHOD(swoole_postgresql_coro, query) { - zval *zquery; - PGconn *pgsql; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zquery) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - object->request_type = PGQueryType::NORMAL_QUERY; - pgsql = object->conn; - - bool in_trans = swoole_pgsql_in_transaction(object); - - zend::String query = zquery; - if (PQsendQuery(pgsql, query.val()) == 0) { - char *err_msg = PQerrorMessage(pgsql); - zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), err_msg); - RETURN_FALSE; - } - - if (!object->wait_write_ready()) { - RETURN_FALSE; - } - - if (object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout)) { - RETVAL_OBJ(php_swoole_postgresql_coro_statement_create_object(object, query.val())); - } - - if (in_trans && !swoole_pgsql_in_transaction(object)) { - swoole_pgsql_close_lob_streams(object); - } -} - -static PHP_METHOD(swoole_postgresql_coro, prepare) { - zval *zquery; - PGconn *pgsql; - int is_non_blocking; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zquery) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - object->request_type = PGQueryType::PREPARE; - pgsql = object->conn; - - is_non_blocking = PQisnonblocking(pgsql); - - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_swoole_fatal_error(E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } - - std::string stmtname = swoole::std_string::format("swoole_stmt_%ld", ++object->stmt_counter); - zend::String query = zquery; - if (!PQsendPrepare(pgsql, stmtname.c_str(), query.val(), 0, nullptr)) { - if (is_non_blocking) { - RETURN_FALSE; - } else { - /*if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { - PQreset(pgsql); - }*/ - if (!PQsendPrepare(pgsql, stmtname.c_str(), query.val(), 0, nullptr)) { - RETURN_FALSE; - } - } - } - - if (!object->wait_write_ready()) { - RETURN_FALSE; - } - - if (object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout)) { - RETVAL_OBJ(php_swoole_postgresql_coro_statement_create_object(object, stmtname.c_str(), query.val())); - } -} - -static PHP_METHOD(swoole_postgresql_coro_statement, execute) { - zval *pv_param_arr = nullptr, *tmp; - int num_params = 0; - char **params = nullptr; - PGconn *pgsql; - int is_non_blocking; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(pv_param_arr) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - PGObject *object = statement->pg_object; - if (!object || !object->conn) { - RETURN_FALSE; - } - object->statement = statement; - ON_SCOPE_EXIT { - object->statement = nullptr; - }; - object->request_type = PGQueryType::NORMAL_QUERY; - pgsql = object->conn; - - is_non_blocking = PQisnonblocking(pgsql); - - if (is_non_blocking == 0 && PQsetnonblocking(pgsql, 1) == -1) { - php_swoole_fatal_error(E_NOTICE, "Cannot set connection to nonblocking mode"); - RETURN_FALSE; - } - - bool in_trans = swoole_pgsql_in_transaction(object); - - num_params = pv_param_arr ? zend_hash_num_elements(Z_ARRVAL_P(pv_param_arr)) : 0; - - ON_SCOPE_EXIT { - if (num_params > 0) { - _php_pgsql_free_params(params, num_params); - } - }; - - if (num_params > 0) { - int i = 0; - params = (char **) safe_emalloc(sizeof(char *), num_params, 0); - - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pv_param_arr), tmp) { - if (Z_TYPE_P(tmp) == IS_NULL) { - params[i] = nullptr; - } else { - zval tmp_val; - if (Z_TYPE_P(tmp) == IS_RESOURCE) { - php_stream *stm = NULL; - php_stream_from_zval_no_verify(stm, tmp); - if (stm) { - if (php_stream_is(stm, &swoole_pgsql_lob_stream_ops)) { - struct swoole_pgsql_lob_self *self = (struct swoole_pgsql_lob_self *) stm->abstract; - std::stringstream ss; - ss << self->oid; - ZVAL_STRING(&tmp_val, ss.str().c_str()); - } else { - zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); - ZVAL_STR(&tmp_val, mem ? mem : ZSTR_EMPTY_ALLOC()); - } - } else { - php_swoole_fatal_error(E_WARNING, "Expected a stream resource"); - RETURN_FALSE; - } - } else { - ZVAL_COPY(&tmp_val, tmp); - convert_to_string(&tmp_val); - if (Z_TYPE(tmp_val) != IS_STRING) { - php_swoole_fatal_error(E_WARNING, "Error converting parameter"); - zval_ptr_dtor(&tmp_val); - RETURN_FALSE; - } - } - params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val)); - zval_ptr_dtor(&tmp_val); - } - i++; - } - ZEND_HASH_FOREACH_END(); - } - - if (PQsendQueryPrepared(pgsql, statement->name, num_params, (const char *const *) params, nullptr, nullptr, 0)) { - } else if (is_non_blocking) { - RETURN_FALSE; - } else { - /* - if ((PGG(auto_reset_persistent) & 2) && PQstatus(pgsql) != CONNECTION_OK) { - PQreset(pgsql); - } - */ - if (!PQsendQueryPrepared( - pgsql, statement->name, num_params, (const char *const *) params, nullptr, nullptr, 0)) { - RETURN_FALSE; - } - } - if (!object->wait_write_ready()) { - RETURN_FALSE; - } - if (object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout)) { - statement->result = object->result; - if (in_trans && !swoole_pgsql_in_transaction(object)) { - swoole_pgsql_close_lob_streams(object); - } - RETURN_TRUE; - } -} - -static PHP_METHOD(swoole_postgresql_coro_statement, fetchAll) { - zend_long result_type = PGSQL_ASSOC; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(result_type) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - if (!statement->result) { - RETURN_FALSE; - } - - array_init(return_value); - swoole_pgsql_result2array(statement->result, return_value, result_type); -} - -static PHP_METHOD(swoole_postgresql_coro_statement, affectedRows) { - PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - if (!statement->result) { - RETURN_FALSE; - } - - RETVAL_LONG(atoi(PQcmdTuples(statement->result))); -} - -// query's num -static PHP_METHOD(swoole_postgresql_coro_statement, numRows) { - PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - if (!statement->result) { - RETURN_FALSE; - } - - RETVAL_LONG(PQntuples(statement->result)); -} - -// query's field count -static PHP_METHOD(swoole_postgresql_coro_statement, fieldCount) { - PGStatement *statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - if (!statement->result) { - RETURN_FALSE; - } - - RETVAL_LONG(PQnfields(statement->result)); -} - -/* {{{ proto array fetchRow([, int row [, int result_type]]) - Get a row as an enumerated array */ -static PHP_METHOD(swoole_postgresql_coro_statement, fetchRow) { - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_NUM, 0); -} -/* }}} */ - -/* {{{ proto array fetchAssoc([, int row]) - Fetch a row as an assoc array */ -static PHP_METHOD(swoole_postgresql_coro_statement, fetchAssoc) { - /* pg_fetch_assoc() is added from PHP 4.3.0. It should raise error, when - there is 3rd parameter */ - if (ZEND_NUM_ARGS() > 2) WRONG_PARAM_COUNT; - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 0); -} -/* }}} */ - -/* {{{ proto array fetchArray([, int row [, int result_type]]) - Fetch a row as an array */ -static PHP_METHOD(swoole_postgresql_coro_statement, fetchArray) { - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_BOTH, 0); -} -/* }}} */ - -/* {{{ proto object fetchObject([, int row [, string class_name [, NULL|array ctor_params]]]) - Fetch a row as an object */ -static PHP_METHOD(swoole_postgresql_coro_statement, fetchObject) { - /* fetchObject() allowed result_type used to be. 3rd parameter - must be allowed for compatibility */ - php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, PGSQL_ASSOC, 1); -} - -static void _php_pgsql_free_params(char **params, int num_params) { - if (num_params > 0) { - for (int i = 0; i < num_params; i++) { - if (params[i]) { - efree(params[i]); - } - } - efree(params); - } -} - -/* {{{ void php_pgsql_get_field_value */ -static inline void php_pgsql_get_field_value( - zval *value, PGresult *pgsql_result, zend_long result_type, int row, int column) { - if (PQgetisnull(pgsql_result, row, column)) { - ZVAL_NULL(value); - } else { - char *element = PQgetvalue(pgsql_result, row, column); - if (element) { - const size_t element_len = PQgetlength(pgsql_result, row, column); - Oid pgsql_type = PQftype(pgsql_result, column); - - switch (pgsql_type) { - case BOOLOID: - ZVAL_BOOL(value, *element == 't'); - break; - case FLOAT4OID: - case FLOAT8OID: - if (element_len == sizeof("Infinity") - 1 && strcmp(element, "Infinity") == 0) { - ZVAL_DOUBLE(value, ZEND_INFINITY); - } else if (element_len == sizeof("-Infinity") - 1 && strcmp(element, "-Infinity") == 0) { - ZVAL_DOUBLE(value, -ZEND_INFINITY); - } else if (element_len == sizeof("NaN") - 1 && strcmp(element, "NaN") == 0) { - ZVAL_DOUBLE(value, ZEND_NAN); - } else { - ZVAL_DOUBLE(value, zend_strtod(element, nullptr)); - } - break; - case OIDOID: - case INT2OID: - case INT4OID: -#if SIZEOF_ZEND_LONG >= 8 - case INT8OID: -#endif - { - zend_long long_value; -#if PHP_VERSION_ID < 80100 - ZEND_ATOL(long_value, element); -#else - long_value = ZEND_ATOL(element); -#endif - ZVAL_LONG(value, long_value); - break; - } - case BYTEAOID: { - size_t tmp_len; - char *tmp_ptr = (char *) PQunescapeBytea((unsigned char *) element, &tmp_len); - if (!tmp_ptr) { - /* PQunescapeBytea returned an error */ - ZVAL_NULL(value); - } else { - ZVAL_STRINGL(value, tmp_ptr, tmp_len); - PQfreemem(tmp_ptr); - } - break; - } - default: - ZVAL_STRINGL(value, element, element_len); - } - } else { - ZVAL_NULL(value); - } - } -} -/* }}} */ - -/* {{{ swoole_pgsql_result2array - */ -static void swoole_pgsql_result2array(PGresult *pg_result, zval *ret_array, long result_type) { - zval row; - const char *field_name; - size_t num_fields, unknown_columns; - int pg_numrows, pg_row; - uint32_t i; - assert(Z_TYPE_P(ret_array) == IS_ARRAY); - - pg_numrows = PQntuples(pg_result); - for (pg_row = 0; pg_row < pg_numrows; pg_row++) { - array_init(&row); - unknown_columns = 0; - for (i = 0, num_fields = PQnfields(pg_result); i < num_fields; i++) { - if (result_type & PGSQL_ASSOC) { - zval value; - php_pgsql_get_field_value(&value, pg_result, result_type, pg_row, i); - field_name = PQfname(pg_result, i); - if (0 == strcmp("?column?", field_name)) { - if (unknown_columns > 0) { - field_name = (std::string(field_name) + std::to_string(unknown_columns)).c_str(); - } - ++unknown_columns; - } - add_assoc_zval(&row, field_name, &value); - } - if (result_type & PGSQL_NUM) { - zval value; - php_pgsql_get_field_value(&value, pg_result, result_type, pg_row, i); - add_next_index_zval(&row, &value); - } - } - add_index_zval(ret_array, pg_row, &row); - } -} -/* }}} */ - -static PHP_METHOD(swoole_postgresql_coro, metaData) { - char *table_name; - size_t table_name_len; - zend_bool extended = 0; - PGconn *pgsql; - - char *src, *tmp_name, *tmp_name2 = nullptr; - char *escaped; - smart_str querystr = {0}; - size_t new_len; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(table_name, table_name_len) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - object->request_type = PGQueryType::META_DATA; - pgsql = object->conn; - - if (table_name_len == 0) { - php_swoole_fatal_error(E_WARNING, "The table name must be specified"); - RETURN_FALSE; - } - - src = estrdup(table_name); - tmp_name = php_strtok_r(src, ".", &tmp_name2); - if (!tmp_name) { - efree(src); - php_swoole_fatal_error(E_WARNING, "The table name must be specified"); - RETURN_FALSE; - } - if (!tmp_name2 || !*tmp_name2) { - /* Default schema */ - tmp_name2 = tmp_name; - tmp_name = (char *) "public"; - } - - if (extended) { - smart_str_appends( - &querystr, - "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotNULL, a.atthasdef, a.attndims, t.typtype, " - "d.description " - "FROM pg_class as c " - " JOIN pg_attribute a ON (a.attrelid = c.oid) " - " JOIN pg_type t ON (a.atttypid = t.oid) " - " JOIN pg_namespace n ON (c.relnamespace = n.oid) " - " LEFT JOIN pg_description d ON (d.objoid=a.attrelid AND d.objsubid=a.attnum AND c.oid=d.objoid) " - "WHERE a.attnum > 0 AND c.relname = '"); - } else { - smart_str_appends( - &querystr, - "SELECT a.attname, a.attnum, t.typname, a.attlen, a.attnotnull, a.atthasdef, a.attndims, t.typtype " - "FROM pg_class as c " - " JOIN pg_attribute a ON (a.attrelid = c.oid) " - " JOIN pg_type t ON (a.atttypid = t.oid) " - " JOIN pg_namespace n ON (c.relnamespace = n.oid) " - "WHERE a.attnum > 0 AND c.relname = '"); - } - escaped = (char *) safe_emalloc(strlen(tmp_name2), 2, 1); - new_len = PQescapeStringConn(pgsql, escaped, tmp_name2, strlen(tmp_name2), nullptr); - if (new_len) { - smart_str_appendl(&querystr, escaped, new_len); - } - efree(escaped); - - smart_str_appends(&querystr, "' AND n.nspname = '"); - escaped = (char *) safe_emalloc(strlen(tmp_name), 2, 1); - new_len = PQescapeStringConn(pgsql, escaped, tmp_name, strlen(tmp_name), nullptr); - if (new_len) { - smart_str_appendl(&querystr, escaped, new_len); - } - efree(escaped); - - smart_str_appends(&querystr, "' ORDER BY a.attnum;"); - smart_str_0(&querystr); - efree(src); - - int ret = PQsendQuery(pgsql, ZSTR_VAL(querystr.s)); - if (ret == 0) { - char *err_msg = PQerrorMessage(pgsql); - swoole_warning("error:[%s]", err_msg); - } - smart_str_free(&querystr); - object->yield(return_value, SW_EVENT_READ, Socket::default_read_timeout); -} - -static PHP_METHOD(swoole_postgresql_coro, createLOB) { - ZEND_PARSE_PARAMETERS_NONE(); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - Oid lfd = 0; - swoole::coroutine::async([&]() { - lfd = lo_creat(object->conn, INV_READ | INV_WRITE); - PGresult *pgsql_result = swoole_pgsql_get_result(object); - set_error_diag(object, pgsql_result); - PQclear(pgsql_result); - }); - - if (lfd != InvalidOid) { - RETURN_LONG(lfd); - } - - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); - - RETURN_FALSE; -} - -static PHP_METHOD(swoole_postgresql_coro, openLOB) { - Oid oid = 0; - char *modestr = "rb"; - size_t modestrlen; - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l|s", &oid, &modestr, &modestrlen)) { - RETURN_THROWS(); - } - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - - if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { - RETURN_FALSE; - } - - int mode = INV_READ; - - if (strpbrk(modestr, "+w")) { - mode = INV_READ | INV_WRITE; - } - - int lfd = -1; - - swoole::coroutine::async([&]() { - lfd = lo_open(object->conn, oid, mode); - PGresult *pgsql_result = swoole_pgsql_get_result(object); - set_error_diag(object, pgsql_result); - PQclear(pgsql_result); - }); - - if (lfd >= 0) { - php_stream *stream = swoole_pgsql_create_lob_stream(ZEND_THIS, lfd, oid); - if (stream) { - php_stream_to_zval(stream, return_value); - return; - } - } - - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); - - RETURN_FALSE; -} - -static PHP_METHOD(swoole_postgresql_coro, unlinkLOB) { - Oid oid = 0; - - if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &oid)) { - RETURN_THROWS(); - } - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - - if (oid == 0 && (errno == ERANGE || errno == EINVAL)) { - RETURN_FALSE; - } - - int result = 0; - swoole::coroutine::async([&]() { - result = lo_unlink(object->conn, oid); - PGresult *pgsql_result = swoole_pgsql_get_result(object); - set_error_diag(object, pgsql_result); - PQclear(pgsql_result); - }); - if (1 == result) { - RETURN_TRUE; - } - - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), PQerrorMessage(object->conn)); - - RETURN_FALSE; -} - -/* {{{ void php_pgsql_fetch_hash */ -static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_type, int into_object) { - zval *zrow = nullptr; - PGresult *pgsql_result; - PGObject *pg_result; - PGStatement *statement; - int i, num_fields, pgsql_row, use_row; - zend_long row = -1; - char *field_name; - zval *ctor_params = nullptr; - zend_class_entry *ce = nullptr; - - if (into_object) { - zend_string *class_name = nullptr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z!Sz", &zrow, &class_name, &ctor_params) == FAILURE) { - RETURN_FALSE; - } - if (!class_name) { - ce = zend_standard_class_def; - } else { - ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_AUTO); - } - if (!ce) { - php_swoole_fatal_error(E_WARNING, "Could not find class '%s'", ZSTR_VAL(class_name)); - return; - } - result_type = PGSQL_ASSOC; - } else { - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z!l", &zrow, &result_type) == FAILURE) { - RETURN_FALSE; - } - } - if (zrow == nullptr) { - row = -1; - } else { - row = zval_get_long(zrow); - if (row < 0) { - php_swoole_fatal_error(E_WARNING, "The row parameter must be greater or equal to zero"); - RETURN_FALSE; - } - } - use_row = ZEND_NUM_ARGS() > 0 && row != -1; - - if (!(result_type & PGSQL_BOTH)) { - php_swoole_fatal_error(E_WARNING, "Invalid result type"); - RETURN_FALSE; - } - - statement = php_swoole_postgresql_coro_statement_get_object(ZEND_THIS); - if (!statement || !statement->result) { - RETURN_FALSE; - } - pgsql_result = statement->result; - pg_result = statement->pg_object; - if (!pg_result || !pg_result->conn) { - RETURN_FALSE; - } - - if (use_row) { - if (row < 0 || row >= PQntuples(pgsql_result)) { - php_swoole_fatal_error(E_WARNING, "Unable to jump to row " ZEND_LONG_FMT " on PostgreSQL result", row); - RETURN_FALSE; - } - pgsql_row = (int) row; - statement->row = pgsql_row; - } else { - /* If 2nd param is nullptr, use internal row counter to access next row */ - pgsql_row = statement->row; - if (pgsql_row < 0 || pgsql_row >= PQntuples(pgsql_result)) { - RETURN_FALSE; - } - statement->row++; - } - - array_init(return_value); - for (i = 0, num_fields = PQnfields(pgsql_result); i < num_fields; i++) { - if (result_type & PGSQL_NUM) { - zval value; - php_pgsql_get_field_value(&value, pgsql_result, result_type, pgsql_row, i); - add_index_zval(return_value, i, &value); - } - - if (result_type & PGSQL_ASSOC) { - zval value; - php_pgsql_get_field_value(&value, pgsql_result, result_type, pgsql_row, i); - field_name = PQfname(pgsql_result, i); - add_assoc_zval(return_value, field_name, &value); - } - } - - if (into_object) { - zval dataset; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - zval retval; - - ZVAL_COPY_VALUE(&dataset, return_value); - object_and_properties_init(return_value, ce, nullptr); - if (!ce->default_properties_count && !ce->__set) { - Z_OBJ_P(return_value)->properties = Z_ARR(dataset); - } else { - zend_merge_properties(return_value, Z_ARRVAL(dataset)); - zval_ptr_dtor(&dataset); - } - - if (ce->constructor) { - fci.size = sizeof(fci); - ZVAL_UNDEF(&fci.function_name); - fci.object = Z_OBJ_P(return_value); - fci.retval = &retval; - fci.params = nullptr; - fci.param_count = 0; - - if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) { - if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) { - /* Two problems why we throw exceptions here: PHP is typeless - * and hence passing one argument that's not an array could be - * by mistake and the other way round is possible, too. The - * single value is an array. Also we'd have to make that one - * argument passed by reference. - */ - zend_throw_exception(zend_ce_exception, "Parameter ctor_params must be an array", 0); - return; - } - } - - fcc.function_handler = ce->constructor; - fcc.calling_scope = zend_get_executed_scope(); - fcc.called_scope = Z_OBJCE_P(return_value); - fcc.object = Z_OBJ_P(return_value); - - if (zend_call_function(&fci, &fcc) == FAILURE) { - zend_throw_exception_ex(zend_ce_exception, - 0, - "Could not execute %s::%s()", - ZSTR_VAL(ce->name), - ZSTR_VAL(ce->constructor->common.function_name)); - } else { - zval_ptr_dtor(&retval); - } - if (fci.params) { - efree(fci.params); - } - } else if (ctor_params) { - zend_throw_exception_ex(zend_ce_exception, - 0, - "Class %s does not have a constructor hence you cannot use ctor_params", - ZSTR_VAL(ce->name)); - } - } -} -/* }}} */ - -static int swoole_pgsql_coro_onError(Reactor *reactor, Event *event) { - PGObject *object = (PGObject *) (event->socket->object); - - zend_update_property_string(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(object->object), ZEND_STRL("error"), "onerror"); - if (object->statement) { - zend_update_property_string( - swoole_postgresql_coro_statement_ce, SW_Z8_OBJ_P(object->statement->object), ZEND_STRL("error"), "onerror"); - object->statement = nullptr; - } - object->connected = false; - ZVAL_FALSE(object->return_value); - object->co->resume(); - - return SW_OK; -} - -static PHP_METHOD(swoole_postgresql_coro, __destruct) {} - -static int swoole_postgresql_coro_close(zval *zobject) { - PGObject *object = php_swoole_postgresql_coro_get_object(zobject); - if (!object || !object->conn) { - php_swoole_fatal_error(E_WARNING, "object is not instanceof swoole_postgresql_coro"); - return FAILURE; - } - - if (sw_reactor()) { - Socket *_socket = object->socket; - if (!_socket->removed) { - sw_reactor()->del(_socket); - } - _socket->object = nullptr; - _socket->free(); - } - - PGresult *res; - if (object->connected) { - while ((res = PQgetResult(object->conn))) { - PQclear(res); - } - /** - * PQfinish will close fd - */ - PQfinish(object->conn); - /** - * fd marked -1, prevent double close - */ - object->socket->fd = -1; - object->conn = nullptr; - object->connected = false; - if (object->lob_streams) { - swoole_pgsql_close_lob_streams(object); - zend_hash_destroy(object->lob_streams); - pefree(object->lob_streams, 1); - object->lob_streams = nullptr; - } - } - object->co = nullptr; - return SUCCESS; -} - -static PHP_METHOD(swoole_postgresql_coro, escape) { - char *str; - size_t l_str; - PGconn *pgsql; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(str, l_str) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - pgsql = object->conn; - - zend_string *result = zend_string_alloc(l_str * 2, 0); - int error = 0; - size_t new_len = PQescapeStringConn(object->conn, result->val, str, l_str, &error); - - if (new_len == 0 || error) { - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), PQerrorMessage(pgsql)); - zend_update_property_long(swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), error); - zend_string_free(result); - RETURN_FALSE; - } else { - result->val[new_len] = 0; - result->len = new_len; - RETURN_STR(result); - } -} - -static PHP_METHOD(swoole_postgresql_coro, escapeLiteral) { - char *str, *tmp; - size_t l_str; - PGconn *pgsql; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(str, l_str) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - pgsql = object->conn; - - tmp = PQescapeLiteral(pgsql, str, l_str); - if (tmp == nullptr) { - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), PQerrorMessage(pgsql)); - - RETURN_FALSE; - } - - RETVAL_STRING(tmp); - PQfreemem(tmp); -} - -static PHP_METHOD(swoole_postgresql_coro, escapeIdentifier) { - char *str, *tmp; - size_t l_str; - PGconn *pgsql; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(str, l_str) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - PGObject *object = php_swoole_postgresql_coro_get_object(ZEND_THIS); - if (!object || !object->conn) { - RETURN_FALSE; - } - pgsql = object->conn; - - tmp = PQescapeIdentifier(pgsql, str, l_str); - if (tmp == nullptr) { - zend_update_property_string( - swoole_postgresql_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("error"), PQerrorMessage(pgsql)); - - RETURN_FALSE; - } - - RETVAL_STRING(tmp); - PQfreemem(tmp); -} - -/* {{{ swoole_pgsql_get_result */ -static PGresult *swoole_pgsql_get_result(PGObject *object) { - PGresult *result, *last_result = nullptr; - - while ((result = PQgetResult(object->conn))) { - PQclear(last_result); - last_result = result; - } - - return last_result; -} -/* }}} */ - -/* {{{ swoole_pgsql_close_lob_streams */ -static void swoole_pgsql_close_lob_streams(PGObject *object) { - zval *zres; - if (object->lob_streams) { - ZEND_HASH_FOREACH_VAL(object->lob_streams, zres) { - zend_list_close(Z_RES_P(zres)); - } - ZEND_HASH_FOREACH_END(); - } -} -/* }}} */ - -/* {{{ swoole_pgsql_in_transaction */ -static inline bool swoole_pgsql_in_transaction(PGObject *object) { - return PQtransactionStatus(object->conn) > PQTRANS_IDLE; -} -/* }}} */ - -#endif diff --git a/ext-src/swoole_redis_coro.cc b/ext-src/swoole_redis_coro.cc deleted file mode 100644 index 8ab59ea27dc..00000000000 --- a/ext-src/swoole_redis_coro.cc +++ /dev/null @@ -1,5545 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Swoole | - +----------------------------------------------------------------------+ - | This source file is subject to version 2.0 of the Apache license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.apache.org/licenses/LICENSE-2.0.html | - | If you did not receive a copy of the Apache2.0 license and are unable| - | to obtain it through the world-wide-web, please send a note to | - | license@swoole.com so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#include "php_swoole_cxx.h" - -#include "thirdparty/hiredis/hiredis.h" - -#include "ext/standard/php_var.h" - -using swoole::coroutine::Socket; -using namespace swoole; - -#define SW_REDIS_COMMAND_ALLOC_ARGS_ARR zval *z_args = (zval *) emalloc(argc * sizeof(zval)); -#define SW_REDIS_COMMAND_ARGS_TYPE(arg) Z_TYPE(arg) -#define SW_REDIS_COMMAND_ARGS_LVAL(arg) Z_LVAL(arg) -#define SW_REDIS_COMMAND_ARGS_DVAL(arg) Z_DVAL(arg) -#define SW_REDIS_COMMAND_ARGS_ARRVAL(arg) Z_ARRVAL(arg) -#define SW_REDIS_COMMAND_ARGS_STRVAL(arg) Z_STRVAL(arg) -#define SW_REDIS_COMMAND_ARGS_STRLEN(arg) Z_STRLEN(arg) -#define SW_REDIS_COMMAND_ARGS_REF(arg) &arg - -#define SW_REDIS_COMMAND_BUFFER_SIZE 64 -#define SW_BITOP_MIN_OFFSET 0 -#define SW_BITOP_MAX_OFFSET 4294967295 -#define SW_REDIS_TYPE_NOT_FOUND 0 -#define SW_REDIS_TYPE_STRING 1 -#define SW_REDIS_TYPE_SET 2 -#define SW_REDIS_TYPE_LIST 3 -#define SW_REDIS_TYPE_ZSET 4 -#define SW_REDIS_TYPE_HASH 5 - -/* The same errCode define with hiredis */ -enum swRedisError { - SW_REDIS_ERR_IO = 1, /* Error in read or write */ - SW_REDIS_ERR_OTHER = 2, /* Everything else... */ - SW_REDIS_ERR_EOF = 3, /* End of file */ - SW_REDIS_ERR_PROTOCOL = 4, /* Protocol error */ - SW_REDIS_ERR_OOM = 5, /* Out of memory */ - SW_REDIS_ERR_CLOSED = 6, /* Closed */ - SW_REDIS_ERR_NOAUTH = 7, /* Authentication required */ - SW_REDIS_ERR_ALLOC = 8, /* Alloc failed */ -}; - -/* Extended SET argument detection */ -// clang-format off -#define IS_EX_ARG(a) \ - ((a[0]=='e' || a[0]=='E') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_PX_ARG(a) \ - ((a[0]=='p' || a[0]=='P') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_NX_ARG(a) \ - ((a[0]=='n' || a[0]=='N') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') -#define IS_XX_ARG(a) \ - ((a[0]=='x' || a[0]=='X') && (a[1]=='x' || a[1]=='X') && a[2]=='\0') - -static zend_class_entry *swoole_redis_coro_ce; -static zend_object_handlers swoole_redis_coro_handlers; - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_construct, 0, 0, 0) - ZEND_ARG_INFO(0, config) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_connect, 0, 0, 1) - ZEND_ARG_INFO(0, host) - ZEND_ARG_INFO(0, port) - ZEND_ARG_INFO(0, serialize) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setOptions, 0, 0, 1) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setDefer, 0, 0, 1) - ZEND_ARG_INFO(0, defer) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_void, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_key, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_key_value, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_key_long, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, integer) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_request, 0, 0, 1) - ZEND_ARG_ARRAY_INFO(0, params, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_append, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_auth, 0, 0, 1) - ZEND_ARG_INFO(0, password) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bgSave, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bgrewriteaof, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bitcount, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bitop, 0, 0, 3) - ZEND_ARG_INFO(0, operation) - ZEND_ARG_INFO(0, ret_key) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_blPop, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_brPop, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_brpoplpush, 0, 0, 3) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_close, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_dbSize, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_debug, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_decr, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_decrBy, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_dump, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_eval, 0, 0, 1) - ZEND_ARG_INFO(0, script) - ZEND_ARG_INFO(0, args) - ZEND_ARG_INFO(0, num_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_evalsha, 0, 0, 1) - ZEND_ARG_INFO(0, script_sha) - ZEND_ARG_INFO(0, args) - ZEND_ARG_INFO(0, num_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_exec, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_exists, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_expireAt, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_flushAll, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_flushDB, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_get, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getBit, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getKeys, 0, 0, 1) - ZEND_ARG_INFO(0, pattern) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_getSet, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hDel, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hExists, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hGet, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hGetAll, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hIncrBy, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hIncrByFloat, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hKeys, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hLen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hMget, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hMset, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, pairs) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hSet, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hSetNx, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_hVals, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_incr, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_incrBy, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_incrByFloat, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lGet, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, index) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lGetRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lInsert, 0, 0, 4) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, position) - ZEND_ARG_INFO(0, pivot) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lPop, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lPush, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lPushx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lRemove, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lSet, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, index) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lSize, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lastSave, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_listTrim, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, stop) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_move, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, dbindex) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_mset, 0, 0, 1) - ZEND_ARG_INFO(0, pairs) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_msetnx, 0, 0, 1) - ZEND_ARG_INFO(0, pairs) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_multi, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_persist, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pexpire, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pexpireAt, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pfadd, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, elements) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pfcount, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pfmerge, 0, 0, 2) - ZEND_ARG_INFO(0, dstkey) - ZEND_ARG_INFO(0, keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_ping, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_psetex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, expire) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_psubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, patterns) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_punsubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, patterns) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_pttl, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_publish, 0, 0, 2) - ZEND_ARG_INFO(0, channel) - ZEND_ARG_INFO(0, message) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rPop, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rPush, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rPushx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_randomKey, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_renameKey, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, newkey) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_renameNx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, newkey) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_restore, 0, 0, 3) - ZEND_ARG_INFO(0, ttl) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_role, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rpoplpush, 0, 0, 2) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sAdd, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sContains, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sDiff, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sDiffStore, 0, 0, 2) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sInter, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sInterStore, 0, 0, 2) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sMembers, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sMove, 0, 0, 3) - ZEND_ARG_INFO(0, src) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sPop, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sRandMember, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sRemove, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sSize, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sUnion, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_sUnionStore, 0, 0, 2) - ZEND_ARG_INFO(0, dst) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_save, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_script, 0, 0, 1) - ZEND_ARG_INFO(0, cmd) - ZEND_ARG_INFO(0, args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_select, 0, 0, 1) - ZEND_ARG_INFO(0, dbindex) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_set, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, timeout) - ZEND_ARG_INFO(0, opt) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setBit, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setTimeout, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, expire) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_setnx, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_strlen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_subscribe, 0, 0, 1) - ZEND_ARG_INFO(0, channels) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_unsubscribe, 0, 0, 1) - ZEND_ARG_INFO(0, channels) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_time, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_ttl, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_type, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_unwatch, 0, 0, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_watch, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zAdd, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, score) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zPopMin, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zPopMax, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bzPopMin, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_bzPopMax, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, timeout_or_key) - ZEND_ARG_INFO(0, extra_args) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zCard, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zCount, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zDelete, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zDeleteRangeByRank, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zDeleteRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zIncrBy, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zInter, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, scores) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRangeByLex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRank, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, scores) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRangeByLex, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, limit) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRevRank, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zScore, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zUnion, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_del, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, other_keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lLen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lrange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_lrem, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, value) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_ltrim, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, stop) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_mget, 0, 0, 1) - ZEND_ARG_INFO(0, keys) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_rename, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, newkey) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_scard, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRem, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRemRangeByRank, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRemRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, max) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zRemove, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, other_members) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zSize, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zinterstore, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_zunionstore, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, keys) - ZEND_ARG_INFO(0, weights) - ZEND_ARG_INFO(0, aggregate) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xLen, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xAdd, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, id) - ZEND_ARG_INFO(0, pairs) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xRead, 0, 0, 1) - ZEND_ARG_INFO(0, streams) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xDel, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xRevRange, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, count) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xTrim, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupCreate, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, id) - ZEND_ARG_INFO(0, mkstream) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupSetId, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupDestroy, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupCreateConsumer, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xGroupDelConsumer, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xReadGroup, 0, 0, 3) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) - ZEND_ARG_INFO(0, streams) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xPending, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xAck, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, id) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xClaim, 0, 0, 5) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) - ZEND_ARG_INFO(0, min_idle_time) - ZEND_ARG_INFO(0, id) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xAutoClaim, 0, 0, 5) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) - ZEND_ARG_INFO(0, consumer_name) - ZEND_ARG_INFO(0, min_idle_time) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xInfoConsumers, 0, 0, 2) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, group_name) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xInfoGroups, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_coro_xInfoStream, 0, 0, 1) - ZEND_ARG_INFO(0, key) -ZEND_END_ARG_INFO() -// clang-format on - -#define IS_EX_PX_ARG(a) (IS_EX_ARG(a) || IS_PX_ARG(a)) -#define IS_NX_XX_ARG(a) (IS_NX_ARG(a) || IS_XX_ARG(a)) - -struct RedisClient { - redisContext *context; - struct { - bool auth; - long db_num; - bool subscribe; - } session; - double connect_timeout; - double timeout; - bool serialize; - bool defer; - uint8_t reconnect_interval; - uint8_t reconnected_count; - bool auth; - bool compatibility_mode; - long database; - zval *zobject; - zval _zobject; - zend_object std; -}; - -#define SW_REDIS_COMMAND_CHECK \ - Coroutine::get_current_safe(); \ - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - -#define SW_REDIS_COMMAND_ARGV_FILL(str, str_len) \ - argvlen[i] = str_len; \ - argv[i] = estrndup(str, str_len); \ - i++; - -#define SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(_val) \ - if (redis->serialize) { \ - smart_str sstr = {}; \ - php_serialize_data_t s_ht; \ - PHP_VAR_SERIALIZE_INIT(s_ht); \ - php_var_serialize(&sstr, _val, &s_ht); \ - argvlen[i] = (size_t) sstr.s->len; \ - argv[i] = estrndup(sstr.s->val, sstr.s->len); \ - zend_string_release(sstr.s); \ - PHP_VAR_SERIALIZE_DESTROY(s_ht); \ - } else { \ - zend_string *convert_str = zval_get_string(_val); \ - argvlen[i] = ZSTR_LEN(convert_str); \ - argv[i] = estrndup(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); \ - zend_string_release(convert_str); \ - } \ - i++; - -#define SW_REDIS_COMMAND_ALLOC_ARGV \ - size_t stack_argvlen[SW_REDIS_COMMAND_BUFFER_SIZE]; \ - char *stack_argv[SW_REDIS_COMMAND_BUFFER_SIZE]; \ - size_t *argvlen; \ - char **argv; \ - if (argc > SW_REDIS_COMMAND_BUFFER_SIZE) { \ - argvlen = (size_t *) emalloc(sizeof(size_t) * (argc)); \ - argv = (char **) emalloc(sizeof(char *) * (argc)); \ - } else { \ - argvlen = stack_argvlen; \ - argv = stack_argv; \ - } - -#define SW_REDIS_COMMAND_INCREASE_ARGV(_new_argc) \ - if (_new_argc > SW_REDIS_COMMAND_BUFFER_SIZE && _new_argc > argc) { \ - size_t *tmp_argvlen; \ - char **tmp_argv; \ - tmp_argvlen = (size_t *) emalloc(sizeof(size_t) * (_new_argc)); \ - tmp_argv = (char **) emalloc(sizeof(char *) * (_new_argc)); \ - for (int argc_i = 0; argc_i < argc; argc_i++) { \ - tmp_argvlen[argc_i] = argvlen[argc_i]; \ - tmp_argv[argc_i] = argv[argc_i]; \ - } \ - argvlen = tmp_argvlen; \ - argv = tmp_argv; \ - } \ - argc = _new_argc; - -#define SW_REDIS_COMMAND_FREE_ARGV \ - if (argv != stack_argv) { \ - efree(argvlen); \ - efree(argv); \ - } - -enum { SW_REDIS_MODE_MULTI, SW_REDIS_MODE_PIPELINE }; - -static void swoole_redis_coro_parse_result(RedisClient *redis, zval *return_value, redisReply *reply); - -static sw_inline RedisClient *php_swoole_redis_coro_fetch_object(zend_object *obj) { - return (RedisClient *) ((char *) obj - swoole_redis_coro_handlers.offset); -} - -static sw_inline RedisClient *php_swoole_get_redis_client(zval *zobject) { - RedisClient *redis = (RedisClient *) php_swoole_redis_coro_fetch_object(Z_OBJ_P(zobject)); - if (UNEXPECTED(!redis)) { - php_swoole_fatal_error(E_ERROR, "you must call Redis constructor first"); - } - return redis; -} - -static sw_inline std::shared_ptr swoole_redis_coro_get_socket(redisContext *context) { - if (context->fd > 0 && SwooleTG.reactor) { - return swoole_coroutine_get_socket_object(context->fd); - } - return nullptr; -} - -static sw_inline bool swoole_redis_coro_close(RedisClient *redis) { - if (redis->context) { - int sockfd = redis->context->fd; - auto socket = swoole_redis_coro_get_socket(redis->context); - swoole_trace_log(SW_TRACE_REDIS_CLIENT, "redis connection closed, fd=%d", sockfd); - zend_update_property_bool(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("connected"), 0); - if (!(socket != nullptr && socket->has_bound())) { - redisFreeKeepFd(redis->context); - redis->context = nullptr; - redis->session = {false, 0, false}; - } - if (socket != nullptr) { - swoole_coroutine_close(sockfd); - } - return true; - } - return false; -} - -static void php_swoole_redis_coro_free_object(zend_object *object) { - RedisClient *redis = php_swoole_redis_coro_fetch_object(object); - - if (redis && redis->context) { - swoole_redis_coro_close(redis); - } - - zend_object_std_dtor(&redis->std); -} - -static zend_object *php_swoole_redis_coro_create_object(zend_class_entry *ce) { - RedisClient *redis = (RedisClient *) zend_object_alloc(sizeof(RedisClient), ce); - zend_object_std_init(&redis->std, ce); - object_properties_init(&redis->std, ce); - redis->std.handlers = &swoole_redis_coro_handlers; - return &redis->std; -} - -static sw_inline int sw_redis_convert_err(int err) { - switch (err) { - case SW_REDIS_ERR_IO: - return errno; - case SW_REDIS_ERR_EOF: - case SW_REDIS_ERR_CLOSED: - return ECONNRESET; - case SW_REDIS_ERR_OTHER: - return EINVAL; - case SW_REDIS_ERR_OOM: - case SW_REDIS_ERR_ALLOC: - return ENOMEM; - case SW_REDIS_ERR_PROTOCOL: - return EPROTO; - case SW_REDIS_ERR_NOAUTH: - return EACCES; - case 0: - return 0; - default: - return errno; - } -} - -static sw_inline void swoole_redis_handle_assoc_array_result(zval *return_value, bool str2double) { - zval *zkey, *zvalue; - zval zret; - bool is_key = false; - - array_init(&zret); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(return_value), zvalue) { - if ((is_key = !is_key)) { - zkey = zvalue; - } else { - if (str2double) { - convert_to_double(zvalue); - } else { - Z_ADDREF_P(zvalue); - } - add_assoc_zval_ex(&zret, Z_STRVAL_P(zkey), Z_STRLEN_P(zkey), zvalue); - } - } - ZEND_HASH_FOREACH_END(); - - zval_ptr_dtor(return_value); - RETVAL_ZVAL(&zret, 1, 1); -} - -static bool redis_auth(RedisClient *redis, char *pw, size_t pw_len); -static bool redis_select_db(RedisClient *redis, long db_number); -static void redis_request( - RedisClient *redis, int argc, char **argv, size_t *argvlen, zval *return_value, bool retry = false); - -static bool swoole_redis_coro_connect(RedisClient *redis) { - zval *zobject = redis->zobject; - redisContext *context; - struct timeval tv; - zval *ztmp; - zval *zhost = sw_zend_read_property_ex(swoole_redis_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HOST), 0); - zval *zport = sw_zend_read_property_ex(swoole_redis_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_PORT), 0); - zend::String host(zhost); - zend_long port = zval_get_long(zport); - - if (host.len() == 0) { - php_swoole_fatal_error(E_WARNING, "The host is empty"); - return false; - } - - if (redis->context) { - context = redis->context; - if (context->connection_type == REDIS_CONN_TCP && strcmp(context->tcp.host, host.val()) == 0 && - context->tcp.port == port) { - return true; - } else if (context->connection_type == REDIS_CONN_UNIX && - (strstr(host.val(), context->unix_sock.path) - host.val()) + strlen(context->unix_sock.path) == - host.len()) { - return true; - } else { - swoole_redis_coro_close(redis); - } - } - - php_swoole_check_reactor(); - - if (redis->connect_timeout > 0) { - tv.tv_sec = redis->connect_timeout; - tv.tv_usec = (redis->connect_timeout - (double) tv.tv_sec) * 1000 * 1000; - } - if (SW_STR_ISTARTS_WITH(host.val(), host.len(), "unix:/")) { - context = redisConnectUnixWithTimeout(host.val() + 5 + strspn(host.val() + 5, "/") - 1, tv); - } else { - if (port <= 0 || port > SW_CLIENT_MAX_PORT) { - php_swoole_fatal_error(E_WARNING, "The port " ZEND_LONG_FMT " is invalid", port); - return false; - } - context = redisConnectWithTimeout(host.val(), (int) port, tv); - } - - redis->context = context; - - if (!context) { - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), SW_REDIS_ERR_ALLOC); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_ALLOC)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "cannot allocate redis context"); - return false; - } - if (context->err) { - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), context->err); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sw_redis_convert_err(context->err)); - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), context->errstr); - swoole_redis_coro_close(redis); - return false; - } - auto socket = swoole_redis_coro_get_socket(context); - if (socket == nullptr) { - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), "Can not found the connection"); - swoole_redis_coro_close(redis); - return false; - } - - socket->set_timeout(redis->timeout, Socket::TIMEOUT_RDWR); - redis->reconnected_count = 0; - zend_update_property_bool(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("sock"), context->fd); - - // auth and select db after connected - zval *zsetting = - sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("setting"), 0); - HashTable *vht = Z_ARRVAL_P(zsetting); - - if (php_swoole_array_get_value(vht, "password", ztmp)) { - zend::String passowrd(ztmp); - if (passowrd.len() > 0 && !redis_auth(redis, passowrd.val(), passowrd.len())) { - swoole_redis_coro_close(redis); - return false; - } - } - if (php_swoole_array_get_value(vht, "database", ztmp)) { - zend_long db_number = zval_get_long(ztmp); - // default is 0, don't need select - if (db_number > 0 && !redis_select_db(redis, db_number)) { - swoole_redis_coro_close(redis); - return false; - } - } - return true; -} - -static sw_inline bool swoole_redis_coro_keep_liveness(RedisClient *redis) { - std::shared_ptr socket; - if (!redis->context || !(socket = swoole_redis_coro_get_socket(redis->context)) || !socket->check_liveness()) { - if (socket) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_CLOSED); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), socket->errMsg); - } - swoole_redis_coro_close(redis); - for (; redis->reconnected_count < redis->reconnect_interval; redis->reconnected_count++) { - if (swoole_redis_coro_connect(redis)) { - return true; - } - } - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_CLOSED); - // Notice: do not update errCode - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), "connection is not available"); - return false; - } - return true; -} - -static bool redis_auth(RedisClient *redis, char *pw, size_t pw_len) { - int i = 0; - size_t argvlen[2]; - char *argv[2]; - bool ret; - zval retval; - - SW_REDIS_COMMAND_ARGV_FILL("AUTH", 4) - SW_REDIS_COMMAND_ARGV_FILL(pw, pw_len) - redis_request(redis, 2, argv, argvlen, &retval); - ret = Z_BVAL_P(&retval); - if (ret) { - redis->session.auth = true; - } - return ret; -} - -static bool redis_select_db(RedisClient *redis, long db_number) { - int i = 0; - size_t argvlen[2]; - char *argv[2]; - char str[32]; - bool ret; - zval retval; - - SW_REDIS_COMMAND_ARGV_FILL("SELECT", 6) - sprintf(str, "%ld", db_number); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, 2, argv, argvlen, &retval); - ret = Z_BVAL_P(&retval); - if (ret) { - redis->session.db_num = db_number; - } - return ret; -} - -static void redis_request(RedisClient *redis, int argc, char **argv, size_t *argvlen, zval *return_value, bool retry) { - redisReply *reply = nullptr; - if (!swoole_redis_coro_keep_liveness(redis)) { - ZVAL_FALSE(return_value); - } else { - // must clear err before request - redis->context->err = 0; - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), 0); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errCode"), 0); - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), ""); - if (redis->defer) { - if (redisAppendCommandArgv(redis->context, argc, (const char **) argv, (const size_t *) argvlen) == - REDIS_ERR) { - goto _error; - } else { - ZVAL_TRUE(return_value); - } - } else { - reply = - (redisReply *) redisCommandArgv(redis->context, argc, (const char **) argv, (const size_t *) argvlen); - if (reply == nullptr) { - _error: - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - ZVAL_FALSE(return_value); - swoole_redis_coro_close(redis); - } else { - // Redis Cluster - if (reply->type == REDIS_REPLY_ERROR && - (!strncmp(reply->str, "MOVED", 5) || !strcmp(reply->str, "ASK"))) { - char *p1, *p2; - // MOVED 1234 127.0.0.1:1234 - p1 = strrchr(reply->str, ' ') + 1; // MOVED 1234 [p1]27.0.0.1:1234 - p2 = strrchr(p1, ':'); // MOVED 1234 [p1]27.0.0.1[p2]1234 - *p2 = '\0'; - int port = atoi(p2 + 1); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("host"), p1); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("port"), port); - - if (swoole_redis_coro_connect(redis) > 0) { - freeReplyObject(reply); - redis_request(redis, argc, argv, argvlen, return_value, retry); - return; - } else { - ZVAL_FALSE(return_value); - } - } - // Normal Response - else { - swoole_redis_coro_parse_result(redis, return_value, reply); - } - freeReplyObject(reply); - } - } - } - SW_LOOP_N(argc) { - efree(argv[i]); - } -} - -static sw_inline void sw_redis_command_empty(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[1]; - char *argv[1]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - redis_request(redis, 1, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_var_key( - INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len, int min_argc, int has_timeout) { - long timeout; - int argc = ZEND_NUM_ARGS(); - if (argc < min_argc) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (argc == 0 || zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - zend_bool single_array = 0; - if (has_timeout == 0) { - single_array = argc == 1 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY; - } else { - single_array = argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY && - SW_REDIS_COMMAND_ARGS_TYPE(z_args[1]) == IS_LONG; - timeout = SW_REDIS_COMMAND_ARGS_LVAL(z_args[1]); - } - if (single_array) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 1; - } else { - argc++; - } - - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - char buf[32]; - size_t buf_len; - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - if (has_timeout) { - buf_len = sw_snprintf(buf, sizeof(buf), "%ld", timeout); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - } else { - if (has_timeout && SW_REDIS_COMMAND_ARGS_TYPE(z_args[argc - 2]) != IS_LONG) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), "Timeout value must be a LONG"); - efree(z_args); - RETURN_FALSE; - } - int j, tail; - tail = has_timeout ? argc - 2 : argc - 1; - for (j = 0; j < tail; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - if (has_timeout) { - buf_len = sw_snprintf(buf, sizeof(buf), ZEND_LONG_FMT, SW_REDIS_COMMAND_ARGS_LVAL(z_args[tail])); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static inline void sw_redis_command_key(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[2]; - char *argv[2]; - int argc = 2; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode) { - if (ZVAL_IS_ARRAY(return_value) && sw_mem_equal(ZEND_STRL("HGETALL"), cmd, cmd_len)) { - swoole_redis_handle_assoc_array_result(return_value, false); - } else if (ZVAL_IS_NULL(return_value) && sw_mem_equal(ZEND_STRL("GET"), cmd, cmd_len)) { - RETURN_FALSE; - } - } -} - -static sw_inline void sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - int argc = ZEND_NUM_ARGS(); - // We at least need a key and one value - if (argc < 2) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - // Make sure we at least have a key, and we can get other args - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - - int i = 0, j; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - zend_string *convert_str = zval_get_string(&z_args[0]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - for (j = 1; j < argc - 1; ++j) { - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(SW_REDIS_COMMAND_ARGS_REF(z_args[j])) - } - efree(z_args); - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - long l_val; - zval *z_value; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &key, &key_len, &l_val, &z_value) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val; - size_t key_len, val_len; - long l_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &key, &key_len, &l_val, &val, &val_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - SW_REDIS_COMMAND_ARGV_FILL(val, val_len) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - long l_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &key, &key_len, &l_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - long l1_val, l2_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &key, &key_len, &l1_val, &l2_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l1_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - sprintf(str, "%ld", l2_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_dbl(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - double d_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sd", &key, &key_len, &d_val) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%f", d_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_key(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key1, *key2; - size_t key1_len, key2_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key1, &key1_len, &key2, &key2_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key1, key1_len) - SW_REDIS_COMMAND_ARGV_FILL(key2, key2_len) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key; - size_t key_len; - zval *z_value; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &key, &key_len, &z_value) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - redis_request(redis, 3, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_NULL(return_value) && strncmp("ZRANK", cmd, cmd_len) == 0) { - RETURN_FALSE; - } -} - -static sw_inline void sw_redis_command_key_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val; - size_t key_len, val_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &val, &val_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(val, val_len) - redis_request(redis, 3, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_str_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val1, *val2; - size_t key_len, val1_len, val2_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &val1, &val1_len, &val2, &val2_len) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(val1, val1_len) - SW_REDIS_COMMAND_ARGV_FILL(val2, val2_len) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_xrange(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) { - char *key, *val1, *val2; - size_t key_len, val1_len, val2_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|l", &key, &key_len, &val1, &val1_len, &val2, &val2_len, &count) == - FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc; - argc = ZEND_NUM_ARGS() == 4 ? 6 : 4; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(val1, val1_len) - SW_REDIS_COMMAND_ARGV_FILL(val2, val2_len) - if (count > 0) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, ZEND_LONG_FMT, count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -SW_EXTERN_C_BEGIN -static PHP_METHOD(swoole_redis_coro, __construct); -static PHP_METHOD(swoole_redis_coro, __destruct); -static PHP_METHOD(swoole_redis_coro, connect); -static PHP_METHOD(swoole_redis_coro, getAuth); -static PHP_METHOD(swoole_redis_coro, getDBNum); -static PHP_METHOD(swoole_redis_coro, getOptions); -static PHP_METHOD(swoole_redis_coro, setOptions); -static PHP_METHOD(swoole_redis_coro, getDefer); -static PHP_METHOD(swoole_redis_coro, setDefer); -static PHP_METHOD(swoole_redis_coro, recv); -static PHP_METHOD(swoole_redis_coro, request); -static PHP_METHOD(swoole_redis_coro, close); -/*---------------------Redis Command------------------------*/ -static PHP_METHOD(swoole_redis_coro, set); -static PHP_METHOD(swoole_redis_coro, setBit); -static PHP_METHOD(swoole_redis_coro, setEx); -static PHP_METHOD(swoole_redis_coro, psetEx); -static PHP_METHOD(swoole_redis_coro, lSet); -static PHP_METHOD(swoole_redis_coro, get); -static PHP_METHOD(swoole_redis_coro, mGet); -static PHP_METHOD(swoole_redis_coro, del); -static PHP_METHOD(swoole_redis_coro, hDel); -static PHP_METHOD(swoole_redis_coro, hSet); -static PHP_METHOD(swoole_redis_coro, hMSet); -static PHP_METHOD(swoole_redis_coro, hSetNx); -static PHP_METHOD(swoole_redis_coro, mSet); -static PHP_METHOD(swoole_redis_coro, mSetNx); -static PHP_METHOD(swoole_redis_coro, getKeys); -static PHP_METHOD(swoole_redis_coro, exists); -static PHP_METHOD(swoole_redis_coro, type); -static PHP_METHOD(swoole_redis_coro, strLen); -static PHP_METHOD(swoole_redis_coro, lPop); -static PHP_METHOD(swoole_redis_coro, blPop); -static PHP_METHOD(swoole_redis_coro, rPop); -static PHP_METHOD(swoole_redis_coro, brPop); -static PHP_METHOD(swoole_redis_coro, bRPopLPush); -static PHP_METHOD(swoole_redis_coro, lSize); -static PHP_METHOD(swoole_redis_coro, sSize); -static PHP_METHOD(swoole_redis_coro, sPop); -static PHP_METHOD(swoole_redis_coro, sMembers); -static PHP_METHOD(swoole_redis_coro, sRandMember); -static PHP_METHOD(swoole_redis_coro, persist); -static PHP_METHOD(swoole_redis_coro, ttl); -static PHP_METHOD(swoole_redis_coro, pttl); -static PHP_METHOD(swoole_redis_coro, zCard); -static PHP_METHOD(swoole_redis_coro, hLen); -static PHP_METHOD(swoole_redis_coro, hKeys); -static PHP_METHOD(swoole_redis_coro, hVals); -static PHP_METHOD(swoole_redis_coro, hGetAll); -static PHP_METHOD(swoole_redis_coro, restore); -static PHP_METHOD(swoole_redis_coro, dump); -static PHP_METHOD(swoole_redis_coro, debug); -static PHP_METHOD(swoole_redis_coro, renameKey); -static PHP_METHOD(swoole_redis_coro, renameNx); -static PHP_METHOD(swoole_redis_coro, rpoplpush); -static PHP_METHOD(swoole_redis_coro, randomKey); -static PHP_METHOD(swoole_redis_coro, pfadd); -static PHP_METHOD(swoole_redis_coro, pfcount); -static PHP_METHOD(swoole_redis_coro, pfmerge); -static PHP_METHOD(swoole_redis_coro, ping); -static PHP_METHOD(swoole_redis_coro, auth); -static PHP_METHOD(swoole_redis_coro, unwatch); -static PHP_METHOD(swoole_redis_coro, watch); -static PHP_METHOD(swoole_redis_coro, save); -static PHP_METHOD(swoole_redis_coro, bgSave); -static PHP_METHOD(swoole_redis_coro, lastSave); -static PHP_METHOD(swoole_redis_coro, flushDB); -static PHP_METHOD(swoole_redis_coro, flushAll); -static PHP_METHOD(swoole_redis_coro, dbSize); -static PHP_METHOD(swoole_redis_coro, bgrewriteaof); -static PHP_METHOD(swoole_redis_coro, time); -static PHP_METHOD(swoole_redis_coro, role); -static PHP_METHOD(swoole_redis_coro, setRange); -static PHP_METHOD(swoole_redis_coro, setNx); -static PHP_METHOD(swoole_redis_coro, getSet); -static PHP_METHOD(swoole_redis_coro, append); -static PHP_METHOD(swoole_redis_coro, lPushx); -static PHP_METHOD(swoole_redis_coro, lPush); -static PHP_METHOD(swoole_redis_coro, rPush); -static PHP_METHOD(swoole_redis_coro, rPushx); -static PHP_METHOD(swoole_redis_coro, sContains); -static PHP_METHOD(swoole_redis_coro, zScore); -static PHP_METHOD(swoole_redis_coro, zRank); -static PHP_METHOD(swoole_redis_coro, zRevRank); -static PHP_METHOD(swoole_redis_coro, hGet); -static PHP_METHOD(swoole_redis_coro, hMGet); -static PHP_METHOD(swoole_redis_coro, hExists); -static PHP_METHOD(swoole_redis_coro, publish); -static PHP_METHOD(swoole_redis_coro, zIncrBy); -static PHP_METHOD(swoole_redis_coro, zAdd); -static PHP_METHOD(swoole_redis_coro, zPopMin); -static PHP_METHOD(swoole_redis_coro, zPopMax); -static PHP_METHOD(swoole_redis_coro, bzPopMin); -static PHP_METHOD(swoole_redis_coro, bzPopMax); -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByScore); -static PHP_METHOD(swoole_redis_coro, zCount); -static PHP_METHOD(swoole_redis_coro, zRange); -static PHP_METHOD(swoole_redis_coro, zRevRange); -static PHP_METHOD(swoole_redis_coro, zRangeByScore); -static PHP_METHOD(swoole_redis_coro, zRevRangeByScore); -static PHP_METHOD(swoole_redis_coro, zRangeByLex); -static PHP_METHOD(swoole_redis_coro, zRevRangeByLex); -static PHP_METHOD(swoole_redis_coro, zInter); -static PHP_METHOD(swoole_redis_coro, zUnion); -static PHP_METHOD(swoole_redis_coro, incrBy); -static PHP_METHOD(swoole_redis_coro, hIncrBy); -static PHP_METHOD(swoole_redis_coro, incr); -static PHP_METHOD(swoole_redis_coro, decrBy); -static PHP_METHOD(swoole_redis_coro, decr); -static PHP_METHOD(swoole_redis_coro, getBit); -static PHP_METHOD(swoole_redis_coro, lGet); -static PHP_METHOD(swoole_redis_coro, lInsert); -static PHP_METHOD(swoole_redis_coro, setTimeout); -static PHP_METHOD(swoole_redis_coro, pexpire); -static PHP_METHOD(swoole_redis_coro, expireAt); -static PHP_METHOD(swoole_redis_coro, pexpireAt); -static PHP_METHOD(swoole_redis_coro, move); -static PHP_METHOD(swoole_redis_coro, select); -static PHP_METHOD(swoole_redis_coro, getRange); -static PHP_METHOD(swoole_redis_coro, listTrim); -static PHP_METHOD(swoole_redis_coro, lGetRange); -static PHP_METHOD(swoole_redis_coro, lRem); -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByRank); -static PHP_METHOD(swoole_redis_coro, incrByFloat); -static PHP_METHOD(swoole_redis_coro, hIncrByFloat); -static PHP_METHOD(swoole_redis_coro, bitCount); -static PHP_METHOD(swoole_redis_coro, bitOp); -static PHP_METHOD(swoole_redis_coro, sAdd); -static PHP_METHOD(swoole_redis_coro, sMove); -static PHP_METHOD(swoole_redis_coro, sDiff); -static PHP_METHOD(swoole_redis_coro, sDiffStore); -static PHP_METHOD(swoole_redis_coro, sUnion); -static PHP_METHOD(swoole_redis_coro, sUnionStore); -static PHP_METHOD(swoole_redis_coro, sInter); -static PHP_METHOD(swoole_redis_coro, sInterStore); -static PHP_METHOD(swoole_redis_coro, sRemove); -static PHP_METHOD(swoole_redis_coro, zDelete); -static PHP_METHOD(swoole_redis_coro, subscribe); -static PHP_METHOD(swoole_redis_coro, pSubscribe); -static PHP_METHOD(swoole_redis_coro, unsubscribe); -static PHP_METHOD(swoole_redis_coro, pUnSubscribe); -static PHP_METHOD(swoole_redis_coro, multi); -static PHP_METHOD(swoole_redis_coro, exec); -static PHP_METHOD(swoole_redis_coro, eval); -static PHP_METHOD(swoole_redis_coro, evalSha); -static PHP_METHOD(swoole_redis_coro, script); -static PHP_METHOD(swoole_redis_coro, xLen); -static PHP_METHOD(swoole_redis_coro, xAdd); -static PHP_METHOD(swoole_redis_coro, xRead); -static PHP_METHOD(swoole_redis_coro, xDel); -static PHP_METHOD(swoole_redis_coro, xRange); -static PHP_METHOD(swoole_redis_coro, xRevRange); -static PHP_METHOD(swoole_redis_coro, xTrim); -static PHP_METHOD(swoole_redis_coro, xGroupCreate); -static PHP_METHOD(swoole_redis_coro, xGroupSetId); -static PHP_METHOD(swoole_redis_coro, xGroupDestroy); -static PHP_METHOD(swoole_redis_coro, xGroupCreateConsumer); -static PHP_METHOD(swoole_redis_coro, xGroupDelConsumer); -static PHP_METHOD(swoole_redis_coro, xReadGroup); -static PHP_METHOD(swoole_redis_coro, xPending); -static PHP_METHOD(swoole_redis_coro, xAck); -static PHP_METHOD(swoole_redis_coro, xClaim); -static PHP_METHOD(swoole_redis_coro, xAutoClaim); -static PHP_METHOD(swoole_redis_coro, xInfoConsumers); -static PHP_METHOD(swoole_redis_coro, xInfoGroups); -static PHP_METHOD(swoole_redis_coro, xInfoStream); -SW_EXTERN_C_END -/*---------------------Redis Command End------------------------*/ -// clang-format off -static const zend_function_entry swoole_redis_coro_methods[] = -{ - PHP_ME(swoole_redis_coro, __construct, arginfo_swoole_redis_coro_construct, ZEND_ACC_PUBLIC | ZEND_ACC_DEPRECATED) - PHP_ME(swoole_redis_coro, __destruct, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, connect, arginfo_swoole_redis_coro_connect, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getAuth, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getDBNum, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getOptions, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setOptions, arginfo_swoole_redis_coro_setOptions, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getDefer, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setDefer, arginfo_swoole_redis_coro_setDefer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, recv, arginfo_swoole_redis_coro_void, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, request, arginfo_swoole_redis_coro_request, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, close, arginfo_swoole_redis_coro_close, ZEND_ACC_PUBLIC) - /*---------------------Redis Command------------------------*/ - PHP_ME(swoole_redis_coro, set, arginfo_swoole_redis_coro_set, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setBit, arginfo_swoole_redis_coro_setBit, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setEx, arginfo_swoole_redis_coro_setex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, psetEx, arginfo_swoole_redis_coro_psetex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lSet, arginfo_swoole_redis_coro_lSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, get, arginfo_swoole_redis_coro_get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, mGet, arginfo_swoole_redis_coro_mget, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, del, arginfo_swoole_redis_coro_del, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hDel, arginfo_swoole_redis_coro_hDel, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hSet, arginfo_swoole_redis_coro_hSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hMSet, arginfo_swoole_redis_coro_hMset, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hSetNx, arginfo_swoole_redis_coro_hSetNx, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, delete, del, arginfo_swoole_redis_coro_del, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, mSet, arginfo_swoole_redis_coro_mset, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, mSetNx, arginfo_swoole_redis_coro_msetnx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getKeys, arginfo_swoole_redis_coro_getKeys, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, keys, getKeys, arginfo_swoole_redis_coro_getKeys, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, exists, arginfo_swoole_redis_coro_exists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, type, arginfo_swoole_redis_coro_type, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, strLen, arginfo_swoole_redis_coro_strlen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lPop, arginfo_swoole_redis_coro_lPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, blPop, arginfo_swoole_redis_coro_blPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rPop, arginfo_swoole_redis_coro_rPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, brPop, arginfo_swoole_redis_coro_brPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bRPopLPush, arginfo_swoole_redis_coro_brpoplpush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lSize, arginfo_swoole_redis_coro_lSize, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lLen, lSize, arginfo_swoole_redis_coro_lLen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sSize, arginfo_swoole_redis_coro_sSize, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, scard, sSize, arginfo_swoole_redis_coro_scard, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sPop, arginfo_swoole_redis_coro_sPop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sMembers, arginfo_swoole_redis_coro_sMembers, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, sGetMembers, sMembers, arginfo_swoole_redis_coro_key, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sRandMember, arginfo_swoole_redis_coro_sRandMember, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, persist, arginfo_swoole_redis_coro_persist, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, ttl, arginfo_swoole_redis_coro_ttl, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pttl, arginfo_swoole_redis_coro_pttl, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zCard, arginfo_swoole_redis_coro_zCard, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zSize, zCard, arginfo_swoole_redis_coro_zSize, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hLen, arginfo_swoole_redis_coro_hLen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hKeys, arginfo_swoole_redis_coro_hKeys, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hVals, arginfo_swoole_redis_coro_hVals, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hGetAll, arginfo_swoole_redis_coro_hGetAll, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, debug, arginfo_swoole_redis_coro_debug, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, restore, arginfo_swoole_redis_coro_restore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, dump, arginfo_swoole_redis_coro_dump, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, renameKey, arginfo_swoole_redis_coro_renameKey, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, rename, renameKey, arginfo_swoole_redis_coro_rename, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, renameNx, arginfo_swoole_redis_coro_renameNx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rpoplpush, arginfo_swoole_redis_coro_rpoplpush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, randomKey, arginfo_swoole_redis_coro_randomKey, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pfadd, arginfo_swoole_redis_coro_pfadd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pfcount, arginfo_swoole_redis_coro_pfcount, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pfmerge, arginfo_swoole_redis_coro_pfmerge, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, ping, arginfo_swoole_redis_coro_ping, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, auth, arginfo_swoole_redis_coro_auth, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, unwatch, arginfo_swoole_redis_coro_unwatch, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, watch, arginfo_swoole_redis_coro_watch, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, save, arginfo_swoole_redis_coro_save, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bgSave, arginfo_swoole_redis_coro_bgSave, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lastSave, arginfo_swoole_redis_coro_lastSave, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, flushDB, arginfo_swoole_redis_coro_flushDB, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, flushAll, arginfo_swoole_redis_coro_flushAll, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, dbSize, arginfo_swoole_redis_coro_dbSize, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bgrewriteaof, arginfo_swoole_redis_coro_bgrewriteaof, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, time, arginfo_swoole_redis_coro_time, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, role, arginfo_swoole_redis_coro_role, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setRange, arginfo_swoole_redis_coro_setRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setNx, arginfo_swoole_redis_coro_setnx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getSet, arginfo_swoole_redis_coro_getSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, append, arginfo_swoole_redis_coro_append, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lPushx, arginfo_swoole_redis_coro_lPushx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lPush, arginfo_swoole_redis_coro_lPush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rPush, arginfo_swoole_redis_coro_rPush, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, rPushx, arginfo_swoole_redis_coro_rPushx, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sContains, arginfo_swoole_redis_coro_sContains, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, sismember, sContains, arginfo_swoole_redis_coro_key_value, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zScore, arginfo_swoole_redis_coro_zScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRank, arginfo_swoole_redis_coro_zRank, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRank, arginfo_swoole_redis_coro_zRevRank, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hGet, arginfo_swoole_redis_coro_hGet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hMGet, arginfo_swoole_redis_coro_hMget, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hExists, arginfo_swoole_redis_coro_hExists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, publish, arginfo_swoole_redis_coro_publish, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zIncrBy, arginfo_swoole_redis_coro_zIncrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zAdd, arginfo_swoole_redis_coro_zAdd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zPopMin, arginfo_swoole_redis_coro_zPopMin, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zPopMax, arginfo_swoole_redis_coro_zPopMax, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bzPopMin, arginfo_swoole_redis_coro_bzPopMin, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bzPopMax, arginfo_swoole_redis_coro_bzPopMax, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zDeleteRangeByScore, arginfo_swoole_redis_coro_zDeleteRangeByScore, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRemRangeByScore, zDeleteRangeByScore, arginfo_swoole_redis_coro_zRemRangeByScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zCount, arginfo_swoole_redis_coro_zCount, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRange, arginfo_swoole_redis_coro_zRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRange, arginfo_swoole_redis_coro_zRevRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRangeByScore, arginfo_swoole_redis_coro_zRangeByScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRangeByScore, arginfo_swoole_redis_coro_zRevRangeByScore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRangeByLex, arginfo_swoole_redis_coro_zRangeByLex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zRevRangeByLex, arginfo_swoole_redis_coro_zRevRangeByLex, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zInter, arginfo_swoole_redis_coro_zInter, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zinterstore, zInter, arginfo_swoole_redis_coro_zinterstore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zUnion, arginfo_swoole_redis_coro_zUnion, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zunionstore, zUnion, arginfo_swoole_redis_coro_zunionstore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, incrBy, arginfo_swoole_redis_coro_incrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hIncrBy, arginfo_swoole_redis_coro_hIncrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, incr, arginfo_swoole_redis_coro_incr, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, decrBy, arginfo_swoole_redis_coro_decrBy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, decr, arginfo_swoole_redis_coro_decr, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getBit, arginfo_swoole_redis_coro_getBit, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lInsert, arginfo_swoole_redis_coro_lInsert, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lGet, arginfo_swoole_redis_coro_lGet, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lIndex, lGet, arginfo_swoole_redis_coro_key_long, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, setTimeout, arginfo_swoole_redis_coro_setTimeout, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, expire, setTimeout, arginfo_swoole_redis_coro_key_long, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pexpire, arginfo_swoole_redis_coro_pexpire, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, expireAt, arginfo_swoole_redis_coro_expireAt, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pexpireAt, arginfo_swoole_redis_coro_pexpireAt, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, move, arginfo_swoole_redis_coro_move, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, select, arginfo_swoole_redis_coro_select, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, getRange, arginfo_swoole_redis_coro_getRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, listTrim, arginfo_swoole_redis_coro_listTrim, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, ltrim, listTrim, arginfo_swoole_redis_coro_ltrim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lGetRange, arginfo_swoole_redis_coro_lGetRange, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lRange, lGetRange, arginfo_swoole_redis_coro_lrange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, lRem, arginfo_swoole_redis_coro_lrem, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, lRemove,lRem, arginfo_swoole_redis_coro_lRemove, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zDeleteRangeByRank, arginfo_swoole_redis_coro_zDeleteRangeByRank, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRemRangeByRank, zDeleteRangeByRank, arginfo_swoole_redis_coro_zRemRangeByRank, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, incrByFloat, arginfo_swoole_redis_coro_incrByFloat, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, hIncrByFloat, arginfo_swoole_redis_coro_hIncrByFloat, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bitCount, arginfo_swoole_redis_coro_bitcount, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, bitOp, arginfo_swoole_redis_coro_bitop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sAdd, arginfo_swoole_redis_coro_sAdd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sMove, arginfo_swoole_redis_coro_sMove, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sDiff, arginfo_swoole_redis_coro_sDiff, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sDiffStore, arginfo_swoole_redis_coro_sDiffStore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sUnion, arginfo_swoole_redis_coro_sUnion, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sUnionStore, arginfo_swoole_redis_coro_sUnionStore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sInter, arginfo_swoole_redis_coro_sInter, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sInterStore, arginfo_swoole_redis_coro_sInterStore, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, sRemove, arginfo_swoole_redis_coro_sRemove, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, srem, sRemove, arginfo_swoole_redis_coro_key_value, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, zDelete, arginfo_swoole_redis_coro_zDelete, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRemove, zDelete, arginfo_swoole_redis_coro_zRemove, ZEND_ACC_PUBLIC) - PHP_MALIAS(swoole_redis_coro, zRem, zDelete, arginfo_swoole_redis_coro_zRem, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pSubscribe, arginfo_swoole_redis_coro_psubscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, subscribe, arginfo_swoole_redis_coro_subscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, unsubscribe, arginfo_swoole_redis_coro_unsubscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, pUnSubscribe, arginfo_swoole_redis_coro_punsubscribe, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, multi, arginfo_swoole_redis_coro_multi, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, exec, arginfo_swoole_redis_coro_exec, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, eval, arginfo_swoole_redis_coro_eval, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, evalSha, arginfo_swoole_redis_coro_evalsha, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, script, arginfo_swoole_redis_coro_script, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xLen, arginfo_swoole_redis_coro_xLen, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xAdd, arginfo_swoole_redis_coro_xAdd, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xRead, arginfo_swoole_redis_coro_xRead, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xDel, arginfo_swoole_redis_coro_xDel, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xRange, arginfo_swoole_redis_coro_xRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xRevRange, arginfo_swoole_redis_coro_xRevRange, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xTrim, arginfo_swoole_redis_coro_xTrim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupCreate, arginfo_swoole_redis_coro_xGroupCreate, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupSetId, arginfo_swoole_redis_coro_xGroupSetId, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupDestroy, arginfo_swoole_redis_coro_xGroupDestroy, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupCreateConsumer, arginfo_swoole_redis_coro_xGroupCreateConsumer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xGroupDelConsumer, arginfo_swoole_redis_coro_xGroupDelConsumer, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xReadGroup, arginfo_swoole_redis_coro_xReadGroup, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xPending, arginfo_swoole_redis_coro_xPending, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xAck, arginfo_swoole_redis_coro_xAck, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xClaim, arginfo_swoole_redis_coro_xClaim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xAutoClaim, arginfo_swoole_redis_coro_xAutoClaim, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xInfoConsumers, arginfo_swoole_redis_coro_xInfoConsumers, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xInfoGroups, arginfo_swoole_redis_coro_xInfoGroups, ZEND_ACC_PUBLIC) - PHP_ME(swoole_redis_coro, xInfoStream, arginfo_swoole_redis_coro_xInfoStream, ZEND_ACC_PUBLIC) - /*---------------------Redis Command End------------------------*/ - PHP_FE_END -}; -// clang-format on - -void php_swoole_redis_coro_minit(int module_number) { - SW_INIT_CLASS_ENTRY(swoole_redis_coro, "Swoole\\Coroutine\\Redis", "Co\\Redis", swoole_redis_coro_methods); - SW_SET_CLASS_NOT_SERIALIZABLE(swoole_redis_coro); - SW_SET_CLASS_CLONEABLE(swoole_redis_coro, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_redis_coro, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(swoole_redis_coro); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_redis_coro, php_swoole_redis_coro_create_object, php_swoole_redis_coro_free_object, RedisClient, std); -#if PHP_VERSION_ID >= 80200 - zend_add_parameter_attribute((zend_function *) zend_hash_str_find_ptr(&swoole_redis_coro_ce->function_table, SW_STRL("auth")), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); -#endif - - zend_declare_property_string(swoole_redis_coro_ce, ZEND_STRL("host"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_redis_coro_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("sock"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_bool(swoole_redis_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("errType"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_redis_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_redis_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_MODE_MULTI", SW_REDIS_MODE_MULTI); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_MODE_PIPELINE", SW_REDIS_MODE_PIPELINE); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_NOT_FOUND", SW_REDIS_TYPE_NOT_FOUND); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_STRING", SW_REDIS_TYPE_STRING); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_SET", SW_REDIS_TYPE_SET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_LIST", SW_REDIS_TYPE_LIST); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_ZSET", SW_REDIS_TYPE_ZSET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_TYPE_HASH", SW_REDIS_TYPE_HASH); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_IO", SW_REDIS_ERR_IO); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_OTHER", SW_REDIS_ERR_OTHER); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_EOF", SW_REDIS_ERR_EOF); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_PROTOCOL", SW_REDIS_ERR_PROTOCOL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_OOM", SW_REDIS_ERR_OOM); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_CLOSED", SW_REDIS_ERR_CLOSED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_NOAUTH", SW_REDIS_ERR_NOAUTH); - SW_REGISTER_LONG_CONSTANT("SWOOLE_REDIS_ERR_ALLOC", SW_REDIS_ERR_ALLOC); -} - -static void swoole_redis_coro_set_options(RedisClient *redis, zval *zoptions, bool backward_compatibility = false) { - zval *zsettings = - sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("setting"), 0); - HashTable *vht = Z_ARRVAL_P(zoptions); - zval *ztmp; - - php_array_merge(Z_ARRVAL_P(zsettings), vht); - - if (php_swoole_array_get_value(vht, "connect_timeout", ztmp)) { - redis->connect_timeout = zval_get_double(ztmp); - if (redis->connect_timeout <= 0) { - redis->connect_timeout = SW_TIMER_MAX_SEC; - } - } - if (php_swoole_array_get_value(vht, "timeout", ztmp)) { - redis->timeout = zval_get_double(ztmp); - if (backward_compatibility) { - redis->connect_timeout = redis->timeout; - if (redis->connect_timeout <= 0) { - redis->connect_timeout = SW_TIMER_MAX_SEC; - } - } - if (redis->context) { - auto socket = swoole_redis_coro_get_socket(redis->context); - if (socket) { - socket->set_timeout(redis->timeout, Socket::TIMEOUT_RDWR); - } - } - } - if (php_swoole_array_get_value(vht, "serialize", ztmp)) { - redis->serialize = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "reconnect", ztmp)) { - redis->reconnect_interval = (uint8_t) SW_MIN(zval_get_long(ztmp), UINT8_MAX); - } - if (php_swoole_array_get_value(vht, "compatibility_mode", ztmp)) { - redis->compatibility_mode = zval_is_true(ztmp); - } -} - -static PHP_METHOD(swoole_redis_coro, __construct) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - zval *zsettings = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - zval *zset = nullptr; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (redis->zobject) { - zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } - - redis->zobject = &redis->_zobject; - redis->_zobject = *ZEND_THIS; - - redis->connect_timeout = network::Socket::default_connect_timeout; - redis->timeout = network::Socket::default_read_timeout; - redis->reconnect_interval = 1; - - // settings init - add_assoc_double(zsettings, "connect_timeout", redis->connect_timeout); - add_assoc_double(zsettings, "timeout", redis->timeout); - add_assoc_bool(zsettings, "serialize", redis->serialize); - add_assoc_long(zsettings, "reconnect", redis->reconnect_interval); - // after connected - add_assoc_string(zsettings, "password", (char *) ""); - add_assoc_long(zsettings, "database", 0); - - if (zset) { - swoole_redis_coro_set_options(redis, zset, true); - } -} - -static PHP_METHOD(swoole_redis_coro, connect) { - zval *zobject = ZEND_THIS; - char *host = nullptr; - size_t host_len = 0; - zend_long port = 0; - zend_bool serialize = 0; - - SW_REDIS_COMMAND_CHECK - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lb", &host, &host_len, &port, &serialize) == FAILURE) { - RETURN_FALSE; - } - - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("host"), host); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("port"), port); - redis->serialize = serialize; - - if (swoole_redis_coro_connect(redis) > 0) { - // clear the error code only when the developer manually tries to connect successfully - // if the kernel retries automatically, keep silent. - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errType"), 0); - zend_update_property_long(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), 0); - zend_update_property_string(swoole_redis_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), ""); - RETURN_TRUE; - } else { - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_redis_coro, getAuth) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - if (redis->session.auth) { - zval *ztmp = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - if (php_swoole_array_get_value(Z_ARRVAL_P(ztmp), "password", ztmp)) { - RETURN_ZVAL(ztmp, 1, 0); - } - RETURN_EMPTY_STRING(); - } - RETURN_FALSE; -} - -static PHP_METHOD(swoole_redis_coro, getDBNum) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - if (!redis->context) { - RETURN_FALSE; - } - RETURN_LONG(redis->session.db_num); -} - -static PHP_METHOD(swoole_redis_coro, getOptions) { - RETURN_ZVAL( - sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, setOptions) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - zval *zoptions; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zoptions) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_redis_coro_set_options(redis, zoptions); - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_redis_coro, getDefer) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - - RETURN_BOOL(redis->defer); -} - -static PHP_METHOD(swoole_redis_coro, setDefer) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - zend_bool defer = 1; - - if (redis->session.subscribe) { - php_swoole_fatal_error(E_WARNING, "you should not use setDefer after subscribe"); - RETURN_FALSE; - } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &defer) == FAILURE) { - RETURN_FALSE; - } - redis->defer = defer; - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_redis_coro, recv) { - SW_REDIS_COMMAND_CHECK - - if (UNEXPECTED(!redis->context)) { - RETURN_FALSE; - } - if (UNEXPECTED(!redis->defer && !redis->session.subscribe)) { - php_swoole_fatal_error(E_WARNING, "you should not use recv without defer or subscribe"); - RETURN_FALSE; - } - - redisReply *reply; -_recv: - if (redisGetReply(redis->context, (void **) &reply) == REDIS_OK) { - swoole_redis_coro_parse_result(redis, return_value, reply); - freeReplyObject(reply); - - if (redis->session.subscribe) { - zval *ztype; - - if (!ZVAL_IS_ARRAY(return_value)) { - zval_ptr_dtor(return_value); - goto _error; - } - - ztype = zend_hash_index_find(Z_ARRVAL_P(return_value), 0); - if (Z_TYPE_P(ztype) == IS_STRING) { - char *type = Z_STRVAL_P(ztype); - - if (!strcmp(type, "unsubscribe") || !strcmp(type, "punsubscribe")) { - zval *znum = zend_hash_index_find(Z_ARRVAL_P(return_value), 2); - if (Z_LVAL_P(znum) == 0) { - redis->session.subscribe = false; - } - - return; - } else if (!strcmp(type, "message") || !strcmp(type, "pmessage") || !strcmp(type, "subscribe") || - !strcmp(type, "psubscribe")) { - return; - } - } - - zval_ptr_dtor(return_value); - goto _recv; - } - } else { - _error: - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - - swoole_redis_coro_close(redis); - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_redis_coro, close) { - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - RETURN_BOOL(swoole_redis_coro_close(redis)); -} - -static PHP_METHOD(swoole_redis_coro, __destruct) { - SW_PREVENT_USER_DESTRUCT(); -} - -static PHP_METHOD(swoole_redis_coro, set) { - char *key, *exp_type = nullptr, *set_type = nullptr; - size_t key_len, argc = 3; - zval *z_value, *z_opts = nullptr; - zend_long expire = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|z", &key, &key_len, &z_value, &z_opts) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - - if (z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY && Z_TYPE_P(z_opts) != IS_NULL) { - RETURN_FALSE; - } - - if (z_opts && ZVAL_IS_ARRAY(z_opts)) { - HashTable *kt = Z_ARRVAL_P(z_opts); - - zend_string *zkey; - zend_ulong idx; - zval *zv; - - /* Iterate our option array */ - ZEND_HASH_FOREACH_KEY_VAL(kt, idx, zkey, zv) { - /* Detect PX or EX argument and validate timeout */ - if (!exp_type && zkey && IS_EX_PX_ARG(ZSTR_VAL(zkey))) { - /* Set expire type */ - exp_type = ZSTR_VAL(zkey); - - /* Try to extract timeout */ - if (Z_TYPE_P(zv) == IS_LONG) { - expire = Z_LVAL_P(zv); - } else if (Z_TYPE_P(zv) == IS_STRING) { - expire = atol(Z_STRVAL_P(zv)); - } - - /* Expiry can't be set < 1 */ - if (expire < 1) { - RETURN_FALSE; - } - argc += 2; - } else if (!set_type && Z_TYPE_P(zv) == IS_STRING && IS_NX_XX_ARG(Z_STRVAL_P(zv))) { - argc += 1; - set_type = Z_STRVAL_P(zv); - } - (void) idx; - } - ZEND_HASH_FOREACH_END(); - } else if (z_opts && Z_TYPE_P(z_opts) == IS_LONG) { - /* Grab expiry and fail if it's < 1 */ - expire = Z_LVAL_P(z_opts); - /* Expiry can't be set < 1 */ - if (expire < 1) { - RETURN_FALSE; - } - argc += 1; - } - - SW_REDIS_COMMAND_ALLOC_ARGV - - int i = 0; - if (exp_type || set_type) { - SW_REDIS_COMMAND_ARGV_FILL("SET", 3) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - - if (set_type) { - SW_REDIS_COMMAND_ARGV_FILL(set_type, (size_t) strlen(set_type)) - } - - if (exp_type) { - SW_REDIS_COMMAND_ARGV_FILL(exp_type, (size_t) strlen(exp_type)) - - char str[32]; - sprintf(str, ZEND_LONG_FMT, expire); - SW_REDIS_COMMAND_ARGV_FILL(str, (size_t) strlen(str)) - } - } else if (expire > 0) { - SW_REDIS_COMMAND_ARGV_FILL("SETEX", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - char str[32]; - sprintf(str, ZEND_LONG_FMT, expire); - SW_REDIS_COMMAND_ARGV_FILL(str, (size_t) strlen(str)) - - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - } else { - SW_REDIS_COMMAND_ARGV_FILL("SET", 3) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, setBit) { - char *key; - size_t key_len; - long offset; - zend_bool val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "slb", &key, &key_len, &offset, &val) == FAILURE) { - return; - } - - // Validate our offset - if (offset < SW_BITOP_MIN_OFFSET || offset > SW_BITOP_MAX_OFFSET) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Invalid OFFSET for bitop command (must be between 0-2^32-1)"); - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - - SW_REDIS_COMMAND_ARGV_FILL("SETBIT", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - char str[32]; - sprintf(str, "%ld", offset); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - SW_REDIS_COMMAND_ARGV_FILL(val ? "1" : "0", 1) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, setEx) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SETEX")); -} - -static PHP_METHOD(swoole_redis_coro, psetEx) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PSETEX")); -} - -static PHP_METHOD(swoole_redis_coro, lSet) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LSET")); -} - -static PHP_METHOD(swoole_redis_coro, restore) { - sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RESTORE")); -} - -static PHP_METHOD(swoole_redis_coro, dump) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DUMP")); -} - -static PHP_METHOD(swoole_redis_coro, debug) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DEBUG")); -} - -static PHP_METHOD(swoole_redis_coro, get) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GET")); -} - -static PHP_METHOD(swoole_redis_coro, mGet) { - zval *z_args; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_args) == FAILURE) { - RETURN_FALSE; - } - int argc; - argc = zend_hash_num_elements(Z_ARRVAL_P(z_args)); - if (argc == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - zval *value; - SW_REDIS_COMMAND_ARGV_FILL("MGET", 4) - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(z_args), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, hSet) { - char *key, *field; - size_t key_len, field_len; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &key, &key_len, &field, &field_len, &z_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("HSET", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(field, field_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, hMSet) { - char *key; - size_t key_len, argc; - zval *z_arr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - return; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc * 2 + 2; - zval *value; - char buf[32]; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("HMSET", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_arr), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), "%ld", (long) idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, hSetNx) { - char *key, *field; - size_t key_len, field_len; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &key, &key_len, &field, &field_len, &z_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - convert_to_string(z_val); - SW_REDIS_COMMAND_ARGV_FILL("HSETNX", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(field, field_len) - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_val), Z_STRLEN_P(z_val)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, hDel) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (argc < 2 || zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - argc++; - int i = 0, j; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("HDEL", 4) - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - efree(z_args); - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, watch) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("WATCH"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, del) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DEL"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sDiff) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SDIFF"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sDiffStore) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SDIFFSTORE"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sUnion) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SUNION"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sUnionStore) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SUNIONSTORE"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sInter) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SINTER"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, sInterStore) { - sw_redis_command_var_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SINTERSTORE"), 1, 0); -} - -static PHP_METHOD(swoole_redis_coro, mSet) { - zval *z_args; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_args) == FAILURE) { - RETURN_FALSE; - } - int argc; - argc = zend_hash_num_elements(Z_ARRVAL_P(z_args)); - if (argc == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc *= 2; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - SW_REDIS_COMMAND_ARGV_FILL("MSET", 4) - zval *value; - char buf[32]; - char *key; - uint32_t key_len; - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_args), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), "%ld", (long) idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, mSetNx) { - zval *z_args; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_args) == FAILURE) { - return; - } - int argc; - argc = zend_hash_num_elements(Z_ARRVAL_P(z_args)); - if (argc == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc *= 2; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - int i = 0; - SW_REDIS_COMMAND_ARGV_FILL("MSETNX", 6) - zval *value; - char buf[32]; - char *key; - uint32_t key_len; - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_args), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), "%ld", (long) idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, getKeys) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("KEYS")); -} - -static PHP_METHOD(swoole_redis_coro, exists) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXISTS")); -} - -static PHP_METHOD(swoole_redis_coro, type) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("TYPE")); -} - -static PHP_METHOD(swoole_redis_coro, strLen) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("STRLEN")); -} - -static PHP_METHOD(swoole_redis_coro, lPop) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LPOP")); -} - -static PHP_METHOD(swoole_redis_coro, bRPopLPush) { - char *key1, *key2; - size_t key1_len, key2_len; - long timeout; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &key1, &key1_len, &key2, &key2_len, &timeout) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int argc, i = 0; - argc = timeout < 0 ? 3 : 4; - SW_REDIS_COMMAND_ALLOC_ARGV - if (timeout < 0) { - SW_REDIS_COMMAND_ARGV_FILL("RPOPLPUSH", 9) - SW_REDIS_COMMAND_ARGV_FILL(key1, key1_len) - SW_REDIS_COMMAND_ARGV_FILL(key2, key2_len) - } else { - SW_REDIS_COMMAND_ARGV_FILL("BRPOPLPUSH", 10) - SW_REDIS_COMMAND_ARGV_FILL(key1, key1_len) - SW_REDIS_COMMAND_ARGV_FILL(key2, key2_len) - char str[32]; - sprintf(str, "%ld", timeout); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, blPop) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BLPOP", 5) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, brPop) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BRPOP", 5) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, rPop) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPOP")); -} - -static PHP_METHOD(swoole_redis_coro, lSize) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LLEN")); -} - -static PHP_METHOD(swoole_redis_coro, sSize) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SCARD")); -} - -static PHP_METHOD(swoole_redis_coro, sPop) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SPOP")); -} - -static PHP_METHOD(swoole_redis_coro, sMembers) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SMEMBERS")); -} - -static PHP_METHOD(swoole_redis_coro, sRandMember) { - char *key; - size_t key_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc, buf_len; - char buf[32]; - argc = ZEND_NUM_ARGS() == 2 ? 3 : 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("SRANDMEMBER", 11); - SW_REDIS_COMMAND_ARGV_FILL(key, key_len); - if (argc == 3) { - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, persist) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PERSIST")); -} - -static PHP_METHOD(swoole_redis_coro, ttl) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("TTL")); -} - -static PHP_METHOD(swoole_redis_coro, pttl) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PTTL")); -} - -static PHP_METHOD(swoole_redis_coro, zCard) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZCARD")); -} - -static PHP_METHOD(swoole_redis_coro, hLen) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HLEN")); -} - -static PHP_METHOD(swoole_redis_coro, hKeys) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HKEYS")); -} - -static PHP_METHOD(swoole_redis_coro, hVals) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HVALS")); -} - -static PHP_METHOD(swoole_redis_coro, hGetAll) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HGETALL")); -} - -static PHP_METHOD(swoole_redis_coro, renameKey) { - sw_redis_command_key_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RENAME")); -} - -static PHP_METHOD(swoole_redis_coro, renameNx) { - sw_redis_command_key_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RENAMENX")); -} - -static PHP_METHOD(swoole_redis_coro, rpoplpush) { - sw_redis_command_key_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPOPLPUSH")); -} - -static PHP_METHOD(swoole_redis_coro, randomKey) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RANDOMKEY")); -} - -static PHP_METHOD(swoole_redis_coro, unwatch) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("UNWATCH")); -} - -static PHP_METHOD(swoole_redis_coro, pfadd) { - char *key; - size_t key_len, argc; - zval *z_arr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - return; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc + 2; - zval *value; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("PFADD", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(z_arr), value) { - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); - zend_string_release(convert_str); - } - SW_HASHTABLE_FOREACH_END() - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, pfcount) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc != 1) { - efree(z_args); - RETURN_FALSE; - } - - zend_bool single_array = 0; - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])); - single_array = 1; - } - - argc += 1; - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("PFCOUNT", 7) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END() - } else { - zend_string *convert_str = zval_get_string(&z_args[0]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, pfmerge) { - char *key; - size_t key_len, argc; - zval *z_arr; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - RETURN_FALSE; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc + 2; - zval *value; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("PFMERGE", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(z_arr), value) { - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); - zend_string_release(convert_str); - } - SW_HASHTABLE_FOREACH_END() - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, ping) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PING")); -} - -static PHP_METHOD(swoole_redis_coro, auth) { - char *pw; - size_t pw_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &pw, &pw_len) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - add_assoc_stringl(zsetting, "password", pw, pw_len); - RETURN_BOOL(redis_auth(redis, pw, pw_len)); -} - -static PHP_METHOD(swoole_redis_coro, save) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SAVE")); -} - -static PHP_METHOD(swoole_redis_coro, bgSave) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("BGSAVE")); -} - -static PHP_METHOD(swoole_redis_coro, lastSave) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LASTSAVE")); -} - -static PHP_METHOD(swoole_redis_coro, flushDB) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("FLUSHDB")); -} - -static PHP_METHOD(swoole_redis_coro, flushAll) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("FLUSHALL")); -} - -static PHP_METHOD(swoole_redis_coro, dbSize) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DBSIZE")); -} - -static PHP_METHOD(swoole_redis_coro, bgrewriteaof) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("BGREWRITEAOF")); -} - -static PHP_METHOD(swoole_redis_coro, time) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("TIME")); -} - -static PHP_METHOD(swoole_redis_coro, role) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ROLE")); -} - -static PHP_METHOD(swoole_redis_coro, setRange) { - sw_redis_command_key_long_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SETRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, setNx) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SETNX")); -} - -static PHP_METHOD(swoole_redis_coro, getSet) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GETSET")); -} - -static PHP_METHOD(swoole_redis_coro, append) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("APPEND")); -} - -static PHP_METHOD(swoole_redis_coro, lPushx) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LPUSHX")); -} - -static PHP_METHOD(swoole_redis_coro, lPush) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LPUSH")); -} - -static PHP_METHOD(swoole_redis_coro, rPush) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPUSH")); -} - -static PHP_METHOD(swoole_redis_coro, rPushx) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("RPUSHX")); -} - -static PHP_METHOD(swoole_redis_coro, sContains) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SISMEMBER")); -} - -static PHP_METHOD(swoole_redis_coro, zRange) { - char *key; - size_t key_len; - zend_long start, end; - zend_bool ws = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|b", &key, &key_len, &start, &end, &ws) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc; - argc = ZEND_NUM_ARGS() + 1; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZRANGE", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", start); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", end); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - if (ws) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } else { - argc = 4; - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (ws && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRevRange) { - char *key; - size_t key_len; - zend_long start, end; - zend_bool ws = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|b", &key, &key_len, &start, &end, &ws) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc; - argc = ZEND_NUM_ARGS() + 1; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZREVRANGE", 9) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", start); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", end); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - if (ws) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } else { - argc = 4; - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (ws && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zUnion) { - char *key, *agg_op; - size_t key_len; - zval *z_keys, *z_weights = nullptr; - HashTable *ht_keys, *ht_weights = nullptr; - size_t argc = 2, agg_op_len = 0, keys_count; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|a!s", &key, &key_len, &z_keys, &z_weights, &agg_op, &agg_op_len) == - FAILURE) { - RETURN_FALSE; - } - - ht_keys = Z_ARRVAL_P(z_keys); - - if ((keys_count = zend_hash_num_elements(ht_keys)) == 0) { - RETURN_FALSE; - } else { - argc += keys_count + 1; - } - - if (z_weights != nullptr) { - ht_weights = Z_ARRVAL_P(z_weights); - if (zend_hash_num_elements(ht_weights) != keys_count) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "WEIGHTS and keys array should be the same size!"); - RETURN_FALSE; - } - argc += keys_count + 1; - } - - // AGGREGATE option - if (agg_op_len != 0) { - if (strncasecmp(agg_op, "SUM", sizeof("SUM")) && strncasecmp(agg_op, "MIN", sizeof("MIN")) && - strncasecmp(agg_op, "MAX", sizeof("MAX"))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Invalid AGGREGATE option provided!"); - RETURN_FALSE; - } - - // "AGGREGATE" + type - argc += 2; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, j; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZUNIONSTORE", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%zu", keys_count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - - // Process input keys - zval *value; - SW_HASHTABLE_FOREACH_START(ht_keys, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - // Weights - if (ht_weights != nullptr) { - SW_REDIS_COMMAND_ARGV_FILL("WEIGHTS", 7) - - SW_HASHTABLE_FOREACH_START(ht_weights, value) - if (Z_TYPE_P(value) != IS_LONG && Z_TYPE_P(value) != IS_DOUBLE && - strncasecmp(Z_STRVAL_P(value), "inf", sizeof("inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "-inf", sizeof("-inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "+inf", sizeof("+inf")) != 0) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errMsg"), - "Weights must be numeric or '-inf','inf','+inf'"); - for (j = 0; j < i; j++) { - efree((void *) argv[j]); - } - SW_REDIS_COMMAND_FREE_ARGV - RETURN_FALSE; - } - switch (Z_TYPE_P(value)) { - case IS_LONG: - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_DOUBLE: - buf_len = sprintf(buf, "%f", Z_DVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_STRING: - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(value), Z_STRLEN_P(value)) - break; - } - SW_HASHTABLE_FOREACH_END(); - } - - // AGGREGATE - if (agg_op_len != 0) { - SW_REDIS_COMMAND_ARGV_FILL("AGGREGATE", 9) - SW_REDIS_COMMAND_ARGV_FILL(agg_op, agg_op_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zInter) { - char *key, *agg_op; - size_t key_len; - zval *z_keys, *z_weights = nullptr; - HashTable *ht_keys, *ht_weights = nullptr; - size_t argc = 2, agg_op_len = 0, keys_count; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|a!s", &key, &key_len, &z_keys, &z_weights, &agg_op, &agg_op_len) == - FAILURE) { - RETURN_FALSE; - } - - ht_keys = Z_ARRVAL_P(z_keys); - - if ((keys_count = zend_hash_num_elements(ht_keys)) == 0) { - RETURN_FALSE; - } else { - argc += keys_count + 1; - } - - if (z_weights != nullptr) { - ht_weights = Z_ARRVAL_P(z_weights); - if (zend_hash_num_elements(ht_weights) != keys_count) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "WEIGHTS and keys array should be the same size!"); - RETURN_FALSE; - } - - argc += keys_count + 1; - } - - // AGGREGATE option - if (agg_op_len != 0) { - if (strncasecmp(agg_op, "SUM", sizeof("SUM")) && strncasecmp(agg_op, "MIN", sizeof("MIN")) && - strncasecmp(agg_op, "MAX", sizeof("MAX"))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Invalid AGGREGATE option provided!"); - RETURN_FALSE; - } - - // "AGGREGATE" + type - argc += 2; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, j; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZINTERSTORE", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%zu", keys_count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - - // Process input keys - zval *value; - SW_HASHTABLE_FOREACH_START(ht_keys, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - // Weights - if (ht_weights != nullptr) { - SW_REDIS_COMMAND_ARGV_FILL("WEIGHTS", 7) - - SW_HASHTABLE_FOREACH_START(ht_weights, value) - if (Z_TYPE_P(value) != IS_LONG && Z_TYPE_P(value) != IS_DOUBLE && - strncasecmp(Z_STRVAL_P(value), "inf", sizeof("inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "-inf", sizeof("-inf")) != 0 && - strncasecmp(Z_STRVAL_P(value), "+inf", sizeof("+inf")) != 0) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "Weights must be numeric or '-inf','inf','+inf'"); - for (j = 0; j < i; j++) { - efree((void *) argv[j]); - } - SW_REDIS_COMMAND_FREE_ARGV - RETURN_FALSE; - } - switch (Z_TYPE_P(value)) { - case IS_LONG: - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_DOUBLE: - buf_len = sprintf(buf, "%f", Z_DVAL_P(value)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - break; - case IS_STRING: - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(value), Z_STRLEN_P(value)) - break; - } - SW_HASHTABLE_FOREACH_END(); - } - - // AGGREGATE - if (agg_op_len != 0) { - SW_REDIS_COMMAND_ARGV_FILL("AGGREGATE", 9) - SW_REDIS_COMMAND_ARGV_FILL(agg_op, agg_op_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRangeByLex) { - char *key, *min, *max; - size_t key_len, min_len, max_len; - zend_long offset = 0, count = 0; - size_t argc = ZEND_NUM_ARGS(); - - /* We need either 3 or 5 arguments for this to be valid */ - if (argc != 3 && argc != 5) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), "Must pass either 3 or 5 arguments"); - RETURN_FALSE; - } - - if (zend_parse_parameters(argc, "sss|ll", &key, &key_len, &min, &min_len, &max, &max_len, &offset, &count) == - FAILURE) { - RETURN_FALSE; - } - - /* min and max must start with '(' or '[', or be either '-' or '+' */ - if (min_len < 1 || max_len < 1 || - (min[0] != '(' && min[0] != '[' && (min[0] != '-' || min_len > 1) && (min[0] != '+' || min_len > 1)) || - (max[0] != '(' && max[0] != '[' && (max[0] != '-' || max_len > 1) && (max[0] != '+' || max_len > 1))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "min and max arguments must start with '[' or '('"); - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - argc = argc == 3 ? 4 : 7; - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZRANGEBYLEX", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(min, min_len) - SW_REDIS_COMMAND_ARGV_FILL(max, max_len) - if (argc == 7) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, ZEND_LONG_FMT, offset); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRevRangeByLex) { - char *key, *min, *max; - size_t key_len, min_len, max_len; - zend_long offset = 0, count = 0; - int argc = ZEND_NUM_ARGS(); - - /* We need either 3 or 5 arguments for this to be valid */ - if (argc != 3 && argc != 5) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), "Must pass either 3 or 5 arguments"); - RETURN_FALSE; - } - - if (zend_parse_parameters(argc, "sss|ll", &key, &key_len, &min, &min_len, &max, &max_len, &offset, &count) == - FAILURE) { - RETURN_FALSE; - } - - /* min and max must start with '(' or '[', or be either '-' or '+' */ - if (min_len < 1 || max_len < 1 || - (min[0] != '(' && min[0] != '[' && (min[0] != '-' || min_len > 1) && (min[0] != '+' || min_len > 1)) || - (max[0] != '(' && max[0] != '[' && (max[0] != '-' || max_len > 1) && (max[0] != '+' || max_len > 1))) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "min and max arguments must start with '[' or '('"); - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - argc = argc == 3 ? 4 : 7; - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZREVRANGEBYLEX", 14) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(min, min_len) - SW_REDIS_COMMAND_ARGV_FILL(max, max_len) - if (argc == 7) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, ZEND_LONG_FMT, offset); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, count); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRangeByScore) { - char *key; - size_t key_len; - char *start, *end; - size_t start_len, end_len; - long limit_low, limit_high; - zval *z_opt = nullptr, *z_ele; - zend_bool withscores = 0, has_limit = 0; - HashTable *ht_opt; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|a", &key, &key_len, &start, &start_len, &end, &end_len, &z_opt) == - FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int argc = 4, i = 0; - // Check for an options array - if (z_opt && ZVAL_IS_ARRAY(z_opt)) { - ht_opt = Z_ARRVAL_P(z_opt); - - // Check for WITHSCORES - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("withscores"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - withscores = 1; - argc++; - } - - // LIMIT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit")))) { - HashTable *ht_limit = Z_ARRVAL_P(z_ele); - zval *z_off, *z_cnt; - z_off = zend_hash_index_find(ht_limit, 0); - z_cnt = zend_hash_index_find(ht_limit, 1); - if (z_off && z_cnt && Z_TYPE_P(z_off) == IS_LONG && Z_TYPE_P(z_cnt) == IS_LONG) { - has_limit = 1; - limit_low = Z_LVAL_P(z_off); - limit_high = Z_LVAL_P(z_cnt); - argc += 3; - } - } - } - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZRANGEBYSCORE", 13) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(start, start_len) - SW_REDIS_COMMAND_ARGV_FILL(end, end_len) - - if (withscores) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } - if (has_limit) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%ld", limit_low); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, "%ld", limit_high); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (withscores && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zRevRangeByScore) { - char *key; - size_t key_len; - char *start, *end; - size_t start_len, end_len; - long limit_low, limit_high; - zval *z_opt = nullptr, *z_ele; - zend_bool withscores = 0, has_limit = 0; - HashTable *ht_opt; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|a", &key, &key_len, &start, &start_len, &end, &end_len, &z_opt) == - FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int argc = 4, i = 0; - // Check for an options array - if (z_opt && ZVAL_IS_ARRAY(z_opt)) { - ht_opt = Z_ARRVAL_P(z_opt); - - // Check for WITHSCORES - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("withscores"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - withscores = 1; - argc++; - } - - // LIMIT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit")))) { - HashTable *ht_limit = Z_ARRVAL_P(z_ele); - zval *z_off, *z_cnt; - z_off = zend_hash_index_find(ht_limit, 0); - z_cnt = zend_hash_index_find(ht_limit, 1); - if (z_off && z_cnt && Z_TYPE_P(z_off) == IS_LONG && Z_TYPE_P(z_cnt) == IS_LONG) { - has_limit = 1; - limit_low = Z_LVAL_P(z_off); - limit_high = Z_LVAL_P(z_cnt); - argc += 3; - } - } - } - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZREVRANGEBYSCORE", 16) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(start, start_len) - SW_REDIS_COMMAND_ARGV_FILL(end, end_len) - - if (withscores) { - SW_REDIS_COMMAND_ARGV_FILL("WITHSCORES", 10) - } - if (has_limit) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%ld", limit_low); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - buf_len = sprintf(buf, "%ld", limit_high); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - } - - redis_request(redis, argc, argv, argvlen, return_value); - - if (withscores && redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zIncrBy) { - char *key; - size_t key_len; - double incrby; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sdz", &key, &key_len, &incrby, &z_val) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK; - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("ZINCRBY", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char buf[32]; - size_t buf_len; - buf_len = sprintf(buf, "%f", incrby); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, zAdd) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - RETURN_FALSE; - } - - if (argc > 0) { - convert_to_string(&z_args[0]); - } - if (argc < 3 || SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) != IS_STRING) { - efree(z_args); - RETURN_FALSE; - } - - int i = 0, j, k, valid_params; - valid_params = argc - 1; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZADD", 4) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), - (size_t) SW_REDIS_COMMAND_ARGS_STRLEN(z_args[0])) - k = 1; - - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[k]) == IS_STRING && IS_NX_XX_ARG(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]))) { - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]), - (size_t) SW_REDIS_COMMAND_ARGS_STRLEN(z_args[k])) - k++; - valid_params--; - } - - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[k]) == IS_STRING && - strncasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]), "CH", 2) == 0) { - SW_REDIS_COMMAND_ARGV_FILL("CH", 2) - k++; - valid_params--; - } - - if (SW_REDIS_COMMAND_ARGS_TYPE(z_args[k]) == IS_STRING && - strncasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[k]), "INCR", 4) == 0) { - SW_REDIS_COMMAND_ARGV_FILL("INCR", 4) - k++; - valid_params--; - } - - if (valid_params % 2 != 0) { - for (i = 0; i < 1 + k; i++) { - efree((void *) argv[i]); - } - SW_REDIS_COMMAND_FREE_ARGV - efree(z_args); - RETURN_FALSE; - } - - char buf[32]; - size_t buf_len; - for (j = k; j < argc - 1; j += 2) { - buf_len = sw_snprintf(buf, sizeof(buf), "%f", zval_get_double(&z_args[j])); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(SW_REDIS_COMMAND_ARGS_REF(z_args[j + 1])) - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zPopMin) { - char *key; - size_t key_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc, buf_len; - char buf[32]; - argc = ZEND_NUM_ARGS() == 2 ? 3 : 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZPOPMIN", 7); - SW_REDIS_COMMAND_ARGV_FILL(key, key_len); - if (argc == 3) { - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zPopMax) { - char *key; - size_t key_len; - zend_long count = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &key, &key_len, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0, argc, buf_len; - char buf[32]; - argc = ZEND_NUM_ARGS() == 2 ? 3 : 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("ZPOPMAX", 7); - SW_REDIS_COMMAND_ARGV_FILL(key, key_len); - if (argc == 3) { - buf_len = sw_snprintf(buf, sizeof(buf), "%" PRId64 "", count); - SW_REDIS_COMMAND_ARGV_FILL((char *) buf, buf_len); - } - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, bzPopMin) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BZPOPMIN", 8) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, bzPopMax) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 1) { - efree(z_args); - return; - } - - zend_bool single_array = 0; - if (argc == 2 && SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) == IS_ARRAY) { - argc = zend_hash_num_elements(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0])) + 2; - single_array = 1; - } else { - argc += 1; - } - int i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BZPOPMAX", 8) - if (single_array) { - zval *value; - SW_HASHTABLE_FOREACH_START(SW_REDIS_COMMAND_ARGS_ARRVAL(z_args[0]), value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - zend_string *convert_str = zval_get_string(&z_args[1]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } else { - int j; - for (j = 0; j < argc - 1; ++j) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, zScore) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZSCORE")); -} - -static PHP_METHOD(swoole_redis_coro, zRank) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZRANK")); -} - -static PHP_METHOD(swoole_redis_coro, zRevRank) { - sw_redis_command_key_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREVRANK")); -} - -static PHP_METHOD(swoole_redis_coro, hGet) { - sw_redis_command_key_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HGET")); -} - -static PHP_METHOD(swoole_redis_coro, hMGet) { - char *key; - zval *z_arr; - size_t argc, key_len; - HashTable *ht_chan; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &key, &key_len, &z_arr) == FAILURE) { - return; - } - - ht_chan = Z_ARRVAL_P(z_arr); - - if ((argc = zend_hash_num_elements(ht_chan)) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - zval *value; - int i = 0; - argc = argc + 2; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("HMGET", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_HASHTABLE_FOREACH_START(ht_chan, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - size_t index = 0; - zval *zkey, *zvalue; - zval zret; - array_init(&zret); - - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_arr), zkey) { - zend::String zkey_str(zkey); - - zvalue = zend_hash_index_find(Z_ARRVAL_P(return_value), index++); - if (ZVAL_IS_NULL(zvalue)) { - add_assoc_bool_ex(&zret, zkey_str.val(), zkey_str.len(), 0); - } else { - Z_ADDREF_P(zvalue); - add_assoc_zval_ex(&zret, zkey_str.val(), zkey_str.len(), zvalue); - } - } - ZEND_HASH_FOREACH_END(); - - zval_ptr_dtor(return_value); - RETVAL_ZVAL(&zret, 1, 1); - } -} - -static PHP_METHOD(swoole_redis_coro, hExists) { - sw_redis_command_key_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("HEXISTS")); - - RedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - if (redis->compatibility_mode && ZVAL_IS_LONG(return_value)) { - RETURN_BOOL(zval_get_long(return_value)); - } -} - -static PHP_METHOD(swoole_redis_coro, publish) { - sw_redis_command_key_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PUBLISH")); -} - -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByScore) { - sw_redis_command_key_str_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREMRANGEBYSCORE")); -} - -static PHP_METHOD(swoole_redis_coro, zCount) { - sw_redis_command_key_str_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZCOUNT")); -} - -static PHP_METHOD(swoole_redis_coro, incrBy) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("INCRBY")); -} - -static PHP_METHOD(swoole_redis_coro, hIncrBy) { - char *key, *mem; - size_t key_len, mem_len; - long byval; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &key, &key_len, &mem, &mem_len, &byval) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("HINCRBY", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(mem, mem_len) - char str[32]; - sprintf(str, "%ld", byval); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, hIncrByFloat) { - char *key, *mem; - size_t key_len, mem_len; - double byval; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssd", &key, &key_len, &mem, &mem_len, &byval) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("HINCRBYFLOAT", 12) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(mem, mem_len) - char str[32]; - sprintf(str, "%f", byval); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, incr) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("INCR")); -} - -static PHP_METHOD(swoole_redis_coro, decrBy) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DECRBY")); -} - -static PHP_METHOD(swoole_redis_coro, decr) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DECR")); -} - -static PHP_METHOD(swoole_redis_coro, getBit) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GETBIT")); -} - -static PHP_METHOD(swoole_redis_coro, lInsert) { - char *key, *pos; - size_t key_len, pos_len; - zval *z_val, *z_pivot; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszz", &key, &key_len, &pos, &pos_len, &z_pivot, &z_val) == FAILURE) { - return; - } - - if (strncasecmp(pos, "after", 5) && strncasecmp(pos, "before", 6)) { - php_swoole_error(E_WARNING, "Position must be either 'BEFORE' or 'AFTER'"); - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[5]; - char *argv[5]; - - SW_REDIS_COMMAND_ARGV_FILL("LINSERT", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(pos, pos_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_pivot) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - redis_request(redis, 5, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, lGet) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LINDEX")); -} - -static PHP_METHOD(swoole_redis_coro, setTimeout) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXPIRE")); -} - -static PHP_METHOD(swoole_redis_coro, pexpire) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PEXPIRE")); -} - -static PHP_METHOD(swoole_redis_coro, expireAt) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXPIREAT")); -} - -static PHP_METHOD(swoole_redis_coro, pexpireAt) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("PEXPIREAT")); -} - -static PHP_METHOD(swoole_redis_coro, move) { - sw_redis_command_key_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("MOVE")); -} - -static PHP_METHOD(swoole_redis_coro, select) { - zend_long db_number; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(db_number) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_REDIS_COMMAND_CHECK - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - add_assoc_long(zsetting, "database", db_number); - RETURN_BOOL(redis_select_db(redis, db_number)); -} - -static PHP_METHOD(swoole_redis_coro, getRange) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("GETRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, listTrim) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LTRIM")); -} - -static PHP_METHOD(swoole_redis_coro, lGetRange) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("LRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, lRem) { - char *key; - size_t key_len; - zend_long count = 0; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|l", &key, &key_len, &z_val, &count) == FAILURE) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("LREM", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%d", (int) count); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, zDeleteRangeByRank) { - sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREMRANGEBYRANK")); -} - -static PHP_METHOD(swoole_redis_coro, incrByFloat) { - sw_redis_command_key_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("INCRBYFLOAT")); -} - -static PHP_METHOD(swoole_redis_coro, bitCount) { - char *key; - size_t key_len; - zend_long start = 0, end = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &key, &key_len, &start, &end) == FAILURE) { - return; - } - - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("BITCOUNT", 8) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%d", (int) start); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - sprintf(str, "%d", (int) end); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, bitOp) { - int argc = ZEND_NUM_ARGS(); - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || argc < 3 || - SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) != IS_STRING) { - efree(z_args); - return; - } - - int j, i = 0; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("BITOP", 5) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), SW_REDIS_COMMAND_ARGS_STRLEN(z_args[0])) - for (j = 1; j < argc - 1; j++) { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV - efree(z_args); -} - -static PHP_METHOD(swoole_redis_coro, sMove) { - char *src, *dst; - size_t src_len, dst_len; - zval *z_val; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssz", &src, &src_len, &dst, &dst_len, &z_val) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - - int i = 0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("SMOVE", 5) - SW_REDIS_COMMAND_ARGV_FILL(src, src_len) - SW_REDIS_COMMAND_ARGV_FILL(dst, dst_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_val) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, sAdd) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SADD")); -} - -static PHP_METHOD(swoole_redis_coro, sRemove) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("SREM")); -} - -static PHP_METHOD(swoole_redis_coro, zDelete) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("ZREM")); -} - -static sw_inline void redis_subscribe(INTERNAL_FUNCTION_PARAMETERS, const char *cmd) { - zval *z_arr; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &z_arr) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - if (redis->defer) { - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errMsg"), - "subscribe cannot be used with defer enabled"); - RETURN_FALSE; - } - - HashTable *ht_chan = Z_ARRVAL_P(z_arr); - size_t chan_num = zend_hash_num_elements(ht_chan); - int argc = 1 + chan_num, i = 0; - SW_REDIS_COMMAND_ALLOC_ARGV - - SW_REDIS_COMMAND_ARGV_FILL(cmd, strlen(cmd)); - - zval *value; - SW_HASHTABLE_FOREACH_START(ht_chan, value) - zend_string *convert_str = zval_get_string(value); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - SW_HASHTABLE_FOREACH_END(); - - redis->defer = true; - redis_request(redis, argc, argv, argvlen, return_value); - redis->defer = false; - SW_REDIS_COMMAND_FREE_ARGV - - if (Z_TYPE_P(return_value) == IS_TRUE) { - redis->session.subscribe = true; - } -} - -static PHP_METHOD(swoole_redis_coro, subscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "SUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, pSubscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PSUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, unsubscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNSUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, pUnSubscribe) { - redis_subscribe(INTERNAL_FUNCTION_PARAM_PASSTHRU, "PUNSUBSCRIBE"); -} - -static PHP_METHOD(swoole_redis_coro, multi) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("MULTI")); -} - -static PHP_METHOD(swoole_redis_coro, exec) { - sw_redis_command_empty(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("EXEC")); -} - -static PHP_METHOD(swoole_redis_coro, request) { - SW_REDIS_COMMAND_CHECK - - zval *params = nullptr; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", ¶ms) == FAILURE) { - RETURN_FALSE; - } - - int argc = zend_hash_num_elements(Z_ARRVAL_P(params)); - int i = 0; - zval *value; - - SW_REDIS_COMMAND_ALLOC_ARGV - - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(params), value) - if (i == argc) { - break; - } - zend_string *convert_str = zval_get_string(value); - argvlen[i] = ZSTR_LEN(convert_str); - argv[i] = estrndup(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)); - zend_string_release(convert_str); - i++; - SW_HASHTABLE_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, eval) { - char *script; - size_t script_len; - zval *params = nullptr; - zend_long keys_num = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|al", &script, &script_len, ¶ms, &keys_num) == FAILURE) { - RETURN_FALSE; - } - - HashTable *params_ht = nullptr; - uint32_t params_num = 0; - if (params) { - params_ht = Z_ARRVAL_P(params); - params_num = zend_hash_num_elements(params_ht); - } - - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t *argvlen = (size_t *) emalloc(sizeof(size_t) * (params_num + 3)); - char **argv = (char **) emalloc(sizeof(char *) * (params_num + 3)); - - SW_REDIS_COMMAND_ARGV_FILL("EVAL", 4) - SW_REDIS_COMMAND_ARGV_FILL(script, script_len) - - char keys_num_str[32] = {}; - sprintf(keys_num_str, ZEND_LONG_FMT, keys_num); - SW_REDIS_COMMAND_ARGV_FILL(keys_num_str, strlen(keys_num_str)); - - if (params_ht) { - zval *param; - SW_HASHTABLE_FOREACH_START(params_ht, param) - zend_string *param_str = zval_get_string(param); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(param_str), ZSTR_LEN(param_str)) - zend_string_release(param_str); - SW_HASHTABLE_FOREACH_END(); - } - - redis_request(redis, params_num + 3, argv, argvlen, return_value); - efree(argvlen); - efree(argv); -} - -static PHP_METHOD(swoole_redis_coro, evalSha) { - char *sha; - size_t sha_len; - zval *params = nullptr; - long keys_num = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|al", &sha, &sha_len, ¶ms, &keys_num) == FAILURE) { - RETURN_FALSE; - } - - HashTable *params_ht = nullptr; - uint32_t params_num = 0; - if (params) { - params_ht = Z_ARRVAL_P(params); - params_num = zend_hash_num_elements(params_ht); - } - - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t *argvlen = (size_t *) emalloc(sizeof(size_t) * (params_num + 3)); - char **argv = (char **) emalloc(sizeof(char *) * (params_num + 3)); - - SW_REDIS_COMMAND_ARGV_FILL("EVALSHA", 7) - SW_REDIS_COMMAND_ARGV_FILL(sha, sha_len) - - char keys_num_str[32] = {}; - sprintf(keys_num_str, "%ld", keys_num); - SW_REDIS_COMMAND_ARGV_FILL(keys_num_str, strlen(keys_num_str)); - - if (params) { - zval *param; - SW_HASHTABLE_FOREACH_START(params_ht, param) - zend_string *param_str = zval_get_string(param); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(param_str), ZSTR_LEN(param_str)) - zend_string_release(param_str); - SW_HASHTABLE_FOREACH_END(); - } - - redis_request(redis, params_num + 3, argv, argvlen, return_value); - efree(argvlen); - efree(argv); -} - -static PHP_METHOD(swoole_redis_coro, script) { - int argc = ZEND_NUM_ARGS(); - if (argc < 1) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE || SW_REDIS_COMMAND_ARGS_TYPE(z_args[0]) != IS_STRING) { - efree(z_args); - RETURN_FALSE; - } - - int i = 0; - if (!strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "flush") || - !strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "kill")) { - size_t argvlen[2]; - char *argv[2]; - SW_REDIS_COMMAND_ARGV_FILL("SCRIPT", 6) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), SW_REDIS_COMMAND_ARGS_STRLEN(z_args[0])) - redis_request(redis, 2, argv, argvlen, return_value); - efree(z_args); - } else if (!strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "exists")) { - if (argc < 2) { - efree(z_args); - RETURN_FALSE; - } else { - size_t *argvlen = (size_t *) emalloc(sizeof(size_t) * (argc + 1)); - char **argv = (char **) emalloc(sizeof(char *) * (argc + 1)); - SW_REDIS_COMMAND_ARGV_FILL("SCRIPT", 6) - SW_REDIS_COMMAND_ARGV_FILL("EXISTS", 6) - int j = 1; - for (; j < argc; j++) { - zend_string *z_arg_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(z_arg_str), ZSTR_LEN(z_arg_str)) - zend_string_release(z_arg_str); - } - - redis_request(redis, argc + 1, argv, argvlen, return_value); - efree(argvlen); - efree(argv); - efree(z_args); - } - } else if (!strcasecmp(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[0]), "load")) { - if (argc < 2 || SW_REDIS_COMMAND_ARGS_TYPE(z_args[1]) != IS_STRING) { - efree(z_args); - RETURN_FALSE; - } else { - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL("SCRIPT", 6) - SW_REDIS_COMMAND_ARGV_FILL("LOAD", 4) - SW_REDIS_COMMAND_ARGV_FILL(SW_REDIS_COMMAND_ARGS_STRVAL(z_args[1]), SW_REDIS_COMMAND_ARGS_STRLEN(z_args[1])) - redis_request(redis, 3, argv, argvlen, return_value); - efree(z_args); - } - } else { - efree(z_args); - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_redis_coro, xLen) { - sw_redis_command_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XLEN")); -} - -static PHP_METHOD(swoole_redis_coro, xAdd) { - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt, *ht_ele; - char *key, *id; - size_t key_len, id_len; - zval *z_arr; - int argc, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|a", &key, &key_len, &id, &id_len, &z_arr, &z_options) == FAILURE) { - return; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_arr))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - argc = argc * 2 + 3; - zval *value; - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XADD", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - int has_maxlen_minid = 0; - int can_limit = 0; - // NOMKSTREAM - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("nomkstream"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("NOMKSTREAM", 10) - options_argc++; - } - // MAXLEN - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("maxlen")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_maxlen_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_maxlen_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_maxlen_p1) == IS_STRING && Z_TYPE_P(z_maxlen_p2) == IS_LONG) { - char *maxlen_p1 = Z_STRVAL_P(z_maxlen_p1); - zend_long maxlen_p2 = Z_LVAL_P(z_maxlen_p2); - if ((strcmp(maxlen_p1, "=") == 0 || strcmp(maxlen_p1, "~") == 0) && maxlen_p2 >= 0) { - if ((strcmp(maxlen_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - SW_REDIS_COMMAND_ARGV_FILL(maxlen_p1, 1) - buf_len = sprintf(buf, ZEND_LONG_FMT, maxlen_p2); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 3; - } - } - } - } - // MINID - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("minid")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_STRING && Z_STRLEN_P(z_ele) > 0) { - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_minid_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_minid_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_minid_p1) == IS_STRING && Z_TYPE_P(z_minid_p2) == IS_STRING) { - char *minid_p1 = Z_STRVAL_P(z_minid_p1); - char *minid_p2 = Z_STRVAL_P(z_minid_p2); - if ((strcmp(minid_p1, "=") == 0 || strcmp(minid_p1, "~") == 0) && strlen(minid_p2) > 0) { - if ((strcmp(minid_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(minid_p1, 1) - SW_REDIS_COMMAND_ARGV_FILL(minid_p2, strlen(minid_p2)) - options_argc += 3; - } - } - } - } - // LIMIT - if (can_limit == 1 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - // id - SW_REDIS_COMMAND_ARGV_FILL(id, id_len) - - // k-v - zend_ulong idx; - zend_string *_key; - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(z_arr), idx, _key, value) { - if (_key == nullptr) { - key_len = sw_snprintf(buf, sizeof(buf), ZEND_LONG_FMT, idx); - key = (char *) buf; - } else { - key_len = ZSTR_LEN(_key); - key = ZSTR_VAL(_key); - } - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(value) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xRead) { - zval *z_streams = nullptr, *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 0, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a", &z_streams, &z_options) == FAILURE) { - RETURN_FALSE; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_streams))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - - argc = argc * 2 + 2; - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XREAD", 5) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // BLOCK - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("block"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("BLOCK", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - // streams - SW_REDIS_COMMAND_ARGV_FILL("STREAMS", 7) - zend_long _num_key; - zend_string *_str_key; - zval *_val; - ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(z_streams), _num_key, _str_key) { - if (_str_key == NULL) { - _str_key = zend_long_to_str(_num_key); - } - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(_str_key), ZSTR_LEN(_str_key)) - } - ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_streams), _val) { - convert_to_string(_val); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_val), Z_STRLEN_P(_val)) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xRange) { - sw_redis_command_xrange(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, xRevRange) { - sw_redis_command_xrange(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XREVRANGE")); -} - -static PHP_METHOD(swoole_redis_coro, xTrim) { - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt, *ht_ele; - int i = 0, argc = 2, options_argc = 0; - char buf[32], *key; - size_t buf_len, key_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &key, &key_len, &z_options) == FAILURE) { - RETURN_FALSE; - } - if (php_swoole_array_length_safe(z_options) < 1) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XTRIM", 5) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - int has_maxlen_minid = 0; - int can_limit = 0; - // MAXLEN - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("maxlen")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_maxlen_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_maxlen_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_maxlen_p1) == IS_STRING && Z_TYPE_P(z_maxlen_p2) == IS_LONG) { - char *maxlen_p1 = Z_STRVAL_P(z_maxlen_p1); - zend_long maxlen_p2 = Z_LVAL_P(z_maxlen_p2); - if ((strcmp(maxlen_p1, "=") == 0 || strcmp(maxlen_p1, "~") == 0) && maxlen_p2 >= 0) { - if ((strcmp(maxlen_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MAXLEN", 6) - SW_REDIS_COMMAND_ARGV_FILL(maxlen_p1, 1) - buf_len = sprintf(buf, ZEND_LONG_FMT, maxlen_p2); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 3; - } - } - } - } - // MINID - if (has_maxlen_minid == 0 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("minid")))) { - has_maxlen_minid = 1; - if (Z_TYPE_P(z_ele) == IS_STRING && Z_STRLEN_P(z_ele) > 0) { - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc += 2; - } else if (Z_TYPE_P(z_ele) == IS_ARRAY) { - ht_ele = Z_ARRVAL_P(z_ele); - zval *z_minid_p1 = zend_hash_index_find(ht_ele, 0); - zval *z_minid_p2 = zend_hash_index_find(ht_ele, 1); - if (Z_TYPE_P(z_minid_p1) == IS_STRING && Z_TYPE_P(z_minid_p2) == IS_STRING) { - char *minid_p1 = Z_STRVAL_P(z_minid_p1); - char *minid_p2 = Z_STRVAL_P(z_minid_p2); - if ((strcmp(minid_p1, "=") == 0 || strcmp(minid_p1, "~") == 0) && strlen(minid_p2) > 0) { - if ((strcmp(minid_p1, "~") == 0)) { - can_limit = 1; - } - SW_REDIS_COMMAND_ARGV_FILL("MINID", 5) - SW_REDIS_COMMAND_ARGV_FILL(minid_p1, 1) - SW_REDIS_COMMAND_ARGV_FILL(minid_p2, strlen(minid_p2)) - options_argc += 3; - } - } - } - } - // LIMIT - if (can_limit == 1 && (z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("limit"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("LIMIT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xDel) { - sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("XDEL")); -} - -static PHP_METHOD(swoole_redis_coro, xGroupCreate) { - char *key, *group_name, *id; - size_t key_len, group_name_len, id_len; - zend_bool mkstream = 0; - - if (zend_parse_parameters( - ZEND_NUM_ARGS(), "sss|b", &key, &key_len, &group_name, &group_name_len, &id, &id_len, &mkstream) == - FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[6]; - char *argv[6]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("CREATE", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(id, id_len) - if (mkstream) { - SW_REDIS_COMMAND_ARGV_FILL("MKSTREAM", 8) - argc = 6; - } - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupSetId) { - char *key, *group_name, *id; - size_t key_len, group_name_len, id_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &id, &id_len) == - FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[5]; - char *argv[5]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("CREATECONSUMER", 14) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(id, id_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupDestroy) { - char *key, *group_name; - size_t key_len, group_name_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &group_name, &group_name_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 4; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("DESTROY", 7) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupCreateConsumer) { - char *key, *group_name, *consumer_name; - size_t key_len, group_name_len, consumer_name_len; - - if (zend_parse_parameters( - ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len) == - FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[5]; - char *argv[5]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("CREATECONSUMER", 14) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xGroupDelConsumer) { - char *key, *group_name, *consumer_name; - size_t key_len, group_name_len, consumer_name_len; - - if (zend_parse_parameters( - ZEND_NUM_ARGS(), "sss", &key, &key_len, &group_name, &group_name_len, &consumer_name, &consumer_name_len) == - FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 5; - size_t argvlen[5]; - char *argv[5]; - SW_REDIS_COMMAND_ARGV_FILL("XGROUP", 6) - SW_REDIS_COMMAND_ARGV_FILL("DELCONSUMER", 11) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static PHP_METHOD(swoole_redis_coro, xReadGroup) { - char *group_name, *consumer_name; - size_t group_name_len, consumer_name_len; - zval *z_streams = nullptr, *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 0, options_argc = 0; - char buf[32]; - size_t buf_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), - "ssa|a", - &group_name, - &group_name_len, - &consumer_name, - &consumer_name_len, - &z_streams, - &z_options) == FAILURE) { - RETURN_FALSE; - } - if ((argc = zend_hash_num_elements(Z_ARRVAL_P(z_streams))) == 0) { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - argc = argc * 2 + 5; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XREADGROUP", 10) - SW_REDIS_COMMAND_ARGV_FILL("GROUP", 5) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // BLOCK - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("block"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("BLOCK", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // NOACK - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("noack"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("NOACK", 5) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - // streams - SW_REDIS_COMMAND_ARGV_FILL("STREAMS", 7) - zend_long _num_key; - zend_string *_str_key; - zval *_val; - ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(z_streams), _num_key, _str_key) { - if (_str_key == NULL) { - _str_key = zend_long_to_str(_num_key); - } - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(_str_key), ZSTR_LEN(_str_key)) - } - ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_streams), _val) { - convert_to_string(_val); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_val), Z_STRLEN_P(_val)) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xPending) { - char *key, *group_name; - size_t key_len, group_name_len; - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 3, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|a", &key, &key_len, &group_name, &group_name_len, &z_options) == - FAILURE) { - RETURN_FALSE; - } - - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XPENDING", 8) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // IDLE - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("idle"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("IDLE", 4) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // START - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("start"))) && Z_TYPE_P(z_ele) == IS_STRING) { - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc++; - } - // END - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("end"))) && Z_TYPE_P(z_ele) == IS_STRING) { - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc++; - } - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc++; - } - // CONSUMER - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("consumer"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xAck) { - char *key, *group_name; - size_t key_len, group_name_len; - zval *z_id = nullptr; - int i = 0, argc = 3, id_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa", &key, &key_len, &group_name, &group_name_len, &z_id) == FAILURE) { - RETURN_FALSE; - } - if ((id_argc = zend_hash_num_elements(Z_ARRVAL_P(z_id))) == 0) { - RETURN_FALSE; - } - argc += id_argc; - SW_REDIS_COMMAND_CHECK - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XACK", 4) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - // id - zval *_id; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_id), _id) { - convert_to_string(_id); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_id), Z_STRLEN_P(_id)) - } - ZEND_HASH_FOREACH_END(); - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xClaim) { - char *key, *group_name, *consumer_name; - size_t key_len, group_name_len, consumer_name_len; - zend_long min_idle_time = 0; - zval *z_id = nullptr, *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 5, id_argc = 0, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), - "sssla|a", - &key, - &key_len, - &group_name, - &group_name_len, - &consumer_name, - &consumer_name_len, - &min_idle_time, - &z_id, - &z_options) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - id_argc = zend_hash_num_elements(Z_ARRVAL_P(z_id)); - argc += id_argc; - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XCLAIM", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, min_idle_time); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - - // id - zval *_id; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_id), _id) { - convert_to_string(_id); - SW_REDIS_COMMAND_ARGV_FILL(Z_STRVAL_P(_id), Z_STRLEN_P(_id)) - } - ZEND_HASH_FOREACH_END(); - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // IDLE - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("idle"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("IDLE", 4) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // TIME - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("time"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("TIME", 4) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // RETRYCOUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("retrycount"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("RETRYCOUNT", 10) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // FORCE - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("force"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("FORCE", 5) - options_argc++; - } - // JUSTID - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("justid"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("JUSTID", 6) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xAutoClaim) { - char *key, *group_name, *consumer_name, *start; - size_t key_len, group_name_len, consumer_name_len, start_len; - zend_long min_idle_time = 0; - zval *z_options = nullptr, *z_ele; - HashTable *ht_opt; - int i = 0, argc = 6, options_argc = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), - "sssls|a", - &key, - &key_len, - &group_name, - &group_name_len, - &consumer_name, - &consumer_name_len, - &min_idle_time, - &start, - &start_len, - &z_options) == FAILURE) { - RETURN_FALSE; - } - - SW_REDIS_COMMAND_CHECK - char buf[32]; - size_t buf_len; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL("XAUTOCLAIM", 10) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - SW_REDIS_COMMAND_ARGV_FILL(consumer_name, consumer_name_len) - buf_len = sprintf(buf, ZEND_LONG_FMT, min_idle_time); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - SW_REDIS_COMMAND_ARGV_FILL(start, start_len) - - // options - if (z_options && ZVAL_IS_ARRAY(z_options)) { - ht_opt = Z_ARRVAL_P(z_options); - // COUNT - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("count"))) && Z_TYPE_P(z_ele) == IS_LONG) { - SW_REDIS_COMMAND_ARGV_FILL("COUNT", 5) - buf_len = sprintf(buf, ZEND_LONG_FMT, Z_LVAL_P(z_ele)); - SW_REDIS_COMMAND_ARGV_FILL(buf, buf_len) - options_argc += 2; - } - // JUSTID - if ((z_ele = zend_hash_str_find(ht_opt, ZEND_STRL("justid"))) && Z_TYPE_P(z_ele) == IS_TRUE) { - SW_REDIS_COMMAND_ARGV_FILL("JUSTID", 6) - options_argc++; - } - } - - SW_REDIS_COMMAND_INCREASE_ARGV(argc + options_argc) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } - - SW_REDIS_COMMAND_FREE_ARGV -} - -static PHP_METHOD(swoole_redis_coro, xInfoConsumers) { - char *key, *group_name; - size_t key_len, group_name_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &group_name, &group_name_len) == FAILURE) { - return; - } - - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 4; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL("XINFO", 5) - SW_REDIS_COMMAND_ARGV_FILL("CONSUMERS", 9) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(group_name, group_name_len) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } -} - -static PHP_METHOD(swoole_redis_coro, xInfoGroups) { - char *key; - size_t key_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) { - return; - } - - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 3; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL("XINFO", 5) - SW_REDIS_COMMAND_ARGV_FILL("GROUPS", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } -} - -static PHP_METHOD(swoole_redis_coro, xInfoStream) { - char *key; - size_t key_len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0, argc = 3; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL("XINFO", 5) - SW_REDIS_COMMAND_ARGV_FILL("STREAM", 6) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_ARRAY(return_value)) { - swoole_redis_handle_assoc_array_result(return_value, true); - } -} - -static void swoole_redis_coro_parse_result(RedisClient *redis, zval *return_value, redisReply *reply) { - int j; - zval _val, *val = &_val; - - switch (reply->type) { - case REDIS_REPLY_INTEGER: - ZVAL_LONG(return_value, reply->integer); - break; - - case REDIS_REPLY_DOUBLE: - ZVAL_DOUBLE(return_value, reply->dval); - break; - - case REDIS_REPLY_BOOL: - ZVAL_BOOL(return_value, reply->integer); - break; - - case REDIS_REPLY_ERROR: - ZVAL_FALSE(return_value); - if (redis->context->err == 0) { - if (strncmp(reply->str, "NOAUTH", 6) == 0) { - redis->context->err = SW_REDIS_ERR_NOAUTH; - } else { - redis->context->err = SW_REDIS_ERR_OTHER; - } - size_t str_len = strlen(reply->str); - memcpy(redis->context->errstr, reply->str, SW_MIN(str_len, sizeof(redis->context->errstr) - 1)); - } - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - break; - - case REDIS_REPLY_STATUS: - if (redis->context->err == 0) { - if (reply->len > 0) { - if (strncmp(reply->str, "OK", 2) == 0) { - ZVAL_TRUE(return_value); - break; - } - long l; - if (strncmp(reply->str, "string", 6) == 0) { - l = SW_REDIS_TYPE_STRING; - } else if (strncmp(reply->str, "set", 3) == 0) { - l = SW_REDIS_TYPE_SET; - } else if (strncmp(reply->str, "list", 4) == 0) { - l = SW_REDIS_TYPE_LIST; - } else if (strncmp(reply->str, "zset", 4) == 0) { - l = SW_REDIS_TYPE_ZSET; - } else if (strncmp(reply->str, "hash", 4) == 0) { - l = SW_REDIS_TYPE_HASH; - } else { - l = SW_REDIS_TYPE_NOT_FOUND; - } - ZVAL_LONG(return_value, l); - } else { - ZVAL_TRUE(return_value); - } - } else { - ZVAL_FALSE(return_value); - zend_update_property_long( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, - SW_Z8_OBJ_P(redis->zobject), - ZEND_STRL("errCode"), - sw_redis_convert_err(redis->context->err)); - zend_update_property_string( - swoole_redis_coro_ce, SW_Z8_OBJ_P(redis->zobject), ZEND_STRL("errMsg"), redis->context->errstr); - } - break; - - case REDIS_REPLY_STRING: - if (redis->serialize) { - char *reserve_str = reply->str; - php_unserialize_data_t s_ht; - PHP_VAR_UNSERIALIZE_INIT(s_ht); - if (!php_var_unserialize(return_value, - (const unsigned char **) &reply->str, - (const unsigned char *) reply->str + reply->len, - &s_ht)) { - ZVAL_STRINGL(return_value, reply->str, reply->len); - } - PHP_VAR_UNSERIALIZE_DESTROY(s_ht); - reply->str = reserve_str; - } else { - ZVAL_STRINGL(return_value, reply->str, reply->len); - } - break; - - case REDIS_REPLY_ARRAY: - array_init(return_value); - for (j = 0; j < (int) reply->elements; j++) { - swoole_redis_coro_parse_result(redis, val, reply->element[j]); - (void) add_next_index_zval(return_value, val); - } - break; - - case REDIS_REPLY_NIL: - default: - ZVAL_NULL(return_value); - return; - } -} From 6969a3620ba7901eb209222bdd316212ed7c40cf Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 25 Mar 2024 20:22:09 +0800 Subject: [PATCH 015/103] Added Thread\Queue --- ext-src/stubs/php_swoole_thread.stub.php | 8 + ext-src/stubs/php_swoole_thread_arginfo.h | 14 +- ext-src/swoole_thread.cc | 229 ++++++++++++++++++++-- scripts/code-stats.sh | 7 +- 4 files changed, 236 insertions(+), 22 deletions(-) diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index 5d0e241b0b8..94ff7efc7bf 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -36,4 +36,12 @@ public function count(): int {} public function clean(): void {} public function __wakeup(): void {} } + class Queue implements Countable { + public function __construct() {} + public function push(mixed $key): mixed {} + public function pop(mixed $key): bool {} + public function count(): int {} + public function clean(): void {} + public function __wakeup(): void {} + } } diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index 04a137e70a3..6430f3ebd90 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 86ee0e408295738c52a0f9944e4ed83b6f4fadf3 */ + * Stub hash: aa86c24d1a84295bb8daee060270b5185f09bac1 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -65,3 +65,15 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Thread_ArrayList_clean arginfo_class_Swoole_Thread_Map_clean #define arginfo_class_Swoole_Thread_ArrayList___wakeup arginfo_class_Swoole_Thread_Map_clean + +#define arginfo_class_Swoole_Thread_Queue___construct arginfo_class_Swoole_Thread___construct + +#define arginfo_class_Swoole_Thread_Queue_push arginfo_class_Swoole_Thread_Map_offsetGet + +#define arginfo_class_Swoole_Thread_Queue_pop arginfo_class_Swoole_Thread_Map_offsetExists + +#define arginfo_class_Swoole_Thread_Queue_count arginfo_class_Swoole_Thread_getId + +#define arginfo_class_Swoole_Thread_Queue_clean arginfo_class_Swoole_Thread_Map_clean + +#define arginfo_class_Swoole_Thread_Queue___wakeup arginfo_class_Swoole_Thread_Map_clean diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 0bb6abc0c2b..ebc05ffd21d 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -40,6 +40,9 @@ static zend_object_handlers swoole_thread_map_handlers; zend_class_entry *swoole_thread_arraylist_ce; static zend_object_handlers swoole_thread_arraylist_handlers; +zend_class_entry *swoole_thread_queue_ce; +static zend_object_handlers swoole_thread_queue_handlers; + zend_string *php_swoole_thread_serialize(zval *zdata); bool php_swoole_thread_unserialize(zend_string *data, zval *zv); @@ -137,9 +140,24 @@ struct ArrayItem { } }; -struct ZendArray { +struct Resource { RWLock lock_; uint32_t ref_count; + + Resource() : lock_(0) { + ref_count = 1; + } + + uint32_t add_ref() { + return ++ref_count; + } + + uint32_t del_ref() { + return --ref_count; + } +}; + +struct ZendArray : Resource { zend_array ht; static void item_dtor(zval *pDest) { @@ -147,8 +165,7 @@ struct ZendArray { delete item; } - ZendArray() : lock_(0) { - ref_count = 1; + ZendArray() : Resource() { zend_hash_init(&ht, 0, NULL, item_dtor, 1); } @@ -156,14 +173,6 @@ struct ZendArray { zend_hash_destroy(&ht); } - uint32_t add_ref() { - return ++ref_count; - } - - uint32_t del_ref() { - return --ref_count; - } - void clean() { lock_.lock(); zend_hash_clean(&ht); @@ -340,12 +349,61 @@ struct ThreadArrayListObject { zend_object std; }; +struct Queue : Resource { + std::queue queue; + + Queue() : Resource() {} + + ~Queue() { + clean(); + } + + void push(zval *zvalue) { + auto item = new ArrayItem(zvalue); + lock_.lock(); + queue.push(item); + lock_.unlock(); + } + + void pop(zval *return_value) { + lock_.lock(); + ArrayItem *item = queue.front(); + if (item) { + queue.pop(); + item->fetch(return_value); + } + lock_.unlock(); + } + + void count(zval *return_value) { + lock_.lock_rd(); + RETVAL_LONG(queue.size()); + lock_.unlock(); + } + + void clean() { + lock_.lock(); + while (!queue.empty()) { + ArrayItem *item = queue.front(); + delete item; + queue.pop(); + } + lock_.unlock(); + } +}; + +struct ThreadQueueObject { + Queue *queue; + zend_object std; +}; + static void php_swoole_thread_join(zend_object *object); thread_local zval thread_argv; static std::mutex thread_lock; static zend_long thread_resource_id = 0; -static std::unordered_map thread_resources; +static std::unordered_map thread_hashtables; +static std::unordered_map thread_queues; static sw_inline ThreadObject *php_swoole_thread_fetch_object(zend_object *obj) { return (ThreadObject *) ((char *) obj - swoole_thread_handlers.offset); @@ -373,6 +431,7 @@ static void php_swoole_thread_join(zend_object *object) { } } +// =======================================Map============================================== static sw_inline ThreadMapObject *thread_map_fetch_object(zend_object *obj) { return (ThreadMapObject *) ((char *) obj - swoole_thread_map_handlers.offset); } @@ -392,7 +451,7 @@ static void thread_map_free_object(zend_object *object) { if (mo->map) { thread_lock.lock(); if (mo->map->del_ref() == 0) { - thread_resources.erase(resource_id); + thread_hashtables.erase(resource_id); delete mo->map; mo->map = nullptr; } @@ -417,6 +476,7 @@ ThreadMapObject *thread_map_fetch_object_check(zval *zobject) { return map; } +// =======================================ArrayList============================================== static sw_inline ThreadArrayListObject *thread_arraylist_fetch_object(zend_object *obj) { return (ThreadArrayListObject *) ((char *) obj - swoole_thread_arraylist_handlers.offset); } @@ -436,7 +496,7 @@ static void thread_arraylist_free_object(zend_object *object) { if (ao->list) { thread_lock.lock(); if (ao->list->del_ref() == 0) { - thread_resources.erase(resource_id); + thread_hashtables.erase(resource_id); delete ao->list; ao->list = nullptr; } @@ -461,6 +521,51 @@ ThreadArrayListObject *thread_arraylist_fetch_object_check(zval *zobject) { return ao; } +// =======================================Queue============================================== +static sw_inline ThreadQueueObject *thread_queue_fetch_object(zend_object *obj) { + return (ThreadQueueObject *) ((char *) obj - swoole_thread_map_handlers.offset); +} + +static sw_inline zend_long thread_queue_get_resource_id(zend_object *obj) { + zval rv, *property = zend_read_property(swoole_thread_queue_ce, obj, ZEND_STRL("id"), 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static sw_inline zend_long thread_queue_get_resource_id(zval *zobject) { + return thread_queue_get_resource_id(Z_OBJ_P(zobject)); +} + +static void thread_queue_free_object(zend_object *object) { + zend_long resource_id = thread_queue_get_resource_id(object); + ThreadQueueObject *qo = thread_queue_fetch_object(object); + if (qo->queue) { + thread_lock.lock(); + if (qo->queue->del_ref() == 0) { + thread_hashtables.erase(resource_id); + delete qo->queue; + qo->queue = nullptr; + } + thread_lock.unlock(); + } + zend_object_std_dtor(object); +} + +static zend_object *thread_queue_create_object(zend_class_entry *ce) { + ThreadQueueObject *mo = (ThreadQueueObject *) zend_object_alloc(sizeof(ThreadQueueObject), ce); + zend_object_std_init(&mo->std, ce); + object_properties_init(&mo->std, ce); + mo->std.handlers = &swoole_thread_map_handlers; + return &mo->std; +} + +ThreadQueueObject *thread_queue_fetch_object_check(zval *zobject) { + ThreadQueueObject *qo = thread_queue_fetch_object(Z_OBJ_P(zobject)); + if (!qo->queue) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return qo; +} + SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_thread, __construct); static PHP_METHOD(swoole_thread, join); @@ -489,6 +594,13 @@ static PHP_METHOD(swoole_thread_arraylist, count); static PHP_METHOD(swoole_thread_arraylist, clean); static PHP_METHOD(swoole_thread_arraylist, __wakeup); +static PHP_METHOD(swoole_thread_queue, __construct); +static PHP_METHOD(swoole_thread_queue, push); +static PHP_METHOD(swoole_thread_queue, pop); +static PHP_METHOD(swoole_thread_queue, count); +static PHP_METHOD(swoole_thread_queue, clean); +static PHP_METHOD(swoole_thread_queue, __wakeup); + SW_EXTERN_C_END // clang-format off @@ -527,6 +639,16 @@ static const zend_function_entry swoole_thread_arraylist_methods[] = { PHP_ME(swoole_thread_arraylist, __wakeup, arginfo_class_Swoole_Thread_ArrayList___wakeup, ZEND_ACC_PUBLIC) PHP_FE_END }; + +static const zend_function_entry swoole_thread_queue_methods[] = { + PHP_ME(swoole_thread_queue, __construct, arginfo_class_Swoole_Thread_Queue___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, push, arginfo_class_Swoole_Thread_Queue_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, pop, arginfo_class_Swoole_Thread_Queue_pop, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, clean, arginfo_class_Swoole_Thread_Queue_clean, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, count, arginfo_class_Swoole_Thread_Queue_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, __wakeup, arginfo_class_Swoole_Thread_Queue___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; // clang-format on void php_swoole_thread_minit(int module_number) { @@ -561,6 +683,15 @@ void php_swoole_thread_minit(int module_number) { zend_class_implements(swoole_thread_arraylist_ce, 2, zend_ce_arrayaccess, zend_ce_countable); zend_declare_property_long(swoole_thread_arraylist_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + + SW_INIT_CLASS_ENTRY(swoole_thread_queue, "Swoole\\Thread\\Queue", nullptr, swoole_thread_queue_methods); + SW_SET_CLASS_CLONEABLE(swoole_thread_queue, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_queue, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_queue, thread_queue_create_object, thread_queue_free_object, ThreadQueueObject, std); + + zend_class_implements(swoole_thread_queue_ce, 1, zend_ce_countable); + zend_declare_property_long(swoole_thread_queue_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); } static PHP_METHOD(swoole_thread, __construct) {} @@ -719,7 +850,7 @@ static PHP_METHOD(swoole_thread_map, __construct) { thread_lock.lock(); zend_long resource_id = ++thread_resource_id; - thread_resources[resource_id] = mo->map; + thread_hashtables[resource_id] = mo->map; thread_lock.unlock(); zend_update_property_long(swoole_thread_map_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); @@ -799,8 +930,8 @@ static PHP_METHOD(swoole_thread_map, __wakeup) { zend_long resource_id = thread_map_get_resource_id(ZEND_THIS); thread_lock.lock(); - auto iter = thread_resources.find(resource_id); - if (iter != thread_resources.end()) { + auto iter = thread_hashtables.find(resource_id); + if (iter != thread_hashtables.end()) { ZendArray *map = iter->second; map->add_ref(); mo->map = map; @@ -821,7 +952,7 @@ static PHP_METHOD(swoole_thread_arraylist, __construct) { thread_lock.lock(); zend_long resource_id = ++thread_resource_id; - thread_resources[resource_id] = ao->list; + thread_hashtables[resource_id] = ao->list; thread_lock.unlock(); zend_update_property_long(swoole_thread_arraylist_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); @@ -886,8 +1017,8 @@ static PHP_METHOD(swoole_thread_arraylist, __wakeup) { zend_long resource_id = thread_arraylist_get_resource_id(ZEND_THIS); thread_lock.lock(); - auto iter = thread_resources.find(resource_id); - if (iter != thread_resources.end()) { + auto iter = thread_hashtables.find(resource_id); + if (iter != thread_hashtables.end()) { ZendArray *list = iter->second; list->add_ref(); mo->list = list; @@ -900,4 +1031,62 @@ static PHP_METHOD(swoole_thread_arraylist, __wakeup) { } } +static PHP_METHOD(swoole_thread_queue, __construct) { + auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); + qo->queue = new Queue(); + + thread_lock.lock(); + zend_long resource_id = ++thread_resource_id; + thread_queues[resource_id] = qo->queue; + thread_lock.unlock(); + + zend_update_property_long(swoole_thread_queue_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +} + +static PHP_METHOD(swoole_thread_queue, push) { + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + qo->queue->push(zvalue); +} + +static PHP_METHOD(swoole_thread_queue, pop) { + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + qo->queue->pop(return_value); +} + +static PHP_METHOD(swoole_thread_queue, count) { + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + qo->queue->count(return_value); +} + +static PHP_METHOD(swoole_thread_queue, clean) { + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + qo->queue->clean(); +} + +static PHP_METHOD(swoole_thread_queue, __wakeup) { + auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); + bool success = false; + zend_long resource_id = thread_queue_get_resource_id(ZEND_THIS); + + thread_lock.lock(); + auto iter = thread_queues.find(resource_id); + if (iter != thread_queues.end()) { + Queue *queue = iter->second; + queue->add_ref(); + qo->queue = queue; + success = true; + } + thread_lock.unlock(); + + if (!success) { + zend_throw_exception(swoole_exception_ce, "resource not found", -2); + } +} + #endif diff --git a/scripts/code-stats.sh b/scripts/code-stats.sh index d044e3cfd93..eed0fce4386 100755 --- a/scripts/code-stats.sh +++ b/scripts/code-stats.sh @@ -1,2 +1,7 @@ -#!/bin/sh +#!/bin/sh -e +__CURRENT__=$(pwd) +__DIR__=$(cd "$(dirname "$0")";pwd) + +# enter the dir +cd "${__DIR__}" cloc ../ --exclude-dir=thirdparty,Debug,CMakeFiles,build,CMakeFiles,.git From fe199d7d1ee36dc4050124eaaa8ed777dd0fa6b4 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 26 Mar 2024 12:20:39 +0800 Subject: [PATCH 016/103] Fix Thread\Queue --- examples/thread/thread_pool.php | 37 +++++++ ext-src/php_swoole.cc | 4 - ext-src/php_swoole_private.h | 3 - .../stubs/php_swoole_postgresql_coro.stub.php | 30 ----- .../php_swoole_postgresql_coro_arginfo.h | 78 ------------- ext-src/stubs/php_swoole_thread.stub.php | 4 +- ext-src/stubs/php_swoole_thread_arginfo.h | 11 +- ext-src/swoole_thread.cc | 104 +++++++++++++++--- 8 files changed, 133 insertions(+), 138 deletions(-) create mode 100644 examples/thread/thread_pool.php delete mode 100644 ext-src/stubs/php_swoole_postgresql_coro.stub.php delete mode 100644 ext-src/stubs/php_swoole_postgresql_coro_arginfo.h diff --git a/examples/thread/thread_pool.php b/examples/thread/thread_pool.php new file mode 100644 index 00000000000..5bd876d09fa --- /dev/null +++ b/examples/thread/thread_pool.php @@ -0,0 +1,37 @@ +push(base64_encode(random_bytes(16)), Queue::NOTIFY_ONE); + usleep(random_int(10000, 100000)); + } + $n = 4; + while ($n--) { + $queue->push('', Queue::NOTIFY_ONE); + } + for ($i = 0; $i < $c; $i++) { + $threads[$i]->join(); + } + var_dump($queue->count()); +} else { + $queue = $args[1]; + while (1) { + $job = $queue->pop(-1); + if (!$job) { + break; + } + var_dump($job); + } +} diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 1c08779119a..80acc013f38 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -727,8 +727,6 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_client_coro_minit(module_number); php_swoole_http_client_coro_minit(module_number); php_swoole_http2_client_coro_minit(module_number); - php_swoole_mysql_coro_minit(module_number); - php_swoole_redis_coro_minit(module_number); // server php_swoole_server_minit(module_number); php_swoole_server_port_minit(module_number); @@ -740,7 +738,6 @@ PHP_MINIT_FUNCTION(swoole) { php_swoole_redis_server_minit(module_number); php_swoole_name_resolver_minit(module_number); #ifdef SW_USE_PGSQL - php_swoole_postgresql_coro_minit(module_number); php_swoole_pgsql_minit(module_number); #endif #ifdef SW_USE_ODBC @@ -749,7 +746,6 @@ PHP_MINIT_FUNCTION(swoole) { #ifdef SW_USE_ORACLE php_swoole_oracle_minit(module_number); #endif - #ifdef SW_USE_SQLITE php_swoole_sqlite_minit(module_number); #endif diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index f5fb192eae2..ce6dcde3a11 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -245,10 +245,7 @@ void php_swoole_client_minit(int module_number); void php_swoole_client_coro_minit(int module_number); void php_swoole_http_client_coro_minit(int module_number); void php_swoole_http2_client_coro_minit(int module_number); -void php_swoole_mysql_coro_minit(int module_number); -void php_swoole_redis_coro_minit(int module_number); #ifdef SW_USE_PGSQL -void php_swoole_postgresql_coro_minit(int module_number); void php_swoole_pgsql_minit(int module_number); #endif #ifdef SW_USE_ODBC diff --git a/ext-src/stubs/php_swoole_postgresql_coro.stub.php b/ext-src/stubs/php_swoole_postgresql_coro.stub.php deleted file mode 100644 index 04b74e68151..00000000000 --- a/ext-src/stubs/php_swoole_postgresql_coro.stub.php +++ /dev/null @@ -1,30 +0,0 @@ - #include +#include #include "swoole_lock.h" @@ -141,10 +142,9 @@ struct ArrayItem { }; struct Resource { - RWLock lock_; uint32_t ref_count; - Resource() : lock_(0) { + Resource() { ref_count = 1; } @@ -158,6 +158,7 @@ struct Resource { }; struct ZendArray : Resource { + RWLock lock_; zend_array ht; static void item_dtor(zval *pDest) { @@ -165,7 +166,7 @@ struct ZendArray : Resource { delete item; } - ZendArray() : Resource() { + ZendArray() : Resource(), lock_(0) { zend_hash_init(&ht, 0, NULL, item_dtor, 1); } @@ -351,8 +352,16 @@ struct ThreadArrayListObject { struct Queue : Resource { std::queue queue; + std::mutex lock_; + std::condition_variable cv_; + + enum { + NOTIFY_NONE = 0, + NOTIFY_ONE = 1, + NOTIFY_ALL = 2, + }; - Queue() : Resource() {} + Queue() : Resource(), queue() {} ~Queue() { clean(); @@ -366,17 +375,55 @@ struct Queue : Resource { } void pop(zval *return_value) { + ArrayItem *item = nullptr; lock_.lock(); - ArrayItem *item = queue.front(); - if (item) { + if (!queue.empty()) { + item = queue.front(); queue.pop(); - item->fetch(return_value); } lock_.unlock(); + if (item) { + item->fetch(return_value); + } + } + + void push_notify(zval *zvalue, bool notify_all) { + auto item = new ArrayItem(zvalue); + std::unique_lock _lock(lock_); + queue.push(item); + if (notify_all) { + cv_.notify_all(); + } else { + cv_.notify_one(); + } + } + + void pop_wait(zval *return_value, double timeout) { + ArrayItem *item = nullptr; + std::unique_lock _lock(lock_); + SW_LOOP { + if (!queue.empty()) { + item = queue.front(); + queue.pop(); + break; + } else { + if (timeout > 0) { + if (cv_.wait_for(_lock, std::chrono::duration(timeout)) == std::cv_status::timeout) { + break; + } + } else { + cv_.wait(_lock); + } + } + } + _lock.unlock(); + if (item) { + item->fetch(return_value); + } } void count(zval *return_value) { - lock_.lock_rd(); + lock_.lock(); RETVAL_LONG(queue.size()); lock_.unlock(); } @@ -541,7 +588,7 @@ static void thread_queue_free_object(zend_object *object) { if (qo->queue) { thread_lock.lock(); if (qo->queue->del_ref() == 0) { - thread_hashtables.erase(resource_id); + thread_queues.erase(resource_id); delete qo->queue; qo->queue = nullptr; } @@ -551,11 +598,11 @@ static void thread_queue_free_object(zend_object *object) { } static zend_object *thread_queue_create_object(zend_class_entry *ce) { - ThreadQueueObject *mo = (ThreadQueueObject *) zend_object_alloc(sizeof(ThreadQueueObject), ce); - zend_object_std_init(&mo->std, ce); - object_properties_init(&mo->std, ce); - mo->std.handlers = &swoole_thread_map_handlers; - return &mo->std; + ThreadQueueObject *qo = (ThreadQueueObject *) zend_object_alloc(sizeof(ThreadQueueObject), ce); + zend_object_std_init(&qo->std, ce); + object_properties_init(&qo->std, ce); + qo->std.handlers = &swoole_thread_queue_handlers; + return &qo->std; } ThreadQueueObject *thread_queue_fetch_object_check(zval *zobject) { @@ -692,6 +739,9 @@ void php_swoole_thread_minit(int module_number) { zend_class_implements(swoole_thread_queue_ce, 1, zend_ce_countable); zend_declare_property_long(swoole_thread_queue_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + + zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ONE"), Queue::NOTIFY_ONE); + zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ALL"), Queue::NOTIFY_ALL); } static PHP_METHOD(swoole_thread, __construct) {} @@ -1045,18 +1095,36 @@ static PHP_METHOD(swoole_thread_queue, __construct) { static PHP_METHOD(swoole_thread_queue, push) { zval *zvalue; + zend_long notify_which = 0; - ZEND_PARSE_PARAMETERS_START(1, 1) + ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ZVAL(zvalue) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(notify_which) ZEND_PARSE_PARAMETERS_END(); auto qo = thread_queue_fetch_object_check(ZEND_THIS); - qo->queue->push(zvalue); + if (notify_which > 0) { + qo->queue->push_notify(zvalue, notify_which == Queue::NOTIFY_ALL); + } else { + qo->queue->push(zvalue); + } } static PHP_METHOD(swoole_thread_queue, pop) { - auto qo = thread_queue_fetch_object_check(ZEND_THIS); - qo->queue->pop(return_value); + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END(); + + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + if (timeout == 0) { + qo->queue->pop(return_value); + } else { + qo->queue->pop_wait(return_value, timeout); + } } static PHP_METHOD(swoole_thread_queue, count) { From 112d692b649e503f4f158a9239f69ced9e5ae08a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 26 Mar 2024 16:09:01 +0800 Subject: [PATCH 017/103] fix mem leak --- ext-src/swoole_thread.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 076541db773..eb11d10137f 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -384,6 +384,7 @@ struct Queue : Resource { lock_.unlock(); if (item) { item->fetch(return_value); + delete item; } } @@ -419,6 +420,7 @@ struct Queue : Resource { _lock.unlock(); if (item) { item->fetch(return_value); + delete item; } } From 449adf93f8a9071cf483dc515206945e92e87b93 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 26 Mar 2024 16:16:49 +0800 Subject: [PATCH 018/103] optimize, reduce memory copy --- ext-src/swoole_thread.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index eb11d10137f..b3e7f3ea8c4 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -111,7 +111,7 @@ struct ArrayItem { RETVAL_FALSE; break; case IS_STRING: - RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); + RETVAL_STR_COPY(value.str); break; case IS_SERIALIZED_OBJECT: php_swoole_thread_unserialize(value.serialized_object, return_value); From 88797a5a7c4f33e6f1f525487e4e901e27f123a3 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 26 Mar 2024 16:33:00 +0800 Subject: [PATCH 019/103] Revert "optimize, reduce memory copy" This reverts commit 449adf93f8a9071cf483dc515206945e92e87b93. --- ext-src/swoole_thread.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index b3e7f3ea8c4..eb11d10137f 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -111,7 +111,7 @@ struct ArrayItem { RETVAL_FALSE; break; case IS_STRING: - RETVAL_STR_COPY(value.str); + RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); break; case IS_SERIALIZED_OBJECT: php_swoole_thread_unserialize(value.serialized_object, return_value); From b6a2c22d84a72c0f8211405aab7f0123dee82267 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 26 Mar 2024 18:30:14 +0800 Subject: [PATCH 020/103] optimize code, remove 8.0 supports --- .github/workflows/conflict-exts.yml | 2 +- .github/workflows/framework.yml | 2 +- .github/workflows/test-linux.yml | 2 +- ext-src/php_swoole_private.h | 10 ++ ext-src/swoole_lock.cc | 19 +--- ext-src/swoole_thread.cc | 160 +++++++++++----------------- include/swoole_lock.h | 2 - 7 files changed, 81 insertions(+), 116 deletions(-) diff --git a/.github/workflows/conflict-exts.yml b/.github/workflows/conflict-exts.yml index 7ddae7bbcaf..0ecb394afa6 100644 --- a/.github/workflows/conflict-exts.yml +++ b/.github/workflows/conflict-exts.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.0', '8.1', '8.2', '8.3'] + php: ['8.1', '8.2', '8.3'] name: PHP ${{ matrix.php }} - Swoole diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 1a591640c4e..d5346886186 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: [ '8.0', '8.1', '8.2', '8.3' ] + php-version: [ '8.1', '8.2', '8.3' ] framework: [ 'Laravel Octane', 'Hyperf', 'Simps', 'imi' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} steps: diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 85cdd5ab65e..f7ed0167007 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.0', '8.1', '8.2', '8.3'] + php: ['8.1', '8.2', '8.3'] steps: - uses: actions/checkout@v4 - name: Setup PHP diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index ce6dcde3a11..6d733e554c5 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -363,6 +363,16 @@ typedef const char error_filename_t; #else typedef zend_string error_filename_t; #endif + +#ifdef SW_THREAD +typedef uint32_t ThreadResourceId; +struct ThreadResource; + +ThreadResourceId php_swoole_thread_resource_insert(ThreadResource *res); +bool php_swoole_thread_resource_free(ThreadResourceId resource_id, ThreadResource *res); +ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id); +#endif + //----------------------------------Zval API------------------------------------ // Deprecated: do not use it anymore diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 2c8961448c0..b4c993ff254 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -111,9 +111,7 @@ void php_swoole_lock_minit(int module_number) { SW_SET_CLASS_CUSTOM_OBJECT( swoole_lock, php_swoole_lock_create_object, php_swoole_lock_free_object, LockObject, std); - zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("FILELOCK"), Lock::FILE_LOCK); zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("MUTEX"), Lock::MUTEX); - zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("SEM"), Lock::SEM); #ifdef HAVE_RWLOCK zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("RWLOCK"), Lock::RW_LOCK); #endif @@ -122,9 +120,7 @@ void php_swoole_lock_minit(int module_number) { #endif zend_declare_property_long(swoole_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_FILELOCK", Lock::FILE_LOCK); SW_REGISTER_LONG_CONSTANT("SWOOLE_MUTEX", Lock::MUTEX); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SEM", Lock::SEM); #ifdef HAVE_RWLOCK SW_REGISTER_LONG_CONSTANT("SWOOLE_RWLOCK", Lock::RW_LOCK); #endif @@ -141,20 +137,13 @@ static PHP_METHOD(swoole_lock, __construct) { } zend_long type = Lock::MUTEX; - char *filelock; - size_t filelock_len = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ls", &type, &filelock, &filelock_len) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(type) + ZEND_PARSE_PARAMETERS_END(); switch (type) { - case Lock::FILE_LOCK: - case Lock::SEM: - zend_throw_exception( - swoole_exception_ce, "FileLock and SemLock is no longer supported, please use mutex lock", errno); - RETURN_FALSE; - break; #ifdef HAVE_SPINLOCK case Lock::SPIN_LOCK: lock = new SpinLock(1); diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index eb11d10137f..e65928374b9 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -47,6 +47,9 @@ static zend_object_handlers swoole_thread_queue_handlers; zend_string *php_swoole_thread_serialize(zval *zdata); bool php_swoole_thread_unserialize(zend_string *data, zval *zv); +#define EMSG_NO_RESOURCE "resource not found" +#define ECODE_NO_RESOURCE -2 + #define IS_SERIALIZED_OBJECT 99 struct ArrayItem { @@ -141,10 +144,10 @@ struct ArrayItem { } }; -struct Resource { +struct ThreadResource { uint32_t ref_count; - Resource() { + ThreadResource() { ref_count = 1; } @@ -157,7 +160,7 @@ struct Resource { } }; -struct ZendArray : Resource { +struct ZendArray : ThreadResource { RWLock lock_; zend_array ht; @@ -166,7 +169,7 @@ struct ZendArray : Resource { delete item; } - ZendArray() : Resource(), lock_(0) { + ZendArray() : ThreadResource(), lock_(0) { zend_hash_init(&ht, 0, NULL, item_dtor, 1); } @@ -350,7 +353,7 @@ struct ThreadArrayListObject { zend_object std; }; -struct Queue : Resource { +struct Queue : ThreadResource { std::queue queue; std::mutex lock_; std::condition_variable cv_; @@ -361,7 +364,7 @@ struct Queue : Resource { NOTIFY_ALL = 2, }; - Queue() : Resource(), queue() {} + Queue() : ThreadResource(), queue() {} ~Queue() { clean(); @@ -449,10 +452,38 @@ struct ThreadQueueObject { static void php_swoole_thread_join(zend_object *object); thread_local zval thread_argv; -static std::mutex thread_lock; +std::mutex thread_lock; + static zend_long thread_resource_id = 0; -static std::unordered_map thread_hashtables; -static std::unordered_map thread_queues; +static std::unordered_map thread_resources; + +ThreadResourceId php_swoole_thread_resource_insert(ThreadResource *res) { + std::unique_lock _lock(thread_lock); + zend_long resource_id = ++thread_resource_id; + thread_resources[resource_id] = res; + return resource_id; +} + +ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id) { + ThreadResource *res = nullptr; + std::unique_lock _lock(thread_lock); + auto iter = thread_resources.find(resource_id); + if (iter != thread_resources.end()) { + res = iter->second; + res->add_ref(); + } + return res; +} + +bool php_swoole_thread_resource_free(ThreadResourceId resource_id, ThreadResource *res) { + std::unique_lock _lock(thread_lock); + if (res->del_ref() == 0) { + thread_resources.erase(resource_id); + return true; + } else { + return false; + } +} static sw_inline ThreadObject *php_swoole_thread_fetch_object(zend_object *obj) { return (ThreadObject *) ((char *) obj - swoole_thread_handlers.offset); @@ -497,14 +528,9 @@ static sw_inline zend_long thread_map_get_resource_id(zval *zobject) { static void thread_map_free_object(zend_object *object) { zend_long resource_id = thread_map_get_resource_id(object); ThreadMapObject *mo = thread_map_fetch_object(object); - if (mo->map) { - thread_lock.lock(); - if (mo->map->del_ref() == 0) { - thread_hashtables.erase(resource_id); - delete mo->map; - mo->map = nullptr; - } - thread_lock.unlock(); + if (mo->map && php_swoole_thread_resource_free(resource_id, mo->map)) { + delete mo->map; + mo->map = nullptr; } zend_object_std_dtor(object); } @@ -542,14 +568,9 @@ static sw_inline zend_long thread_arraylist_get_resource_id(zval *zobject) { static void thread_arraylist_free_object(zend_object *object) { zend_long resource_id = thread_arraylist_get_resource_id(object); ThreadArrayListObject *ao = thread_arraylist_fetch_object(object); - if (ao->list) { - thread_lock.lock(); - if (ao->list->del_ref() == 0) { - thread_hashtables.erase(resource_id); - delete ao->list; - ao->list = nullptr; - } - thread_lock.unlock(); + if (ao->list && php_swoole_thread_resource_free(resource_id, ao->list)) { + delete ao->list; + ao->list = nullptr; } zend_object_std_dtor(object); } @@ -587,14 +608,9 @@ static sw_inline zend_long thread_queue_get_resource_id(zval *zobject) { static void thread_queue_free_object(zend_object *object) { zend_long resource_id = thread_queue_get_resource_id(object); ThreadQueueObject *qo = thread_queue_fetch_object(object); - if (qo->queue) { - thread_lock.lock(); - if (qo->queue->del_ref() == 0) { - thread_queues.erase(resource_id); - delete qo->queue; - qo->queue = nullptr; - } - thread_lock.unlock(); + if (qo->queue && php_swoole_thread_resource_free(resource_id, qo->queue)) { + delete qo->queue; + qo->queue = nullptr; } zend_object_std_dtor(object); } @@ -708,7 +724,7 @@ void php_swoole_thread_minit(int module_number) { SW_SET_CLASS_CUSTOM_OBJECT( swoole_thread, php_swoole_thread_create_object, php_swoole_thread_free_object, ThreadObject, std); - zend_declare_property_long(swoole_thread_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_thread_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); zend_declare_class_constant_long( swoole_thread_ce, ZEND_STRL("HARDWARE_CONCURRENCY"), std::thread::hardware_concurrency()); @@ -731,7 +747,7 @@ void php_swoole_thread_minit(int module_number) { std); zend_class_implements(swoole_thread_arraylist_ce, 2, zend_ce_arrayaccess, zend_ce_countable); - zend_declare_property_long(swoole_thread_arraylist_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_thread_arraylist_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); SW_INIT_CLASS_ENTRY(swoole_thread_queue, "Swoole\\Thread\\Queue", nullptr, swoole_thread_queue_methods); SW_SET_CLASS_CLONEABLE(swoole_thread_queue, sw_zend_class_clone_deny); @@ -740,7 +756,7 @@ void php_swoole_thread_minit(int module_number) { swoole_thread_queue, thread_queue_create_object, thread_queue_free_object, ThreadQueueObject, std); zend_class_implements(swoole_thread_queue_ce, 1, zend_ce_countable); - zend_declare_property_long(swoole_thread_queue_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_thread_queue_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ONE"), Queue::NOTIFY_ONE); zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ALL"), Queue::NOTIFY_ALL); @@ -899,12 +915,7 @@ static PHP_METHOD(swoole_thread, exec) { static PHP_METHOD(swoole_thread_map, __construct) { auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); mo->map = new ZendArray(); - - thread_lock.lock(); - zend_long resource_id = ++thread_resource_id; - thread_hashtables[resource_id] = mo->map; - thread_lock.unlock(); - + auto resource_id = php_swoole_thread_resource_insert(mo->map); zend_update_property_long(swoole_thread_map_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); } @@ -978,21 +989,10 @@ static PHP_METHOD(swoole_thread_map, clean) { static PHP_METHOD(swoole_thread_map, __wakeup) { auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); - bool success = false; zend_long resource_id = thread_map_get_resource_id(ZEND_THIS); - - thread_lock.lock(); - auto iter = thread_hashtables.find(resource_id); - if (iter != thread_hashtables.end()) { - ZendArray *map = iter->second; - map->add_ref(); - mo->map = map; - success = true; - } - thread_lock.unlock(); - - if (!success) { - zend_throw_exception(swoole_exception_ce, "resource not found", -2); + mo->map = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!mo->map) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); } } @@ -1001,12 +1001,7 @@ static PHP_METHOD(swoole_thread_arraylist, __construct) { auto ao = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); ao->list = new ZendArray(); - - thread_lock.lock(); - zend_long resource_id = ++thread_resource_id; - thread_hashtables[resource_id] = ao->list; - thread_lock.unlock(); - + auto resource_id = php_swoole_thread_resource_insert(ao->list); zend_update_property_long(swoole_thread_arraylist_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); } @@ -1065,33 +1060,17 @@ static PHP_METHOD(swoole_thread_arraylist, clean) { static PHP_METHOD(swoole_thread_arraylist, __wakeup) { auto mo = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); - bool success = false; zend_long resource_id = thread_arraylist_get_resource_id(ZEND_THIS); - - thread_lock.lock(); - auto iter = thread_hashtables.find(resource_id); - if (iter != thread_hashtables.end()) { - ZendArray *list = iter->second; - list->add_ref(); - mo->list = list; - success = true; - } - thread_lock.unlock(); - - if (!success) { - zend_throw_exception(swoole_exception_ce, "resource not found", -2); + mo->list = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!mo->list) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); } } static PHP_METHOD(swoole_thread_queue, __construct) { auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); qo->queue = new Queue(); - - thread_lock.lock(); - zend_long resource_id = ++thread_resource_id; - thread_queues[resource_id] = qo->queue; - thread_lock.unlock(); - + auto resource_id = php_swoole_thread_resource_insert(qo->queue); zend_update_property_long(swoole_thread_queue_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); } @@ -1141,21 +1120,10 @@ static PHP_METHOD(swoole_thread_queue, clean) { static PHP_METHOD(swoole_thread_queue, __wakeup) { auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); - bool success = false; zend_long resource_id = thread_queue_get_resource_id(ZEND_THIS); - - thread_lock.lock(); - auto iter = thread_queues.find(resource_id); - if (iter != thread_queues.end()) { - Queue *queue = iter->second; - queue->add_ref(); - qo->queue = queue; - success = true; - } - thread_lock.unlock(); - - if (!success) { - zend_throw_exception(swoole_exception_ce, "resource not found", -2); + qo->queue = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!qo->queue) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); } } diff --git a/include/swoole_lock.h b/include/swoole_lock.h index b5b851902a1..870ec02a80f 100644 --- a/include/swoole_lock.h +++ b/include/swoole_lock.h @@ -29,9 +29,7 @@ class Lock { enum Type { NONE, RW_LOCK = 1, - FILE_LOCK = 2, MUTEX = 3, - SEM = 4, SPIN_LOCK = 5, ATOMIC_LOCK = 6, }; From 684e4192070c89b377b360e69c6d1ecaf23adc37 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 26 Mar 2024 19:59:51 +0800 Subject: [PATCH 021/103] refactor atomic/lock, support thread --- examples/thread/atomic.php | 28 +++++ examples/thread/lock.php | 19 ++++ ext-src/php_swoole_cxx.h | 12 ++ ext-src/php_swoole_thread.h | 42 +++++++ ext-src/stubs/php_swoole_atomic.stub.php | 6 + ext-src/stubs/php_swoole_atomic_arginfo.h | 11 +- ext-src/stubs/php_swoole_lock.stub.php | 3 + ext-src/stubs/php_swoole_lock_arginfo.h | 7 +- ext-src/swoole_atomic.cc | 132 +++++++++++++++++++++- ext-src/swoole_lock.cc | 77 +++++++++++-- ext-src/swoole_thread.cc | 22 +--- 11 files changed, 323 insertions(+), 36 deletions(-) create mode 100644 examples/thread/atomic.php create mode 100644 examples/thread/lock.php create mode 100644 ext-src/php_swoole_thread.h diff --git a/examples/thread/atomic.php b/examples/thread/atomic.php new file mode 100644 index 00000000000..f4a60e8a1a2 --- /dev/null +++ b/examples/thread/atomic.php @@ -0,0 +1,28 @@ +join(); + } + var_dump($a1->get(), $a2->get()); +} else { + $a1 = $args[1]; + $a2 = $args[2]; + + $a1->add(3); + $a2->add(7); +} diff --git a/examples/thread/lock.php b/examples/thread/lock.php new file mode 100644 index 00000000000..926714207d4 --- /dev/null +++ b/examples/thread/lock.php @@ -0,0 +1,19 @@ +lock(); + $thread = Thread::exec(__FILE__, $lock); + $lock->lock(); + echo "main thread\n"; + $thread->join(); +} else { + $lock = $args[0]; + sleep(1); + $lock->unlock(); +} diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 68603c716a6..db0b1af3245 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -641,6 +641,18 @@ static inline void array_unset(zval *arg, const char *key, size_t l_key) { zend_hash_str_del(Z_ARRVAL_P(arg), key, l_key); } +static inline zend_long read_property_long(zval *obj, const char *key, size_t l_key) { + static zval rv; + zval *property = zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), key, l_key, 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static inline zend_long read_property_long(zend_object *obj, const char *key, size_t l_key) { + static zval rv; + zval *property = zend_read_property(obj->ce, obj, key, l_key, 1, &rv); + return property ? zval_get_long(property) : 0; +} + //-----------------------------------namespace end-------------------------------------------- } // namespace zend diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h new file mode 100644 index 00000000000..1b915755e2e --- /dev/null +++ b/ext-src/php_swoole_thread.h @@ -0,0 +1,42 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Twosee | + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "php_swoole_cxx.h" + +#define EMSG_NO_RESOURCE "resource not found" +#define ECODE_NO_RESOURCE -2 + +#define IS_SERIALIZED_OBJECT 99 + +struct ThreadResource { + uint32_t ref_count; + + ThreadResource() { + ref_count = 1; + } + + uint32_t add_ref() { + return ++ref_count; + } + + uint32_t del_ref() { + return --ref_count; + } +}; + diff --git a/ext-src/stubs/php_swoole_atomic.stub.php b/ext-src/stubs/php_swoole_atomic.stub.php index adfd74b0f5f..71bb5eec9d4 100644 --- a/ext-src/stubs/php_swoole_atomic.stub.php +++ b/ext-src/stubs/php_swoole_atomic.stub.php @@ -9,6 +9,9 @@ public function set(int $value): void {} public function cmpset(int $cmp_value, int $new_value): bool {} public function wait(float $timeout = 1.0): bool {} public function wakeup(int $count = 1): bool {} + #ifdef SW_THREAD + public function __wakeup(): void {} + #endif } } @@ -20,5 +23,8 @@ public function sub(int $sub_value = 1): int {} public function get(): int {} public function set(int $value): void {} public function cmpset(int $cmp_value, int $new_value): bool {} + #ifdef SW_THREAD + public function __wakeup(): void {} + #endif } } diff --git a/ext-src/stubs/php_swoole_atomic_arginfo.h b/ext-src/stubs/php_swoole_atomic_arginfo.h index fbf4d01aa4d..62fb6bf7dd1 100644 --- a/ext-src/stubs/php_swoole_atomic_arginfo.h +++ b/ext-src/stubs/php_swoole_atomic_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7c83f8fbe7fd48ac2c7a2756a4e33a9edd666e42 */ + * Stub hash: 081724c8ea467fa6daf08906f0d0fcb7603795ed */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Atomic___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "0") @@ -33,6 +33,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Atomic_wakeup, 0, 0 ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1") ZEND_END_ARG_INFO() +#if defined(SW_THREAD) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Atomic___wakeup, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() +#endif + #define arginfo_class_Swoole_Atomic_Long___construct arginfo_class_Swoole_Atomic___construct #define arginfo_class_Swoole_Atomic_Long_add arginfo_class_Swoole_Atomic_add @@ -44,3 +49,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Atomic_Long_set arginfo_class_Swoole_Atomic_set #define arginfo_class_Swoole_Atomic_Long_cmpset arginfo_class_Swoole_Atomic_cmpset + +#if defined(SW_THREAD) +#define arginfo_class_Swoole_Atomic_Long___wakeup arginfo_class_Swoole_Atomic___wakeup +#endif diff --git a/ext-src/stubs/php_swoole_lock.stub.php b/ext-src/stubs/php_swoole_lock.stub.php index 9e9f0406275..7c23e7a8532 100644 --- a/ext-src/stubs/php_swoole_lock.stub.php +++ b/ext-src/stubs/php_swoole_lock.stub.php @@ -10,5 +10,8 @@ public function lock_read(): bool {} public function trylock_read(): bool {} public function unlock(): bool {} public function destroy(): void {} + #ifdef SW_THREAD + public function __wakeup(): void {} + #endif } } diff --git a/ext-src/stubs/php_swoole_lock_arginfo.h b/ext-src/stubs/php_swoole_lock_arginfo.h index 90c5d476b3a..990c0ce5aba 100644 --- a/ext-src/stubs/php_swoole_lock_arginfo.h +++ b/ext-src/stubs/php_swoole_lock_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8cacb39e88a0467e15371d94a0efc62534bb383a */ + * Stub hash: ba4e97afe28b9cd1d6f0de33c10827fc3382c952 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Lock___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, "SWOOLE_MUTEX") @@ -26,3 +26,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Lock_destroy, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() + +#if defined(SW_THREAD) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Lock___wakeup, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() +#endif diff --git a/ext-src/swoole_atomic.cc b/ext-src/swoole_atomic.cc index e702a2b3433..c36a5eafbd7 100644 --- a/ext-src/swoole_atomic.cc +++ b/ext-src/swoole_atomic.cc @@ -14,7 +14,8 @@ +----------------------------------------------------------------------+ */ -#include "php_swoole_private.h" +#include "php_swoole_cxx.h" +#include "php_swoole_thread.h" #include "swoole_memory.h" BEGIN_EXTERN_C() @@ -89,8 +90,23 @@ static zend_object_handlers swoole_atomic_handlers; zend_class_entry *swoole_atomic_long_ce; static zend_object_handlers swoole_atomic_long_handlers; +#ifdef SW_THREAD +struct AtomicResource: public ThreadResource { + sw_atomic_t *ptr_; + AtomicResource(): ThreadResource() { + ptr_ = new sw_atomic_t; + } + ~AtomicResource() { + delete ptr_; + } +}; +#endif + struct AtomicObject { sw_atomic_t *ptr; +#ifdef SW_THREAD + AtomicResource *res; +#endif zend_object std; }; @@ -107,7 +123,17 @@ void php_swoole_atomic_set_ptr(zval *zobject, sw_atomic_t *ptr) { } static void php_swoole_atomic_free_object(zend_object *object) { +#ifdef SW_THREAD + AtomicObject *o = php_swoole_atomic_fetch_object(object); + zend_long resource_id = zend::read_property_long(object, ZEND_STRL("id")); + if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { + delete o->res; + o->res = nullptr; + o->ptr = nullptr; + } +#else sw_mem_pool()->free((void *) php_swoole_atomic_fetch_object(object)->ptr); +#endif zend_object_std_dtor(object); } @@ -120,16 +146,34 @@ static zend_object *php_swoole_atomic_create_object(zend_class_entry *ce) { zend_object_std_init(&atomic->std, ce); object_properties_init(&atomic->std, ce); atomic->std.handlers = &swoole_atomic_handlers; + +#ifndef SW_THREAD atomic->ptr = (sw_atomic_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_t)); if (atomic->ptr == nullptr) { zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); } +#endif return &atomic->std; } +#ifdef SW_THREAD +struct AtomicLongResource: public ThreadResource { + sw_atomic_long_t *ptr_; + AtomicLongResource(): ThreadResource() { + ptr_ = new sw_atomic_long_t; + } + ~AtomicLongResource() { + delete ptr_; + } +}; +#endif + struct AtomicLongObject { sw_atomic_long_t *ptr; +#ifdef SW_THREAD + AtomicLongResource *res; +#endif zend_object std; }; @@ -146,7 +190,18 @@ void php_swoole_atomic_long_set_ptr(zval *zobject, sw_atomic_long_t *ptr) { } static void php_swoole_atomic_long_free_object(zend_object *object) { +#ifdef SW_THREAD + AtomicLongObject *o = php_swoole_atomic_long_fetch_object(object); + zend_long resource_id = zend::read_property_long(object, ZEND_STRL("id")); + if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { + delete o->res; + o->res = nullptr; + o->ptr = nullptr; + } +#else sw_mem_pool()->free((void *) php_swoole_atomic_long_fetch_object(object)->ptr); +#endif + zend_object_std_dtor(object); } @@ -160,10 +215,12 @@ static zend_object *php_swoole_atomic_long_create_object(zend_class_entry *ce) { object_properties_init(&atomic_long->std, ce); atomic_long->std.handlers = &swoole_atomic_long_handlers; +#ifndef SW_THREAD atomic_long->ptr = (sw_atomic_long_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_long_t)); if (atomic_long->ptr == nullptr) { zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); } +#endif return &atomic_long->std; } @@ -177,6 +234,9 @@ static PHP_METHOD(swoole_atomic, set); static PHP_METHOD(swoole_atomic, cmpset); static PHP_METHOD(swoole_atomic, wait); static PHP_METHOD(swoole_atomic, wakeup); +#ifdef SW_THREAD +static PHP_METHOD(swoole_atomic, __wakeup); +#endif static PHP_METHOD(swoole_atomic_long, __construct); static PHP_METHOD(swoole_atomic_long, add); @@ -184,6 +244,9 @@ static PHP_METHOD(swoole_atomic_long, sub); static PHP_METHOD(swoole_atomic_long, get); static PHP_METHOD(swoole_atomic_long, set); static PHP_METHOD(swoole_atomic_long, cmpset); +#ifdef SW_THREAD +static PHP_METHOD(swoole_atomic_long, __wakeup); +#endif SW_EXTERN_C_END // clang-format off @@ -198,6 +261,9 @@ static const zend_function_entry swoole_atomic_methods[] = PHP_ME(swoole_atomic, wait, arginfo_class_Swoole_Atomic_wait, ZEND_ACC_PUBLIC) PHP_ME(swoole_atomic, wakeup, arginfo_class_Swoole_Atomic_wakeup, ZEND_ACC_PUBLIC) PHP_ME(swoole_atomic, cmpset, arginfo_class_Swoole_Atomic_cmpset, ZEND_ACC_PUBLIC) +#ifdef SW_THREAD + PHP_ME(swoole_atomic, __wakeup, arginfo_class_Swoole_Atomic___wakeup, ZEND_ACC_PUBLIC) +#endif PHP_FE_END }; @@ -209,6 +275,9 @@ static const zend_function_entry swoole_atomic_long_methods[] = PHP_ME(swoole_atomic_long, get, arginfo_class_Swoole_Atomic_Long_get, ZEND_ACC_PUBLIC) PHP_ME(swoole_atomic_long, set, arginfo_class_Swoole_Atomic_Long_set, ZEND_ACC_PUBLIC) PHP_ME(swoole_atomic_long, cmpset, arginfo_class_Swoole_Atomic_Long_cmpset, ZEND_ACC_PUBLIC) +#ifdef SW_THREAD + PHP_ME(swoole_atomic_long, __wakeup, arginfo_class_Swoole_Atomic_Long___wakeup, ZEND_ACC_PUBLIC) +#endif PHP_FE_END }; @@ -216,14 +285,18 @@ static const zend_function_entry swoole_atomic_long_methods[] = void php_swoole_atomic_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_atomic, "Swoole\\Atomic", nullptr, swoole_atomic_methods); +#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic); +#endif SW_SET_CLASS_CLONEABLE(swoole_atomic, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( swoole_atomic, php_swoole_atomic_create_object, php_swoole_atomic_free_object, AtomicObject, std); SW_INIT_CLASS_ENTRY(swoole_atomic_long, "Swoole\\Atomic\\Long", nullptr, swoole_atomic_long_methods); +#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic_long); +#endif SW_SET_CLASS_CLONEABLE(swoole_atomic_long, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic_long, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_atomic_long, @@ -234,7 +307,11 @@ void php_swoole_atomic_minit(int module_number) { } PHP_METHOD(swoole_atomic, __construct) { - sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS); + auto o = php_swoole_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (o->ptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } zend_long value = 0; ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) @@ -242,7 +319,14 @@ PHP_METHOD(swoole_atomic, __construct) { Z_PARAM_LONG(value) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - *atomic = (sw_atomic_t) value; +#ifdef SW_THREAD + o->res = new AtomicResource(); + auto resource_id = php_swoole_thread_resource_insert(o->res); + zend_update_property_long(swoole_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); + o->ptr = o->res->ptr_; +#endif + + *o->ptr = (sw_atomic_t) value; } PHP_METHOD(swoole_atomic, add) { @@ -329,8 +413,25 @@ PHP_METHOD(swoole_atomic, wakeup) { #endif } +#ifdef SW_THREAD +static PHP_METHOD(swoole_atomic, __wakeup) { + auto o = php_swoole_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = zend::read_property_long(ZEND_THIS, ZEND_STRL("id")); + o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!o->res) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } + o->ptr = o->res->ptr_; +} +#endif + PHP_METHOD(swoole_atomic_long, __construct) { - sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(ZEND_THIS); + auto o = php_swoole_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (o->ptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } zend_long value = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -338,8 +439,14 @@ PHP_METHOD(swoole_atomic_long, __construct) { Z_PARAM_LONG(value) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - *atomic_long = (sw_atomic_long_t) value; - RETURN_TRUE; +#ifdef SW_THREAD + o->res = new AtomicLongResource(); + auto resource_id = php_swoole_thread_resource_insert(o->res); + zend_update_property_long(swoole_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); + o->ptr = o->res->ptr_; +#endif + + *o->ptr = (sw_atomic_long_t) value; } PHP_METHOD(swoole_atomic_long, add) { @@ -393,3 +500,16 @@ PHP_METHOD(swoole_atomic_long, cmpset) { RETURN_BOOL(sw_atomic_cmp_set(atomic_long, (sw_atomic_long_t) cmp_value, (sw_atomic_long_t) set_value)); } + +#ifdef SW_THREAD +static PHP_METHOD(swoole_atomic_long, __wakeup) { + auto o = php_swoole_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = zend::read_property_long(ZEND_THIS, ZEND_STRL("id")); + o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!o->res) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } + o->ptr = o->res->ptr_; +} +#endif diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index b4c993ff254..b5303ec5bf1 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -15,6 +15,7 @@ */ #include "php_swoole_private.h" +#include "php_swoole_thread.h" #include "swoole_memory.h" #include "swoole_lock.h" @@ -34,8 +35,23 @@ using swoole::RWLock; static zend_class_entry *swoole_lock_ce; static zend_object_handlers swoole_lock_handlers; +#ifdef SW_THREAD +struct LockResource: public ThreadResource { + Lock *lock_; + LockResource(Lock *lock): ThreadResource() { + lock_ = lock; + } + ~LockResource() { + delete lock_; + } +}; +#endif + struct LockObject { Lock *lock; +#ifdef SW_THREAD + LockResource *lock_res; +#endif zend_object std; }; @@ -50,7 +66,7 @@ static Lock *php_swoole_lock_get_ptr(zval *zobject) { static Lock *php_swoole_lock_get_and_check_ptr(zval *zobject) { Lock *lock = php_swoole_lock_get_ptr(zobject); if (!lock) { - php_swoole_fatal_error(E_ERROR, "you must call Lock constructor first"); + php_swoole_fatal_error(E_ERROR, "must call constructor first"); } return lock; } @@ -61,9 +77,18 @@ void php_swoole_lock_set_ptr(zval *zobject, Lock *ptr) { static void php_swoole_lock_free_object(zend_object *object) { LockObject *o = php_swoole_lock_fetch_object(object); +#ifdef SW_THREAD + zend_long resource_id = zend::read_property_long(object, ZEND_STRL("id")); + if (o->lock_res && php_swoole_thread_resource_free(resource_id, o->lock_res)) { + delete o->lock_res; + o->lock_res = nullptr; + o->lock = nullptr; + } +#else if (o->lock) { delete o->lock; } +#endif zend_object_std_dtor(object); } @@ -85,6 +110,9 @@ static PHP_METHOD(swoole_lock, lock_read); static PHP_METHOD(swoole_lock, trylock_read); static PHP_METHOD(swoole_lock, unlock); static PHP_METHOD(swoole_lock, destroy); +#ifdef SW_THREAD +static PHP_METHOD(swoole_lock, __wakeup); +#endif SW_EXTERN_C_END // clang-format off @@ -99,13 +127,18 @@ static const zend_function_entry swoole_lock_methods[] = PHP_ME(swoole_lock, trylock_read, arginfo_class_Swoole_Lock_trylock_read, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, unlock, arginfo_class_Swoole_Lock_unlock, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, destroy, arginfo_class_Swoole_Lock_destroy, ZEND_ACC_PUBLIC) +#ifdef SW_THREAD + PHP_ME(swoole_lock, __wakeup, arginfo_class_Swoole_Lock___wakeup, ZEND_ACC_PUBLIC) +#endif PHP_FE_END }; // clang-format on void php_swoole_lock_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_lock, "Swoole\\Lock", nullptr, swoole_lock_methods); +#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_lock); +#endif SW_SET_CLASS_CLONEABLE(swoole_lock, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_lock, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( @@ -119,6 +152,9 @@ void php_swoole_lock_minit(int module_number) { zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("SPINLOCK"), Lock::SPIN_LOCK); #endif zend_declare_property_long(swoole_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); +#ifdef SW_THREAD + zend_declare_property_long(swoole_lock_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); +#endif SW_REGISTER_LONG_CONSTANT("SWOOLE_MUTEX", Lock::MUTEX); #ifdef HAVE_RWLOCK @@ -130,35 +166,47 @@ void php_swoole_lock_minit(int module_number) { } static PHP_METHOD(swoole_lock, __construct) { - Lock *lock = php_swoole_lock_get_ptr(ZEND_THIS); - if (lock != nullptr) { + auto o = php_swoole_lock_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (o->lock != nullptr) { zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } - zend_long type = Lock::MUTEX; + zend_long type = swoole::Lock::MUTEX; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL Z_PARAM_LONG(type) ZEND_PARSE_PARAMETERS_END(); +#ifdef SW_THREAD + bool process_shared = false; +#else + bool process_shared = true; +#endif + + Lock *lock; switch (type) { #ifdef HAVE_SPINLOCK case Lock::SPIN_LOCK: - lock = new SpinLock(1); + lock = new SpinLock(process_shared ? 1 : 0); break; #endif #ifdef HAVE_RWLOCK case Lock::RW_LOCK: - lock = new RWLock(1); + lock = new RWLock(process_shared ? 1 : 0); break; #endif case Lock::MUTEX: default: - lock = new Mutex(Mutex::PROCESS_SHARED); + lock = new Mutex(process_shared ? Mutex::PROCESS_SHARED: 0); break; } +#ifdef SW_THREAD + o->lock_res = new LockResource(lock); + auto resource_id = php_swoole_thread_resource_insert(o->lock_res); + zend_update_property_long(swoole_lock_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +#endif php_swoole_lock_set_ptr(ZEND_THIS, lock); RETURN_TRUE; } @@ -210,7 +258,22 @@ static PHP_METHOD(swoole_lock, lock_read) { } static PHP_METHOD(swoole_lock, destroy) { +#ifndef SW_THREAD Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); delete lock; php_swoole_lock_set_ptr(ZEND_THIS, nullptr); +#endif } + +#ifdef SW_THREAD +static PHP_METHOD(swoole_lock, __wakeup) { + auto o = php_swoole_lock_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = zend::read_property_long(ZEND_THIS, ZEND_STRL("id")); + o->lock_res = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!o->lock_res) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } + php_swoole_lock_set_ptr(ZEND_THIS, o->lock_res->lock_); +} +#endif diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index e65928374b9..9991785298c 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -15,6 +15,7 @@ */ #include "php_swoole_cxx.h" +#include "php_swoole_thread.h" #ifdef SW_THREAD @@ -47,11 +48,6 @@ static zend_object_handlers swoole_thread_queue_handlers; zend_string *php_swoole_thread_serialize(zval *zdata); bool php_swoole_thread_unserialize(zend_string *data, zval *zv); -#define EMSG_NO_RESOURCE "resource not found" -#define ECODE_NO_RESOURCE -2 - -#define IS_SERIALIZED_OBJECT 99 - struct ArrayItem { uint32_t type; zend_string *key; @@ -144,22 +140,6 @@ struct ArrayItem { } }; -struct ThreadResource { - uint32_t ref_count; - - ThreadResource() { - ref_count = 1; - } - - uint32_t add_ref() { - return ++ref_count; - } - - uint32_t del_ref() { - return --ref_count; - } -}; - struct ZendArray : ThreadResource { RWLock lock_; zend_array ht; From afae718917b81a3bda6e777507b91104921f6b43 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 27 Mar 2024 12:06:42 +0800 Subject: [PATCH 022/103] clang-format --- ext-src/swoole_server.cc | 8 +++----- src/server/master.cc | 8 ++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 879a6ac1793..0284303619a 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -716,7 +716,7 @@ static bool php_swoole_server_task_unpack(zval *zresult, EventData *task_result) PHP_VAR_UNSERIALIZE_DESTROY(var_hash); if (!unserialized) { swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", - (zend_long)((char *) p - packet.data), + (zend_long) ((char *) p - packet.data), l); return false; } @@ -1768,15 +1768,13 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD *zserv = *((zval *) serv->private_data_2); ZVAL_LONG(zfd, conn ? conn->session_id : data->info.fd); - ZVAL_LONG(ztype, (zend_long)(data ? data->info.type : (int) SW_SERVER_EVENT_CLOSE)); + ZVAL_LONG(ztype, (zend_long) (data ? data->info.type : (int) SW_SERVER_EVENT_CLOSE)); if (data && sw_zend_function_max_num_args(fci_cache->function_handler) > 3) { // TODO: reduce memory copy zdata = &args[3]; ZVAL_STRINGL(zdata, data->data, data->info.len > SW_IPC_BUFFER_SIZE ? SW_IPC_BUFFER_SIZE : data->info.len); } - HOOK_PHP_CALL_STACK( - auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, zdata ? 4 : 3, args, &retval); - ); + HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, zdata ? 4 : 3, args, &retval);); if (UNEXPECTED(call_result != SUCCESS)) { php_swoole_error(E_WARNING, "%s->onDispatch handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); } else if (!ZVAL_IS_NULL(&retval)) { diff --git a/src/server/master.cc b/src/server/master.cc index 6c38338fb13..138daf1a70c 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -978,7 +978,7 @@ void Server::destroy() { join_reactor_thread(); } - release_pipe_buffers(); + release_pipe_buffers(); for (auto port : ports) { port->close(); @@ -1515,7 +1515,7 @@ bool Server::sendfile(SessionId session_id, const char *file, uint32_t l_file, o "sendfile name[%.8s...] length %u is exceed the max name len %u", file, l_file, - (uint32_t)(SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1)); + (uint32_t) (SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1)); return false; } // string must be zero termination (for `state` system call) @@ -1769,7 +1769,7 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { #ifdef SW_USE_OPENSSL if (type & SW_SOCK_SSL) { - type = (SocketType)(type & (~SW_SOCK_SSL)); + type = (SocketType) (type & (~SW_SOCK_SSL)); ls->type = type; ls->ssl = 1; ls->ssl_context = new SSLContext(); @@ -2016,7 +2016,7 @@ int Server::create_pipe_buffers() { } void Server::release_pipe_buffers() { - message_bus.free_buffer(); + message_bus.free_buffer(); } int Server::get_idle_worker_num() { From 143f71e32e9e771c71dd08590017caa56bedf5d0 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 27 Mar 2024 13:46:02 +0800 Subject: [PATCH 023/103] fix tests --- tests/init | 32 +--- tests/start.sh | 2 - .../abandon_prepare_dtor.phpt | 29 ---- tests/swoole_mysql_coro/aborted_clients.phpt | 25 --- .../swoole_mysql_coro/another_coroutine.phpt | 51 ------- tests/swoole_mysql_coro/bc_fetchAll.phpt | 75 --------- .../swoole_mysql_coro/bc_sync_properties.phpt | 39 ----- tests/swoole_mysql_coro/big_data.phpt | 38 ----- tests/swoole_mysql_coro/bug_0814.phpt | 72 --------- tests/swoole_mysql_coro/connect_timeout.phpt | 43 ------ tests/swoole_mysql_coro/db_destruct.phpt | 28 ---- tests/swoole_mysql_coro/defer_and_fetch.phpt | 41 ----- .../swoole_mysql_coro/err_instead_of_eof.phpt | 26 ---- tests/swoole_mysql_coro/escape.phpt | 20 --- tests/swoole_mysql_coro/fetch.phpt | 34 ----- tests/swoole_mysql_coro/fetch_mode.phpt | 33 ---- tests/swoole_mysql_coro/fetch_mode_twice.phpt | 29 ---- tests/swoole_mysql_coro/illegal_extends.phpt | 62 -------- tests/swoole_mysql_coro/invalid_host.phpt | 25 --- tests/swoole_mysql_coro/kill_process.phpt | 56 ------- tests/swoole_mysql_coro/many_rows.phpt | 43 ------ tests/swoole_mysql_coro/multi_packets.phpt | 125 --------------- tests/swoole_mysql_coro/not_exist.phpt | 23 --- tests/swoole_mysql_coro/null.phpt | 51 ------- tests/swoole_mysql_coro/null_bit_map.phpt | 103 ------------- tests/swoole_mysql_coro/numbers.phpt | 143 ------------------ .../swoole_mysql_coro/prepare_field_type.phpt | 48 ------ tests/swoole_mysql_coro/prepare_insert.phpt | 43 ------ tests/swoole_mysql_coro/prepare_multi.phpt | 49 ------ tests/swoole_mysql_coro/prepare_select.phpt | 42 ----- tests/swoole_mysql_coro/procedure.phpt | 60 -------- .../swoole_mysql_coro/procedure_by_query.phpt | 63 -------- .../swoole_mysql_coro/procedure_in_fetch.phpt | 82 ---------- tests/swoole_mysql_coro/procedure_single.phpt | 42 ----- .../procedure_with_query.phpt | 65 -------- .../procedure_with_query_and_prepare.phpt | 39 ----- tests/swoole_mysql_coro/query.phpt | 63 -------- tests/swoole_mysql_coro/query_multifield.phpt | 31 ---- tests/swoole_mysql_coro/query_timeout.phpt | 32 ---- tests/swoole_mysql_coro/readonly.phpt | 53 ------- tests/swoole_mysql_coro/simple_query.phpt | 24 --- tests/swoole_mysql_coro/statement_closed.phpt | 30 ---- .../swoole_mysql_coro/statement_destruct.phpt | 53 ------- tests/swoole_mysql_coro/timeout.phpt | 38 ----- tests/swoole_mysql_coro/transaction.phpt | 44 ------ tests/swoole_mysql_coro/unixsocket.phpt | 26 ---- tests/swoole_mysql_coro/userinfo.phpt | 41 ----- tests/swoole_mysql_coro/without_fetch.phpt | 30 ---- tests/swoole_mysql_coro/wrong_password.phpt | 25 --- tests/swoole_mysql_coro/z_reset.phpt | 11 -- tests/swoole_redis_coro/auth.phpt | 38 ----- tests/swoole_redis_coro/auto_reconnect.phpt | 23 --- .../swoole_redis_coro/auto_reconnect_ex.phpt | 68 --------- tests/swoole_redis_coro/basic.phpt | 63 -------- tests/swoole_redis_coro/bug_lock.phpt | 35 ----- .../compatibility_mode/hExists.phpt | 23 --- tests/swoole_redis_coro/connect_timeout.phpt | 21 --- tests/swoole_redis_coro/connect_to_wrong.phpt | 19 --- tests/swoole_redis_coro/connect_twice-2.phpt | 64 -------- tests/swoole_redis_coro/connect_twice.phpt | 33 ---- tests/swoole_redis_coro/curd.phpt | 25 --- tests/swoole_redis_coro/defer.phpt | 45 ------ .../swoole_redis_coro/different_connect.phpt | 47 ------ tests/swoole_redis_coro/disable_retry.phpt | 68 --------- .../donot_retry_after_failed.phpt | 43 ------ .../donot_retry_after_server_down.phpt | 62 -------- tests/swoole_redis_coro/err.phpt | 22 --- tests/swoole_redis_coro/getDbNum.phpt | 40 ----- tests/swoole_redis_coro/getOptions.phpt | 50 ------ tests/swoole_redis_coro/hgetall.phpt | 97 ------------ tests/swoole_redis_coro/lock.phpt | 30 ---- tests/swoole_redis_coro/multi_exec.phpt | 30 ---- tests/swoole_redis_coro/pool.phpt | 79 ---------- tests/swoole_redis_coro/psubscribe_1.phpt | 37 ----- tests/swoole_redis_coro/psubscribe_2.phpt | 41 ----- tests/swoole_redis_coro/psubscribe_eof_1.phpt | 40 ----- tests/swoole_redis_coro/psubscribe_eof_2.phpt | 46 ------ tests/swoole_redis_coro/punsubscribe.phpt | 51 ------- tests/swoole_redis_coro/reconnect.phpt | 17 --- .../request_without_connected.phpt | 16 -- tests/swoole_redis_coro/select.phpt | 37 ----- tests/swoole_redis_coro/set.phpt | 43 ------ tests/swoole_redis_coro/setOptions.phpt | 39 ----- tests/swoole_redis_coro/stream.phpt | 124 --------------- tests/swoole_redis_coro/subscribe_1.phpt | 38 ----- tests/swoole_redis_coro/subscribe_2.phpt | 40 ----- tests/swoole_redis_coro/subscribe_multi.phpt | 46 ------ .../subscribe_punsubscribe.phpt | 54 ------- .../subscribe_reconnect.phpt | 35 ----- tests/swoole_redis_coro/timeout.phpt | 52 ------- tests/swoole_redis_coro/unconnected.phpt | 16 -- tests/swoole_redis_coro/unixsocket.phpt | 29 ---- tests/swoole_redis_coro/unsubscribe.phpt | 51 ------- tests/swoole_redis_coro/unsubscribe_all.phpt | 56 ------- .../unsubscribe_not_all.phpt | 56 ------- tests/swoole_redis_coro/zpop.phpt | 75 --------- 96 files changed, 7 insertions(+), 4339 deletions(-) delete mode 100644 tests/swoole_mysql_coro/abandon_prepare_dtor.phpt delete mode 100644 tests/swoole_mysql_coro/aborted_clients.phpt delete mode 100644 tests/swoole_mysql_coro/another_coroutine.phpt delete mode 100644 tests/swoole_mysql_coro/bc_fetchAll.phpt delete mode 100644 tests/swoole_mysql_coro/bc_sync_properties.phpt delete mode 100644 tests/swoole_mysql_coro/big_data.phpt delete mode 100644 tests/swoole_mysql_coro/bug_0814.phpt delete mode 100644 tests/swoole_mysql_coro/connect_timeout.phpt delete mode 100644 tests/swoole_mysql_coro/db_destruct.phpt delete mode 100644 tests/swoole_mysql_coro/defer_and_fetch.phpt delete mode 100644 tests/swoole_mysql_coro/err_instead_of_eof.phpt delete mode 100644 tests/swoole_mysql_coro/escape.phpt delete mode 100644 tests/swoole_mysql_coro/fetch.phpt delete mode 100644 tests/swoole_mysql_coro/fetch_mode.phpt delete mode 100644 tests/swoole_mysql_coro/fetch_mode_twice.phpt delete mode 100644 tests/swoole_mysql_coro/illegal_extends.phpt delete mode 100644 tests/swoole_mysql_coro/invalid_host.phpt delete mode 100644 tests/swoole_mysql_coro/kill_process.phpt delete mode 100644 tests/swoole_mysql_coro/many_rows.phpt delete mode 100644 tests/swoole_mysql_coro/multi_packets.phpt delete mode 100644 tests/swoole_mysql_coro/not_exist.phpt delete mode 100644 tests/swoole_mysql_coro/null.phpt delete mode 100644 tests/swoole_mysql_coro/null_bit_map.phpt delete mode 100644 tests/swoole_mysql_coro/numbers.phpt delete mode 100644 tests/swoole_mysql_coro/prepare_field_type.phpt delete mode 100644 tests/swoole_mysql_coro/prepare_insert.phpt delete mode 100644 tests/swoole_mysql_coro/prepare_multi.phpt delete mode 100644 tests/swoole_mysql_coro/prepare_select.phpt delete mode 100644 tests/swoole_mysql_coro/procedure.phpt delete mode 100644 tests/swoole_mysql_coro/procedure_by_query.phpt delete mode 100644 tests/swoole_mysql_coro/procedure_in_fetch.phpt delete mode 100644 tests/swoole_mysql_coro/procedure_single.phpt delete mode 100644 tests/swoole_mysql_coro/procedure_with_query.phpt delete mode 100644 tests/swoole_mysql_coro/procedure_with_query_and_prepare.phpt delete mode 100644 tests/swoole_mysql_coro/query.phpt delete mode 100644 tests/swoole_mysql_coro/query_multifield.phpt delete mode 100644 tests/swoole_mysql_coro/query_timeout.phpt delete mode 100644 tests/swoole_mysql_coro/readonly.phpt delete mode 100644 tests/swoole_mysql_coro/simple_query.phpt delete mode 100644 tests/swoole_mysql_coro/statement_closed.phpt delete mode 100644 tests/swoole_mysql_coro/statement_destruct.phpt delete mode 100644 tests/swoole_mysql_coro/timeout.phpt delete mode 100644 tests/swoole_mysql_coro/transaction.phpt delete mode 100644 tests/swoole_mysql_coro/unixsocket.phpt delete mode 100644 tests/swoole_mysql_coro/userinfo.phpt delete mode 100644 tests/swoole_mysql_coro/without_fetch.phpt delete mode 100644 tests/swoole_mysql_coro/wrong_password.phpt delete mode 100644 tests/swoole_mysql_coro/z_reset.phpt delete mode 100644 tests/swoole_redis_coro/auth.phpt delete mode 100644 tests/swoole_redis_coro/auto_reconnect.phpt delete mode 100644 tests/swoole_redis_coro/auto_reconnect_ex.phpt delete mode 100644 tests/swoole_redis_coro/basic.phpt delete mode 100644 tests/swoole_redis_coro/bug_lock.phpt delete mode 100644 tests/swoole_redis_coro/compatibility_mode/hExists.phpt delete mode 100644 tests/swoole_redis_coro/connect_timeout.phpt delete mode 100644 tests/swoole_redis_coro/connect_to_wrong.phpt delete mode 100644 tests/swoole_redis_coro/connect_twice-2.phpt delete mode 100644 tests/swoole_redis_coro/connect_twice.phpt delete mode 100644 tests/swoole_redis_coro/curd.phpt delete mode 100644 tests/swoole_redis_coro/defer.phpt delete mode 100644 tests/swoole_redis_coro/different_connect.phpt delete mode 100644 tests/swoole_redis_coro/disable_retry.phpt delete mode 100644 tests/swoole_redis_coro/donot_retry_after_failed.phpt delete mode 100644 tests/swoole_redis_coro/donot_retry_after_server_down.phpt delete mode 100644 tests/swoole_redis_coro/err.phpt delete mode 100644 tests/swoole_redis_coro/getDbNum.phpt delete mode 100644 tests/swoole_redis_coro/getOptions.phpt delete mode 100644 tests/swoole_redis_coro/hgetall.phpt delete mode 100644 tests/swoole_redis_coro/lock.phpt delete mode 100644 tests/swoole_redis_coro/multi_exec.phpt delete mode 100644 tests/swoole_redis_coro/pool.phpt delete mode 100644 tests/swoole_redis_coro/psubscribe_1.phpt delete mode 100644 tests/swoole_redis_coro/psubscribe_2.phpt delete mode 100644 tests/swoole_redis_coro/psubscribe_eof_1.phpt delete mode 100644 tests/swoole_redis_coro/psubscribe_eof_2.phpt delete mode 100644 tests/swoole_redis_coro/punsubscribe.phpt delete mode 100644 tests/swoole_redis_coro/reconnect.phpt delete mode 100644 tests/swoole_redis_coro/request_without_connected.phpt delete mode 100644 tests/swoole_redis_coro/select.phpt delete mode 100644 tests/swoole_redis_coro/set.phpt delete mode 100644 tests/swoole_redis_coro/setOptions.phpt delete mode 100644 tests/swoole_redis_coro/stream.phpt delete mode 100644 tests/swoole_redis_coro/subscribe_1.phpt delete mode 100644 tests/swoole_redis_coro/subscribe_2.phpt delete mode 100644 tests/swoole_redis_coro/subscribe_multi.phpt delete mode 100644 tests/swoole_redis_coro/subscribe_punsubscribe.phpt delete mode 100644 tests/swoole_redis_coro/subscribe_reconnect.phpt delete mode 100644 tests/swoole_redis_coro/timeout.phpt delete mode 100644 tests/swoole_redis_coro/unconnected.phpt delete mode 100644 tests/swoole_redis_coro/unixsocket.phpt delete mode 100644 tests/swoole_redis_coro/unsubscribe.phpt delete mode 100644 tests/swoole_redis_coro/unsubscribe_all.phpt delete mode 100644 tests/swoole_redis_coro/unsubscribe_not_all.phpt delete mode 100644 tests/swoole_redis_coro/zpop.phpt diff --git a/tests/init b/tests/init index b97a63e4d3f..0b730ce7063 100755 --- a/tests/init +++ b/tests/init @@ -36,17 +36,16 @@ function read_sql_file(string $file) } require __DIR__ . '/include/config.php'; +require __DIR__ . '/swoole_pdo_pgsql/pdo_pgsql.inc'; Swoole\Coroutine\run(function () { echo "[DB-init] initialization MySQL database...\n"; - $mysql = new Swoole\Coroutine\MySQL(); - $connected = $mysql->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); + $mysql = new mysqli(); + $connected = $mysql->connect(MYSQL_SERVER_HOST, + MYSQL_SERVER_USER, + MYSQL_SERVER_PWD, + MYSQL_SERVER_DB, + MYSQL_SERVER_PORT); if (!$connected) { echo "[DB-init] Connect failed! Error#{$mysql->connect_errno}: {$mysql->connect_error}\n"; exit(1); @@ -60,23 +59,6 @@ Swoole\Coroutine\run(function () { } echo "[DB-init] MySQL Done!\n"; - echo "[DB-init] initialization PostgreSQL database...\n"; - $pgsql = new Swoole\Coroutine\PostgreSQL(); - $connected = $pgsql->connect(PGSQL_CONNECTION_STRING); - if (!$connected) { - echo sprintf("[DB-init] Connect failed! Error#%s: %s", $pgsql->error, $pgsql->notices['sqlstate'] ?? ''), PHP_EOL; - exit(1); - } - $sql_file = read_sql_file(__DIR__ . '/pgsql.sql'); - foreach ($sql_file as $line) { - if (!$pgsql->query($line)) { - echo sprintf("[DB-init] Failed! Error#%s: %s", $pgsql->error, $pgsql->notices['sqlstate'] ?? ''), PHP_EOL; - exit(1); - } - } - echo "[DB-init] PostgreSQL Done!\n"; - - echo "[DB-init] initialization ODBC...\n"; echo `set -ex`; diff --git a/tests/start.sh b/tests/start.sh index ae744e3f118..43cdac2e73a 100755 --- a/tests/start.sh +++ b/tests/start.sh @@ -38,8 +38,6 @@ else swoole_http_server \ swoole_websocket_server \ swoole_redis_server \ - swoole_mysql_coro \ - swoole_redis_coro \ swoole_socket_coro \ swoole_runtime" if [ ${#} -gt 1 ]; then diff --git a/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt b/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt deleted file mode 100644 index a965bf6313b..00000000000 --- a/tests/swoole_mysql_coro/abandon_prepare_dtor.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare dtor ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - for ($n = MAX_REQUESTS; $n--;) { - $statement = $mysql->prepare('SELECT ?'); - $statement = null; - Co::sleep(0.001); - $result = $mysql->query('show status like \'Prepared_stmt_count\''); - Assert::eq($result[0]['Value'], '0'); - } -}); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/aborted_clients.phpt b/tests/swoole_mysql_coro/aborted_clients.phpt deleted file mode 100644 index 0b1b14f42f5..00000000000 --- a/tests/swoole_mysql_coro/aborted_clients.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql-close/reconnect/aborted-client-num ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::true($db->connect($server)); - $before_num = (int)$db->query('show status like "Aborted_clients"')[0]["Value"]; - Assert::true($db->close()); - Assert::true($db->connect($server)); - $after_num = (int)$db->query('show status like "Aborted_clients"')[0]["Value"]; - Assert::same($after_num - $before_num, 0); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/another_coroutine.phpt b/tests/swoole_mysql_coro/another_coroutine.phpt deleted file mode 100644 index bf1dc32b0d1..00000000000 --- a/tests/swoole_mysql_coro/another_coroutine.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -swoole_mysql_coro: illegal another coroutine ---SKIPIF-- - ---FILE-- -query('SELECT SLEEP(1)'); - Assert::assert(false, 'never here'); - } - - $cli = new Co\MySQL; - $connected = $cli->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - if (Assert::true($connected)) { - go(function () use ($cli) { - $cli->query('SELECT SLEEP(1)'); - Assert::assert(false, 'never here'); - }); - go(function () use ($cli) { - (function () use ($cli) { - (function () use ($cli) { - get($cli); - })(); - })(); - }); - } - }); - echo "end\n"; -}, false, null, false); -$process->start(); -Swoole\Process::wait(); -?> ---EXPECTF-- -Fatal error: Uncaught Swoole\Error: Socket#%d has already been bound to another coroutine#%d, reading of the same socket in coroutine#%d at the same time is not allowed in %s:%d -Stack trace: -#0 %s(%d): Swoole\Coroutine\MySQL->query('SELECT SLEEP(%d)') -#1 %s(%d): get(Object(Swoole\Coroutine\MySQL)) -#2 %s(%d): {closure}() -#3 %s(%d): {closure}() -%A - thrown in %s on line %d diff --git a/tests/swoole_mysql_coro/bc_fetchAll.phpt b/tests/swoole_mysql_coro/bc_fetchAll.phpt deleted file mode 100644 index 02a3d15d1cc..00000000000 --- a/tests/swoole_mysql_coro/bc_fetchAll.phpt +++ /dev/null @@ -1,75 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql fetchAll should return empty array (#2674) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - if (Assert::true($client->connect($server))) { - defer(function () use ($server, $client) { - $client->connect($server); - $client->query('DROP TABLE `empty`'); - }); - if (Assert::true($client->query("CREATE TABLE `empty` (`id` int(11))"))) { - // query - Assert::notEmpty($client->query('SELECT * FROM `ckl`')); - Assert::same($client->query('SELECT * FROM `empty`'), []); - Assert::same($client->query('SELECT * FROM `notexist`'), false); - // execute - Assert::notEmpty($client->prepare('SELECT * FROM `ckl`')->execute()); - Assert::same(($statement = $client->prepare('SELECT * FROM `empty`'))->execute(), []); - Assert::same($client->prepare('SELECT * FROM `notexist`'), false); - // closed - Assert::true($client->close()); - Assert::same($client->query('SELECT * FROM `empty`'), false); - Assert::same($client->prepare('SELECT * FROM `empty`'), false); - Assert::same($statement->execute(), false); - - if (Assert::true($client->connect($server + ['fetch_mode' => true]))) { - // query - Assert::true($client->query('SELECT * FROM `ckl` LIMIT 1')); - Assert::notEmpty($client->fetch()); - Assert::null($client->fetch()); - Assert::null($client->fetch()); - Assert::same($client->fetchAll(), []); - Assert::true($client->query('SELECT * FROM `ckl` LIMIT 1')); - Assert::count($client->fetchAll(), 1); - Assert::same($client->fetchAll(), []); - // execute - Assert::isInstanceOf( - $statement = $client->prepare('SELECT * FROM `ckl` LIMIT 1'), - Swoole\Coroutine\MySQL\Statement::class - ); - Assert::same($statement->fetchAll(), []); - Assert::true($statement->execute()); - Assert::notEmpty($statement->fetch()); - Assert::null($statement->fetch()); - Assert::true($statement->execute()); - Assert::notEmpty($statement->fetchAll()); - Assert::same($statement->fetchAll(), []); - // closed - Assert::true($client->close()); - Assert::false($client->query('SELECT * FROM `ckl` LIMIT 1')); - Assert::false($client->fetch()); - Assert::false($client->fetchAll()); - Assert::false($statement->execute()); - Assert::false($statement->fetch()); - Assert::false($statement->fetchAll()); - echo "DONE\n"; - } - } - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/bc_sync_properties.phpt b/tests/swoole_mysql_coro/bc_sync_properties.phpt deleted file mode 100644 index a23866ff9da..00000000000 --- a/tests/swoole_mysql_coro/bc_sync_properties.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (insert) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - if (Assert::true($client->connect($server))) { - /* @var $statement Swoole\Coroutine\MySQL\Statement */ - $statement = $client->prepare('INSERT INTO ckl (`domain`,`path`,`name`) VALUES (?,?,?)'); - if (Assert::isInstanceOf($statement, Swoole\Coroutine\MySQL\Statement::class)) { - if (Assert::true($statement->execute(['www.baidu.com', '/search', 'baidu']))) { - Assert::same($statement->affected_rows, 1); - Assert::greaterThan($statement->insert_id, 0); - Assert::same($client->affected_rows, $statement->affected_rows); - Assert::same($client->insert_id, $statement->insert_id); - if (Assert::false($statement->execute())) { - Assert::same($statement->errno, SWOOLE_MYSQLND_CR_INVALID_PARAMETER_NO); - Assert::same($client->error, $statement->error); - Assert::same($client->errno, $statement->errno); - } - echo "SUCCESS\n"; - } - } - } -}); -?> ---EXPECT-- -SUCCESS diff --git a/tests/swoole_mysql_coro/big_data.phpt b/tests/swoole_mysql_coro/big_data.phpt deleted file mode 100644 index 02f6b3f6f87..00000000000 --- a/tests/swoole_mysql_coro/big_data.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_mysql_coro: select big data from db ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::assert($db->connect($server)); - - $table_name = get_safe_random(16); - $createTable = "CREATE TABLE {$table_name} (\nid bigint PRIMARY KEY AUTO_INCREMENT,\n`content` text NOT NULL\n);"; - if (Assert::assert($db->query($createTable))) { - $statement = $db->prepare("INSERT INTO {$table_name} VALUES (?, ?)"); - $random = []; - for ($n = 0; $n < MAX_REQUESTS; $n++) { - $random[$n] = str_repeat(get_safe_random(256), 128); // 32K - $ret = $statement->execute([$n + 1, $random[$n]]); - Assert::assert($ret); - } - $statement = $db->prepare("SELECT * FROM {$table_name}"); - $ret = $statement->execute(); - for ($n = 0; $n < MAX_REQUESTS; $n++) { - Assert::same($ret[$n]['content'], $random[$n]); - } - Assert::assert($db->query("DROP TABLE {$table_name}")); - } -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/bug_0814.phpt b/tests/swoole_mysql_coro/bug_0814.phpt deleted file mode 100644 index a3626e69814..00000000000 --- a/tests/swoole_mysql_coro/bug_0814.phpt +++ /dev/null @@ -1,72 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (select) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT[1] ERROR\n"; - return; - } - - /** - * 第一次执行prepare - */ - $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute([5]); - if (!$ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - return; - } - Assert::assert(count($ret3) > 0); - - $s = microtime(true); - $ret = $db->query("select sleep(20)", 0.1); - time_approximate(0.1, microtime(true) - $s); - Assert::false($ret); - Assert::same($db->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT[2] ERROR\n"; - return; - } - - /** - * 第二次执行prepare - */ - $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute([5]); - if (!$ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - return; - } - Assert::assert(count($ret3) > 0); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/connect_timeout.phpt b/tests/swoole_mysql_coro/connect_timeout.phpt deleted file mode 100644 index e9f4954dde0..00000000000 --- a/tests/swoole_mysql_coro/connect_timeout.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_mysql_coro: connect timeout ---SKIPIF-- - ---FILE-- -connect([ - 'host' => '192.0.0.1', - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'timeout' => ($timeout = mt_rand(100, 500) / 1000) - ]); - time_approximate($timeout, microtime(true) - $s); - Assert::assert(!$connected); - Assert::assert($mysql->connected === false); - Assert::assert($mysql->connect_errno === SWOOLE_MYSQLND_CR_CONNECTION_ERROR); - // handshake timeout - $s = microtime(true); - $connected = $mysql->connect([ - 'host' => REDIS_SERVER_HOST, - 'port' => REDIS_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'timeout' => ($timeout = mt_rand(100, 500) / 1000) - ]); - time_approximate($timeout, microtime(true) - $s); - Assert::false($connected); - Assert::same($mysql->connected, false); - Assert::same($mysql->connect_errno, SWOOLE_MYSQLND_CR_CONNECTION_ERROR); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/db_destruct.phpt b/tests/swoole_mysql_coro/db_destruct.phpt deleted file mode 100644 index 53d102077f8..00000000000 --- a/tests/swoole_mysql_coro/db_destruct.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql db destruct ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $ret = $db->connect($server); - if (Assert::true($ret)) { - $statement = $db->prepare('SELECT 1'); - Assert::isInstanceOf($statement, Co\Mysql\Statement::class); - $ret = $statement->execute(); - Assert::same($ret[0][1], 1); - echo "DONE\n"; - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/defer_and_fetch.phpt b/tests/swoole_mysql_coro/defer_and_fetch.phpt deleted file mode 100644 index 758f3459825..00000000000 --- a/tests/swoole_mysql_coro/defer_and_fetch.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql defer and fetch ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]); - $mysql->setDefer(true); - for ($n = 0; $n < MAX_REQUESTS; $n++) { - if ($n === 0 || mt_rand(0, 1)) { - $ret = $mysql->prepare('SELECT ?+?'); - Assert::true($ret); - $statement = $mysql->recv(); - Assert::isInstanceOf($statement, Swoole\Coroutine\MySQL\Statement::class); - } - $a = mt_rand(0, 65535); - $b = mt_rand(0, 65535); - /** @var $statement Swoole\Coroutine\MySQL\Statement */ - Assert::true($statement->execute([$a, $b])); - Assert::true($statement->recv()); - $result = $statement->fetchAll(); - if (Assert::isArray($result)) { - Assert::same(reset($result[0]), (float)($a + $b)); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/err_instead_of_eof.phpt b/tests/swoole_mysql_coro/err_instead_of_eof.phpt deleted file mode 100644 index d453be39b51..00000000000 --- a/tests/swoole_mysql_coro/err_instead_of_eof.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -swoole_mysql_coro: ERR Instead of EOF ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $db->connect($server); - if (!$db->query("EXPLAIN SELECT * FROM dual;")) { - echo $db->errno . PHP_EOL; - echo $db->error . PHP_EOL; - } -}); -?> ---EXPECT-- -1096 -SQLSTATE[HY000] [1096] No tables used diff --git a/tests/swoole_mysql_coro/escape.phpt b/tests/swoole_mysql_coro/escape.phpt deleted file mode 100644 index a58987ae935..00000000000 --- a/tests/swoole_mysql_coro/escape.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql escape ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::same($mysql->escape(""), ""); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/fetch.phpt b/tests/swoole_mysql_coro/fetch.phpt deleted file mode 100644 index 3c794d133e3..00000000000 --- a/tests/swoole_mysql_coro/fetch.phpt +++ /dev/null @@ -1,34 +0,0 @@ ---TEST-- -swoole_mysql_coro: use fetch to get data ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - - // now we can make the responses independent - $stmt = $db->prepare('SELECT `id` FROM `userinfo` LIMIT 2'); - Assert::true($stmt->execute()); - if (!Assert::assert(is_array($ret = $stmt->fetch()) && !empty($ret))) { - echo "FETCH1 ERROR#{$stmt->errno}: {$stmt->error}\n"; - } - if (!Assert::assert(is_array($ret = $stmt->fetch()) && !empty($ret))) { - echo "FETCH2 ERROR#{$stmt->errno}: {$stmt->error}\n"; - } - Assert::same($stmt->fetch(), null); - Assert::same($stmt->fetchAll(), []); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/fetch_mode.phpt b/tests/swoole_mysql_coro/fetch_mode.phpt deleted file mode 100644 index 5000a2a8cb8..00000000000 --- a/tests/swoole_mysql_coro/fetch_mode.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -swoole_mysql_coro: use fetch to get data ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - - // now we can make the responses independent - $stmt1 = $db->prepare('SELECT * FROM ckl LIMIT 1'); - Assert::true($stmt1->execute()); - $stmt2 = $db->prepare('SELECT * FROM ckl LIMIT 2'); - Assert::true($stmt2->execute()); - Assert::same(count($stmt1->fetchAll()), 1); - Assert::same(count($stmt2->fetchAll()), 2); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/fetch_mode_twice.phpt b/tests/swoole_mysql_coro/fetch_mode_twice.phpt deleted file mode 100644 index 1a312c4b9ee..00000000000 --- a/tests/swoole_mysql_coro/fetch_mode_twice.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -swoole_mysql_coro: call fetch twice ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - - Assert::true($db->query("INSERT INTO ckl (`domain`,`path`,`name`) VALUES ('www.baidu.com', '/search', 'baidu')")); - // now we can make the responses independent - $stmt = $db->prepare('SELECT * FROM ckl LIMIT 1'); - Assert::true($stmt->execute()); - Assert::assert(($ret = $stmt->fetchAll()) && is_array($ret) && count($ret) === 1); - Assert::same($stmt->fetchAll(), []); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/illegal_extends.phpt b/tests/swoole_mysql_coro/illegal_extends.phpt deleted file mode 100644 index 558bade8abc..00000000000 --- a/tests/swoole_mysql_coro/illegal_extends.phpt +++ /dev/null @@ -1,62 +0,0 @@ ---TEST-- -swoole_mysql_coro: illegal child class ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - - // invalid connect - Assert::true($db->connect($server)); - Assert::false($db->connected); - Assert::false($db->query('select 1')); - Assert::same($db->errno, SWOOLE_MYSQLND_CR_CONNECTION_ERROR); - - // right implementation - Assert::true($db->connectRaw($server)); - Assert::same($db->query('select 1')[0][1], 1); -}); - -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/invalid_host.phpt b/tests/swoole_mysql_coro/invalid_host.phpt deleted file mode 100644 index 2c5a945fb4f..00000000000 --- a/tests/swoole_mysql_coro/invalid_host.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_mysql_coro: invalid host ---SKIPIF-- - ---FILE-- -connect([ - 'host' => get_safe_random(), - 'port' => MYSQL_SERVER_PORT, - 'database' => MYSQL_SERVER_DB, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'timeout' => 0.5 - ]); - echo 'Connection: ' . ($connected ? 'Connected' : 'Not connected') . PHP_EOL; - Assert::same($mysql->connect_errno, SWOOLE_MYSQLND_CR_CONNECTION_ERROR); - echo $mysql->connect_error . PHP_EOL; -}); -?> ---EXPECTF-- -Connection: Not connected -SQLSTATE[HY000] [2002] %s diff --git a/tests/swoole_mysql_coro/kill_process.phpt b/tests/swoole_mysql_coro/kill_process.phpt deleted file mode 100644 index 025cad9bc93..00000000000 --- a/tests/swoole_mysql_coro/kill_process.phpt +++ /dev/null @@ -1,56 +0,0 @@ ---TEST-- -swoole_mysql_coro: kill process and check liveness ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - $mysql = new Swoole\Coroutine\MySQL; - Assert::true($mysql->connect($config)); - Assert::same($mysql->query('SELECT 1')[0][1], 1); - - $killer = new Swoole\Coroutine\MySQL; - Assert::true($killer->connect($config)); - - foreach ( - [ - function () use ($mysql) { - return $mysql->query('SELECT 1'); - }, - function () use ($mysql) { - return $mysql->begin(); - }, - function () use ($mysql) { - return $mysql->prepare('SELECT 1'); - }, - ] as $command - ) { - $processList = $killer->query('show processlist'); - $processList = array_filter($processList, function (array $value) { - return $value['db'] == MYSQL_SERVER_DB && $value['Info'] != 'show processlist'; - }); - foreach ($processList as $process) { - $killer->query("KILL {$process['Id']}"); - } - switch_process(); - Assert::false($command()); - Assert::same($mysql->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - Assert::true($mysql->connect($config)); - } - - echo $mysql->error . PHP_EOL; -}); -echo "DONE\n"; -?> ---EXPECT-- -SQLSTATE[HY000] [2006] MySQL server has gone away -DONE diff --git a/tests/swoole_mysql_coro/many_rows.phpt b/tests/swoole_mysql_coro/many_rows.phpt deleted file mode 100644 index 1742140cd0d..00000000000 --- a/tests/swoole_mysql_coro/many_rows.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_mysql_coro: insert and select many rows ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::assert($db->connect($server)); - - $table_name = get_safe_random(16); - $createTable = "CREATE TABLE {$table_name} (\nid bigint PRIMARY KEY AUTO_INCREMENT,\n`content` text NOT NULL\n);"; - $row_num = [100, 200, 1000, 3000][PRESSURE_LEVEL]; - if (Assert::assert($db->query($createTable))) { - $sql = "INSERT INTO {$table_name} (`content`) VALUES " . rtrim(str_repeat('(?), ', $row_num), ', '); - $statement = $db->prepare($sql); - $random = []; - for ($n = 0; $n < $row_num; $n++) { - $random[$n] = get_safe_random(64); - } - $statement->execute($random); - $statement = $db->prepare("SELECT * FROM {$table_name}"); - $result = $statement->execute(); - if (Assert::assert(count($result) === $row_num)) { - for ($n = 0; $n < $row_num; $n++) { - Assert::same($result[$n]['content'], $random[$n]); - } - } - Assert::assert($db->query("DROP TABLE {$table_name}")); - echo "DONE\n"; - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/multi_packets.phpt b/tests/swoole_mysql_coro/multi_packets.phpt deleted file mode 100644 index 570b31caa25..00000000000 --- a/tests/swoole_mysql_coro/multi_packets.phpt +++ /dev/null @@ -1,125 +0,0 @@ ---TEST-- -swoole_mysql_coro: select and insert huge data from db (10M~64M) ---SKIPIF-- - ---FILE-- - -1 -]); -go(function () { - $mysql = new Swoole\Coroutine\Mysql; - $mysql_server = [ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - // set max_allowed_packet - $mysql->connect($mysql_server); - if (!$mysql->query('set global max_allowed_packet = 100 * 1024 * 1024')) { - exit('unable to set max_allowed_packet to 100M.'); - } - // reconnect and we can see changes - $mysql->close(); - $mysql->connect($mysql_server); - @$mysql->query('DROP TABLE `firmware`'); - $ret = $mysql->query(<<error); - } - $max_allowed_packet = $mysql->query('show VARIABLES like \'max_allowed_packet\''); - $max_allowed_packet = $max_allowed_packet[0]['Value'] / 1024 / 1024; - phpt_var_dump("max_allowed_packet: {$max_allowed_packet}M"); - if (IS_IN_CI) { - $max_allowed_packet = 36; - } else { - $max_allowed_packet = 64; - } - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $mysql_query = new Swoole\Coroutine\Mysql; - $mysql_query->connect($mysql_server); - $mysql_prepare = new Swoole\Coroutine\Mysql; - $mysql_prepare->connect($mysql_server); - for ($fid = 1; $fid <= $max_allowed_packet / 10; $fid++) { - $random_size = 2 << mt_rand(2, 9); - $text_size = min($fid * 10 + mt_rand(1, 9), $max_allowed_packet) * 1024 * 1024; // 1xM ~ 5xM - $firmware = str_repeat(get_safe_random($random_size), $text_size / $random_size); - $f_md5 = md5($firmware); - $f_remark = get_safe_random(); - if (mt_rand(0, 1)) { - $sql = "INSERT INTO `firmware` (`fid`, `firmware`, `f_md5`, `f_remark`) " . - "VALUES ({$fid}, '{$firmware}', '{$f_md5}', '{$f_remark}')"; - $ret = $mysql_query->query($sql); - } else { - $sql = "INSERT INTO `firmware` (`fid`, `firmware`, `f_md5`, `f_remark`) VALUES (?, ?, ?, ?)"; - $pdo_stmt = $mysql_prepare->prepare($sql); - if ($pdo_stmt) { - $ret = $pdo_stmt->execute([$fid, $firmware, $f_md5, $f_remark]); - if (!$ret) { - var_dump($pdo_stmt); - exit; - } - } else { - $ret = false; - } - } - if (Assert::assert($ret)) { - $sql = 'SELECT * FROM `test`.`firmware` WHERE fid='; - $pdo_stmt = $pdo->prepare("{$sql}?"); - $mysql_stmt = $mysql_prepare->prepare("{$sql}?"); - $chan = new Chan(); - go(function () use ($chan, $pdo_stmt, $fid) { - $pdo_stmt->execute([$fid]); - $result = $pdo_stmt->fetch(PDO::FETCH_ASSOC); - $chan->push(['pdo', $result]); - }); - go(function () use ($chan, $mysql_stmt, $fid) { - $result = $mysql_stmt->execute([$fid])[0]; - $chan->push(['mysql_prepare', $result]); - }); - go(function () use ($chan, $mysql_query, $sql, $fid) { - $chan->push(['mysql_query', $mysql_query->query("{$sql}{$fid}")[0]]); - }); - for ($i = 3; $i--;) { - list($from, $result) = $chan->pop(); - if ($result['fid'] === $fid) { - Assert::same($result['firmware'], $firmware); - Assert::same($result['f_md5'], $f_md5); - Assert::same($result['f_remark'], $f_remark); - } else { - Assert::assert(0, 'wrong result from ' . $from); - unset($result['firmware']); // too long to show - phpt_var_dump($result); - } - phpt_var_dump(sprintf('%-16s: %s', $from, (strlen($firmware) / 1024 / 1024) . 'M')); - } - } - } - echo "DONE\n"; -}); -Swoole\Event::wait(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/not_exist.phpt b/tests/swoole_mysql_coro/not_exist.phpt deleted file mode 100644 index 280b1fad61c..00000000000 --- a/tests/swoole_mysql_coro/not_exist.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql connect to wrong database ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => 'not_exist' - ]; - $connected = $db->connect($server); - Assert::assert(!$connected); - Assert::same($db->connect_errno, 1049); // unknown database - Assert::assert(strpos($db->connect_error, 'not_exist')); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/null.phpt b/tests/swoole_mysql_coro/null.phpt deleted file mode 100644 index 9f29d660ca9..00000000000 --- a/tests/swoole_mysql_coro/null.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql null ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]); - Assert::assert($connected); - Assert::assert($mysql->query('INSERT INTO `custom` (`content`) VALUES (NULL)')); - Assert::assert($mysql->query('INSERT INTO `custom` (`content`) VALUES ("")')); - Assert::assert($mysql->query('INSERT INTO `custom` (`content`) VALUES ("NULL")')); - $result = $mysql->query('select `content` from custom'); - var_dump(array_merge_recursive(...$result)['content']); - Assert::assert($mysql->query('TRUNCATE TABLE `custom`')); - - $stmt = $mysql->prepare('INSERT INTO `custom` (`content`) VALUES (?)'); - Assert::assert($stmt->execute([NULL])); - Assert::assert($stmt->execute([''])); - Assert::assert($stmt->execute(['NULL'])); - $result = $mysql->query('select `content` from custom'); - var_dump(array_merge_recursive(...$result)['content']); - Assert::assert($mysql->query('TRUNCATE TABLE `custom`')); -}); -?> ---EXPECT-- -array(3) { - [0]=> - NULL - [1]=> - string(0) "" - [2]=> - string(4) "NULL" -} -array(3) { - [0]=> - NULL - [1]=> - string(0) "" - [2]=> - string(4) "NULL" -} diff --git a/tests/swoole_mysql_coro/null_bit_map.phpt b/tests/swoole_mysql_coro/null_bit_map.phpt deleted file mode 100644 index f8cdec902e0..00000000000 --- a/tests/swoole_mysql_coro/null_bit_map.phpt +++ /dev/null @@ -1,103 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql null bit map rand test ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert($connected); - return $mysql; -} - -for ($c = MAX_CONCURRENCY_LOW; $c--;) { - go(function () use ($c) { - // gen table structure - $table_name = 't' . substr(md5(mt_rand()), 0, 15); - $field_size = mt_rand(1, 100); - list($fields, $fields_info) = (function () use ($field_size) { - $fields_info = []; - $fields = ''; - for ($i = $field_size; $i--;) { - $info = $fields_info[] = [ - 'name' => 'f' . substr(md5(mt_rand()), 0, 7), - 'type' => gen_type() - ]; - $fields .= "{$info['name']} {$info['type']} NULL,\n"; - } - return [rtrim($fields, " \n,"), $fields_info]; - })(); - $mysql = mysql(); - // create table - $createTable = <<query($createTable)) { - trigger_error("create table error by query statement [{$createTable}]", E_USER_WARNING); - return; - } - $_insert = "INSERT INTO {$table_name} VALUES (" . rtrim(str_repeat('?, ', $field_size + 1), ', ') . ")"; - $data_list = []; - try { - for ($n = MAX_REQUESTS; $n--;) { - $insert = $mysql->prepare($_insert); - Assert::assert($insert instanceof Co\Mysql\Statement); - $data_list[] = $gen = (function ($id, $fields_info) { - $r = ['id' => $id]; - foreach ($fields_info as $info) { - if (mt_rand(0, 1)) { - $r[$info['name']] = null; - } else { - $r[$info['name']] = gen_data_from_type($info['type']); - } - } - return $r; - })($n + 1, $fields_info); - Assert::assert($insert->execute(array_values($gen))); - } - $result = $mysql->prepare("SELECT * FROM {$table_name}")->execute(); - Assert::same(array_reverse($data_list), $result); - } catch (Throwable $e) { - Assert::assert(0); - } finally { - Assert::assert($mysql->query("DROP TABLE {$table_name}")); - } - }); -} -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/numbers.phpt b/tests/swoole_mysql_coro/numbers.phpt deleted file mode 100644 index 6dfd2c217b7..00000000000 --- a/tests/swoole_mysql_coro/numbers.phpt +++ /dev/null @@ -1,143 +0,0 @@ ---TEST-- -swoole_mysql_coro: floating point value precision and unsigned big int overflow ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => (PHP_VERSION_ID >= 80100) - ]; - $db->connect($server); - $r_string1 = $db->query('SELECT * FROM numbers'); - $db->close(); - $server['strict_type'] = true; - $db->connect($server); - $r_strong1 = $db->query('SELECT * FROM numbers'); - $stmt = $db->prepare('SELECT * FROM numbers'); - $r_strong2 = $stmt->execute(); - - try { - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $r_string2 = $pdo->query('SELECT * FROM numbers')->fetchAll(PDO::FETCH_ASSOC); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $stmt = $pdo->prepare('SELECT * FROM numbers'); - $stmt->execute(); - $r_strong3 = $stmt->fetchAll(PDO::FETCH_ASSOC); - Assert::same($r_string1, $r_string2); - Assert::same($r_strong2, $r_strong3); - } catch (\PDOException $e) { - Assert::same($e->getCode(), 2054); // not support auth plugin - } - - if (!is_musl_libc()) { - Assert::same($r_strong1, $r_strong2); - } - var_dump($r_strong2); -}); -?> ---EXPECT-- -array(3) { - [0]=> - array(13) { - ["id"]=> - int(1) - ["tinyint"]=> - int(127) - ["utinyint"]=> - int(255) - ["smallint"]=> - int(32767) - ["usmallint"]=> - int(65535) - ["mediumint"]=> - int(8388607) - ["umediumint"]=> - int(16777215) - ["int"]=> - int(2147483647) - ["uint"]=> - int(4294967294) - ["bigint"]=> - int(9223372036854775807) - ["ubigint"]=> - string(20) "18446744073709551615" - ["float"]=> - float(1.23457) - ["double"]=> - float(1.2345678901234567) - } - [1]=> - array(13) { - ["id"]=> - int(2) - ["tinyint"]=> - int(-128) - ["utinyint"]=> - int(123) - ["smallint"]=> - int(-32768) - ["usmallint"]=> - int(12345) - ["mediumint"]=> - int(-8388608) - ["umediumint"]=> - int(123456) - ["int"]=> - int(-2147483648) - ["uint"]=> - int(123456) - ["bigint"]=> - int(-9223372036854775808) - ["ubigint"]=> - int(123456) - ["float"]=> - float(-1.23457) - ["double"]=> - float(-1.2345678901234567) - } - [2]=> - array(13) { - ["id"]=> - int(3) - ["tinyint"]=> - int(0) - ["utinyint"]=> - int(0) - ["smallint"]=> - int(0) - ["usmallint"]=> - int(0) - ["mediumint"]=> - int(0) - ["umediumint"]=> - int(0) - ["int"]=> - int(0) - ["uint"]=> - int(0) - ["bigint"]=> - int(0) - ["ubigint"]=> - int(0) - ["float"]=> - float(1.23) - ["double"]=> - float(1.23) - } -} diff --git a/tests/swoole_mysql_coro/prepare_field_type.phpt b/tests/swoole_mysql_coro/prepare_field_type.phpt deleted file mode 100644 index 339ef2c6e11..00000000000 --- a/tests/swoole_mysql_coro/prepare_field_type.phpt +++ /dev/null @@ -1,48 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare field type ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true, - ]; - - $ret1 = $db->connect($server); - if (! $ret1) { - echo "CONNECT ERROR\n"; - - return; - } - - $stmt = $db->prepare('SELECT ? as a, ? as b, ? as c, ? as d, ? + ? as e'); - if (! $stmt) { - echo "PREPARE ERROR\n"; - - return; - } - - $ret3 = $stmt->execute([123, 3.14, true, false, 11, 22]); - if (! $ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - - return; - } - if (Assert::isArray($ret3)) { - Assert::same(reset($ret3), ['a' => 123, 'b' => 3.14, 'c' => 1, 'd' => 0, 'e' => 33]); - } -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/prepare_insert.phpt b/tests/swoole_mysql_coro/prepare_insert.phpt deleted file mode 100644 index 152a5e2dd83..00000000000 --- a/tests/swoole_mysql_coro/prepare_insert.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (insert) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ); - - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT ERROR\n"; - return; - } - - $stmt = $db->prepare('INSERT INTO ckl (`domain`,`path`,`name`) VALUES (?,?,?)'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute(array('www.baidu.com', '/search', 'baidu')); - if (!$ret3) { - echo "EXECUTE ERROR\n"; - return; - } - Assert::assert($stmt->insert_id > 0); - Assert::assert($db->insert_id == $stmt->insert_id); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/prepare_multi.phpt b/tests/swoole_mysql_coro/prepare_multi.phpt deleted file mode 100644 index ca73c5eab19..00000000000 --- a/tests/swoole_mysql_coro/prepare_multi.phpt +++ /dev/null @@ -1,49 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare multi (insert and delete) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - $connected = $db->connect($server); - if (!$connected) { - echo "CONNECT ERROR\n"; - return; - } - for ($n = MAX_REQUESTS_MID; $n--;) { - $statement = $db->prepare('INSERT INTO ckl (`domain`,`path`,`name`) VALUES (?, ?, ?)'); - if (!$statement) { - echo "PREPARE ERROR\n"; - return; - } - $executed = $statement->execute(['www.baidu.com', '/search', 'baidu']); - if (!$executed) { - echo "EXECUTE ERROR\n"; - return; - } - if ($statement->insert_id > 0) { - $deleted = $db->query("DELETE FROM ckl WHERE id={$statement->insert_id}"); - if (!$deleted) { - echo "DELETE ERROR\n"; - } - } else { - echo "INSERT ERROR\n"; - } - } - }); -} -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/prepare_select.phpt b/tests/swoole_mysql_coro/prepare_select.phpt deleted file mode 100644 index 46ebc89112a..00000000000 --- a/tests/swoole_mysql_coro/prepare_select.phpt +++ /dev/null @@ -1,42 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (select) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ); - - $ret1 = $db->connect($server); - if (!$ret1) { - echo "CONNECT ERROR\n"; - return; - } - - $stmt = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt) { - echo "PREPARE ERROR\n"; - return; - } - - $ret3 = $stmt->execute([5]); - if (!$ret3) { - echo "EXECUTE ERROR#{$stmt->errno}: {$stmt->error}\n"; - return; - } - Assert::assert(count($ret3) > 0); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/procedure.phpt b/tests/swoole_mysql_coro/procedure.phpt deleted file mode 100644 index b0fe8966c93..00000000000 --- a/tests/swoole_mysql_coro/procedure.phpt +++ /dev/null @@ -1,60 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure without fetch mode ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - - if ($db->query($clear) && $db->query($procedure)) { - $stmt = $db->prepare('CALL reply(?)'); - for ($n = MAX_REQUESTS; $n--;) { - //SWOOLE - $_map = $map; - $res = $stmt->execute(['hello mysql!']); - do { - Assert::same(current($res[0]), array_shift($_map)); - } while ($res = $stmt->nextResult()); - Assert::same($stmt->affected_rows, 1, 'get the affected rows failed!'); - Assert::assert(empty($_map), 'there are some results lost!'); - } - } - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_by_query.phpt b/tests/swoole_mysql_coro/procedure_by_query.phpt deleted file mode 100644 index 4d1a7470dbd..00000000000 --- a/tests/swoole_mysql_coro/procedure_by_query.phpt +++ /dev/null @@ -1,63 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure without fetch mode by query ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]; - - $clear = <<connect($server); - - if ($db->query($clear) && $db->query($procedure)) { - for ($n = MAX_REQUESTS; $n--;) { - $_map = $map; - $res = $db->query('CALL reply("hello mysql!")'); - do { - if (is_array($res)) { - Assert::same(current($res[0]), array_shift($_map)); - } else { - Assert::true($res); - } - } while ($res = $db->nextResult()); - Assert::same($db->affected_rows, 1); - Assert::assert(empty($_map), 'there are some results lost!'); - } - } - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_in_fetch.phpt b/tests/swoole_mysql_coro/procedure_in_fetch.phpt deleted file mode 100644 index baf72fdb7cc..00000000000 --- a/tests/swoole_mysql_coro/procedure_in_fetch.phpt +++ /dev/null @@ -1,82 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure in fetch mode ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $clear = <<connect($server); - if ($db->query($clear) && $db->query($procedure)) { - - //SWOOLE - $_map = $map; - $stmt = $db->prepare('CALL reply(?)'); - Assert::true($stmt->execute(['hello mysql!'])); - do { - $res = $stmt->fetchAll(); - Assert::same(current($res[0]), array_shift($_map)); - } while ($ret = $stmt->nextResult()); - Assert::same($stmt->affected_rows, 1); - Assert::assert(empty($_map), 'there are some results lost!'); - - //PDO - if (extension_loaded('PDO')) { - $_map = $map; - try { - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $stmt = $pdo->prepare("CALL reply(?)"); - Assert::true($stmt->execute(['hello mysql!'])); - do { - $res = $stmt->fetchAll(); - Assert::same(current($res[0]), array_shift($_map)); - } while ($ret = $stmt->nextRowset() and count($_map) > 0); - Assert::same($stmt->rowCount(), 1, 'get the affected rows failed!'); - Assert::assert(empty($_map), 'there are some results lost!'); - } catch (\PDOException $e) { - Assert::same($e->getCode(), 2054); // not support auth plugin - } - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_single.phpt b/tests/swoole_mysql_coro/procedure_single.phpt deleted file mode 100644 index cc876712e77..00000000000 --- a/tests/swoole_mysql_coro/procedure_single.phpt +++ /dev/null @@ -1,42 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql procedure single ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - if ($db->query($clear) && $db->query($procedure)) { - $stmt = $db->prepare('CALL say(?)'); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $stmt->execute(['hello mysql!']); - Assert::same(current($ret[0]), 'You said: "hello mysql!"'); - Assert::null($stmt->nextResult()); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_with_query.phpt b/tests/swoole_mysql_coro/procedure_with_query.phpt deleted file mode 100644 index 1fec3a58a6e..00000000000 --- a/tests/swoole_mysql_coro/procedure_with_query.phpt +++ /dev/null @@ -1,65 +0,0 @@ ---TEST-- -swoole_mysql_coro: procedure without query (#2117) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - - if ($db->query($clear) && $db->query($procedure)) { - for ($n = MAX_REQUESTS_LOW; $n--;) { - $res = $db->query('CALL reply("hello mysql!")'); - $_map = $map; - do { - Assert::same(current($res[0]), array_shift($_map)); - } while ($res = $db->nextResult()); - } - for ($n = MAX_REQUESTS_LOW; $n--;) { - $res = $db->query('CALL reply("hello mysql!")'); - $_map = $map; - do { - Assert::same(current($res[0]), array_shift($_map)); - } while ($res = $db->nextResult()); - Assert::same($db->affected_rows, 1, 'get the affected rows failed!'); - Assert::assert(empty($_map), 'there are some results lost!'); - } - } - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/procedure_with_query_and_prepare.phpt b/tests/swoole_mysql_coro/procedure_with_query_and_prepare.phpt deleted file mode 100644 index 22a92273e5d..00000000000 --- a/tests/swoole_mysql_coro/procedure_with_query_and_prepare.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -swoole_mysql_coro: query 'CALL' statement & prepare (#2117) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - - $clear = <<connect($server); - if ($db->query($clear) && $db->query($procedure)) { - $db->query('CALL sp_whoami()'); - Assert::null($db->nextResult()); - $stmt = $db->prepare('CALL sp_whoami()'); - $ret = $stmt->execute(); - Assert::assert(strpos(current($ret[0]), MYSQL_SERVER_USER) !== false); - Assert::null($stmt->nextResult()); - } -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/query.phpt b/tests/swoole_mysql_coro/query.phpt deleted file mode 100644 index d459d979df8..00000000000 --- a/tests/swoole_mysql_coro/query.phpt +++ /dev/null @@ -1,63 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$pm->childFunc = function () use ($pm) -{ - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (Swoole\Server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) - { - $mysql = new Swoole\Coroutine\MySQL(); - $res = $mysql->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - if (!$res) - { - fail: - $response->end("ERROR\n"); - return; - } - $ret = $mysql->query('show tables', 2); - if (!$ret) { - goto fail; - } - if (count($ret) > 0) { - $response->end("OK\n"); - } - }); - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_mysql_coro/query_multifield.phpt b/tests/swoole_mysql_coro/query_multifield.phpt deleted file mode 100644 index b39c02da277..00000000000 --- a/tests/swoole_mysql_coro/query_multifield.phpt +++ /dev/null @@ -1,31 +0,0 @@ ---TEST-- -swoole_mysql_coro: multi field ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $ret = $db->connect($server); - if (Assert::true($ret)) { - $n = range(0, FIELD_NUM - 1); - $fields = implode(", ", $n); - $result = $db->query("select $fields"); - Assert::assert(count($result[0]) == FIELD_NUM); - echo "DONE\n"; - } -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/query_timeout.phpt b/tests/swoole_mysql_coro/query_timeout.phpt deleted file mode 100644 index 5105153f733..00000000000 --- a/tests/swoole_mysql_coro/query_timeout.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql query timeout ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - if (Assert::true($ret)) { - $s = microtime(true); - $timeout = mt_rand(100, 500) / 1000; - $ret = $mysql->query('select sleep(1)', $timeout); - time_approximate($timeout, microtime(true) - $s); - if (Assert::false($ret)) { - Assert::same($mysql->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/readonly.phpt b/tests/swoole_mysql_coro/readonly.phpt deleted file mode 100644 index 69850419b46..00000000000 --- a/tests/swoole_mysql_coro/readonly.phpt +++ /dev/null @@ -1,53 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql use readonly user ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $connected = $root->connect($server); - Assert::assert($connected); - - // create read only user - $create = $root->query('CREATE USER `readonly`@`%` IDENTIFIED BY \'123456\';'); - Assert::assert($create); - $grant = $root->query('GRANT SELECT ON *.* TO `readonly`@`%` WITH GRANT OPTION;'); - Assert::assert($grant); - - // use readonly - $server['user'] = 'readonly'; - $server['password'] = '123456'; - $readonly = new Swoole\Coroutine\MySQL; - $connected = $readonly->connect($server); - Assert::assert($connected); - - // read - $result = $readonly->query('SELECT * FROM userinfo'); - Assert::assert(is_array($result) && count($result) > 5); - $id = $result[0]['id']; - // write - $delete = $readonly->query('DELETE FROM userinfo WHERE id=' . $id); - Assert::assert(!$delete); - echo $readonly->errno . "\n"; - echo $readonly->error . "\n"; - - // drop - Assert::assert($root->query('DROP ROLE readonly')); -}); -Swoole\Event::wait(); -?> ---EXPECTF-- -1142 -SQLSTATE[42000] [1142] DELETE command denied to user 'readonly'@'%s' for table 'userinfo' diff --git a/tests/swoole_mysql_coro/simple_query.phpt b/tests/swoole_mysql_coro/simple_query.phpt deleted file mode 100644 index 484696df28b..00000000000 --- a/tests/swoole_mysql_coro/simple_query.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql simple query ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert($res); - $ret = $mysql->query('show tables', 2); - Assert::assert(is_array($ret)); - Assert::assert(count($ret) > 0); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/statement_closed.phpt b/tests/swoole_mysql_coro/statement_closed.phpt deleted file mode 100644 index 2d908376287..00000000000 --- a/tests/swoole_mysql_coro/statement_closed.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (destruct) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $ret = $db->connect($server); - assert($ret); - $statement = $db->prepare('SELECT 1'); - assert($statement instanceof Co\Mysql\Statement); - $ret = $statement->execute(); - assert($ret[0][1] === 1); - $db->close(); - $ret = $db->connect($server); - assert($ret); - $ret = $statement->execute(); - assert(!$ret); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/statement_destruct.phpt b/tests/swoole_mysql_coro/statement_destruct.phpt deleted file mode 100644 index a226edb2bab..00000000000 --- a/tests/swoole_mysql_coro/statement_destruct.phpt +++ /dev/null @@ -1,53 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (destruct) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - ]; - - $ret1 = $db->connect($server); - - $start_prepared_num = (int)(($db->query('show status like \'Prepared_stmt_count\''))[0]['Value']); - if (!$ret1) { - echo "CONNECT ERROR\n"; - return; - } - $stmt1 = $db->prepare('SELECT * FROM userinfo WHERE id=?'); - if (!$stmt1) { - echo "PREPARE1 ERROR\n"; - return; - } - $stmt2 = $db->prepare('SELECT * FROM `userinfo`'); - if (!$stmt2) { - echo "PREPARE2 ERROR\n"; - return; - } - $stmt3 = $db->prepare('SELECT `id` FROM `userinfo`'); - if (!$stmt3) { - echo "PREPARE3 ERROR\n"; - return; - } - - $prepared_num1 = (int)(($db->query('show status like \'Prepared_stmt_count\''))[0]['Value']); - Assert::same($prepared_num1 - $start_prepared_num, 3); - $stmt1 = null; //destruct - unset($stmt2); //destruct - $prepared_num2 = (int)(($db->query('show status like \'Prepared_stmt_count\''))[0]['Value']); - Assert::same($prepared_num1 - $prepared_num2, 2); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/timeout.phpt b/tests/swoole_mysql_coro/timeout.phpt deleted file mode 100644 index 9c0e78abb92..00000000000 --- a/tests/swoole_mysql_coro/timeout.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_mysql_coro: timeout ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert($connected); - $statement = $mysql->prepare('SELECT SLEEP(1)'); - Assert::assert($statement instanceof Co\Mysql\Statement); - $timeout = ms_random(0.1, 0.5); - $s = microtime(true); - $use_query = !!mt_rand(0, 1); - if ($use_query) { - $ret = $mysql->query('SELECT SLEEP(1)', $timeout); - } else { - $ret = $statement->execute(null, $timeout); - } - time_approximate($timeout, microtime(true) - $s); - Assert::assert(!$ret); - Assert::same($use_query ? $mysql->errno : $statement->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - }); -} -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/transaction.phpt b/tests/swoole_mysql_coro/transaction.phpt deleted file mode 100644 index 34b9ff47f09..00000000000 --- a/tests/swoole_mysql_coro/transaction.phpt +++ /dev/null @@ -1,44 +0,0 @@ ---TEST-- -swoole_mysql_coro: transaction ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - $db->connect($server); - - $random = mt_rand(); - Assert::assert($db->begin()); - Assert::assert($db->query('INSERT INTO ckl (`domain`,`path`,`name`) VALUES ("www.swoole.com", "/", "' . $random . '")')); - Assert::assert(!empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - Assert::assert($db->rollback()); - Assert::assert(empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - $random = mt_rand(); - Assert::assert($db->begin()); - Assert::assert($db->query('INSERT INTO ckl (`domain`,`path`,`name`) VALUES ("www.swoole.com", "/", "' . $random . '")')); - Assert::assert($db->commit()); - Assert::assert(!empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - Assert::assert($db->query('DELETE FROM `ckl` WHERE `name`="' . $random . '"')); - Assert::assert(empty($db->query('SELECT `name` FROM `ckl` WHERE `name`="' . $random . '"'))); - - $db->setDefer(); - Assert::throws(function () use ($db) { $db->begin(); }, Exception::class); - Assert::throws(function () use ($db) { $db->commit(); }, Exception::class); - Assert::throws(function () use ($db) { $db->rollback(); }, Exception::class); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/unixsocket.phpt b/tests/swoole_mysql_coro/unixsocket.phpt deleted file mode 100644 index 377ee006d04..00000000000 --- a/tests/swoole_mysql_coro/unixsocket.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql connection on unix socket ---SKIPIF-- - ---FILE-- - 'unix:/' . MYSQL_SERVER_PATH, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]; - Assert::assert($db->connect($server)); - Assert::same($db->query('SELECT 1'), [['1' => '1']]); - echo "DONE\n"; -}); -?> ---EXPECTF-- -DONE diff --git a/tests/swoole_mysql_coro/userinfo.phpt b/tests/swoole_mysql_coro/userinfo.phpt deleted file mode 100644 index f4af02765ac..00000000000 --- a/tests/swoole_mysql_coro/userinfo.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare dtor ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => (PHP_VERSION_ID >= 80100) - ]); - $result = $mysql->query('SELECT * FROM `userinfo`'); - $pdo = new PDO( - "mysql:host=" . MYSQL_SERVER_HOST . ";port=" . MYSQL_SERVER_PORT . ";dbname=" . MYSQL_SERVER_DB . ";charset=utf8", - MYSQL_SERVER_USER, MYSQL_SERVER_PWD - ); - $pdo_result = $pdo->query('SELECT * FROM `userinfo`')->fetchAll(PDO::FETCH_ASSOC); - Assert::same($result, $pdo_result); - - $result = $mysql->prepare('SELECT * FROM `userinfo`')->execute(); - $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); - $pdo_stmt = $pdo->prepare('SELECT * FROM `userinfo`'); - $pdo_stmt->execute(); - $pdo_result =$pdo_stmt->fetchAll(PDO::FETCH_ASSOC); - - Assert::same($result, $pdo_result); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_mysql_coro/without_fetch.phpt b/tests/swoole_mysql_coro/without_fetch.phpt deleted file mode 100644 index 253444f1a36..00000000000 --- a/tests/swoole_mysql_coro/without_fetch.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_mysql_coro: just execute (test memory leak) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'fetch_mode' => true - ]; - - $db->connect($server); - $stmt = $db->prepare('SELECT * FROM `userinfo` LIMIT 1'); - Assert::true($stmt->execute()); - Assert::true($stmt->execute()); - Assert::true($stmt->execute()); - Assert::assert(is_array($stmt->fetchAll())); -}); -?> ---EXPECT-- diff --git a/tests/swoole_mysql_coro/wrong_password.phpt b/tests/swoole_mysql_coro/wrong_password.phpt deleted file mode 100644 index 8b7e619254d..00000000000 --- a/tests/swoole_mysql_coro/wrong_password.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql connect with wrong password ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => 'i am hack', - 'database' => MYSQL_SERVER_DB - ]; - $connected = $db->connect($server); - Assert::assert(!$connected); - echo $db->connect_errno . "\n"; - echo $db->connect_error, "\n"; -}); -?> ---EXPECTF-- -1045 -SQLSTATE[28000] [1045] Access denied for user 'root'@'%s' (using password: YES) diff --git a/tests/swoole_mysql_coro/z_reset.phpt b/tests/swoole_mysql_coro/z_reset.phpt deleted file mode 100644 index fd307469210..00000000000 --- a/tests/swoole_mysql_coro/z_reset.phpt +++ /dev/null @@ -1,11 +0,0 @@ ---TEST-- -swoole_mysql_coro: reset test mysql database ---SKIPIF-- - ---FILE-- - /dev/null`; -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/auth.phpt b/tests/swoole_redis_coro/auth.phpt deleted file mode 100644 index c872c100022..00000000000 --- a/tests/swoole_redis_coro/auth.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_redis_coro: redis auth ---SKIPIF-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); -if (!$redis->auth(REDIS_SERVER_PWD)) { - skip('no auth'); -} -?> ---FILE-- -getAuth()); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::false($redis->getAuth()); - Assert::assert(!$redis->auth(get_safe_random())); - Assert::same($redis->errCode, SOCKET_EINVAL); - Assert::false($redis->getAuth()); - Assert::assert($redis->auth(REDIS_SERVER_PWD)); - Assert::same($redis->getAuth(), REDIS_SERVER_PWD); - // auth by connect - $redis = new Swoole\Coroutine\Redis(['password' => REDIS_SERVER_PWD]); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::assert($redis->set('foo', $random = get_safe_random())); - Assert::same($redis->get('foo'), $random); - // auth failed when connect - $redis = new Swoole\Coroutine\Redis(['password' => get_safe_random()]); - Assert::assert(!$redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/auto_reconnect.phpt b/tests/swoole_redis_coro/auto_reconnect.phpt deleted file mode 100644 index 7806f315daf..00000000000 --- a/tests/swoole_redis_coro/auto_reconnect.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -swoole_redis_coro: redis reconnect ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - $ret = $redis->close(); - Assert::assert($ret); - $ret = $redis->set('foo', 'bar'); - Assert::assert($ret); - $ret = $redis->get('foo'); - Assert::same($ret, 'bar'); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/auto_reconnect_ex.phpt b/tests/swoole_redis_coro/auto_reconnect_ex.phpt deleted file mode 100644 index fa4dabb587f..00000000000 --- a/tests/swoole_redis_coro/auto_reconnect_ex.phpt +++ /dev/null @@ -1,68 +0,0 @@ ---TEST-- -swoole_redis_coro: auto reconnect after server side close the connection ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - Co\run(function () use ($pm) { - $redis = new Swoole\Coroutine\Redis; - $ret = $redis->connect('127.0.0.1', $pm->getFreePort()); - Assert::true($ret); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::true($ret, "code: {$redis->errCode}, msg={$redis->errMsg}"); - $ret = $redis->get('random_val'); - Assert::true($ret && $ret === $random, "code: {$redis->errCode}, msg={$redis->errMsg}"); - Co::sleep(0.001); - } - $redis->setOptions(['reconnect' => false]); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::true($n === MAX_REQUESTS ? $ret : !$ret); - $ret = $redis->get('random_val'); - Assert::true($n === MAX_REQUESTS ? ($ret && $ret === $random) : !$ret); - Co::sleep(0.001); - } - }); - $pm->kill(); - echo "DONE\n"; -}; -$pm->childFunc = function () use ($pm) { - $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->data = []; - $server->on('WorkerStart', function ($server) use ($pm) { - $pm->wakeup(); - }); - $server->setHandler('GET', function ($fd, $data) use ($server) { - if (count($data) == 0) { - return Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command"); - } - $key = $data[0]; - if (empty($server->data[$key])) { - $server->send($fd, Server::format(Server::NIL)); - } else { - $server->send($fd, Server::format(Server::STRING, $server->data[$key])); - } - $server->close($fd); - }); - $server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); - } - $key = $data[0]; - $server->data[$key] = $data[1]; - $server->send($fd, Server::format(Server::STATUS, 'OK')); - }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/basic.phpt b/tests/swoole_redis_coro/basic.phpt deleted file mode 100644 index 8afcfcaaab8..00000000000 --- a/tests/swoole_redis_coro/basic.phpt +++ /dev/null @@ -1,63 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$pm->childFunc = function () use ($pm) -{ - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (Swoole\Server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) - { - $redis = new Swoole\Coroutine\Redis(); - $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - if (!$res) - { - fail: - $response->end("ERROR\n"); - return; - } - - $ret = $redis->set('key', 'value'); - if (!$ret) { - goto fail; - } - $ret = $redis->get('key'); - if (!$ret) { - goto fail; - } - Assert::same($ret, "value"); - if (strlen($ret) > 0) { - $response->end("OK\n"); - } - }); - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/bug_lock.phpt b/tests/swoole_redis_coro/bug_lock.phpt deleted file mode 100644 index 1eb2c6e5c3e..00000000000 --- a/tests/swoole_redis_coro/bug_lock.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -lock('SWOOLE_TEST_LOCK')) { - echo "ERROR\n"; - $redis_lock->unlock('SWOOLE_TEST_LOCK'); - } else { - echo "FREE\n"; - } - } - SQLPool::release(); -}); - -Swoole\Event::wait(); -?> ---EXPECT-- -LOCK -FREE -LOCK -ERROR -LOCK -FREE diff --git a/tests/swoole_redis_coro/compatibility_mode/hExists.phpt b/tests/swoole_redis_coro/compatibility_mode/hExists.phpt deleted file mode 100644 index d93df485443..00000000000 --- a/tests/swoole_redis_coro/compatibility_mode/hExists.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -swoole_redis_coro/compatibility_mode: hExists ---SKIPIF-- - ---FILE-- -setOptions(['compatibility_mode' => true]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis->delete(KEY); - $redis->hSet(KEY, 'field', 'val1'); - - Assert::true($redis->hExists(KEY, 'field') === true); - Assert::true($redis->hExists(KEY, 'field_not_found') === false); -}); -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/connect_timeout.phpt b/tests/swoole_redis_coro/connect_timeout.phpt deleted file mode 100644 index cf49ac852cf..00000000000 --- a/tests/swoole_redis_coro/connect_timeout.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client connect timeout ---SKIPIF-- - ---FILE-- - $timeout]); - $s = microtime(true); - $ret = $redis->connect('192.0.0.1', 9000); - Assert::assert(!$ret); - Assert::assert($redis->errCode === SOCKET_ETIMEDOUT); - time_approximate($timeout, microtime(true) - $s); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/connect_to_wrong.phpt b/tests/swoole_redis_coro/connect_to_wrong.phpt deleted file mode 100644 index c58384b409e..00000000000 --- a/tests/swoole_redis_coro/connect_to_wrong.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client set options ---SKIPIF-- - ---FILE-- - -1]); -go(function () { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(MYSQL_SERVER_HOST, MYSQL_SERVER_PORT); - Assert::assert(!$redis->set('foo', 'bar')); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_PROTOCOL); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/connect_twice-2.phpt b/tests/swoole_redis_coro/connect_twice-2.phpt deleted file mode 100644 index 126862278fe..00000000000 --- a/tests/swoole_redis_coro/connect_twice-2.phpt +++ /dev/null @@ -1,64 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$pm->childFunc = function () use ($pm) -{ - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (Swoole\Server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) - { - $redis = new Swoole\Coroutine\Redis(); - $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $res2 = @$redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::true($res2); - if (!$res) - { - fail: - $response->end("ERROR\n"); - return; - } - $ret = $redis->set('key', 'value'); - if (!$ret) { - goto fail; - } - $ret = $redis->get('key'); - if (!$ret) { - goto fail; - } - Assert::same($ret, "value"); - if (strlen($ret) > 0) { - $response->end("OK\n"); - } - }); - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/connect_twice.phpt b/tests/swoole_redis_coro/connect_twice.phpt deleted file mode 100644 index 6bc35009d85..00000000000 --- a/tests/swoole_redis_coro/connect_twice.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -swoole_redis_coro: connect twice ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -go(function () { - $redis = new Swoole\Coroutine\Redis(); - echo "connect [1]\n"; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::true($redis->connected); - echo "close [1]\n"; - $redis->close(); - Assert::false($redis->connected); - echo "connect [2]\n"; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::true($redis->connected); - echo "close [2]\n"; - $redis->close(); - Assert::false($redis->connected); -}); - -Swoole\Event::wait(); -?> ---EXPECT-- -connect [1] -close [1] -connect [2] -close [2] diff --git a/tests/swoole_redis_coro/curd.phpt b/tests/swoole_redis_coro/curd.phpt deleted file mode 100644 index fb3d0e6208f..00000000000 --- a/tests/swoole_redis_coro/curd.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_redis_coro: use unixsocket ---SKIPIF-- - ---FILE-- - 0.5]); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - for ($c = MAX_CONCURRENCY_MID; $c--;) { - for ($n = MAX_REQUESTS; $n--;) { - $key = md5(get_safe_random(mt_rand(1, 128))); - $value = md5(get_safe_random(mt_rand(1, 128))); - Assert::assert($redis->set($key, $value)); - Assert::same($redis->get($key), $value); - Assert::assert($redis->delete($key)); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/defer.phpt b/tests/swoole_redis_coro/defer.phpt deleted file mode 100644 index db4d238b1ed..00000000000 --- a/tests/swoole_redis_coro/defer.phpt +++ /dev/null @@ -1,45 +0,0 @@ ---TEST-- -swoole_redis_coro: defer ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -go(function () { - $redis = new Swoole\Coroutine\Redis(); - echo "CONNECT [1]\n"; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis->setDefer(); - echo "SET [1]\n"; - $redis->set('key1', 'value'); - - $redis2 = new Swoole\Coroutine\Redis(); - echo "CONNECT [2]\n"; - $redis2->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis2->setDefer(); - echo "GET [2]\n"; - $redis2->get('key1'); - - echo "RECV [1]\n"; - $result1 = $redis->recv(); - var_dump($result1); - - echo "RECV [2]\n"; - $result2 = $redis2->recv(); - var_dump($result2); -}); - -Swoole\Event::wait(); -?> ---EXPECT-- -CONNECT [1] -SET [1] -CONNECT [2] -GET [2] -RECV [1] -bool(true) -RECV [2] -string(5) "value" diff --git a/tests/swoole_redis_coro/different_connect.phpt b/tests/swoole_redis_coro/different_connect.phpt deleted file mode 100644 index 51bcb46567c..00000000000 --- a/tests/swoole_redis_coro/different_connect.phpt +++ /dev/null @@ -1,47 +0,0 @@ ---TEST-- -swoole_redis_coro: connect the same target and different ---SKIPIF-- - ---FILE-- - -1]); -function test(string $host, int $port = 0) -{ - $redis = new Swoole\Coroutine\Redis(); - Assert::same($redis->sock, -1); - - $real_connect_time = microtime(true); - $ret = $redis->connect($host, $port); - $real_connect_time = microtime(true) - $real_connect_time; - - Assert::assert($ret); - Assert::assert(($fd = $redis->sock) > 0); - - $fake_connect_time = 0; - for ($n = MAX_REQUESTS; $n--;) { - $fake_connect_time = microtime(true); - $ret = $redis->connect($host, $port); - $fake_connect_time = microtime(true) - $fake_connect_time; - Assert::assert($ret); - Assert::assert($fake_connect_time < $real_connect_time); - } - - $real_connect_time = microtime(true); - $redis->connect(MYSQL_SERVER_HOST, MYSQL_SERVER_PORT); - $real_connect_time = microtime(true) - $real_connect_time; - Assert::assert($fake_connect_time < $real_connect_time); - Assert::assert(!$redis->get('foo')); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_PROTOCOL); -} - -go('test', REDIS_SERVER_HOST, REDIS_SERVER_PORT); -if (file_exists(REDIS_SERVER_PATH)) { - go('test', 'unix:' . str_repeat('/', mt_rand(1, 3)) . REDIS_SERVER_PATH); -} - -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/disable_retry.phpt b/tests/swoole_redis_coro/disable_retry.phpt deleted file mode 100644 index 6e5149a1cfe..00000000000 --- a/tests/swoole_redis_coro/disable_retry.phpt +++ /dev/null @@ -1,68 +0,0 @@ ---TEST-- -swoole_redis_coro: disable retry ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - go(function () use ($pm) { - $redis = new Swoole\Coroutine\Redis; - $ret = $redis->connect('127.0.0.1', $pm->getFreePort()); - Assert::assert($ret); - $redis->setOptions(['retry' => false]); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::assert($ret); - $ret = $redis->get('random_val'); - if ($n % 2) { - Assert::same($ret, $random); - } else { - Assert::assert(!$ret); - } - } - $pm->kill(); - echo "DONE\n"; - }); -}; -$pm->childFunc = function () use ($pm) { - $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->data = []; - $server->on('WorkerStart', function ($server) use ($pm) { - $pm->wakeup(); - }); - $server->setHandler('GET', function ($fd, $data) use ($server) { - static $rid = 0; - if (count($data) == 0) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command")); - } - $key = $data[0]; - if ($rid++ % 2) { - $server->close($fd); - } else { - if (empty($server->data[$key])) { - $server->send($fd, Server::format(Server::NIL)); - } else { - $server->send($fd, Server::format(Server::STRING, $server->data[$key])); - } - } - }); - $server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); - } - $key = $data[0]; - $server->data[$key] = $data[1]; - $server->send($fd, Server::format(Server::STATUS, 'OK')); - }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/donot_retry_after_failed.phpt b/tests/swoole_redis_coro/donot_retry_after_failed.phpt deleted file mode 100644 index 3528b9e4010..00000000000 --- a/tests/swoole_redis_coro/donot_retry_after_failed.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_redis_coro: don not retry again after connect failed ---SKIPIF-- - ---FILE-- -bind('127.0.0.1'); -$info = $sock->getsockname(); -$port = $info['port']; - -$cid = go(function () use ($sock) { - $sock->listen(); - $sock->accept(); - co::yield(); - $sock->close(); -}); - -go(function () use ($cid, $port) { - $redis = new Swoole\Coroutine\Redis(); - $ret = $redis->connect('127.0.0.1', 65535); - Assert::assert(!$ret); - Assert::same($redis->errCode, SOCKET_ECONNREFUSED); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->get('foo'); - Assert::assert(!$ret); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_CLOSED); - } - $ret = $redis->connect('127.0.0.1', $port); - Assert::assert($ret); - Assert::assert($redis->connected); - Assert::same($redis->errCode, 0, $redis->errCode); - Assert::same($redis->errMsg, '', $redis->errMsg); - co::sleep(0.001); - co::resume($cid); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/donot_retry_after_server_down.phpt b/tests/swoole_redis_coro/donot_retry_after_server_down.phpt deleted file mode 100644 index c16cf432f69..00000000000 --- a/tests/swoole_redis_coro/donot_retry_after_server_down.phpt +++ /dev/null @@ -1,62 +0,0 @@ ---TEST-- -swoole_redis_coro: do not retry after server down ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - go(function () use ($pm) { - $redis = new Swoole\Coroutine\Redis; - $ret = $redis->connect('127.0.0.1', $pm->getFreePort()); - Assert::assert($ret); - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::assert($ret); - $ret = $redis->get('random_val'); - Assert::same($ret, $random); - $pm->kill(); - Assert::assert(!$redis->get('random_val')); - Assert::same($redis->errCode, SOCKET_ECONNRESET); - for ($n = MAX_REQUESTS; $n--;) { - Assert::assert(!$redis->set('random_val', get_safe_random(128))); - Assert::same($redis->errCode, SOCKET_ECONNREFUSED); - Assert::assert(!$redis->get('random_val')); - Assert::same($redis->errCode, SOCKET_ECONNREFUSED); - } - }); -}; -$pm->childFunc = function () use ($pm) { - $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $server->data = []; - $server->on('workerStart', function ($server) use ($pm) { - $pm->wakeup(); - }); - $server->setHandler('GET', function ($fd, $data) use ($server) { - if (count($data) == 0) { - return Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command"); - } - $key = $data[0]; - if (empty($server->data[$key])) { - $server->send($fd, Server::format(Server::NIL)); - } else { - $server->send($fd, Server::format(Server::STRING, $server->data[$key])); - } - }); - $server->setHandler('SET', function ($fd, $data) use ($server) { - if (count($data) < 2) { - $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command")); - } - $key = $data[0]; - $server->data[$key] = $data[1]; - $server->send($fd, Server::format(Server::STATUS, 'OK')); - }); - $server->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/err.phpt b/tests/swoole_redis_coro/err.phpt deleted file mode 100644 index 6b2ad5c4667..00000000000 --- a/tests/swoole_redis_coro/err.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -swoole_redis_coro: redis error return ---SKIPIF-- - ---FILE-- - 3]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $res = $redis->set('foo', 'bar'); - Assert::assert($res && $redis->errCode === 0 && $redis->errMsg === ''); - $res = $redis->hIncrBy('foo', 'bar', 123); - Assert::assert(!$res); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_OTHER); - var_dump($redis->errMsg); - $res = $redis->set('foo', 'baz'); - Assert::assert($res && $redis->errCode === 0 && $redis->errMsg === ''); -}); -?> ---EXPECT-- -string(65) "WRONGTYPE Operation against a key holding the wrong kind of value" diff --git a/tests/swoole_redis_coro/getDbNum.phpt b/tests/swoole_redis_coro/getDbNum.phpt deleted file mode 100644 index 581ba0c552c..00000000000 --- a/tests/swoole_redis_coro/getDbNum.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -swoole_redis_coro: redis select db ---SKIPIF-- - ---FILE-- -getDBNum()); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - // connected but not selected - Assert::same($redis->getDBNum(), 0); - // select and success - Assert::true($redis->select(1)); - Assert::same($redis->getDBNum(), 1); - // select but failed - Assert::false($redis->select(-1)); - Assert::same($redis->errCode, SOCKET_EINVAL); - Assert::false($redis->select(1001)); - Assert::same($redis->errCode, SOCKET_EINVAL); - Assert::same($redis->getDBNum(), 1); - - $redis = new Swoole\Coroutine\Redis(['database' => 1]); - // connected but not selected - Assert::false($redis->getDBNum()); - Assert::assert($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - // connected but not selected - Assert::same($redis->getDBNum(), 1); - // set database but failed - $redis = new Swoole\Coroutine\Redis(['database' => 1001]); - Assert::false($redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::false($redis->getDBNum()); - Assert::same($redis->errCode, SOCKET_EINVAL); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/getOptions.phpt b/tests/swoole_redis_coro/getOptions.phpt deleted file mode 100644 index 7626bd2bc49..00000000000 --- a/tests/swoole_redis_coro/getOptions.phpt +++ /dev/null @@ -1,50 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client get options ---SKIPIF-- - ---FILE-- - 100, - 'socket_timeout' => 100, -]); -$redis = new Swoole\Coroutine\Redis(); -var_dump($redis->getOptions()); -$redis->setOptions([ - 'connect_timeout' => 0.001, - 'timeout' => 0.001, - 'serialize' => true, - 'reconnect' => 3 -]); -var_dump($redis->getOptions()); -?> ---EXPECT-- -array(6) { - ["connect_timeout"]=> - float(2) - ["timeout"]=> - float(100) - ["serialize"]=> - bool(false) - ["reconnect"]=> - int(1) - ["password"]=> - string(0) "" - ["database"]=> - int(0) -} -array(6) { - ["connect_timeout"]=> - float(0.001) - ["timeout"]=> - float(0.001) - ["serialize"]=> - bool(true) - ["reconnect"]=> - int(3) - ["password"]=> - string(0) "" - ["database"]=> - int(0) -} diff --git a/tests/swoole_redis_coro/hgetall.phpt b/tests/swoole_redis_coro/hgetall.phpt deleted file mode 100644 index 7990aa642dc..00000000000 --- a/tests/swoole_redis_coro/hgetall.phpt +++ /dev/null @@ -1,97 +0,0 @@ ---TEST-- -swoole_redis_coro: hGetAll hmGet zRange zRevRange zRangeByScore zRevRangeByScore ---SKIPIF-- - ---FILE-- -setOptions(['compatibility_mode' => true]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis->delete('hkey'); - $redis->hSet('hkey', false, 'val0'); - $redis->hSet('hkey', "field", 'val1'); - $redis->hSet('hkey', 5, 'val5'); - - $redis->delete('zkey'); - $redis->zAdd('zkey', "field", 'val0'); - $redis->zAdd('zkey', true, 'val1'); - $redis->zAdd('zkey', 5, 'val5'); - - echo "-----get---\n"; - var_dump($redis->get('novalue')); - echo "-----zRank---\n"; - var_dump($redis->zRank('novalue', 1)); - echo "-----hGetAll---\n"; - var_dump($redis->hGetAll('hkey')); - echo "-----hmGet---\n"; - var_dump($redis->hmGet('hkey', [3, 5])); - echo "-----zRange---\n"; - var_dump($redis->zRange('zkey', 0, 99, true)); - echo "-----zRevRange---\n"; - var_dump($redis->zRevRange('zkey', 0, 99, true)); - echo "-----zRangeByScore---\n"; - var_dump($redis->zRangeByScore('zkey', 0, 99, ['withscores' => true])); - echo "-----zRevRangeByScore---\n"; - var_dump($redis->zRevRangeByScore('zkey', 99, 0, ['withscores' => true])); -}); -?> ---EXPECT-- ------get--- -bool(false) ------zRank--- -bool(false) ------hGetAll--- -array(3) { - [""]=> - string(4) "val0" - ["field"]=> - string(4) "val1" - [5]=> - string(4) "val5" -} ------hmGet--- -array(2) { - [3]=> - bool(false) - [5]=> - string(4) "val5" -} ------zRange--- -array(3) { - ["val0"]=> - float(0) - ["val1"]=> - float(1) - ["val5"]=> - float(5) -} ------zRevRange--- -array(3) { - ["val5"]=> - float(5) - ["val1"]=> - float(1) - ["val0"]=> - float(0) -} ------zRangeByScore--- -array(3) { - ["val0"]=> - float(0) - ["val1"]=> - float(1) - ["val5"]=> - float(5) -} ------zRevRangeByScore--- -array(3) { - ["val5"]=> - float(5) - ["val1"]=> - float(1) - ["val0"]=> - float(0) -} diff --git a/tests/swoole_redis_coro/lock.phpt b/tests/swoole_redis_coro/lock.phpt deleted file mode 100644 index 6614474f4fa..00000000000 --- a/tests/swoole_redis_coro/lock.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_redis_coro: redis lock ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis->delete('lock'); - $ret = $redis->set('lock', 1, ['nx', 'ex' => 1, 'px' => 1000]); // px will be ignored - Assert::assert($ret); - $ret = $redis->set('lock', 1, ['nx', 'ex' => 1, 'px' => 1000]); // px will be ignored - Assert::assert(!$ret); - $redis->delete('lock'); - $ret = $redis->set('lock', 1, ['nx', 'px' => 100]); - Assert::assert($ret); - usleep(50 * 1000); - $ret = $redis->set('lock', 1, ['nx', 'px' => 100]); - Assert::assert(!$ret); - usleep(100 * 1000); - $ret = $redis->set('lock', 1, ['nx', 'px' => 100]); - Assert::assert($ret); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/multi_exec.phpt b/tests/swoole_redis_coro/multi_exec.phpt deleted file mode 100644 index 0a3ed2f8009..00000000000 --- a/tests/swoole_redis_coro/multi_exec.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_redis_coro: redis multi and exec ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT, false); - Assert::assert($result); - - Assert::assert($redis->hmset('u:i:1', ['a' => 'hello', 'b' => 'world'])); - Assert::assert($redis->hmset('u:i:2', ['a' => 'rango', 'b' => 'swoole'])); - Assert::assert($redis->multi()); - $redis->hmget('u:i:1', array('a', 'b')); - $redis->hmget('u:i:2', array('a', 'b')); - - $rs = $redis->exec(); - Assert::assert($rs and is_array($rs)); - Assert::same($rs[0][0], 'hello'); - Assert::same($rs[0][1], 'world'); - Assert::same($rs[1][0], 'rango'); - Assert::same($rs[1][1], 'swoole'); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/pool.phpt b/tests/swoole_redis_coro/pool.phpt deleted file mode 100644 index 98294be2594..00000000000 --- a/tests/swoole_redis_coro/pool.phpt +++ /dev/null @@ -1,79 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) { - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - echo httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); - $pm->kill(); - }); -}; - -$count = 0; -$pool = new SplQueue(); - -$pm->childFunc = function () use ($pm) -{ - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->set(array( - 'log_file' => '/dev/null' - )); - $http->on("WorkerStart", function (Swoole\Server $serv) - { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - - $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) - { - global $count, $pool; - if (count($pool) == 0) - { - $redis = new Swoole\Coroutine\Redis(); - $redis->id = $count; - $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - if ($res == false) - { - fail: - $response->end("ERROR\n"); - return; - } - $count++; - $pool->enqueue($redis); - } - - $redis = $pool->dequeue(); - $ret = $redis->set('key', 'value'); - if ($ret) - { - $response->end("OK[$count]\n"); - } - else - { - goto fail; - } - $pool->enqueue($redis); - - }); - - $http->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK[1] -OK[1] -OK[1] diff --git a/tests/swoole_redis_coro/psubscribe_1.phpt b/tests/swoole_redis_coro/psubscribe_1.phpt deleted file mode 100644 index 1945ac083ab..00000000000 --- a/tests/swoole_redis_coro/psubscribe_1.phpt +++ /dev/null @@ -1,37 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $val = $redis->psubscribe(['test.*']); - Assert::assert($val); - $val = $redis->recv(); - Assert::assert($val[0] == 'psubscribe' && $val[1] == 'test.*'); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'pmessage'); - } - - $redis->close(); -}); - -go(function () { - $redis = new Co\redis; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - co::sleep(0.1); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $ret = $redis->publish('test.a', 'hello-' . $i); - Assert::assert($ret); - } -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/psubscribe_2.phpt b/tests/swoole_redis_coro/psubscribe_2.phpt deleted file mode 100644 index 255a5eef5d7..00000000000 --- a/tests/swoole_redis_coro/psubscribe_2.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe 2 ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis2 = new Co\Redis; - $redis2->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $channel = 'channel' . floor($i / 10) . $i; - $val = $redis->psubscribe([$channel . '*']); - Assert::assert($val); - - $val = $redis->recv(); - Assert::same($val[0], 'psubscribe'); - Assert::same($val[1], $channel . '*'); - - $channel .= 'test'; - - go(function () use ($channel, $redis2) { - $ret = $redis2->publish($channel, 'test' . $channel); - Assert::assert($ret); - }); - - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'pmessage'); - } - - $redis->close(); - $redis2->close(); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/psubscribe_eof_1.phpt b/tests/swoole_redis_coro/psubscribe_eof_1.phpt deleted file mode 100644 index 2f3153fa2d1..00000000000 --- a/tests/swoole_redis_coro/psubscribe_eof_1.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe eof 1 ---SKIPIF-- - ---FILE-- -bind('127.0.0.1'); -$info = $sock->getsockname(); -$port = $info['port']; -go(function () use ($sock) { - $sock->listen(); - while ($client = $sock->accept(-1)) { - $client->close(); - } - echo "DONE\n"; -}); - -go(function () use ($sock, $port) { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect('127.0.0.1', $port); - for ($n = 0; $n < MAX_REQUESTS; $n++) { - $val = $redis->psubscribe(['test.*']); - Assert::assert($val); - $val = $redis->recv(); - Assert::false($val); - Assert::false($redis->connected); - Assert::assert(in_array($redis->errType, [SWOOLE_REDIS_ERR_IO, SWOOLE_REDIS_ERR_EOF], true)); - if ($redis->errType === SWOOLE_REDIS_ERR_IO) { - Assert::same($redis->errCode, SOCKET_ECONNRESET); - } - } - $redis->close(); - $sock->close(); -}); -Swoole\Event::wait(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/psubscribe_eof_2.phpt b/tests/swoole_redis_coro/psubscribe_eof_2.phpt deleted file mode 100644 index d6aa4a1b337..00000000000 --- a/tests/swoole_redis_coro/psubscribe_eof_2.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -swoole_redis_coro: redis psubscribe eof 2 ---SKIPIF-- - ---FILE-- -bind('127.0.0.1'); -$info = $sock->getsockname(); -$port = $info['port']; -go(function () use ($sock) { - $sock->listen(); - - while ($client = $sock->accept(-1)) { - $client->recv(); - $client->send("*3\r\n\$10\r\npsubscribe\r\n\$8\r\nchannel1\r\n:1\r\n"); - co::sleep(0.1); - $client->close(); - } - - echo "DONE\n"; -}); -go(function () use ($sock, $port) { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect('127.0.0.1', $port); - - $val = $redis->psubscribe(['channel1']); - Assert::assert($val); - - $val = $redis->recv(); - Assert::assert($val[0] == 'psubscribe' && $val[1] == 'channel1'); - - $val = $redis->recv(); - Assert::false($val); - - Assert::false($redis->connected); - Assert::same($redis->errType, SWOOLE_REDIS_ERR_EOF); - - $redis->close(); - $sock->close(); -}); -Swoole\Event::wait(); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/punsubscribe.phpt b/tests/swoole_redis_coro/punsubscribe.phpt deleted file mode 100644 index 6326828cadf..00000000000 --- a/tests/swoole_redis_coro/punsubscribe.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -swoole_redis_coro: redis punsubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->psubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'psubscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->punsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'punsubscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $ret = $redis->setDefer(false); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/punsubscribe.php on line 22 diff --git a/tests/swoole_redis_coro/reconnect.phpt b/tests/swoole_redis_coro/reconnect.phpt deleted file mode 100644 index 613dd17990a..00000000000 --- a/tests/swoole_redis_coro/reconnect.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -swoole_redis_coro: redis reconnect ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($res); - $redis->close(); - $res2 = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($res2); -}); -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/request_without_connected.phpt b/tests/swoole_redis_coro/request_without_connected.phpt deleted file mode 100644 index f323ff7db2d..00000000000 --- a/tests/swoole_redis_coro/request_without_connected.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -swoole_redis_coro: redis request without connected ---SKIPIF-- - ---FILE-- -get('foo')); - echo "DONE\n"; -}); -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::get(): The host is empty in %s/tests/swoole_redis_coro/request_without_connected.php on line 5 -DONE diff --git a/tests/swoole_redis_coro/select.phpt b/tests/swoole_redis_coro/select.phpt deleted file mode 100644 index 770c4f28a22..00000000000 --- a/tests/swoole_redis_coro/select.phpt +++ /dev/null @@ -1,37 +0,0 @@ ---TEST-- -swoole_redis_coro: redis select ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT)); - Assert::assert($redis->select(0)); - Assert::assert($redis->set('foo', $random0 = get_safe_random())); - Assert::assert($redis->select(1)); - Assert::assert($redis->set('foo', $random1 = get_safe_random())); - $foo = $redis->get('foo'); - Assert::assert($foo !== $random0); - Assert::same($foo, $random1); - Assert::assert($redis->select(0)); - $foo = $redis->get('foo'); - Assert::same($foo, $random0); - Assert::assert($foo !== $random1); - Assert::assert($redis->select(1)); - - // test whether it's OK after automatic reconnected - $redis_killer = new Swoole\Coroutine\Redis; - $redis_killer->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $redis_killer->request(['CLIENT', 'KILL', 'TYPE', 'normal']); - - $foo = $redis->get('foo'); - Assert::assert($foo !== $random0); - Assert::same($foo, $random1); - - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/set.phpt b/tests/swoole_redis_coro/set.phpt deleted file mode 100644 index 20a8f46ad1e..00000000000 --- a/tests/swoole_redis_coro/set.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_redis_coro: set ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -go(function () { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($redis->set('key1', 'value')); - Assert::assert($redis->set('key1', 'value', 10)); - Assert::assert($redis->ttl('key1') == 10); - /** - * xx+ex - */ - Assert::assert($redis->set('key1', 'value', ['xx', 'ex' => 30])); - Assert::assert($redis->ttl('key1') == 30); - /** - * delete - */ - Assert::assert($redis->delete('key1')); - /** - * nx+ex - */ - Assert::assert($redis->set('key1', 'value', ['nx', 'ex' => 20])); - Assert::assert($redis->ttl('key1') == 20); - - /** - * px - */ - Assert::assert($redis->set('key1', 'value', ['xx', 'px' => 10000])); - Assert::assert($redis->ttl('key1') == 10); - echo "OK\n"; -}); - -Swoole\Event::wait(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/setOptions.phpt b/tests/swoole_redis_coro/setOptions.phpt deleted file mode 100644 index eb5819fc6f9..00000000000 --- a/tests/swoole_redis_coro/setOptions.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client set options ---SKIPIF-- - ---FILE-- - -1]); -go(function () { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - // read time out - $redis->setOptions(['timeout' => 0.001]); - $s = microtime(true); - $ret = $redis->brpoplpush('test', 'test2', 1); - $s = microtime(true) - $s; - time_approximate(0.001, $s, 1); - Assert::assert(!$ret); - - // read ok (after internal auto connect) - $redis->setOptions(['timeout' => 1]); - $ret = $redis->set('foo', 'bar'); - Assert::assert($ret); - Assert::same($redis->errCode, 0); - Assert::same($redis->errMsg, ''); - $redis->close(); - Assert::assert(!$redis->connected); - - // connect timeout - $redis->setOptions(['connect_timeout' => 0.001]); - $redis->connect('www.google.com', 80); - Assert::same($redis->errCode, SOCKET_ETIMEDOUT); -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/stream.phpt b/tests/swoole_redis_coro/stream.phpt deleted file mode 100644 index 00f7d03520b..00000000000 --- a/tests/swoole_redis_coro/stream.phpt +++ /dev/null @@ -1,124 +0,0 @@ ---TEST-- -swoole_redis_coro: stream ---SKIPIF-- - ---FILE-- - SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); - -Co\run(function() { - $redis = new Swoole\Coroutine\Redis(); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $ret = $redis->del('mystream'); - - // xGroupCreate - $ret = $redis->xGroupCreate('mystream', 'group1', '0-0', true); - Assert::assert($ret == '1'); - - // xGroupCreateConsumer - $ret = $redis->xGroupCreateConsumer('mystream', 'group1', 'consumer1'); - Assert::assert($ret == '1'); - $ret = $redis->xGroupCreateConsumer('mystream', 'group1', 'consumer2'); - Assert::assert($ret == '1'); - - // xAdd - $ret = $redis->xAdd('mystream', '0-1', ['field'=>'111'], ['nomkstream'=>true, 'maxlen'=>['~', 5], 'limit'=>5]); - Assert::assert($ret == '0-1'); - $ret = $redis->xAdd('mystream', '0-2', ['field'=>'222'], ['nomkstream'=>false, 'minid'=>['~', '0-0'], 'limit'=>5]); - Assert::assert($ret == '0-2'); - $ret = $redis->xAdd('mystream', '0-3', ['field'=>'333'], ['maxlen'=>['=', 5]]); - Assert::assert($ret == '0-3'); - $ret = $redis->xAdd('mystream', '0-4', ['field'=>'444'], ['maxlen'=>5]); - Assert::assert($ret, '0-4'); - $ret = $redis->xAdd('mystream', '0-5', ['field'=>'555']); - Assert::assert($ret, '0-5'); - - // xLen - $ret = $redis->xLen('mystream'); - Assert::assert($ret == '5'); - - // xRead - $ret = $redis->xRead(['mystream'=>'0-3'], ['count'=>1, 'block'=>100]); - Assert::assert($ret[0][1][0][0] == '0-4'); - - // xRange - $ret = $redis->xRange('mystream', '0-2', '0-3', 1); - Assert::assert($ret[0][0] == '0-2'); - - // xRevRange - $ret = $redis->xRevRange('mystream', '+', '-', 1); - Assert::assert($ret[0][0] == '0-5'); - - // xReadGroup - $ret = $redis->xReadGroup('group1', 'consumer1', ['mystream' => '>'], ['count'=>1, 'block'=>100, 'noack'=>true]); - Assert::assert($ret[0][1][0][0] == '0-1'); - $ret = $redis->xReadGroup('group1', 'consumer1', ['mystream' => '>'], ['count'=>1, 'block'=>100, 'noack'=>false]); - Assert::assert($ret[0][1][0][0] == '0-2'); - $ret = $redis->xReadGroup('group1', 'consumer1', ['mystream' => '>'], ['count'=>1]); - Assert::assert($ret[0][1][0][0] == '0-3'); - - // xPending - $ret = $redis->xPending('mystream', 'group1', ['start'=>'-', 'end'=>'+', 'count'=>5]); - Assert::assert(count($ret) == 2); - Assert::assert($ret[0][0] == '0-2'); - Assert::assert($ret[1][0] == '0-3'); - - // xAck - $ret = $redis->xAck('mystream', 'group1', ['0-2']); - Assert::assert($ret == '1'); - - // xClaim - $ret = $redis->xClaim('mystream', 'group1', 'consumer2', 0, ['0-3']); - Assert::assert($ret[0][0] == '0-3'); - - // xInfoConsumers - $ret = $redis->xInfoConsumers('mystream', 'group1'); - Assert::assert($ret[1][3] == '1'); - - // xAutoClaim - $ret = $redis->xAutoClaim('mystream', 'group1', 'consumer1', 0, '0-3'); - Assert::assert($ret[1][0][0] == '0-3'); - - // xInfoGroups - $ret = $redis->xInfoGroups('mystream'); - Assert::assert($ret[0][1] == 'group1'); - Assert::assert($ret[0][5] == '1'); - - // xInfoStream - $ret = $redis->xInfoStream('mystream'); - Assert::assert($ret[1] == '5'); - - // xDel - $ret = $redis->xDel('mystream', '0-1', '0-2'); - Assert::assert($ret == '2'); - - // xTrim - $ret = $redis->xTrim('mystream', ['maxlen'=>1]); - Assert::assert($ret == '2'); - $ret = $redis->xTrim('mystream', ['minid'=>['~', '0'], 'limit'=>1]); - Assert::assert($ret == '0'); - - // xGroupSetId - $ret = $redis->xGroupSetId('mystream', 'group1', '0-1'); - Assert::assert($ret == '1'); - - // xGroupDelConsumer - $ret = $redis->xGroupDelConsumer('mystream', 'group1', 'consumer1'); - Assert::assert($ret == '1'); - - // xGroupDestroy - $ret = $redis->xGroupDestroy('mystream', 'group1'); - Assert::assert($ret == '1'); - - $ret = $redis->del('mystream'); - - echo "OK\n"; -}); -?> ---EXPECT-- -OK diff --git a/tests/swoole_redis_coro/subscribe_1.phpt b/tests/swoole_redis_coro/subscribe_1.phpt deleted file mode 100644 index e9b4db226c0..00000000000 --- a/tests/swoole_redis_coro/subscribe_1.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe 1 ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - $val = $redis->subscribe(['test']); - Assert::assert($val); - - $val = $redis->recv(); - Assert::assert($val[0] == 'subscribe' && $val[1] == 'test'); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'message'); - } - - $redis->close(); -}); - -go(function () { - $redis = new Co\redis; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - co::sleep(0.1); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $ret = $redis->publish('test', 'hello-' . $i); - Assert::assert($ret); - } -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/subscribe_2.phpt b/tests/swoole_redis_coro/subscribe_2.phpt deleted file mode 100644 index e60c810baae..00000000000 --- a/tests/swoole_redis_coro/subscribe_2.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe 2 ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis2 = new Co\Redis; - $redis2->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - for ($i = 0; $i < MAX_REQUESTS; $i++) { - $channel = 'channel' . $i; - $val = $redis->subscribe([$channel]); - Assert::assert($val); - - $val = $redis->recv(); - Assert::assert($val[0] == 'subscribe' && $val[1] == $channel); - - go(function () use ($channel, $redis2) { - $ret = $redis2->publish($channel, 'test' . $channel); - Assert::assert($ret); - }); - - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'message'); - Assert::same($val[1] ?? '', $channel); - Assert::same($val[2] ?? '', 'test' . $channel); - } - - $redis->close(); - $redis2->close(); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/subscribe_multi.phpt b/tests/swoole_redis_coro/subscribe_multi.phpt deleted file mode 100644 index ce269995409..00000000000 --- a/tests/swoole_redis_coro/subscribe_multi.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe multi ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $val = $redis->subscribe(['test1', 'test2', 'test3']); - Assert::assert($val); - - for ($i = 0; $i < 3; ++$i) - { - $val = $redis->recv(); - Assert::same($val[0], 'subscribe'); - } - - for ($i = 0; $i < 3; $i++) { - $val = $redis->recv(); - Assert::same($val[0] ?? '', 'message'); - } - - $redis->close(); -}); - -go(function () { - $redis = new Co\redis; - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - co::sleep(0.1); - - $ret = $redis->publish('test1', 'hello'); - Assert::assert($ret); - - $ret = $redis->publish('test2', 'hello'); - Assert::assert($ret); - - $ret = $redis->publish('test3', 'hello'); - Assert::assert($ret); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/subscribe_punsubscribe.phpt b/tests/swoole_redis_coro/subscribe_punsubscribe.phpt deleted file mode 100644 index daaba685cb6..00000000000 --- a/tests/swoole_redis_coro/subscribe_punsubscribe.phpt +++ /dev/null @@ -1,54 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe and use punsubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->punsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'punsubscribe'); - Assert::same($ret[1], 'channel1'); - Assert::same($ret[2], 1); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/subscribe_punsubscribe.php on line 22 - -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/subscribe_punsubscribe.php on line 39 diff --git a/tests/swoole_redis_coro/subscribe_reconnect.phpt b/tests/swoole_redis_coro/subscribe_reconnect.phpt deleted file mode 100644 index da9026bbdaf..00000000000 --- a/tests/swoole_redis_coro/subscribe_reconnect.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -swoole_redis_coro: redis subscribe reconnect ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - $redis->close(); - - $ret = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECT-- diff --git a/tests/swoole_redis_coro/timeout.phpt b/tests/swoole_redis_coro/timeout.phpt deleted file mode 100644 index d22fd33e1c6..00000000000 --- a/tests/swoole_redis_coro/timeout.phpt +++ /dev/null @@ -1,52 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client timeout ---SKIPIF-- - ---FILE-- - 0.5]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $keyArray = [QUEUE_KEY_1, QUEUE_KEY_2]; - - $s = microtime(true); - $res = $redis->blpop($keyArray, 3); - Assert::assert(!$res); - Assert::same($redis->errCode, SOCKET_ETIMEDOUT); - $s = microtime(true) - $s; - time_approximate(0.5, $s); // would not retry after timeout - - $s = microtime(true); - $res = $redis->brpoplpush(QUEUE_KEY_1, QUEUE_KEY_2, 3); - Assert::assert(!$res); - Assert::same($redis->errCode, SOCKET_ETIMEDOUT); - $s = microtime(true) - $s; - time_approximate(0.5, $s); // would not retry after timeout - - // right way: no timeout - $redis->setOptions(['timeout' => -1]); - - $s = microtime(true); - $res = $redis->blpop($keyArray, 1); - Assert::same($res, null); - Assert::same($redis->errCode, 0); - $s = microtime(true) - $s; - time_approximate(1, $s); - - $s = microtime(true); - $res = $redis->brpoplpush(QUEUE_KEY_1, QUEUE_KEY_2, 1); - Assert::same($res, null); - Assert::same($redis->errCode, 0); - $s = microtime(true) - $s; - time_approximate(1, $s); -}); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/unconnected.phpt b/tests/swoole_redis_coro/unconnected.phpt deleted file mode 100644 index 83f3d4579f4..00000000000 --- a/tests/swoole_redis_coro/unconnected.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unconnected recv ---SKIPIF-- - ---FILE-- -setDefer(true); - Assert::false($redis->recv()); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/unixsocket.phpt b/tests/swoole_redis_coro/unixsocket.phpt deleted file mode 100644 index bbc9aa8302d..00000000000 --- a/tests/swoole_redis_coro/unixsocket.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -swoole_redis_coro: use unixsocket ---SKIPIF-- - ---FILE-- - 100]); - Assert::assert($redis->connect('unix:/' . REDIS_SERVER_PATH, 0)); - for ($c = MAX_CONCURRENCY_MID; $c--;) { - for ($n = MAX_REQUESTS; $n--;) { - $key = md5(get_safe_random(mt_rand(1, 128))); - $value = md5(get_safe_random(mt_rand(1, 128))); - Assert::assert($redis->set($key, $value)); - Assert::same($redis->get($key), $value); - Assert::assert($redis->delete($key)); - } - } -}); -Swoole\Event::wait(); -echo "DONE\n"; -?> ---EXPECT-- -DONE diff --git a/tests/swoole_redis_coro/unsubscribe.phpt b/tests/swoole_redis_coro/unsubscribe.phpt deleted file mode 100644 index f9541b1ecf9..00000000000 --- a/tests/swoole_redis_coro/unsubscribe.phpt +++ /dev/null @@ -1,51 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unsubscribe ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->unsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'unsubscribe'); - Assert::same($ret[1], 'channel1'); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $ret = $redis->setDefer(false); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe.php on line 22 diff --git a/tests/swoole_redis_coro/unsubscribe_all.phpt b/tests/swoole_redis_coro/unsubscribe_all.phpt deleted file mode 100644 index 3ffbffeb04a..00000000000 --- a/tests/swoole_redis_coro/unsubscribe_all.phpt +++ /dev/null @@ -1,56 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unsubscribe all ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1', 'channel2']); - Assert::assert($ret); - - for ($i = 0; $i < 2; ++$i) - { - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - } - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->unsubscribe(['channel1', 'channel2']); - Assert::assert($ret); - - for ($i = 0; $i < 2; ++$i) - { - $ret = $redis->recv(); - Assert::same($ret[0], 'unsubscribe'); - Assert::same($ret[2], 1 - $i); - } - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert($ret); - - $ret = $redis->setDefer(false); - Assert::assert($ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe_all.php on line 24 diff --git a/tests/swoole_redis_coro/unsubscribe_not_all.phpt b/tests/swoole_redis_coro/unsubscribe_not_all.phpt deleted file mode 100644 index 69776291c41..00000000000 --- a/tests/swoole_redis_coro/unsubscribe_not_all.phpt +++ /dev/null @@ -1,56 +0,0 @@ ---TEST-- -swoole_redis_coro: redis unsubscribe not all ---SKIPIF-- - ---FILE-- -connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - Assert::assert($ret); - - $ret = $redis->subscribe(['channel1', 'channel2']); - Assert::assert($ret); - - for ($i = 0; $i < 2; ++$i) - { - $ret = $redis->recv(); - Assert::same($ret[0], 'subscribe'); - } - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $ret = $redis->unsubscribe(['channel1']); - Assert::assert($ret); - - $ret = $redis->recv(); - Assert::same($ret[0], 'unsubscribe'); - Assert::same($ret[1], 'channel1'); - Assert::same($ret[2], 1); - - $ret = $redis->getDefer(); - Assert::assert(!$ret); - - $ret = $redis->set('a', '1'); - Assert::assert(!$ret); - - $ret = $redis->setDefer(false); - Assert::assert(!$ret); - - $redis->close(); -}); - -?> ---EXPECTF-- -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe_not_all.php on line 24 - -Warning: Swoole\Coroutine\Redis::setDefer(): you should not use setDefer after subscribe in %s/tests/swoole_redis_coro/unsubscribe_not_all.php on line 41 diff --git a/tests/swoole_redis_coro/zpop.phpt b/tests/swoole_redis_coro/zpop.phpt deleted file mode 100644 index 09025992467..00000000000 --- a/tests/swoole_redis_coro/zpop.phpt +++ /dev/null @@ -1,75 +0,0 @@ ---TEST-- -swoole_redis_coro: zPopMin zPopMax bzPopMin bzPopMax ---SKIPIF-- - ---FILE-- -setOptions(['compatibility_mode' => true]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $redis->delete('zkeyA'); - $redis->zAdd('zkeyA', 1, 'val1'); - $redis->zAdd('zkeyA', 2, 'val2'); - $redis->zAdd('zkeyA', 3, 'val3'); - $redis->zAdd('zkeyA', 4, 'val4'); - $redis->zAdd('zkeyA', 5, 'val5'); - - $redis->delete('zkeyB'); - $redis->zAdd('zkeyB', 1, 'val1'); - $redis->zAdd('zkeyB', 2, 'val2'); - $redis->zAdd('zkeyB', 3, 'val3'); - $redis->zAdd('zkeyB', 4, 'val4'); - $redis->zAdd('zkeyB', 5, 'val5'); - - echo "-----zPopMin---\n"; - var_dump($redis->zPopMin('zkeyA')); - echo "-----zPopMax---\n"; - var_dump($redis->zPopMax('zkeyB')); - echo "-----bzPopMin---\n"; - var_dump($redis->bzPopMin(['zkeyB','zkeyA'], 2)); - echo "-----bzPopMax---\n"; - var_dump($redis->bzPopMax('zkeyB','zkeyA', 2)); - echo "-----bzPopMin no data---\n"; - var_dump($redis->bzPopMin('zkeyC','zkeyD', 2)); -}); -?> ---EXPECT-- ------zPopMin--- -array(2) { - [0]=> - string(4) "val1" - [1]=> - string(1) "1" -} ------zPopMax--- -array(2) { - [0]=> - string(4) "val5" - [1]=> - string(1) "5" -} ------bzPopMin--- -array(3) { - [0]=> - string(5) "zkeyB" - [1]=> - string(4) "val1" - [2]=> - string(1) "1" -} ------bzPopMax--- -array(3) { - [0]=> - string(5) "zkeyB" - [1]=> - string(4) "val4" - [2]=> - string(1) "4" -} ------bzPopMin no data--- -NULL From e3a29e6ac412a919026a72babe140537ef0e29be Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Thu, 28 Mar 2024 18:54:36 +0800 Subject: [PATCH 024/103] [6.0] Server for thread mode (#5282) * http server for thread mode * fix compile error * optimize code * optimize code * optimize code[2] * optimize code[3] * optimize code[4] * refactor co-socket, support thread * add signal example --------- Co-authored-by: NathanFreeman <1056159381@qq.com> --- examples/thread/pipe.php | 20 ++++++ examples/thread/server.php | 25 ++++++++ examples/thread/signal.php | 36 +++++++++++ ext-src/php_swoole_thread.h | 6 ++ ext-src/stubs/php_swoole_socket_coro.stub.php | 3 + .../stubs/php_swoole_socket_coro_arginfo.h | 7 ++- ext-src/swoole_coroutine_scheduler.cc | 5 -- ext-src/swoole_http2_server.cc | 2 +- ext-src/swoole_http_server.cc | 4 +- ext-src/swoole_redis_server.cc | 2 +- ext-src/swoole_server.cc | 4 ++ ext-src/swoole_socket_coro.cc | 58 +++++++++++++++--- ext-src/swoole_thread.cc | 15 ++--- ext-src/swoole_websocket_server.cc | 2 +- include/swoole.h | 9 +++ include/swoole_coroutine.h | 6 -- include/swoole_process_pool.h | 2 +- include/swoole_server.h | 5 +- include/swoole_socket.h | 1 + src/core/base.cc | 10 +++ src/network/socket.cc | 7 +++ src/os/async_thread.cc | 8 +-- src/server/master.cc | 61 +++++++++++++------ src/server/reactor_process.cc | 10 ++- src/server/reactor_thread.cc | 9 +-- src/server/worker.cc | 2 +- 26 files changed, 245 insertions(+), 74 deletions(-) create mode 100644 examples/thread/pipe.php create mode 100644 examples/thread/server.php create mode 100644 examples/thread/signal.php diff --git a/examples/thread/pipe.php b/examples/thread/pipe.php new file mode 100644 index 00000000000..94091a838bc --- /dev/null +++ b/examples/thread/pipe.php @@ -0,0 +1,20 @@ +recv(8192), PHP_EOL; + $thread->join(); + }); +} else { + $sockets = $args[0]; + Co\run(function () use ($sockets) { + sleep(1); + $sockets[1]->send(uniqid()); + }); +} diff --git a/examples/thread/server.php b/examples/thread/server.php new file mode 100644 index 00000000000..620bd49ba1a --- /dev/null +++ b/examples/thread/server.php @@ -0,0 +1,25 @@ +join(); + } +} else { + $http = new Swoole\Http\Server("0.0.0.0", 9503); + $http->on('request', function ($req, Swoole\Http\Response $resp) { + $resp->end('hello world'); + }); + $http->start(); +} diff --git a/examples/thread/signal.php b/examples/thread/signal.php new file mode 100644 index 00000000000..11a3c1cb0bd --- /dev/null +++ b/examples/thread/signal.php @@ -0,0 +1,36 @@ +send('exit'); + } + Co\go(function () use ($parent_pipe, $thread) { + // 从管道中读取子线程退出的信息 + echo $parent_pipe->recv(8192), PHP_EOL; + // 回收子线程 + $thread->join(); + }); + }); +} else { + echo "child thread\n"; + $sockets = $args[0]; + $child_pipe = $sockets[0]; + Co\run(function () use ($child_pipe) { + // 收到父线程的指令,开始退出 + echo $child_pipe->recv(8192), PHP_EOL; + // 通知父线程已退出 + $child_pipe->send('child exit'); + }); +} diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index 1b915755e2e..23fecd6b5c5 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -19,6 +19,11 @@ #include "php_swoole_cxx.h" +#ifdef SW_THREAD + +zend_string *php_swoole_thread_serialize(zval *zdata); +bool php_swoole_thread_unserialize(zend_string *data, zval *zv); + #define EMSG_NO_RESOURCE "resource not found" #define ECODE_NO_RESOURCE -2 @@ -40,3 +45,4 @@ struct ThreadResource { } }; +#endif diff --git a/ext-src/stubs/php_swoole_socket_coro.stub.php b/ext-src/stubs/php_swoole_socket_coro.stub.php index 2cd406d4251..b52f62f8005 100644 --- a/ext-src/stubs/php_swoole_socket_coro.stub.php +++ b/ext-src/stubs/php_swoole_socket_coro.stub.php @@ -35,5 +35,8 @@ public function getpeername(): false|array {} public function isClosed(): bool {} /** @param resource $stream */ public static function import($stream) : Socket | false {} + #ifdef SW_THREAD + public function __wakeup(): void {} + #endif } } diff --git a/ext-src/stubs/php_swoole_socket_coro_arginfo.h b/ext-src/stubs/php_swoole_socket_coro_arginfo.h index 9ef732569a6..739cb2a557d 100644 --- a/ext-src/stubs/php_swoole_socket_coro_arginfo.h +++ b/ext-src/stubs/php_swoole_socket_coro_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7d2b3ea5b4d1613340006de2fa67d2a0bf314f09 */ + * Stub hash: d117c3c8b36d0918b5c030a67e9d37ff9a76c669 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Coroutine_Socket___construct, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0) @@ -130,3 +130,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Swoole_Coroutine_Socket_import, 0, 1, Swoole\\Coroutine\\Socket, MAY_BE_FALSE) ZEND_ARG_INFO(0, stream) ZEND_END_ARG_INFO() + +#if defined(SW_THREAD) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Coroutine_Socket___wakeup, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() +#endif diff --git a/ext-src/swoole_coroutine_scheduler.cc b/ext-src/swoole_coroutine_scheduler.cc index c62ff66f583..5b4a530f672 100644 --- a/ext-src/swoole_coroutine_scheduler.cc +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -290,11 +290,6 @@ static PHP_METHOD(swoole_coroutine_scheduler, parallel) { static PHP_METHOD(swoole_coroutine_scheduler, start) { SchedulerObject *s = scheduler_get_object(Z_OBJ_P(ZEND_THIS)); - if (SwooleTG.reactor) { - php_swoole_fatal_error( - E_WARNING, "eventLoop has already been created. unable to start %s", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } if (s->started) { php_swoole_fatal_error( E_WARNING, "scheduler is started, unable to execute %s->start", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 37acbbecdf3..785aaba9630 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -34,7 +34,7 @@ using HttpContext = swoole::http::Context; using Http2Stream = Http2::Stream; using Http2Session = Http2::Session; -static std::unordered_map http2_sessions; +static SW_THREAD_LOCAL std::unordered_map http2_sessions; static bool http2_server_respond(HttpContext *ctx, const String *body); static bool http2_server_send_range_file(HttpContext *ctx, swoole::http_server::StaticHandler *handler); diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index bca5454c9d7..2d87a67bdb2 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -32,8 +32,8 @@ namespace WebSocket = swoole::websocket; zend_class_entry *swoole_http_server_ce; zend_object_handlers swoole_http_server_handlers; -static std::queue queued_http_contexts; -static std::unordered_map client_ips; +static SW_THREAD_LOCAL std::queue queued_http_contexts; +static SW_THREAD_LOCAL std::unordered_map client_ips; static bool http_context_send_data(HttpContext *ctx, const char *data, size_t length); static bool http_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); diff --git a/ext-src/swoole_redis_server.cc b/ext-src/swoole_redis_server.cc index edb8be271e9..bb427cda020 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -35,7 +35,7 @@ namespace Redis = swoole::redis; zend_class_entry *swoole_redis_server_ce; zend_object_handlers swoole_redis_server_handlers; -static std::unordered_map redis_handlers; +static SW_THREAD_LOCAL std::unordered_map redis_handlers; SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_redis_server, setHandler); diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 0284303619a..77f4cd6b477 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1872,6 +1872,10 @@ static PHP_METHOD(swoole_server, __construct) { RETURN_FALSE; } +#ifdef SW_THREAD + serv_mode = Server::MODE_BASE; +#endif + serv = new Server((enum Server::Mode) serv_mode); serv->private_data_2 = sw_zval_dup(zserv); server_set_ptr(zserv, serv); diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index a78ece50e3d..eaf867e3bf3 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -17,6 +17,8 @@ */ #include "php_swoole_cxx.h" +#include "php_swoole_thread.h" + #include "swoole_string.h" #include "swoole_socket.h" #include "swoole_util.h" @@ -87,6 +89,9 @@ static PHP_METHOD(swoole_socket_coro, getsockname); static PHP_METHOD(swoole_socket_coro, getpeername); static PHP_METHOD(swoole_socket_coro, isClosed); static PHP_METHOD(swoole_socket_coro, import); +#ifdef SW_THREAD +static PHP_METHOD(swoole_socket_coro, __wakeup); +#endif SW_EXTERN_C_END // clang-format off @@ -127,6 +132,9 @@ static const zend_function_entry swoole_socket_coro_methods[] = PHP_ME(swoole_socket_coro, getsockname, arginfo_class_Swoole_Coroutine_Socket_getsockname, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, isClosed, arginfo_class_Swoole_Coroutine_Socket_isClosed, ZEND_ACC_PUBLIC) PHP_ME(swoole_socket_coro, import, arginfo_class_Swoole_Coroutine_Socket_import, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) +#ifdef SW_THREAD + PHP_ME(swoole_socket_coro, __wakeup, arginfo_class_Swoole_Coroutine_Socket___wakeup, ZEND_ACC_PUBLIC) +#endif PHP_FE_END }; // clang-format on @@ -712,16 +720,18 @@ static void socket_coro_register_constants(int module_number) { void php_swoole_socket_coro_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_socket_coro, "Swoole\\Coroutine\\Socket", "Co\\Socket", swoole_socket_coro_methods); +#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_socket_coro); +#endif SW_SET_CLASS_CLONEABLE(swoole_socket_coro, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_socket_coro, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( swoole_socket_coro, socket_coro_create_object, socket_coro_free_object, SocketObject, std); - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("fd"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("domain"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("protocol"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("fd"), -1, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("domain"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("protocol"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); zend_declare_property_long(swoole_socket_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_socket_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); @@ -2167,20 +2177,20 @@ static PHP_METHOD(swoole_socket_coro, import) { } int sock_domain = AF_INET, sock_type = SOCK_STREAM; - php_sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); + php_sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); #ifdef SO_DOMAIN socklen_t sock_domain_len = sizeof(sock_domain); if (getsockopt(socket_fd, SOL_SOCKET, SO_DOMAIN, &sock_domain, &sock_domain_len) == 0) { } else #endif - if (getsockname(socket_fd, (struct sockaddr*)&addr, &addr_len) == 0) { - sock_domain = addr.ss_family; - } else { + if (getsockname(socket_fd, (struct sockaddr *) &addr, &addr_len) == 0) { + sock_domain = addr.ss_family; + } else { php_swoole_sys_error(E_WARNING, "getsockname() failed"); RETURN_FALSE; - } + } #ifdef SO_TYPE socklen_t sock_type_len = sizeof(sock_type); @@ -2207,3 +2217,31 @@ static PHP_METHOD(swoole_socket_coro, import) { RETURN_OBJ(object); } + +#ifdef SW_THREAD +static PHP_METHOD(swoole_socket_coro, __wakeup) { + zend_long sockfd = zend::read_property_long(ZEND_THIS, ZEND_STRL("fd")); + if (sockfd < 0) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } + + zend_long new_sockfd = dup(sockfd); + if (sockfd < 0) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } + + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + + zend_long domain = zend::read_property_long(ZEND_THIS, ZEND_STRL("domain")); + zend_long type = zend::read_property_long(ZEND_THIS, ZEND_STRL("type")); + zend_long protocol = zend::read_property_long(ZEND_THIS, ZEND_STRL("protocol")); + + php_swoole_check_reactor(); + sock->socket = new Socket((int) new_sockfd, (int) domain, (int) type, (int) protocol); + sock->socket->set_zero_copy(true); + sock->socket->set_buffer_allocator(sw_zend_string_allocator()); + zend_update_property_long(swoole_socket_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("fd"), sock->socket->get_fd()); +} +#endif diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 9991785298c..a470ad0e785 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -45,9 +45,6 @@ static zend_object_handlers swoole_thread_arraylist_handlers; zend_class_entry *swoole_thread_queue_ce; static zend_object_handlers swoole_thread_queue_handlers; -zend_string *php_swoole_thread_serialize(zval *zdata); -bool php_swoole_thread_unserialize(zend_string *data, zval *zv); - struct ArrayItem { uint32_t type; zend_string *key; @@ -431,14 +428,12 @@ struct ThreadQueueObject { static void php_swoole_thread_join(zend_object *object); -thread_local zval thread_argv; -std::mutex thread_lock; - +static thread_local zval thread_argv; static zend_long thread_resource_id = 0; static std::unordered_map thread_resources; ThreadResourceId php_swoole_thread_resource_insert(ThreadResource *res) { - std::unique_lock _lock(thread_lock); + std::unique_lock _lock(sw_thread_lock); zend_long resource_id = ++thread_resource_id; thread_resources[resource_id] = res; return resource_id; @@ -446,7 +441,7 @@ ThreadResourceId php_swoole_thread_resource_insert(ThreadResource *res) { ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id) { ThreadResource *res = nullptr; - std::unique_lock _lock(thread_lock); + std::unique_lock _lock(sw_thread_lock); auto iter = thread_resources.find(resource_id); if (iter != thread_resources.end()) { res = iter->second; @@ -456,7 +451,7 @@ ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id) { } bool php_swoole_thread_resource_free(ThreadResourceId resource_id, ThreadResource *res) { - std::unique_lock _lock(thread_lock); + std::unique_lock _lock(sw_thread_lock); if (res->del_ref() == 0) { thread_resources.erase(resource_id); return true; @@ -826,6 +821,8 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv) { #endif zend_file_handle file_handle{}; + swoole_thread_init(); + if (php_request_startup() != SUCCESS) { EG(exit_status) = 1; goto _startup_error; diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 386516ffe79..5a2f5f3e73e 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -44,7 +44,7 @@ static zend_object_handlers swoole_websocket_frame_handlers; static zend_class_entry *swoole_websocket_closeframe_ce; static zend_object_handlers swoole_websocket_closeframe_handlers; -static String *swoole_websocket_buffer = nullptr; +static SW_THREAD_LOCAL String *swoole_websocket_buffer = nullptr; SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_websocket_server, push); diff --git a/include/swoole.h b/include/swoole.h index ee12b283ab3..9e31b9e8bce 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -60,6 +60,7 @@ #include #include #include +#include #ifdef SW_USE_IOURING #include @@ -177,6 +178,13 @@ typedef unsigned long ulong_t; #endif #define SW_START_SLEEP usleep(100000) // sleep 1s,wait fork and pthread_create +#ifdef SW_THREAD +#define SW_THREAD_LOCAL thread_local +extern std::mutex sw_thread_lock; +#else +#define SW_THREAD_LOCAL +#endif + /*-----------------------------------Memory------------------------------------*/ void *sw_malloc(size_t size); void sw_free(void *ptr); @@ -567,6 +575,7 @@ void swoole_init(void); void swoole_clean(void); pid_t swoole_fork(int flags); pid_t swoole_fork_exec(const std::function &child_fn); +void swoole_thread_init(void); void swoole_redirect_stdout(int new_fd); int swoole_shell_exec(const char *command, pid_t *pid, bool get_error_stream); int swoole_daemon(int nochdir, int noclose); diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index b1ab5fb3dfb..05464ae75a6 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -41,12 +41,6 @@ typedef std::chrono::microseconds seconds_type; #define CALC_EXECUTE_USEC(yield_coroutine, resume_coroutine) #endif -#ifdef SW_THREAD -#define SW_THREAD_LOCAL thread_local -#else -#define SW_THREAD_LOCAL -#endif - namespace swoole { class Coroutine { public: diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index cf6fc72f289..eec5ac30154 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -325,5 +325,5 @@ static sw_inline int swoole_kill(pid_t __pid, int __sig) { return kill(__pid, __sig); } -extern swoole::WorkerGlobal SwooleWG; // Worker Global Variable +extern SW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG; // Worker Global Variable typedef swoole::ProtocolType swProtocolType; diff --git a/include/swoole_server.h b/include/swoole_server.h index 0ef0f43fff2..cb0eeff571b 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -27,6 +27,9 @@ #include "swoole_pipe.h" #include "swoole_channel.h" #include "swoole_message_bus.h" +#ifdef SW_THREAD +#include "swoole_lock.h" +#endif #ifdef SW_USE_OPENSSL #include "swoole_dtls.h" @@ -1451,7 +1454,7 @@ typedef swoole::Server swServer; typedef swoole::ListenPort swListenPort; typedef swoole::RecvData swRecvData; -extern swoole::Server *g_server_instance; +extern SW_THREAD_LOCAL swoole::Server *g_server_instance; static inline swoole::Server *sw_server() { return g_server_instance; diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 1626d8d09d0..8d647565d9b 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -336,6 +336,7 @@ struct Socket { ssize_t peek(void *__buf, size_t __n, int __flags); Socket *accept(); int bind(const std::string &_host, int *port); + Socket *dup(); ssize_t readv(IOVector *io_vector); ssize_t writev(IOVector *io_vector); diff --git a/src/core/base.cc b/src/core/base.cc index 37b723a6dc6..40512b11153 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -98,6 +98,7 @@ static ssize_t getrandom(void *buffer, size_t size, unsigned int __flags) { swoole::Global SwooleG = {}; __thread swoole::ThreadGlobal SwooleTG = {}; +std::mutex sw_thread_lock; static std::unordered_map functions; static swoole::Logger *g_logger_instance = nullptr; @@ -430,6 +431,15 @@ pid_t swoole_fork(int flags) { return pid; } +void swoole_thread_init(void) { + SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); + ON_SCOPE_EXIT { + delete SwooleTG.buffer_stack; + SwooleTG.buffer_stack = nullptr; + }; + swoole_signal_block_all(); +} + void swoole_dump_ascii(const char *data, size_t size) { for (size_t i = 0; i < size; i++) { printf("%u ", (unsigned) data[i]); diff --git a/src/network/socket.cc b/src/network/socket.cc index d6c5609ec2e..6ef2346eaa6 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -441,6 +441,13 @@ bool Socket::set_timeout(double timeout) { return set_recv_timeout(timeout) and set_send_timeout(timeout); } +Socket *Socket::dup() { + Socket *_socket = new Socket(); + *_socket = *this; + _socket->fd = ::dup(fd); + return _socket; +} + static bool _set_timeout(int fd, int type, double timeout) { int ret; struct timeval timeo; diff --git a/src/os/async_thread.cc b/src/os/async_thread.cc index c09d620de0a..5b1d75ead1a 100644 --- a/src/os/async_thread.cc +++ b/src/os/async_thread.cc @@ -225,13 +225,7 @@ void ThreadPool::create_thread(const bool is_core_worker) { try { std::thread *_thread = new std::thread([this, is_core_worker]() { bool exit_flag = false; - SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); - ON_SCOPE_EXIT { - delete SwooleTG.buffer_stack; - SwooleTG.buffer_stack = nullptr; - }; - - swoole_signal_block_all(); + swoole_thread_init(); while (running) { event_mutex.lock(); diff --git a/src/server/master.cc b/src/server/master.cc index 138daf1a70c..4912562f65f 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -25,7 +25,11 @@ using swoole::network::Address; using swoole::network::SendfileTask; using swoole::network::Socket; -swoole::Server *g_server_instance = nullptr; +SW_THREAD_LOCAL swoole::Server *g_server_instance = nullptr; + +#ifdef SW_THREAD +static std::vector listen_ports; +#endif namespace swoole { @@ -717,8 +721,6 @@ int Server::start() { * initializing server config, set default */ Server::Server(enum Mode _mode) { - swoole_init(); - reactor_num = SW_CPU_NUM > SW_REACTOR_MAX_THREAD ? SW_REACTOR_MAX_THREAD : SW_CPU_NUM; worker_num = SW_CPU_NUM; max_connection = SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets); @@ -1735,6 +1737,43 @@ int Server::add_systemd_socket() { return count; } +static bool Server_create_socket(ListenPort *ls) { +#ifdef SW_THREAD + std::unique_lock _lock(sw_thread_lock); + if (listen_ports.size() > 0) { + for (auto _lp : listen_ports) { + if (_lp->type == ls->type && _lp->port == ls->port && _lp->host == ls->host) { + ls->socket = _lp->socket->dup(); + return true; + } + } + } +#endif + ls->socket = make_socket( + ls->type, ls->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); + if (!ls->socket) { + return false; + } +#if defined(SW_SUPPORT_DTLS) && defined(HAVE_KQUEUE) + if (ls->is_dtls()) { + ls->socket->set_reuse_port(); + } +#endif + + if (ls->socket->bind(ls->host, &ls->port) < 0) { + swoole_set_last_error(errno); + ls->socket->free(); + return false; + } + + ls->socket->info.assign(ls->type, ls->host, ls->port); + +#ifdef SW_THREAD + listen_ports.push_back(ls); +#endif + return true; +} + ListenPort *Server::add_port(SocketType type, const char *host, int port) { if (session_list) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_WRONG_OPERATION, "must add port before server is created"); @@ -1784,7 +1823,6 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { #ifdef SW_SUPPORT_DTLS ls->ssl_context->protocols = SW_SSL_DTLS; ls->dtls_sessions = new std::unordered_map; - #else swoole_warning("DTLS support require openssl-1.1 or later"); return nullptr; @@ -1793,24 +1831,11 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { } #endif - ls->socket = make_socket( - ls->type, ls->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); - if (ls->socket == nullptr) { + if (!Server_create_socket(ls)) { swoole_set_last_error(errno); return nullptr; } -#if defined(SW_SUPPORT_DTLS) && defined(HAVE_KQUEUE) - if (ls->is_dtls()) { - ls->socket->set_reuse_port(); - } -#endif - if (ls->socket->bind(ls->host, &ls->port) < 0) { - swoole_set_last_error(errno); - ls->socket->free(); - return nullptr; - } - ls->socket->info.assign(ls->type, ls->host, ls->port); check_port_type(ls); ptr.release(); ports.push_back(ls); diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 446e03f032f..44e0f17f1bd 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -30,8 +30,12 @@ static int ReactorProcess_reuse_port(ListenPort *ls); #endif static bool Server_is_single(Server *serv) { - return serv->worker_num == 1 && serv->task_worker_num == 0 && serv->max_request == 0 && - serv->user_worker_list.empty(); +#ifdef SW_THREAD + return true; +#else + return (serv->worker_num == 1 && serv->task_worker_num == 0 && serv->max_request == 0 && + serv->user_worker_list.empty()); +#endif } int Server::create_reactor_processes() { @@ -206,7 +210,9 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { SwooleTG.timer->reinit(reactor); } +#ifndef SW_THREAD Server::worker_signal_init(); +#endif for (auto ls : serv->ports) { #ifdef HAVE_REUSEPORT diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 1f5e3335f7f..d1dbaaaac71 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -797,12 +797,7 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { static void ReactorThread_loop(Server *serv, int reactor_id) { SwooleTG.id = reactor_id; SwooleTG.type = Server::THREAD_REACTOR; - - SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); - ON_SCOPE_EXIT { - delete SwooleTG.buffer_stack; - SwooleTG.buffer_stack = nullptr; - }; + swoole_thread_init(); if (swoole_event_init(0) < 0) { return; @@ -830,8 +825,6 @@ static void ReactorThread_loop(Server *serv, int reactor_id) { } #endif - swoole_signal_block_all(); - if (thread->init(serv, reactor, reactor_id) < 0) { return; } diff --git a/src/server/worker.cc b/src/server/worker.cc index f75cb1c6b25..97354593370 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -24,7 +24,7 @@ #include "swoole_msg_queue.h" #include "swoole_coroutine.h" -swoole::WorkerGlobal SwooleWG = {}; +SW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG = {}; namespace swoole { using namespace network; From fe53be370add641f4a29c5756168cce4b74a4eb8 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 28 Mar 2024 19:04:06 +0800 Subject: [PATCH 025/103] Update version --- CMakeLists.txt | 2 +- include/swoole_version.h | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 38492c0600b..08009e56302 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.1.2) +set(SWOOLE_VERSION 6.0.0-dev) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/include/swoole_version.h b/include/swoole_version.h index 468380e451e..990e0dbf6e5 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -18,12 +18,12 @@ #ifndef SWOOLE_VERSION_H_ #define SWOOLE_VERSION_H_ -#define SWOOLE_MAJOR_VERSION 5 -#define SWOOLE_MINOR_VERSION 1 -#define SWOOLE_RELEASE_VERSION 2 -#define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.1.2" -#define SWOOLE_VERSION_ID 50102 +#define SWOOLE_MAJOR_VERSION 6 +#define SWOOLE_MINOR_VERSION 0 +#define SWOOLE_RELEASE_VERSION 0 +#define SWOOLE_EXTRA_VERSION "dev" +#define SWOOLE_VERSION "6.0.0-dev" +#define SWOOLE_VERSION_ID 60000 #define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ From 5b0075069e510878a39f2b2ff3e12ccdd944172f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 28 Mar 2024 19:06:55 +0800 Subject: [PATCH 026/103] Optimize header --- ext-src/php_swoole_private.h | 18 ++---------------- ext-src/php_swoole_thread.h | 7 +++++++ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 6d733e554c5..38d10cad617 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -100,8 +100,8 @@ extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); #define SWOOLE_SOCKETS_SUPPORT #endif -#if PHP_VERSION_ID < 80000 -#error "require PHP version 8.0 or later" +#if PHP_VERSION_ID < 80100 +#error "require PHP version 8.1 or later" #endif #if defined(ZTS) && defined(SW_USE_THREAD_CONTEXT) @@ -357,21 +357,7 @@ zend_bool php_swoole_signal_isset_handler(int signo); #define SW_Z8_OBJ_P(zobj) Z_OBJ_P(zobj) typedef ssize_t php_stream_size_t; - -#if PHP_VERSION_ID < 80100 -typedef const char error_filename_t; -#else typedef zend_string error_filename_t; -#endif - -#ifdef SW_THREAD -typedef uint32_t ThreadResourceId; -struct ThreadResource; - -ThreadResourceId php_swoole_thread_resource_insert(ThreadResource *res); -bool php_swoole_thread_resource_free(ThreadResourceId resource_id, ThreadResource *res); -ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id); -#endif //----------------------------------Zval API------------------------------------ diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index 23fecd6b5c5..9cf926563a8 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -21,6 +21,13 @@ #ifdef SW_THREAD +typedef uint32_t ThreadResourceId; +struct ThreadResource; + +ThreadResourceId php_swoole_thread_resource_insert(ThreadResource *res); +bool php_swoole_thread_resource_free(ThreadResourceId resource_id, ThreadResource *res); +ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id); + zend_string *php_swoole_thread_serialize(zval *zdata); bool php_swoole_thread_unserialize(zend_string *data, zval *zv); From 54071d0195fdf07e961c58f131bb6b7304ce5beb Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 28 Mar 2024 19:24:01 +0800 Subject: [PATCH 027/103] Fix --- ext-src/php_swoole.cc | 4 ++-- ext-src/swoole_atomic.cc | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 80acc013f38..2031c63fa45 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -749,7 +749,7 @@ PHP_MINIT_FUNCTION(swoole) { #ifdef SW_USE_SQLITE php_swoole_sqlite_minit(module_number); #endif -#ifdef ZTS +#ifdef SW_THREAD php_swoole_thread_minit(module_number); #endif @@ -1056,7 +1056,7 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { php_swoole_coroutine_scheduler_rshutdown(); php_swoole_runtime_rshutdown(); php_swoole_process_rshutdown(); -#ifdef ZTS +#ifdef SW_THREAD php_swoole_thread_rshutdown(); #endif diff --git a/ext-src/swoole_atomic.cc b/ext-src/swoole_atomic.cc index c36a5eafbd7..5249d7df812 100644 --- a/ext-src/swoole_atomic.cc +++ b/ext-src/swoole_atomic.cc @@ -308,10 +308,6 @@ void php_swoole_atomic_minit(int module_number) { PHP_METHOD(swoole_atomic, __construct) { auto o = php_swoole_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); - if (o->ptr) { - zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } zend_long value = 0; ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) @@ -320,6 +316,10 @@ PHP_METHOD(swoole_atomic, __construct) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); #ifdef SW_THREAD + if (o->ptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } o->res = new AtomicResource(); auto resource_id = php_swoole_thread_resource_insert(o->res); zend_update_property_long(swoole_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); @@ -428,10 +428,6 @@ static PHP_METHOD(swoole_atomic, __wakeup) { PHP_METHOD(swoole_atomic_long, __construct) { auto o = php_swoole_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); - if (o->ptr) { - zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } zend_long value = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -440,6 +436,10 @@ PHP_METHOD(swoole_atomic_long, __construct) { ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); #ifdef SW_THREAD + if (o->ptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } o->res = new AtomicLongResource(); auto resource_id = php_swoole_thread_resource_insert(o->res); zend_update_property_long(swoole_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); From b6962abbb16cf2a086957e525117ee9a92394eb1 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 29 Mar 2024 10:38:13 +0800 Subject: [PATCH 028/103] refactor async-threads, support ZTS --- examples/thread/aio.php | 42 ++++++++ include/swoole_async.h | 6 +- src/os/async_thread.cc | 216 +++++++++++++++++++++------------------- 3 files changed, 158 insertions(+), 106 deletions(-) create mode 100644 examples/thread/aio.php diff --git a/examples/thread/aio.php b/examples/thread/aio.php new file mode 100644 index 00000000000..38ad30fe5af --- /dev/null +++ b/examples/thread/aio.php @@ -0,0 +1,42 @@ +join(); + } + var_dump($atomic->get()); + sleep(2); + + Co\run(function () use($atomic) { + $n = 1024; + while ($n--) { + $atomic->add(); + $rs = \Swoole\Coroutine\System::readFile(__FILE__); + var_dump(strlen($rs)); + } + }); + var_dump($atomic->get()); +} else { + $atomic = $args[1]; + Co\run(function () use($atomic) { + $n = 1024; + while ($n--) { + $atomic->add(); + $rs = \Swoole\Coroutine\System::readFile(__FILE__); + var_dump(strlen($rs)); + } + }); +} diff --git a/include/swoole_async.h b/include/swoole_async.h index 7f16bc1d0a0..1ba9858f122 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -100,10 +100,9 @@ struct GethostbynameRequest { class AsyncThreads { public: - bool schedule = false; size_t task_num = 0; Pipe *pipe = nullptr; - async::ThreadPool *pool = nullptr; + std::shared_ptr pool; network::Socket *read_socket = nullptr; network::Socket *write_socket = nullptr; @@ -119,9 +118,6 @@ class AsyncThreads { void notify_one(); static int callback(Reactor *reactor, Event *event); - - private: - std::mutex init_lock; }; #ifdef SW_USE_IOURING diff --git a/src/os/async_thread.cc b/src/os/async_thread.cc index 5b1d75ead1a..ca173d42d02 100644 --- a/src/os/async_thread.cc +++ b/src/os/async_thread.cc @@ -32,6 +32,9 @@ #include #include +static std::mutex async_thread_lock; +static std::shared_ptr async_thread_pool; + namespace swoole { namespace async { //------------------------------------------------------------------------------- @@ -82,6 +85,10 @@ class ThreadPool { shutdown(); } + bool is_running() { + return running; + } + bool start() { running = true; current_task_id = 0; @@ -110,16 +117,14 @@ class ThreadPool { } delete _thread; } + threads.clear(); return true; } void schedule() { if (n_waiting == 0 && threads.size() < worker_num && max_wait_time > 0) { - event_mutex.lock(); double _max_wait_time = _queue.get_max_wait_time(); - event_mutex.unlock(); - if (_max_wait_time > max_wait_time) { size_t n = 1; /** @@ -141,14 +146,12 @@ class ThreadPool { } AsyncEvent *dispatch(const AsyncEvent *request) { - if (SwooleTG.async_threads->schedule) { - schedule(); - } auto _event_copy = new AsyncEvent(*request); + event_mutex.lock(); + schedule(); _event_copy->task_id = current_task_id++; _event_copy->timestamp = microtime(); _event_copy->pipe_socket = SwooleTG.async_threads->write_socket; - event_mutex.lock(); _queue.push(_event_copy); _cv.notify_one(); event_mutex.unlock(); @@ -204,6 +207,7 @@ class ThreadPool { private: void create_thread(const bool is_core_worker = false); + void main_func(const bool is_core_worker); size_t core_worker_num; size_t worker_num; @@ -221,96 +225,98 @@ class ThreadPool { std::condition_variable _cv; }; -void ThreadPool::create_thread(const bool is_core_worker) { - try { - std::thread *_thread = new std::thread([this, is_core_worker]() { - bool exit_flag = false; - swoole_thread_init(); - - while (running) { - event_mutex.lock(); - AsyncEvent *event = _queue.pop(); - event_mutex.unlock(); - - swoole_debug("%s: %f", event ? "pop 1 event" : "no event", microtime()); - - if (event) { - if (sw_unlikely(event->handler == nullptr)) { - event->error = SW_ERROR_AIO_BAD_REQUEST; - event->retval = -1; - } else if (sw_unlikely(event->canceled)) { - event->error = SW_ERROR_AIO_CANCELED; - event->retval = -1; - } else { - event->handler(event); - } +void ThreadPool::main_func(bool is_core_worker) { + bool exit_flag = false; + swoole_thread_init(); - swoole_trace_log(SW_TRACE_AIO, - "aio_thread %s. ret=%ld, error=%d", - event->retval > 0 ? "ok" : "failed", - event->retval, - event->error); - - _send_event: - while (true) { - ssize_t ret = event->pipe_socket->write(&event, sizeof(event)); - if (ret < 0) { - if (errno == EAGAIN) { - event->pipe_socket->wait_event(1000, SW_EVENT_WRITE); - continue; - } else if (errno == EINTR) { - continue; - } else { - delete event; - swoole_sys_warning("sendto swoole_aio_pipe_write failed"); - } - } - break; - } + while (running) { + event_mutex.lock(); + AsyncEvent *event = _queue.pop(); + event_mutex.unlock(); - // exit - if (exit_flag) { - n_closing--; - break; - } - } else { - std::unique_lock lock(event_mutex); - if (_queue.count() > 0) { + swoole_debug("%s: %f", event ? "pop 1 event" : "no event", microtime()); + + if (event) { + if (sw_unlikely(event->handler == nullptr)) { + event->error = SW_ERROR_AIO_BAD_REQUEST; + event->retval = -1; + } else if (sw_unlikely(event->canceled)) { + event->error = SW_ERROR_AIO_CANCELED; + event->retval = -1; + } else { + event->handler(event); + } + + swoole_trace_log(SW_TRACE_AIO, + "aio_thread %s. ret=%ld, error=%d", + event->retval > 0 ? "ok" : "failed", + event->retval, + event->error); + + _send_event: + while (true) { + ssize_t ret = event->pipe_socket->write(&event, sizeof(event)); + if (ret < 0) { + if (errno == EAGAIN) { + event->pipe_socket->wait_event(1000, SW_EVENT_WRITE); + continue; + } else if (errno == EINTR) { continue; - } - if (!running) { - break; - } - ++n_waiting; - if (is_core_worker || max_idle_time <= 0) { - _cv.wait(lock); } else { - while (true) { - if (_cv.wait_for(lock, std::chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == - std::cv_status::timeout) { - if (running && n_closing != 0) { - // wait for the next round - continue; - } - /* notifies the main thread to release this thread */ - event = new AsyncEvent; - event->object = new std::thread::id(std::this_thread::get_id()); - event->callback = release_callback; - event->pipe_socket = SwooleG.aio_default_socket; - event->canceled = false; - - --n_waiting; - ++n_closing; - exit_flag = true; - goto _send_event; - } - break; + delete event; + swoole_sys_warning("sendto swoole_aio_pipe_write failed"); + } + } + break; + } + + // exit + if (exit_flag) { + n_closing--; + break; + } + } else { + std::unique_lock lock(event_mutex); + if (_queue.count() > 0) { + continue; + } + if (!running) { + break; + } + ++n_waiting; + if (is_core_worker || max_idle_time <= 0) { + _cv.wait(lock); + } else { + while (true) { + if (_cv.wait_for(lock, std::chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == + std::cv_status::timeout) { + if (running && n_closing != 0) { + // wait for the next round + continue; } + /* notifies the main thread to release this thread */ + event = new AsyncEvent; + event->object = new std::thread::id(std::this_thread::get_id()); + event->callback = release_callback; + event->pipe_socket = SwooleG.aio_default_socket; + event->canceled = false; + + --n_waiting; + ++n_closing; + exit_flag = true; + goto _send_event; } - --n_waiting; + break; } } - }); + --n_waiting; + } + } +} + +void ThreadPool::create_thread(const bool is_core_worker) { + try { + std::thread *_thread = new std::thread([this, is_core_worker]() { main_func(is_core_worker); }); threads[_thread->get_id()] = _thread; } catch (const std::system_error &e) { swoole_sys_notice("create aio thread failed, please check your system configuration or adjust aio_worker_num"); @@ -333,10 +339,6 @@ AsyncEvent *dispatch(const AsyncEvent *request) { } // namespace async int AsyncThreads::callback(Reactor *reactor, Event *event) { - if (SwooleTG.async_threads->schedule) { - SwooleTG.async_threads->pool->schedule(); - } - AsyncEvent *events[SW_AIO_EVENT_NUM]; ssize_t n = event->socket->read(events, sizeof(AsyncEvent *) * SW_AIO_EVENT_NUM); if (n < 0) { @@ -405,20 +407,32 @@ AsyncThreads::AsyncThreads() { return true; }); - init_lock.lock(); - pool = new async::ThreadPool( - SwooleG.aio_core_worker_num, SwooleG.aio_worker_num, SwooleG.aio_max_wait_time, SwooleG.aio_max_idle_time); - pool->start(); - schedule = true; - init_lock.unlock(); + async_thread_lock.lock(); + if (!async_thread_pool) { + async_thread_pool = std::make_shared( + SwooleG.aio_core_worker_num, SwooleG.aio_worker_num, SwooleG.aio_max_wait_time, SwooleG.aio_max_idle_time); + } + if (!async_thread_pool->is_running()) { + async_thread_pool->start(); + } + pool = async_thread_pool; + async_thread_lock.unlock(); SwooleG.aio_default_socket = write_socket; SwooleTG.async_threads = this; } AsyncThreads::~AsyncThreads() { - delete pool; - pool = nullptr; + pool.reset(); + async_thread_lock.lock(); + /** + * When the reference count is 1, it means that all reactor threads have ended + * and all aio threads can be terminated. + */ + if (async_thread_pool.use_count() == 1) { + async_thread_pool->shutdown(); + } + async_thread_lock.unlock(); pipe->close(); read_socket = nullptr; write_socket = nullptr; From bc9b8f8f733518b427cbc0014c088afa6a6264f4 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 29 Mar 2024 15:36:47 +0800 Subject: [PATCH 029/103] Optimize --- ext-src/stubs/php_swoole_server.stub.php | 5 +- ext-src/stubs/php_swoole_server_arginfo.h | 13 +- ext-src/swoole_server.cc | 188 +++++++++++++--------- ext-src/swoole_websocket_server.cc | 4 +- 4 files changed, 127 insertions(+), 83 deletions(-) diff --git a/ext-src/stubs/php_swoole_server.stub.php b/ext-src/stubs/php_swoole_server.stub.php index e9173e9e49d..a13ccb5cde8 100644 --- a/ext-src/stubs/php_swoole_server.stub.php +++ b/ext-src/stubs/php_swoole_server.stub.php @@ -10,7 +10,7 @@ public function listen(string $host, int $port, int $sock_type): false|Server\Po public function sendMessage(mixed $message, int $dst_worker_id): bool {} public function addProcess(\Swoole\Process $process): int {} public function addCommand(string $name, int $accepted_process_types, callable $callback): bool {} - public function start(): bool {} + public function start(string $bootstrap = ''): bool {} public function stop(int $workerId = -1, bool $waitEvent = false): bool {} public function send(int|string $fd, string $send_data, int $serverSocket = -1): bool {} public function sendfile(int $conn_fd, string $filename, int $offset = 0, int $length = 0): bool {} @@ -43,6 +43,9 @@ public function getMasterPid(): int {} public function getSocket(int $port = 0): false|\Socket {} #endif public function getLastError(): int {} + #ifdef SW_THREAD + public function __wakeup(): void {} + #endif } } diff --git a/ext-src/stubs/php_swoole_server_arginfo.h b/ext-src/stubs/php_swoole_server_arginfo.h index 5b1d2e569c8..181c7305d41 100644 --- a/ext-src/stubs/php_swoole_server_arginfo.h +++ b/ext-src/stubs/php_swoole_server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fd753870dff9cec5f5a0e5eb825f550627c93416 */ + * Stub hash: 7e29be6a4a1a8c8fff1a11bf117b2b388ccdbca9 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "\'0.0.0.0\'") @@ -46,6 +46,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_addCommand, ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_start, 0, 0, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bootstrap, IS_STRING, 0, "\'\'") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_stop, 0, 0, _IS_BOOL, 0) @@ -134,7 +135,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_reload, 0, 0 ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, only_reload_taskworker, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() -#define arginfo_class_Swoole_Server_shutdown arginfo_class_Swoole_Server_start +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_shutdown, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_heartbeat, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, ifCloseConnection, _IS_BOOL, 0, "true") @@ -181,6 +183,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Server_getLastError arginfo_class_Swoole_Server_getManagerPid +#if defined(SW_THREAD) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server___wakeup, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() +#endif + #define arginfo_class_Swoole_Connection_Iterator___construct arginfo_class_Swoole_Server___destruct #define arginfo_class_Swoole_Connection_Iterator___destruct arginfo_class_Swoole_Server___destruct @@ -195,7 +202,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Connection_Iterator_key arginfo_class_Swoole_Connection_Iterator_current -#define arginfo_class_Swoole_Connection_Iterator_valid arginfo_class_Swoole_Server_start +#define arginfo_class_Swoole_Connection_Iterator_valid arginfo_class_Swoole_Server_shutdown #define arginfo_class_Swoole_Connection_Iterator_count arginfo_class_Swoole_Server_getManagerPid diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 77f4cd6b477..b2d8261133a 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -158,11 +158,11 @@ zval *php_swoole_server_get_zval_object(Server *serv) { } ServerObject *php_swoole_server_get_zend_object(Server *serv) { - return server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + return server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); } bool php_swoole_server_isset_callback(Server *serv, ListenPort *port, int event_type) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); return server_object->isset_callback(port, event_type); } @@ -344,6 +344,9 @@ static PHP_METHOD(swoole_server, getMasterPid); #ifdef SWOOLE_SOCKETS_SUPPORT static PHP_METHOD(swoole_server, getSocket); #endif +#ifdef SW_THREAD +static PHP_METHOD(swoole_server, __wakeup); +#endif /** * Server\Connection @@ -421,7 +424,10 @@ static zend_function_entry swoole_server_methods[] = { PHP_ME(swoole_server, getSocket, arginfo_class_Swoole_Server_getSocket, ZEND_ACC_PUBLIC) #endif PHP_ME(swoole_server, bind, arginfo_class_Swoole_Server_bind, ZEND_ACC_PUBLIC) - {nullptr, nullptr, nullptr} +#ifdef SW_THREAD + PHP_ME(swoole_server, __wakeup, arginfo_class_Swoole_Server___wakeup, ZEND_ACC_PUBLIC) +#endif + PHP_FE_END }; static const zend_function_entry swoole_connection_iterator_methods[] = @@ -553,6 +559,9 @@ void php_swoole_server_minit(int module_number) { zend_declare_property_long(swoole_server_ce, ZEND_STRL("worker_pid"), 0, ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_ce, ZEND_STRL("stats_timer"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_ce, ZEND_STRL("admin_server"), ZEND_ACC_PUBLIC); +#ifdef SW_THREAD + zend_declare_property_null(swoole_server_ce, ZEND_STRL("bootstrap"), ZEND_ACC_PUBLIC); +#endif /** * mode type @@ -607,7 +616,7 @@ zend_fcall_info_cache *php_swoole_server_get_fci_cache(Server *serv, int server_ ListenPort *port = serv->get_port_by_server_fd(server_fd); ServerPortProperty *property; zend_fcall_info_cache *fci_cache; - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); if (sw_unlikely(!port)) { return nullptr; @@ -756,7 +765,7 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort zend_update_property_bool(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("ssl"), port->ssl); do { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); zval *zports = sw_zend_read_and_convert_property_array(Z_OBJCE_P(zserv), zserv, ZEND_STRL("ports"), 0); (void) add_next_index_zval(zports, zport); } while (0); @@ -1026,9 +1035,9 @@ static int php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *c } static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); zend_fcall_info_cache *fci_cache = server_object->property->callbacks[SW_SERVER_CB_onPipeMessage]; - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); zend::Variable zresult; if (UNEXPECTED(!php_swoole_server_task_unpack(zresult.ptr(), req))) { @@ -1081,7 +1090,7 @@ int php_swoole_server_onReceive(Server *serv, RecvData *req) { auto fci_cache = php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onReceive); if (fci_cache) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); zval args[4]; int argc; @@ -1123,7 +1132,7 @@ int php_swoole_server_onReceive(Server *serv, RecvData *req) { } int php_swoole_server_onPacket(Server *serv, RecvData *req) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); zval args[3]; int argc; @@ -1228,7 +1237,7 @@ static sw_inline void php_swoole_create_task_object(zval *ztask, Server *serv, E static int php_swoole_server_onTask(Server *serv, EventData *req) { sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); zend::Variable zresult; @@ -1273,7 +1282,7 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { } static int php_swoole_server_onFinish(Server *serv, EventData *req) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); zend::Variable zresult; @@ -1374,7 +1383,7 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { static void php_swoole_server_onStart(Server *serv) { serv->lock(); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onStart]; @@ -1392,7 +1401,7 @@ static void php_swoole_server_onStart(Server *serv) { } static void php_swoole_server_onManagerStart(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onManagerStart]; @@ -1409,7 +1418,7 @@ static void php_swoole_server_onManagerStart(Server *serv) { } static void php_swoole_server_onManagerStop(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onManagerStop]; @@ -1424,7 +1433,7 @@ static void php_swoole_server_onManagerStop(Server *serv) { static void php_swoole_server_onBeforeShutdown(Server *serv) { serv->lock(); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onBeforeShutdown]; @@ -1440,7 +1449,7 @@ static void php_swoole_server_onBeforeShutdown(Server *serv) { static void php_swoole_server_onShutdown(Server *serv) { serv->lock(); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onShutdown]; @@ -1455,7 +1464,7 @@ static void php_swoole_server_onShutdown(Server *serv) { } static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStart]; @@ -1483,7 +1492,7 @@ static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { } static void php_swoole_server_onBeforeReload(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onBeforeReload]; @@ -1497,7 +1506,7 @@ static void php_swoole_server_onBeforeReload(Server *serv) { } static void php_swoole_server_onAfterReload(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onAfterReload]; @@ -1516,7 +1525,7 @@ static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { } SwooleWG.shutdown = true; - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStop]; zval args[2]; @@ -1533,7 +1542,7 @@ static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { } static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerExit]; @@ -1554,7 +1563,7 @@ static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { zval *object = (zval *) worker->ptr; zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL("id"), SwooleG.process_id); - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); @@ -1562,7 +1571,7 @@ static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { } static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerError]; @@ -1611,7 +1620,7 @@ void php_swoole_server_onConnect(Server *serv, DataHead *info) { return; } - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); zval args[3]; int argc; args[0] = *zserv; @@ -1641,7 +1650,7 @@ void php_swoole_server_onConnect(Server *serv, DataHead *info) { } void php_swoole_server_onClose(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); SessionId session_id = info->fd; @@ -1673,7 +1682,7 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { } } if (fci_cache) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); zval args[3]; int argc; args[0] = *zserv; @@ -1708,7 +1717,7 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { } void php_swoole_server_onBufferFull(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); auto fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onBufferFull); if (fci_cache) { @@ -1724,7 +1733,7 @@ void php_swoole_server_onBufferFull(Server *serv, DataHead *info) { } void php_swoole_server_send_yield(Server *serv, SessionId session_id, zval *zdata, zval *return_value) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); Coroutine *co = Coroutine::get_current_safe(); char *data; size_t length = php_swoole_get_send_data(zdata, &data); @@ -1766,7 +1775,7 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD zval retval; zend_long worker_id = -1; - *zserv = *((zval *) serv->private_data_2); + *zserv = *(php_swoole_server_get_zval_object(serv)); ZVAL_LONG(zfd, conn ? conn->session_id : data->info.fd); ZVAL_LONG(ztype, (zend_long) (data ? data->info.type : (int) SW_SERVER_EVENT_CLOSE)); if (data && sw_zend_function_max_num_args(fci_cache->function_handler) > 3) { @@ -1800,7 +1809,7 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD } void php_swoole_server_onBufferEmpty(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); if (serv->send_yield) { @@ -1831,6 +1840,33 @@ void php_swoole_server_onBufferEmpty(Server *serv, DataHead *info) { } } +static void Server_ctor(zval *zserv, Server *serv) { + serv->private_data_2 = sw_zval_dup(zserv); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + server_set_ptr(zserv, serv); + + /* primary port */ + do { + for (auto ls : serv->ports) { + php_swoole_server_add_port(server_object, ls); + } + + server_object->property->primary_port = (ServerPortProperty *) serv->get_primary_port()->ptr; + } while (0); + + /* iterator */ + do { + zval connection_iterator; + object_init_ex(&connection_iterator, swoole_connection_iterator_ce); + + ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator); + iterator->serv = serv; + + zend_update_property(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("connections"), &connection_iterator); + zval_ptr_dtor(&connection_iterator); + } while (0); +} + static PHP_METHOD(swoole_server, __construct) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); Server *serv = server_object->serv; @@ -1877,53 +1913,26 @@ static PHP_METHOD(swoole_server, __construct) { #endif serv = new Server((enum Server::Mode) serv_mode); - serv->private_data_2 = sw_zval_dup(zserv); - server_set_ptr(zserv, serv); - - if (serv_mode == Server::MODE_BASE) { - serv->reactor_num = 1; - serv->worker_num = 1; - } + Server_ctor(zserv, serv); - /* primary port */ - do { - if (serv_port == 0 && strcasecmp(host, "SYSTEMD") == 0) { - if (serv->add_systemd_socket() <= 0) { - zend_throw_error(NULL, "failed to add systemd socket"); - RETURN_FALSE; - } - } else { - ListenPort *port = serv->add_port((enum swSocketType) sock_type, host, serv_port); - if (!port) { - zend_throw_exception_ex(swoole_exception_ce, - swoole_get_last_error(), - "failed to listen server port[%s:" ZEND_LONG_FMT "], Error: %s[%d]", - host, - serv_port, - swoole_strerror(swoole_get_last_error()), - swoole_get_last_error()); - RETURN_FALSE; - } + if (serv_port == 0 && strcasecmp(host, "SYSTEMD") == 0) { + if (serv->add_systemd_socket() <= 0) { + zend_throw_error(NULL, "failed to add systemd socket"); + RETURN_FALSE; } - - for (auto ls : serv->ports) { - php_swoole_server_add_port(server_object, ls); + } else { + ListenPort *port = serv->add_port((enum swSocketType) sock_type, host, serv_port); + if (!port) { + zend_throw_exception_ex(swoole_exception_ce, + swoole_get_last_error(), + "failed to listen server port[%s:" ZEND_LONG_FMT "], Error: %s[%d]", + host, + serv_port, + swoole_strerror(swoole_get_last_error()), + swoole_get_last_error()); + RETURN_FALSE; } - - server_object->property->primary_port = (ServerPortProperty *) serv->get_primary_port()->ptr; - } while (0); - - /* iterator */ - do { - zval connection_iterator; - object_init_ex(&connection_iterator, swoole_connection_iterator_ce); - - ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator); - iterator->serv = serv; - - zend_update_property(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("connections"), &connection_iterator); - zval_ptr_dtor(&connection_iterator); - } while (0); + } /* info */ auto port = serv->get_primary_port(); @@ -2526,7 +2535,7 @@ static PHP_METHOD(swoole_server, addCommand) { } Server::Command::Handler fn = [fci_cache](Server *serv, const std::string &msg) { - zval *zserv = (zval *) serv->private_data_2; + zval *zserv = php_swoole_server_get_zval_object(serv); zval argv[2]; argv[0] = *zserv; ZVAL_STRINGL(&argv[1], msg.c_str(), msg.length()); @@ -2575,7 +2584,22 @@ static PHP_METHOD(swoole_server, start) { RETURN_FALSE; } - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + zval *zbootstrap = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zbootstrap) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (zbootstrap) { + zval params[2] = { + *ZEND_THIS, + *zbootstrap, + }; + zend::function::call("\\Swoole\\Server\\Helper::startWithBootstrap", 2, params); + } + + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); server_object->register_callback(); server_object->on_before_start(); @@ -2940,7 +2964,7 @@ static PHP_METHOD(swoole_server, taskwait) { // coroutine if (swoole_coroutine_is_in()) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); TaskCo task_co{}; @@ -3836,6 +3860,16 @@ static PHP_METHOD(swoole_server, stop) { RETURN_TRUE; } +#ifdef SW_THREAD +static PHP_METHOD(swoole_server, __wakeup) { + if (!sw_server() || !sw_server()->is_started()) { + zend_throw_exception(swoole_exception_ce, "server is not running", -2); + RETURN_FALSE; + } + Server_ctor(ZEND_THIS, sw_server()); +} +#endif + // swoole_connection_iterator static PHP_METHOD(swoole_connection_iterator, __construct) { diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 5a2f5f3e73e..30067d81e7d 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -257,7 +257,7 @@ void swoole_websocket_onBeforeHandshakeResponse(Server *serv, int server_fd, Htt php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onBeforeHandshakeResponse); if (fci_cache) { zval args[3]; - args[0] = *((zval *) serv->private_data_2); + args[0] = *php_swoole_server_get_zval_object(serv); args[1] = *ctx->request.zobject; args[2] = *ctx->response.zobject; if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, nullptr, serv->is_enable_coroutine()))) { @@ -277,7 +277,7 @@ void swoole_websocket_onOpen(Server *serv, HttpContext *ctx) { zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, conn->server_fd, SW_SERVER_CB_onOpen); if (fci_cache) { zval args[2]; - args[0] = *((zval *) serv->private_data_2); + args[0] = *php_swoole_server_get_zval_object(serv); args[1] = *ctx->request.zobject; if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onOpen handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); From 80f2d924db1550deec57d146cfc8def47b2b623f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 1 Apr 2024 12:31:54 +0800 Subject: [PATCH 030/103] Optimize --- examples/thread/thread_server.php | 24 +++ ext-src/php_swoole_cxx.h | 24 ++- ext-src/php_swoole_server.h | 10 +- ext-src/php_swoole_thread.h | 3 + ext-src/stubs/php_swoole_server.stub.php | 7 +- ext-src/stubs/php_swoole_server_arginfo.h | 14 +- ext-src/swoole_atomic.cc | 8 +- ext-src/swoole_lock.cc | 4 +- ext-src/swoole_server.cc | 215 +++++++++++++--------- ext-src/swoole_server_port.cc | 4 +- ext-src/swoole_socket_coro.cc | 8 +- ext-src/swoole_thread.cc | 8 +- ext-src/swoole_websocket_server.cc | 8 +- include/swoole_server.h | 18 +- scripts/make.sh | 1 - src/server/master.cc | 6 +- src/server/reactor_process.cc | 13 +- src/server/worker_threads.cc | 137 ++++++++++++++ 18 files changed, 383 insertions(+), 129 deletions(-) create mode 100644 examples/thread/thread_server.php create mode 100644 src/server/worker_threads.cc diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php new file mode 100644 index 00000000000..c2d449d9873 --- /dev/null +++ b/examples/thread/thread_server.php @@ -0,0 +1,24 @@ +set([ + 'worker_num' => 2, +// 'task_worker_num' => 3, + 'enable_coroutine' => false, +]); + +$http->on('Request', function ($req, $resp) use ($http) { + var_dump($http); +}); + +$http->addProcess(new \Swoole\Process(function () { + echo "user process"; + sleep(100); +})); + +$http->on('Task', function () { + var_dump(func_get_args()); +}); + +$http->start(function () use ($http) { + $http->map = new Swoole\Thread\Map(); +}); diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index db0b1af3245..82419ca788e 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -637,22 +637,42 @@ static inline void array_add(zval *arg, zval *zvalue) { add_next_index_zval(arg, zvalue); } +/** + * return reference + */ +static inline zval *array_get(zval *arg, const char *key, size_t l_key) { + return zend_hash_str_find(Z_ARRVAL_P(arg), key, l_key); +} + static inline void array_unset(zval *arg, const char *key, size_t l_key) { zend_hash_str_del(Z_ARRVAL_P(arg), key, l_key); } -static inline zend_long read_property_long(zval *obj, const char *key, size_t l_key) { +static inline zend_long object_get_long(zval *obj, const char *key, size_t l_key) { static zval rv; zval *property = zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), key, l_key, 1, &rv); return property ? zval_get_long(property) : 0; } -static inline zend_long read_property_long(zend_object *obj, const char *key, size_t l_key) { +static inline zend_long object_get_long(zend_object *obj, const char *key, size_t l_key) { static zval rv; zval *property = zend_read_property(obj->ce, obj, key, l_key, 1, &rv); return property ? zval_get_long(property) : 0; } +static inline void object_set(zval *obj, const char *name, size_t l_name, zval *zvalue) { + zend_update_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, zvalue); +} + +static inline void object_set(zval *obj, const char *name, size_t l_name, const char *value) { + zend_update_property_string(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, value); +} + +static inline zval *object_get(zval *obj, const char *name, size_t l_name) { + static zval rv; + return zend_read_property(Z_OBJCE_P(obj), Z_OBJ_P(obj), name, l_name, 1, &rv); +} + //-----------------------------------namespace end-------------------------------------------- } // namespace zend diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index 3e422251d9a..09d7ed93ef1 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -61,6 +61,8 @@ enum php_swoole_server_port_callback_type { #define PHP_SWOOLE_SERVER_CALLBACK_NUM (SW_SERVER_CB_onPipeMessage + 1) #define PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM (SW_SERVER_CB_onBufferEmpty + 1) +zval *php_swoole_server_zval_ptr(swoole::Server *serv); + namespace swoole { struct TaskCo; @@ -90,11 +92,7 @@ struct ServerObject { zend_object std; zend_class_entry *get_ce() { - return Z_OBJCE_P(get_object()); - } - - zval *get_object() { - return (zval *) serv->private_data_2; + return Z_OBJCE_P(php_swoole_server_zval_ptr(serv)); } bool isset_callback(ListenPort *port, int event_type) { @@ -147,4 +145,4 @@ void php_swoole_server_onBufferEmpty(swServer *, swDataHead *); swServer *php_swoole_server_get_and_check_server(zval *zobject); void php_swoole_server_port_deref(zend_object *object); swoole::ServerObject *php_swoole_server_get_zend_object(swoole::Server *serv); -zval *php_swoole_server_get_zval_object(swoole::Server *serv); + diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index 9cf926563a8..78fa5176920 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -28,9 +28,12 @@ ThreadResourceId php_swoole_thread_resource_insert(ThreadResource *res); bool php_swoole_thread_resource_free(ThreadResourceId resource_id, ThreadResource *res); ThreadResource *php_swoole_thread_resource_fetch(ThreadResourceId resource_id); +void php_swoole_thread_start(zend_string *file, zend_string *argv); zend_string *php_swoole_thread_serialize(zval *zdata); bool php_swoole_thread_unserialize(zend_string *data, zval *zv); +zval *php_swoole_thread_get_arguments(); + #define EMSG_NO_RESOURCE "resource not found" #define ECODE_NO_RESOURCE -2 diff --git a/ext-src/stubs/php_swoole_server.stub.php b/ext-src/stubs/php_swoole_server.stub.php index a13ccb5cde8..cc191a11098 100644 --- a/ext-src/stubs/php_swoole_server.stub.php +++ b/ext-src/stubs/php_swoole_server.stub.php @@ -10,7 +10,7 @@ public function listen(string $host, int $port, int $sock_type): false|Server\Po public function sendMessage(mixed $message, int $dst_worker_id): bool {} public function addProcess(\Swoole\Process $process): int {} public function addCommand(string $name, int $accepted_process_types, callable $callback): bool {} - public function start(string $bootstrap = ''): bool {} + public function start(callable $callback = ''): bool {} public function stop(int $workerId = -1, bool $waitEvent = false): bool {} public function send(int|string $fd, string $send_data, int $serverSocket = -1): bool {} public function sendfile(int $conn_fd, string $filename, int $offset = 0, int $length = 0): bool {} @@ -43,9 +43,8 @@ public function getMasterPid(): int {} public function getSocket(int $port = 0): false|\Socket {} #endif public function getLastError(): int {} - #ifdef SW_THREAD - public function __wakeup(): void {} - #endif + public function __get(string $key): mixed {} + public function __set(string $key, mixed $value): void {} } } diff --git a/ext-src/stubs/php_swoole_server_arginfo.h b/ext-src/stubs/php_swoole_server_arginfo.h index 181c7305d41..56ec4339c28 100644 --- a/ext-src/stubs/php_swoole_server_arginfo.h +++ b/ext-src/stubs/php_swoole_server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7e29be6a4a1a8c8fff1a11bf117b2b388ccdbca9 */ + * Stub hash: e6c779c7e2f80b34973c47f1b758730b71659e61 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "\'0.0.0.0\'") @@ -46,7 +46,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_addCommand, ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_start, 0, 0, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bootstrap, IS_STRING, 0, "\'\'") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, callback, IS_CALLABLE, 0, "\'\'") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_stop, 0, 0, _IS_BOOL, 0) @@ -183,10 +183,14 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Server_getLastError arginfo_class_Swoole_Server_getManagerPid -#if defined(SW_THREAD) -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server___wakeup, 0, 0, IS_VOID, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server___get, 0, 1, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server___set, 0, 2, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() -#endif #define arginfo_class_Swoole_Connection_Iterator___construct arginfo_class_Swoole_Server___destruct diff --git a/ext-src/swoole_atomic.cc b/ext-src/swoole_atomic.cc index 5249d7df812..61218cfeff6 100644 --- a/ext-src/swoole_atomic.cc +++ b/ext-src/swoole_atomic.cc @@ -125,7 +125,7 @@ void php_swoole_atomic_set_ptr(zval *zobject, sw_atomic_t *ptr) { static void php_swoole_atomic_free_object(zend_object *object) { #ifdef SW_THREAD AtomicObject *o = php_swoole_atomic_fetch_object(object); - zend_long resource_id = zend::read_property_long(object, ZEND_STRL("id")); + zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { delete o->res; o->res = nullptr; @@ -192,7 +192,7 @@ void php_swoole_atomic_long_set_ptr(zval *zobject, sw_atomic_long_t *ptr) { static void php_swoole_atomic_long_free_object(zend_object *object) { #ifdef SW_THREAD AtomicLongObject *o = php_swoole_atomic_long_fetch_object(object); - zend_long resource_id = zend::read_property_long(object, ZEND_STRL("id")); + zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { delete o->res; o->res = nullptr; @@ -416,7 +416,7 @@ PHP_METHOD(swoole_atomic, wakeup) { #ifdef SW_THREAD static PHP_METHOD(swoole_atomic, __wakeup) { auto o = php_swoole_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = zend::read_property_long(ZEND_THIS, ZEND_STRL("id")); + zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); if (!o->res) { zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); @@ -504,7 +504,7 @@ PHP_METHOD(swoole_atomic_long, cmpset) { #ifdef SW_THREAD static PHP_METHOD(swoole_atomic_long, __wakeup) { auto o = php_swoole_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = zend::read_property_long(ZEND_THIS, ZEND_STRL("id")); + zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); if (!o->res) { zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index b5303ec5bf1..eb7b0519b8b 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -78,7 +78,7 @@ void php_swoole_lock_set_ptr(zval *zobject, Lock *ptr) { static void php_swoole_lock_free_object(zend_object *object) { LockObject *o = php_swoole_lock_fetch_object(object); #ifdef SW_THREAD - zend_long resource_id = zend::read_property_long(object, ZEND_STRL("id")); + zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); if (o->lock_res && php_swoole_thread_resource_free(resource_id, o->lock_res)) { delete o->lock_res; o->lock_res = nullptr; @@ -268,7 +268,7 @@ static PHP_METHOD(swoole_lock, destroy) { #ifdef SW_THREAD static PHP_METHOD(swoole_lock, __wakeup) { auto o = php_swoole_lock_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = zend::read_property_long(ZEND_THIS, ZEND_STRL("id")); + zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); o->lock_res = static_cast(php_swoole_thread_resource_fetch(resource_id)); if (!o->lock_res) { zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index b2d8261133a..a21f5d1fcab 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -17,6 +17,7 @@ #include "php_swoole_server.h" #include "php_swoole_http_server.h" #include "php_swoole_process.h" +#include "php_swoole_thread.h" #include "php_swoole_call_stack.h" #include "swoole_msg_queue.h" @@ -137,6 +138,9 @@ static zend_object_handlers swoole_server_status_info_handlers; static zend_class_entry *swoole_server_task_result_ce; static zend_object_handlers swoole_server_task_result_handlers; +static SW_THREAD_LOCAL zval swoole_server_instance; +static SW_THREAD_LOCAL WorkerFn worker_thread_fn; + static sw_inline ServerObject *server_fetch_object(zend_object *obj) { return (ServerObject *) ((char *) obj - swoole_server_handlers.offset); } @@ -153,16 +157,16 @@ Server *php_swoole_server_get_and_check_server(zval *zobject) { return serv; } -zval *php_swoole_server_get_zval_object(Server *serv) { - return (zval *) serv->private_data_2; +zval *php_swoole_server_zval_ptr(Server *serv) { + return &swoole_server_instance; } ServerObject *php_swoole_server_get_zend_object(Server *serv) { - return server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); + return server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); } bool php_swoole_server_isset_callback(Server *serv, ListenPort *port, int event_type) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); return server_object->isset_callback(port, event_type); } @@ -180,9 +184,7 @@ static void server_free_object(zend_object *object) { sw_zend_fci_cache_discard((zend_fcall_info_cache *) serv->private_data_3); efree(serv->private_data_3); } - if (serv->private_data_2) { - efree(serv->private_data_2); - } + ZVAL_NULL(php_swoole_server_zval_ptr(serv)); for (int i = 0; i < PHP_SWOOLE_SERVER_CALLBACK_NUM; i++) { zend_fcall_info_cache *fci_cache = property->callbacks[i]; if (fci_cache) { @@ -344,9 +346,8 @@ static PHP_METHOD(swoole_server, getMasterPid); #ifdef SWOOLE_SOCKETS_SUPPORT static PHP_METHOD(swoole_server, getSocket); #endif -#ifdef SW_THREAD -static PHP_METHOD(swoole_server, __wakeup); -#endif +static PHP_METHOD(swoole_server, __get); +static PHP_METHOD(swoole_server, __set); /** * Server\Connection @@ -424,9 +425,8 @@ static zend_function_entry swoole_server_methods[] = { PHP_ME(swoole_server, getSocket, arginfo_class_Swoole_Server_getSocket, ZEND_ACC_PUBLIC) #endif PHP_ME(swoole_server, bind, arginfo_class_Swoole_Server_bind, ZEND_ACC_PUBLIC) -#ifdef SW_THREAD - PHP_ME(swoole_server, __wakeup, arginfo_class_Swoole_Server___wakeup, ZEND_ACC_PUBLIC) -#endif + PHP_ME(swoole_server, __get, arginfo_class_Swoole_Server___get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_server, __set, arginfo_class_Swoole_Server___set, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -560,8 +560,9 @@ void php_swoole_server_minit(int module_number) { zend_declare_property_null(swoole_server_ce, ZEND_STRL("stats_timer"), ZEND_ACC_PUBLIC); zend_declare_property_null(swoole_server_ce, ZEND_STRL("admin_server"), ZEND_ACC_PUBLIC); #ifdef SW_THREAD - zend_declare_property_null(swoole_server_ce, ZEND_STRL("bootstrap"), ZEND_ACC_PUBLIC); + zend_declare_property_string(swoole_server_ce, ZEND_STRL("bootstrap"), "", ZEND_ACC_PUBLIC); #endif + zend_declare_property_null(swoole_server_ce, ZEND_STRL("objects"), ZEND_ACC_PUBLIC); /** * mode type @@ -616,7 +617,7 @@ zend_fcall_info_cache *php_swoole_server_get_fci_cache(Server *serv, int server_ ListenPort *port = serv->get_port_by_server_fd(server_fd); ServerPortProperty *property; zend_fcall_info_cache *fci_cache; - ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); if (sw_unlikely(!port)) { return nullptr; @@ -765,7 +766,7 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort zend_update_property_bool(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("ssl"), port->ssl); do { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); zval *zports = sw_zend_read_and_convert_property_array(Z_OBJCE_P(zserv), zserv, ZEND_STRL("ports"), 0); (void) add_next_index_zval(zports, zport); } while (0); @@ -795,7 +796,7 @@ void ServerObject::on_before_start() { return; } - zval *zobject = get_object(); + zval *zobject = php_swoole_server_zval_ptr(serv); auto primary_port = serv->get_primary_port(); #ifdef SW_LOG_TRACE_OPEN @@ -1035,9 +1036,9 @@ static int php_swoole_server_task_finish(Server *serv, zval *zdata, EventData *c } static void php_swoole_server_onPipeMessage(Server *serv, EventData *req) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); zend_fcall_info_cache *fci_cache = server_object->property->callbacks[SW_SERVER_CB_onPipeMessage]; - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); zend::Variable zresult; if (UNEXPECTED(!php_swoole_server_task_unpack(zresult.ptr(), req))) { @@ -1090,7 +1091,7 @@ int php_swoole_server_onReceive(Server *serv, RecvData *req) { auto fci_cache = php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onReceive); if (fci_cache) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[4]; int argc; @@ -1132,7 +1133,7 @@ int php_swoole_server_onReceive(Server *serv, RecvData *req) { } int php_swoole_server_onPacket(Server *serv, RecvData *req) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; @@ -1237,7 +1238,7 @@ static sw_inline void php_swoole_create_task_object(zval *ztask, Server *serv, E static int php_swoole_server_onTask(Server *serv, EventData *req) { sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); zend::Variable zresult; @@ -1282,7 +1283,7 @@ static int php_swoole_server_onTask(Server *serv, EventData *req) { } static int php_swoole_server_onFinish(Server *serv, EventData *req) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); zend::Variable zresult; @@ -1383,7 +1384,7 @@ static int php_swoole_server_onFinish(Server *serv, EventData *req) { static void php_swoole_server_onStart(Server *serv) { serv->lock(); - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onStart]; @@ -1401,7 +1402,7 @@ static void php_swoole_server_onStart(Server *serv) { } static void php_swoole_server_onManagerStart(Server *serv) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onManagerStart]; @@ -1418,7 +1419,7 @@ static void php_swoole_server_onManagerStart(Server *serv) { } static void php_swoole_server_onManagerStop(Server *serv) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onManagerStop]; @@ -1433,7 +1434,7 @@ static void php_swoole_server_onManagerStop(Server *serv) { static void php_swoole_server_onBeforeShutdown(Server *serv) { serv->lock(); - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onBeforeShutdown]; @@ -1449,7 +1450,7 @@ static void php_swoole_server_onBeforeShutdown(Server *serv) { static void php_swoole_server_onShutdown(Server *serv) { serv->lock(); - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onShutdown]; @@ -1464,7 +1465,7 @@ static void php_swoole_server_onShutdown(Server *serv) { } static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStart]; @@ -1492,7 +1493,7 @@ static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { } static void php_swoole_server_onBeforeReload(Server *serv) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onBeforeReload]; @@ -1506,7 +1507,7 @@ static void php_swoole_server_onBeforeReload(Server *serv) { } static void php_swoole_server_onAfterReload(Server *serv) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onAfterReload]; @@ -1525,7 +1526,7 @@ static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { } SwooleWG.shutdown = true; - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerStop]; zval args[2]; @@ -1542,7 +1543,7 @@ static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { } static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerExit]; @@ -1563,7 +1564,7 @@ static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { zval *object = (zval *) worker->ptr; zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL("id"), SwooleG.process_id); - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); @@ -1571,7 +1572,7 @@ static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { } static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); auto fci_cache = server_object->property->callbacks[SW_SERVER_CB_onWorkerError]; @@ -1620,7 +1621,7 @@ void php_swoole_server_onConnect(Server *serv, DataHead *info) { return; } - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; args[0] = *zserv; @@ -1650,7 +1651,7 @@ void php_swoole_server_onConnect(Server *serv, DataHead *info) { } void php_swoole_server_onClose(Server *serv, DataHead *info) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); SessionId session_id = info->fd; @@ -1682,7 +1683,7 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { } } if (fci_cache) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; args[0] = *zserv; @@ -1717,7 +1718,7 @@ void php_swoole_server_onClose(Server *serv, DataHead *info) { } void php_swoole_server_onBufferFull(Server *serv, DataHead *info) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); auto fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onBufferFull); if (fci_cache) { @@ -1733,7 +1734,7 @@ void php_swoole_server_onBufferFull(Server *serv, DataHead *info) { } void php_swoole_server_send_yield(Server *serv, SessionId session_id, zval *zdata, zval *return_value) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); Coroutine *co = Coroutine::get_current_safe(); char *data; size_t length = php_swoole_get_send_data(zdata, &data); @@ -1775,7 +1776,7 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD zval retval; zend_long worker_id = -1; - *zserv = *(php_swoole_server_get_zval_object(serv)); + *zserv = *(php_swoole_server_zval_ptr(serv)); ZVAL_LONG(zfd, conn ? conn->session_id : data->info.fd); ZVAL_LONG(ztype, (zend_long) (data ? data->info.type : (int) SW_SERVER_EVENT_CLOSE)); if (data && sw_zend_function_max_num_args(fci_cache->function_handler) > 3) { @@ -1809,7 +1810,7 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD } void php_swoole_server_onBufferEmpty(Server *serv, DataHead *info) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); if (serv->send_yield) { @@ -1840,9 +1841,9 @@ void php_swoole_server_onBufferEmpty(Server *serv, DataHead *info) { } } -static void Server_ctor(zval *zserv, Server *serv) { - serv->private_data_2 = sw_zval_dup(zserv); +static void server_ctor(zval *zserv, Server *serv) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + *php_swoole_server_zval_ptr(serv) = *zserv; server_set_ptr(zserv, serv); /* primary port */ @@ -1865,6 +1866,15 @@ static void Server_ctor(zval *zserv, Server *serv) { zend_update_property(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("connections"), &connection_iterator); zval_ptr_dtor(&connection_iterator); } while (0); + + /* info */ + auto port = serv->get_primary_port(); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("mode"), serv->get_mode()); + zend_update_property_stringl( + swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("host"), port->host.c_str(), port->host.length()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), port->get_port()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("type"), port->get_type()); + zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("ssl"), port->ssl); } static PHP_METHOD(swoole_server, __construct) { @@ -1889,12 +1899,6 @@ static PHP_METHOD(swoole_server, __construct) { RETURN_FALSE; } - if (sw_server() != nullptr) { - zend_throw_exception_ex( - swoole_exception_ce, -3, "server is running. unable to create %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); - RETURN_FALSE; - } - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4) Z_PARAM_STRING(host, host_len) Z_PARAM_OPTIONAL @@ -1909,11 +1913,19 @@ static PHP_METHOD(swoole_server, __construct) { } #ifdef SW_THREAD - serv_mode = Server::MODE_BASE; + if (sw_server() || sw_server()->is_worker_thread()) { + server_ctor(ZEND_THIS, sw_server()); + return; + } +#else + if (sw_server() != nullptr) { + zend_throw_exception_ex( + swoole_exception_ce, -3, "server is running. unable to create %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); + RETURN_FALSE; + } #endif serv = new Server((enum Server::Mode) serv_mode); - Server_ctor(zserv, serv); if (serv_port == 0 && strcasecmp(host, "SYSTEMD") == 0) { if (serv->add_systemd_socket() <= 0) { @@ -1934,13 +1946,7 @@ static PHP_METHOD(swoole_server, __construct) { } } - /* info */ - auto port = serv->get_primary_port(); - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("mode"), serv_mode); - zend_update_property_stringl(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("host"), host, host_len); - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), port->get_port()); - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("type"), port->get_type()); - zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("ssl"), port->ssl); + server_ctor(zserv, serv); } static PHP_METHOD(swoole_server, __destruct) {} @@ -1948,6 +1954,9 @@ static PHP_METHOD(swoole_server, __destruct) {} static PHP_METHOD(swoole_server, set) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (serv->is_worker_thread()) { + return; + } if (serv->is_started()) { php_swoole_fatal_error( E_WARNING, "server is running, unable to execute %s->set", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); @@ -2342,6 +2351,12 @@ static PHP_METHOD(swoole_server, set) { zend_long v = zval_get_long(ztmp); serv->message_queue_key = SW_MAX(0, SW_MIN(v, INT64_MAX)); } + // bootstrap + if (php_swoole_array_get_value(vht, "bootstrap", ztmp)) { + zend::object_set(ZEND_THIS, ZEND_STRL("bootstrap"), ztmp); + } else { + zend::object_set(ZEND_THIS, ZEND_STRL("bootstrap"), SG(request_info).path_translated); + } if (serv->task_enable_coroutine && (serv->task_ipc_mode == Server::TASK_IPC_MSGQUEUE || serv->task_ipc_mode == Server::TASK_IPC_PREEMPTIVE)) { @@ -2360,7 +2375,7 @@ static PHP_METHOD(swoole_server, set) { static PHP_METHOD(swoole_server, on) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (serv->is_started()) { + if (SwooleTG.type != Server::THREAD_WORKER && serv->is_started()) { php_swoole_fatal_error(E_WARNING, "server is running, unable to register event callback function"); RETURN_FALSE; } @@ -2434,6 +2449,30 @@ static PHP_METHOD(swoole_server, getCallback) { server_object->property->ports.at(0), swoole_server_port_ce, nullptr, "getcallback", return_value, name); } +static PHP_METHOD(swoole_server, __get) { + char *name; + size_t l_name; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(name, l_name) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zval *objects = sw_zend_read_and_convert_property_array(swoole_server_ce, ZEND_THIS, ZEND_STRL("objects"), 0); + RETURN_ZVAL(zend::array_get(objects, name, l_name), 1, 0); +} + +static PHP_METHOD(swoole_server, __set) { + char *name; + size_t l_name; + zval *value; + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(name, l_name) + Z_PARAM_ZVAL(value); + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zval *objects = sw_zend_read_and_convert_property_array(swoole_server_ce, ZEND_THIS, ZEND_STRL("objects"), 0); + zend::array_set(objects, name, l_name, value); +} + static PHP_METHOD(swoole_server, listen) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); if (serv->is_started()) { @@ -2464,7 +2503,7 @@ extern Worker *php_swoole_process_get_and_check_worker(zval *zobject); static PHP_METHOD(swoole_server, addProcess) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (serv->is_started()) { + if (!serv->is_worker_thread() && serv->is_started()) { php_swoole_fatal_error(E_WARNING, "server is running, can't add process"); RETURN_FALSE; } @@ -2535,7 +2574,7 @@ static PHP_METHOD(swoole_server, addCommand) { } Server::Command::Handler fn = [fci_cache](Server *serv, const std::string &msg) { - zval *zserv = php_swoole_server_get_zval_object(serv); + zval *zserv = php_swoole_server_zval_ptr(serv); zval argv[2]; argv[0] = *zserv; ZVAL_STRINGL(&argv[1], msg.c_str(), msg.length()); @@ -2567,6 +2606,14 @@ static PHP_METHOD(swoole_server, start) { zval *zserv = ZEND_THIS; Server *serv = php_swoole_server_get_and_check_server(zserv); +#ifdef SW_THREAD + if (serv->is_worker_thread()) { + zval *thread_argv = php_swoole_thread_get_arguments(); + zend::object_set(ZEND_THIS, ZEND_STRL("objects"), thread_argv); + RETURN_BOOL(worker_thread_fn()); + } +#endif + if (serv->is_started()) { php_swoole_fatal_error( E_WARNING, "server is running, unable to execute %s->start()", SW_Z_OBJCE_NAME_VAL_P(zserv)); @@ -2584,22 +2631,32 @@ static PHP_METHOD(swoole_server, start) { RETURN_FALSE; } - zval *zbootstrap = nullptr; + zval *cb = nullptr; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(zbootstrap) + Z_PARAM_ZVAL(cb) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (zbootstrap) { - zval params[2] = { - *ZEND_THIS, - *zbootstrap, - }; - zend::function::call("\\Swoole\\Server\\Helper::startWithBootstrap", 2, params); + if (cb) { + zval retval; + call_user_function(NULL, NULL, cb, &retval, 0, NULL); + zval_ptr_dtor(&retval); } - ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); + zval *objects = sw_zend_read_and_convert_property_array(swoole_server_ce, ZEND_THIS, ZEND_STRL("objects"), 0); + zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); + zend_string *bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); + zend_string *argv = php_swoole_thread_serialize(objects); + +#ifdef SW_THREAD + serv->worker_thread_start = [bootstrap, argv](const WorkerFn &fn) { + worker_thread_fn = fn; + php_swoole_thread_start(bootstrap, argv); + }; +#endif + + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); server_object->register_callback(); server_object->on_before_start(); @@ -2964,7 +3021,7 @@ static PHP_METHOD(swoole_server, taskwait) { // coroutine if (swoole_coroutine_is_in()) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_get_zval_object(serv))); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); TaskCo task_co{}; @@ -3860,16 +3917,6 @@ static PHP_METHOD(swoole_server, stop) { RETURN_TRUE; } -#ifdef SW_THREAD -static PHP_METHOD(swoole_server, __wakeup) { - if (!sw_server() || !sw_server()->is_started()) { - zend_throw_exception(swoole_exception_ce, "server is not running", -2); - RETURN_FALSE; - } - Server_ctor(ZEND_THIS, sw_server()); -} -#endif - // swoole_connection_iterator static PHP_METHOD(swoole_connection_iterator, __construct) { @@ -3933,7 +3980,7 @@ static PHP_METHOD(swoole_connection_iterator, count) { static PHP_METHOD(swoole_connection_iterator, offsetExists) { ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - zval *zserv = (zval *) iterator->serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(iterator->serv); zval *zfd; zval retval; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) { @@ -3945,7 +3992,7 @@ static PHP_METHOD(swoole_connection_iterator, offsetExists) { static PHP_METHOD(swoole_connection_iterator, offsetGet) { ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - zval *zserv = (zval *) iterator->serv->private_data_2; + zval *zserv = php_swoole_server_zval_ptr(iterator->serv); zval *zfd; zval retval; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) { diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index 2a3fb703148..b16a9e55294 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -154,7 +154,9 @@ const zend_function_entry swoole_server_port_methods[] = void php_swoole_server_port_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_server_port, "Swoole\\Server\\Port", nullptr, swoole_server_port_methods); +#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server_port); +#endif SW_SET_CLASS_CLONEABLE(swoole_server_port, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server_port, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_server_port, @@ -622,7 +624,7 @@ static PHP_METHOD(swoole_server_port, on) { ServerPortProperty *property = php_swoole_server_port_get_and_check_property(ZEND_THIS); Server *serv = property->serv; - if (serv->is_started()) { + if (SwooleTG.type != Server::THREAD_WORKER && serv->is_started()) { php_swoole_fatal_error(E_WARNING, "can't register event callback function after server started"); RETURN_FALSE; } diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index eaf867e3bf3..82868a979ee 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -2220,7 +2220,7 @@ static PHP_METHOD(swoole_socket_coro, import) { #ifdef SW_THREAD static PHP_METHOD(swoole_socket_coro, __wakeup) { - zend_long sockfd = zend::read_property_long(ZEND_THIS, ZEND_STRL("fd")); + zend_long sockfd = zend::object_get_long(ZEND_THIS, ZEND_STRL("fd")); if (sockfd < 0) { zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); return; @@ -2234,9 +2234,9 @@ static PHP_METHOD(swoole_socket_coro, __wakeup) { SocketObject *sock = (SocketObject *) socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long domain = zend::read_property_long(ZEND_THIS, ZEND_STRL("domain")); - zend_long type = zend::read_property_long(ZEND_THIS, ZEND_STRL("type")); - zend_long protocol = zend::read_property_long(ZEND_THIS, ZEND_STRL("protocol")); + zend_long domain = zend::object_get_long(ZEND_THIS, ZEND_STRL("domain")); + zend_long type = zend::object_get_long(ZEND_THIS, ZEND_STRL("type")); + zend_long protocol = zend::object_get_long(ZEND_THIS, ZEND_STRL("protocol")); php_swoole_check_reactor(); sock->socket = new Socket((int) new_sockfd, (int) domain, (int) type, (int) protocol); diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index a470ad0e785..e94af0c8cae 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -767,11 +767,15 @@ static PHP_METHOD(swoole_thread, detach) { RETURN_TRUE; } -static PHP_METHOD(swoole_thread, getArguments) { +zval *php_swoole_thread_get_arguments() { if (!ZVAL_IS_ARRAY(&thread_argv)) { array_init(&thread_argv); } - RETURN_ZVAL(&thread_argv, 1, 0); + return &thread_argv; +} + +static PHP_METHOD(swoole_thread, getArguments) { + RETURN_ZVAL(php_swoole_thread_get_arguments(), 1, 0); } static PHP_METHOD(swoole_thread, getId) { diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc index 30067d81e7d..e471a738b24 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -257,7 +257,7 @@ void swoole_websocket_onBeforeHandshakeResponse(Server *serv, int server_fd, Htt php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onBeforeHandshakeResponse); if (fci_cache) { zval args[3]; - args[0] = *php_swoole_server_get_zval_object(serv); + args[0] = *php_swoole_server_zval_ptr(serv); args[1] = *ctx->request.zobject; args[2] = *ctx->response.zobject; if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, nullptr, serv->is_enable_coroutine()))) { @@ -277,7 +277,7 @@ void swoole_websocket_onOpen(Server *serv, HttpContext *ctx) { zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, conn->server_fd, SW_SERVER_CB_onOpen); if (fci_cache) { zval args[2]; - args[0] = *php_swoole_server_get_zval_object(serv); + args[0] = *php_swoole_server_zval_ptr(serv); args[1] = *ctx->request.zobject; if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) { php_swoole_error(E_WARNING, "%s->onOpen handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); @@ -561,7 +561,7 @@ int swoole_websocket_onMessage(Server *serv, RecvData *req) { php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onMessage); zval args[2]; - args[0] = *(zval *) serv->private_data_2; + args[0] = *php_swoole_server_zval_ptr(serv); php_swoole_websocket_construct_frame(&args[1], opcode, &zdata, flags); zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(&args[1]), ZEND_STRL("fd"), fd); @@ -593,7 +593,9 @@ void php_swoole_websocket_server_minit(int module_number) { nullptr, swoole_websocket_server_methods, swoole_http_server); +#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_websocket_server); +#endif SW_SET_CLASS_CLONEABLE(swoole_websocket_server, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_websocket_server, sw_zend_class_unset_property_deny); diff --git a/include/swoole_server.h b/include/swoole_server.h index cb0eeff571b..22bb4306b98 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -57,6 +57,8 @@ struct Request; class Server; struct Manager; +typedef std::function WorkerFn; + struct Session { SessionId id; int fd; @@ -504,6 +506,7 @@ class Server { THREAD_MASTER = 1, THREAD_REACTOR = 2, THREAD_HEARTBEAT = 3, + THREAD_WORKER = 5, }; enum DispatchMode { @@ -734,6 +737,10 @@ class Server { return ports.front(); } + enum Mode get_mode() { + return mode_; + }; + ListenPort *get_port(int _port) { for (auto port : ports) { if (port->port == _port || _port == 0) { @@ -1096,6 +1103,11 @@ class Server { return SwooleG.process_type == SW_PROCESS_WORKER; } + bool is_worker_thread() { + return SwooleTG.type == THREAD_WORKER; + } + + bool is_task_worker() { return SwooleG.process_type == SW_PROCESS_TASKWORKER; } @@ -1365,6 +1377,9 @@ class Server { void worker_start_callback(Worker *worker); void worker_stop_callback(Worker *worker); void worker_accept_event(DataHead *info); + std::function worker_thread_start; + + static int worker_main_loop(ProcessPool *pool, Worker *worker); static void worker_signal_handler(int signo); static void worker_signal_init(void); static bool task_pack(EventData *task, const void *data, size_t data_len); @@ -1398,6 +1413,7 @@ class Server { int create_reactor_threads(); int start_reactor_threads(); int start_reactor_processes(); + int start_worker_threads(); int start_master_thread(); int start_event_worker(Worker *worker); void start_heartbeat_thread(); @@ -1454,7 +1470,7 @@ typedef swoole::Server swServer; typedef swoole::ListenPort swListenPort; typedef swoole::RecvData swRecvData; -extern SW_THREAD_LOCAL swoole::Server *g_server_instance; +extern swoole::Server *g_server_instance; static inline swoole::Server *sw_server() { return g_server_instance; diff --git a/scripts/make.sh b/scripts/make.sh index 4a478151554..fd46c8c9d90 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -7,7 +7,6 @@ COMPILE_PARAMS="--enable-openssl \ --enable-swoole-curl \ --enable-cares \ --enable-swoole-pgsql \ ---enable-iouring \ --enable-swoole-thread \ --with-swoole-odbc=unixODBC,/usr \ --enable-swoole-sqlite" diff --git a/src/server/master.cc b/src/server/master.cc index 4912562f65f..51da2a5b931 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -25,7 +25,7 @@ using swoole::network::Address; using swoole::network::SendfileTask; using swoole::network::Socket; -SW_THREAD_LOCAL swoole::Server *g_server_instance = nullptr; +swoole::Server *g_server_instance = nullptr; #ifdef SW_THREAD static std::vector listen_ports; @@ -701,7 +701,11 @@ int Server::start() { } int ret; if (is_base_mode()) { +#ifdef SW_THREAD + ret = start_worker_threads(); +#else ret = start_reactor_processes(); +#endif } else { ret = start_reactor_threads(); } diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 44e0f17f1bd..c99a0e22097 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -20,7 +20,6 @@ namespace swoole { using network::Socket; -static int ReactorProcess_loop(ProcessPool *pool, Worker *worker); static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event); static int ReactorProcess_onClose(Reactor *reactor, Event *event); static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode); @@ -30,12 +29,8 @@ static int ReactorProcess_reuse_port(ListenPort *ls); #endif static bool Server_is_single(Server *serv) { -#ifdef SW_THREAD - return true; -#else return (serv->worker_num == 1 && serv->task_worker_num == 0 && serv->max_request == 0 && serv->user_worker_list.empty()); -#endif } int Server::create_reactor_processes() { @@ -93,8 +88,8 @@ int Server::start_reactor_processes() { gs->event_workers.ptr = this; gs->event_workers.max_wait_time = max_wait_time; gs->event_workers.use_msgqueue = 0; - gs->event_workers.main_loop = ReactorProcess_loop; - gs->event_workers.onWorkerNotFound = Server::wait_other_worker; + gs->event_workers.main_loop = worker_main_loop; + gs->event_workers.onWorkerNotFound = wait_other_worker; memcpy(workers, gs->event_workers.workers, sizeof(*workers) * worker_num); gs->event_workers.workers = workers; @@ -110,7 +105,7 @@ int Server::start_reactor_processes() { } if (Server_is_single(this)) { - int retval = ReactorProcess_loop(&gs->event_workers, &gs->event_workers.workers[0]); + int retval = worker_main_loop(&gs->event_workers, &gs->event_workers.workers[0]); if (retval == SW_OK) { gs->event_workers.destroy(); } @@ -182,7 +177,7 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { return SW_OK; } -static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { +int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; SwooleG.process_type = SW_PROCESS_WORKER; diff --git a/src/server/worker_threads.cc b/src/server/worker_threads.cc new file mode 100644 index 00000000000..ae3e2535249 --- /dev/null +++ b/src/server/worker_threads.cc @@ -0,0 +1,137 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "swoole_server.h" +#include "swoole_memory.h" + +namespace swoole { +using network::Socket; + +int Server::start_worker_threads() { + single_thread = 1; + + // listen TCP + if (have_stream_sock == 1) { + for (auto ls : ports) { + if (ls->is_dgram()) { + continue; + } +#ifdef HAVE_REUSEPORT + if (enable_reuse_port) { + if (::close(ls->socket->fd) < 0) { + swoole_sys_warning("close(%d) failed", ls->socket->fd); + } + delete ls->socket; + ls->socket = nullptr; + continue; + } else +#endif + { + // listen server socket + if (ls->listen() < 0) { + return SW_ERR; + } + } + } + } + + ProcessPool *pool = &gs->event_workers; + *pool = {}; + if (pool->create(worker_num, 0, SW_IPC_UNIXSOCK) < 0) { + return SW_ERR; + } + pool->set_max_request(max_request, max_request_grace); + + /** + * store to ProcessPool object + */ + gs->event_workers.ptr = this; + gs->event_workers.max_wait_time = max_wait_time; + gs->event_workers.use_msgqueue = 0; + gs->event_workers.main_loop = worker_main_loop; + memcpy(workers, gs->event_workers.workers, sizeof(*workers) * worker_num); + gs->event_workers.workers = workers; + + SW_LOOP_N(worker_num) { + gs->event_workers.workers[i].pool = &gs->event_workers; + gs->event_workers.workers[i].id = i; + gs->event_workers.workers[i].type = SW_PROCESS_WORKER; + } + + init_ipc_max_size(); + if (create_pipe_buffers() < 0) { + return SW_ERR; + } + + SW_LOOP_N(worker_num) { + create_worker(get_worker(i)); + } + + if (gs->event_workers.create_message_box(SW_MESSAGE_BOX_SIZE) == SW_ERR) { + return SW_ERR; + } + + if (task_worker_num > 0 && create_task_workers() < 0) { + return SW_ERR; + } + + if (get_user_worker_num() > 0 && create_user_workers() < 0) { + return SW_ERR; + } + + std::vector threads; + threads.resize(task_worker_num + worker_num + get_user_worker_num()); + + if (task_worker_num > 0) { + SW_LOOP_N(task_worker_num) { + threads[worker_num + i] = std::thread([=]() { + SwooleTG.type = Server::THREAD_WORKER; + Worker *worker = gs->task_workers.get_worker(i); + worker_thread_start( + [=](void) -> bool { return gs->task_workers.main_loop(&gs->task_workers, worker) == SW_OK; }); + }); + } + } + + SW_LOOP_N(worker_num) { + threads[i] = std::thread([=]() { + SwooleTG.type = Server::THREAD_WORKER; + Worker *worker = get_worker(i); + worker_thread_start([=](void) -> bool { return worker_main_loop(&gs->event_workers, worker) == SW_OK; }); + }); + } + + if (!user_worker_list.empty()) { + int i = 0; + for (auto worker : user_worker_list) { + threads[task_worker_num + worker_num + i] = std::thread([=]() { + SwooleTG.type = Server::THREAD_WORKER; + worker_thread_start([=](void) -> bool { + onUserWorkerStart(this, worker); + return SW_OK; + }); + }); + i++; + } + } + + for (auto &thread : threads) { + thread.join(); + } + + return SW_OK; +} +} // namespace swoole From b2bf40a8e67abd8ce1f4ca4e970734aaab23f55d Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 1 Apr 2024 14:16:59 +0800 Subject: [PATCH 031/103] Optimize --- ext-src/swoole_server.cc | 13 ++++++++++--- ext-src/swoole_server_port.cc | 2 +- ext-src/swoole_thread.cc | 19 ++++++++++++++++--- src/server/worker_threads.cc | 3 +++ 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index a21f5d1fcab..2662ca9a6bb 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2375,7 +2375,7 @@ static PHP_METHOD(swoole_server, set) { static PHP_METHOD(swoole_server, on) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (SwooleTG.type != Server::THREAD_WORKER && serv->is_started()) { + if (!serv->is_worker_thread() && serv->is_started()) { php_swoole_fatal_error(E_WARNING, "server is running, unable to register event callback function"); RETURN_FALSE; } @@ -2644,15 +2644,17 @@ static PHP_METHOD(swoole_server, start) { zval_ptr_dtor(&retval); } +#ifdef SW_THREAD zval *objects = sw_zend_read_and_convert_property_array(swoole_server_ce, ZEND_THIS, ZEND_STRL("objects"), 0); zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); zend_string *bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); zend_string *argv = php_swoole_thread_serialize(objects); -#ifdef SW_THREAD serv->worker_thread_start = [bootstrap, argv](const WorkerFn &fn) { worker_thread_fn = fn; - php_swoole_thread_start(bootstrap, argv); + zend_string *bootstrap_copy = zend_string_dup(bootstrap, 1); + zend_string *argv_copy = zend_string_dup(argv, 1); + php_swoole_thread_start(bootstrap_copy, argv_copy); }; #endif @@ -2664,6 +2666,11 @@ static PHP_METHOD(swoole_server, start) { php_swoole_fatal_error(E_ERROR, "failed to start server. Error: %s", sw_error); } +#ifdef SW_THREAD + zend_string_release(bootstrap); + zend_string_release(argv); +#endif + RETURN_TRUE; } diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index b16a9e55294..e76a07b5c48 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -624,7 +624,7 @@ static PHP_METHOD(swoole_server_port, on) { ServerPortProperty *property = php_swoole_server_port_get_and_check_property(ZEND_THIS); Server *serv = property->serv; - if (SwooleTG.type != Server::THREAD_WORKER && serv->is_started()) { + if (!serv->is_worker_thread() && serv->is_started()) { php_swoole_fatal_error(E_WARNING, "can't register event callback function after server started"); RETURN_FALSE; } diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index e94af0c8cae..d8ad240c4d6 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -45,6 +45,8 @@ static zend_object_handlers swoole_thread_arraylist_handlers; zend_class_entry *swoole_thread_queue_ce; static zend_object_handlers swoole_thread_queue_handlers; +TSRMLS_CACHE_DEFINE(); + struct ArrayItem { uint32_t type; zend_string *key; @@ -820,11 +822,17 @@ void php_swoole_thread_rshutdown() { void php_swoole_thread_start(zend_string *file, zend_string *argv) { ts_resource(0); -#if defined(COMPILE_DL_SWOOLE) - ZEND_TSRMLS_CACHE_UPDATE(); -#endif + TSRMLS_CACHE_UPDATE(); zend_file_handle file_handle{}; + PG(expose_php) = 0; + PG(auto_globals_jit) = 1; +#if PHP_VERSION_ID >= 80100 + PG(enable_dl) = false; +#else + PG(enable_dl) = 0; +#endif + swoole_thread_init(); if (php_request_startup() != SUCCESS) { @@ -832,6 +840,11 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv) { goto _startup_error; } + PG(during_request_startup) = 0; + SG(sapi_started) = 0; + SG(headers_sent) = 1; + SG(request_info).no_headers = 1; + zend_stream_init_filename(&file_handle, ZSTR_VAL(file)); file_handle.primary_script = 1; diff --git a/src/server/worker_threads.cc b/src/server/worker_threads.cc index ae3e2535249..c22ac074b88 100644 --- a/src/server/worker_threads.cc +++ b/src/server/worker_threads.cc @@ -99,6 +99,7 @@ int Server::start_worker_threads() { SW_LOOP_N(task_worker_num) { threads[worker_num + i] = std::thread([=]() { SwooleTG.type = Server::THREAD_WORKER; + SwooleTG.id = worker_num + i; Worker *worker = gs->task_workers.get_worker(i); worker_thread_start( [=](void) -> bool { return gs->task_workers.main_loop(&gs->task_workers, worker) == SW_OK; }); @@ -109,6 +110,7 @@ int Server::start_worker_threads() { SW_LOOP_N(worker_num) { threads[i] = std::thread([=]() { SwooleTG.type = Server::THREAD_WORKER; + SwooleTG.id = i; Worker *worker = get_worker(i); worker_thread_start([=](void) -> bool { return worker_main_loop(&gs->event_workers, worker) == SW_OK; }); }); @@ -119,6 +121,7 @@ int Server::start_worker_threads() { for (auto worker : user_worker_list) { threads[task_worker_num + worker_num + i] = std::thread([=]() { SwooleTG.type = Server::THREAD_WORKER; + SwooleTG.id = task_worker_num + worker_num + i; worker_thread_start([=](void) -> bool { onUserWorkerStart(this, worker); return SW_OK; From dc465299f87edfa4458642e3cfdfdfbcdf3ae3aa Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 1 Apr 2024 14:28:34 +0800 Subject: [PATCH 032/103] Optimize --- ext-src/swoole_server.cc | 8 ++++---- src/server/reactor_process.cc | 2 +- src/server/worker.cc | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 2662ca9a6bb..6f08e1a352a 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1562,7 +1562,7 @@ static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker) { static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker) { zval *object = (zval *) worker->ptr; - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL("id"), SwooleG.process_id); + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL("id"), worker->id); zval *zserv = php_swoole_server_zval_ptr(serv); zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); @@ -3811,7 +3811,7 @@ static PHP_METHOD(swoole_server, getWorkerId) { if (!serv->is_worker() && !serv->is_task_worker()) { RETURN_FALSE; } else { - RETURN_LONG(SwooleG.process_id); + RETURN_LONG(SwooleWG.worker->id); } } @@ -3894,7 +3894,7 @@ static PHP_METHOD(swoole_server, stop) { } zend_bool wait_reactor = 0; - zend_long worker_id = SwooleG.process_id; + zend_long worker_id = SwooleWG.worker->id; ZEND_PARSE_PARAMETERS_START(0, 2) Z_PARAM_OPTIONAL @@ -3902,7 +3902,7 @@ static PHP_METHOD(swoole_server, stop) { Z_PARAM_BOOL(wait_reactor) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (worker_id == SwooleG.process_id && wait_reactor == 0) { + if (worker_id == SwooleWG.worker->id && wait_reactor == 0) { if (SwooleTG.reactor != nullptr) { SwooleTG.reactor->defer( [](void *data) { diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index c99a0e22097..c74f9ddd3c8 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -301,7 +301,7 @@ int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { if (serv->isset_hook(Server::HOOK_WORKER_CLOSE)) { void *hook_args[2]; hook_args[0] = serv; - hook_args[1] = (void *) (uintptr_t) SwooleG.process_id; + hook_args[1] = (void *) (uintptr_t) worker->id; serv->call_hook(Server::HOOK_WORKER_CLOSE, hook_args); } diff --git a/src/server/worker.cc b/src/server/worker.cc index 97354593370..5875af005c2 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -261,7 +261,7 @@ void Server::worker_start_callback(Worker *worker) { } SW_LOOP_N(worker_num + task_worker_num) { - if (SwooleG.process_id == i) { + if (worker->id == i) { continue; } Worker *other_worker = get_worker(i); @@ -287,7 +287,7 @@ void Server::worker_start_callback(Worker *worker) { void Server::worker_stop_callback(Worker *worker) { void *hook_args[2]; hook_args[0] = this; - hook_args[1] = (void *) (uintptr_t) SwooleG.process_id; + hook_args[1] = (void *) (uintptr_t) worker->id; if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP)) { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_WORKER_STOP, hook_args); } @@ -354,7 +354,7 @@ void Server::stop_async_worker(Worker *worker) { } else { WorkerStopMessage msg; msg.pid = SwooleG.pid; - msg.worker_id = SwooleG.process_id; + msg.worker_id = worker->id; if (gs->event_workers.push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) { running = 0; From 98570da3ded6a87e14a0bcf1dddbac1396efc909 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 1 Apr 2024 15:36:16 +0800 Subject: [PATCH 033/103] Optimize 5 --- ext-src/stubs/php_swoole_server.stub.php | 4 +-- ext-src/stubs/php_swoole_server_arginfo.h | 15 ++------ ext-src/swoole_server.cc | 42 ++--------------------- 3 files changed, 7 insertions(+), 54 deletions(-) diff --git a/ext-src/stubs/php_swoole_server.stub.php b/ext-src/stubs/php_swoole_server.stub.php index cc191a11098..2e0ab454b59 100644 --- a/ext-src/stubs/php_swoole_server.stub.php +++ b/ext-src/stubs/php_swoole_server.stub.php @@ -10,7 +10,7 @@ public function listen(string $host, int $port, int $sock_type): false|Server\Po public function sendMessage(mixed $message, int $dst_worker_id): bool {} public function addProcess(\Swoole\Process $process): int {} public function addCommand(string $name, int $accepted_process_types, callable $callback): bool {} - public function start(callable $callback = ''): bool {} + public function start(?array $argv): bool {} public function stop(int $workerId = -1, bool $waitEvent = false): bool {} public function send(int|string $fd, string $send_data, int $serverSocket = -1): bool {} public function sendfile(int $conn_fd, string $filename, int $offset = 0, int $length = 0): bool {} @@ -43,8 +43,6 @@ public function getMasterPid(): int {} public function getSocket(int $port = 0): false|\Socket {} #endif public function getLastError(): int {} - public function __get(string $key): mixed {} - public function __set(string $key, mixed $value): void {} } } diff --git a/ext-src/stubs/php_swoole_server_arginfo.h b/ext-src/stubs/php_swoole_server_arginfo.h index 56ec4339c28..79cb022b16e 100644 --- a/ext-src/stubs/php_swoole_server_arginfo.h +++ b/ext-src/stubs/php_swoole_server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e6c779c7e2f80b34973c47f1b758730b71659e61 */ + * Stub hash: 0389b504333cec64a55f31dad33ec84b88ceab06 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "\'0.0.0.0\'") @@ -45,8 +45,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_addCommand, ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_start, 0, 0, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, callback, IS_CALLABLE, 0, "\'\'") +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_start, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, argv, IS_ARRAY, 1) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_stop, 0, 0, _IS_BOOL, 0) @@ -183,15 +183,6 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Server_getLastError arginfo_class_Swoole_Server_getManagerPid -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server___get, 0, 1, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server___set, 0, 2, IS_VOID, 0) - ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) -ZEND_END_ARG_INFO() - #define arginfo_class_Swoole_Connection_Iterator___construct arginfo_class_Swoole_Server___destruct #define arginfo_class_Swoole_Connection_Iterator___destruct arginfo_class_Swoole_Server___destruct diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 6f08e1a352a..ef83a664bf3 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -346,8 +346,6 @@ static PHP_METHOD(swoole_server, getMasterPid); #ifdef SWOOLE_SOCKETS_SUPPORT static PHP_METHOD(swoole_server, getSocket); #endif -static PHP_METHOD(swoole_server, __get); -static PHP_METHOD(swoole_server, __set); /** * Server\Connection @@ -425,8 +423,6 @@ static zend_function_entry swoole_server_methods[] = { PHP_ME(swoole_server, getSocket, arginfo_class_Swoole_Server_getSocket, ZEND_ACC_PUBLIC) #endif PHP_ME(swoole_server, bind, arginfo_class_Swoole_Server_bind, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, __get, arginfo_class_Swoole_Server___get, ZEND_ACC_PUBLIC) - PHP_ME(swoole_server, __set, arginfo_class_Swoole_Server___set, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -562,7 +558,6 @@ void php_swoole_server_minit(int module_number) { #ifdef SW_THREAD zend_declare_property_string(swoole_server_ce, ZEND_STRL("bootstrap"), "", ZEND_ACC_PUBLIC); #endif - zend_declare_property_null(swoole_server_ce, ZEND_STRL("objects"), ZEND_ACC_PUBLIC); /** * mode type @@ -2449,30 +2444,6 @@ static PHP_METHOD(swoole_server, getCallback) { server_object->property->ports.at(0), swoole_server_port_ce, nullptr, "getcallback", return_value, name); } -static PHP_METHOD(swoole_server, __get) { - char *name; - size_t l_name; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(name, l_name) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zval *objects = sw_zend_read_and_convert_property_array(swoole_server_ce, ZEND_THIS, ZEND_STRL("objects"), 0); - RETURN_ZVAL(zend::array_get(objects, name, l_name), 1, 0); -} - -static PHP_METHOD(swoole_server, __set) { - char *name; - size_t l_name; - zval *value; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(name, l_name) - Z_PARAM_ZVAL(value); - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zval *objects = sw_zend_read_and_convert_property_array(swoole_server_ce, ZEND_THIS, ZEND_STRL("objects"), 0); - zend::array_set(objects, name, l_name, value); -} - static PHP_METHOD(swoole_server, listen) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); if (serv->is_started()) { @@ -2631,24 +2602,17 @@ static PHP_METHOD(swoole_server, start) { RETURN_FALSE; } - zval *cb = nullptr; + zval *thread_argv = nullptr; ZEND_PARSE_PARAMETERS_START(0, 1) Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(cb) + Z_PARAM_ZVAL(thread_argv) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (cb) { - zval retval; - call_user_function(NULL, NULL, cb, &retval, 0, NULL); - zval_ptr_dtor(&retval); - } - #ifdef SW_THREAD - zval *objects = sw_zend_read_and_convert_property_array(swoole_server_ce, ZEND_THIS, ZEND_STRL("objects"), 0); zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); zend_string *bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); - zend_string *argv = php_swoole_thread_serialize(objects); + zend_string *argv = php_swoole_thread_serialize(thread_argv); serv->worker_thread_start = [bootstrap, argv](const WorkerFn &fn) { worker_thread_fn = fn; From a4ed2052dacdd8b76f129dd824d41a2c45ca2cfd Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 1 Apr 2024 16:53:40 +0800 Subject: [PATCH 034/103] Optimize 6 --- ext-src/php_swoole_server.h | 13 +++++++++---- ext-src/swoole_server.cc | 39 +++++++++++++++++++++++++------------ include/swoole_server.h | 4 ++-- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index 09d7ed93ef1..a5541c06baf 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -61,10 +61,16 @@ enum php_swoole_server_port_callback_type { #define PHP_SWOOLE_SERVER_CALLBACK_NUM (SW_SERVER_CB_onPipeMessage + 1) #define PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM (SW_SERVER_CB_onBufferEmpty + 1) +namespace swoole { +struct ServerPortProperty; +struct TaskCo; +}; // namespace swoole + zval *php_swoole_server_zval_ptr(swoole::Server *serv); +swoole::ServerPortProperty *php_swoole_server_get_port_property(swoole::ListenPort *port); +void php_swoole_server_set_port_property(swoole::ListenPort *port, swoole::ServerPortProperty *property); namespace swoole { -struct TaskCo; struct ServerPortProperty { zval *callbacks[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM]; @@ -78,7 +84,6 @@ struct ServerPortProperty { struct ServerProperty { std::vector ports; std::vector user_processes; - ServerPortProperty *primary_port; zend_fcall_info_cache *callbacks[PHP_SWOOLE_SERVER_CALLBACK_NUM]; std::unordered_map task_callbacks; std::unordered_map task_coroutine_map; @@ -96,8 +101,8 @@ struct ServerObject { } bool isset_callback(ListenPort *port, int event_type) { - ServerPortProperty *port_property = (ServerPortProperty *) port->ptr; - return (port_property->callbacks[event_type] || property->primary_port->callbacks[event_type]); + return (php_swoole_server_get_port_property(port)->callbacks[event_type] || + php_swoole_server_get_port_property(serv->get_primary_port())->callbacks[event_type]); } zend_bool is_websocket_server() { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index ef83a664bf3..6fc6d0f58d7 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -140,6 +140,7 @@ static zend_object_handlers swoole_server_task_result_handlers; static SW_THREAD_LOCAL zval swoole_server_instance; static SW_THREAD_LOCAL WorkerFn worker_thread_fn; +static SW_THREAD_LOCAL std::vector swoole_server_port_properties; static sw_inline ServerObject *server_fetch_object(zend_object *obj) { return (ServerObject *) ((char *) obj - swoole_server_handlers.offset); @@ -161,6 +162,25 @@ zval *php_swoole_server_zval_ptr(Server *serv) { return &swoole_server_instance; } +ServerPortProperty *php_swoole_server_get_port_property(ListenPort *port) { +#ifdef SW_THREAD + return swoole_server_port_properties.at(port->socket->get_fd()); +#else + return (ServerPortProperty *) port->ptr; +#endif +} + +void php_swoole_server_set_port_property(ListenPort *port, ServerPortProperty *property) { +#ifdef SW_THREAD + if (swoole_server_port_properties.size() < (size_t) port->socket->get_fd() + 1) { + swoole_server_port_properties.resize((size_t) port->socket->get_fd() + 1); + } + swoole_server_port_properties[port->socket->get_fd()] = property; +#else + port->ptr = property; +#endif +} + ServerObject *php_swoole_server_get_zend_object(Server *serv) { return server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); } @@ -610,17 +630,16 @@ void php_swoole_server_minit(int module_number) { zend_fcall_info_cache *php_swoole_server_get_fci_cache(Server *serv, int server_fd, int event_type) { ListenPort *port = serv->get_port_by_server_fd(server_fd); - ServerPortProperty *property; + ServerPortProperty *property = php_swoole_server_get_port_property(port); zend_fcall_info_cache *fci_cache; - ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); if (sw_unlikely(!port)) { return nullptr; } - if ((property = (ServerPortProperty *) port->ptr) && (fci_cache = property->caches[event_type])) { + if (property && (fci_cache = property->caches[event_type])) { return fci_cache; } else { - return server_object->property->primary_port->caches[event_type]; + return php_swoole_server_get_port_property(serv->get_primary_port())->caches[event_type]; } } @@ -752,7 +771,7 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort property->port = port; /* linked */ - port->ptr = property; + php_swoole_server_set_port_property(port, property); zend_update_property_string(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("host"), port->get_host()); zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("port"), port->get_port()); @@ -1842,13 +1861,9 @@ static void server_ctor(zval *zserv, Server *serv) { server_set_ptr(zserv, serv); /* primary port */ - do { - for (auto ls : serv->ports) { - php_swoole_server_add_port(server_object, ls); - } - - server_object->property->primary_port = (ServerPortProperty *) serv->get_primary_port()->ptr; - } while (0); + for (auto ls : serv->ports) { + php_swoole_server_add_port(server_object, ls); + } /* iterator */ do { diff --git a/include/swoole_server.h b/include/swoole_server.h index 22bb4306b98..b40b00b0284 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -737,11 +737,11 @@ class Server { return ports.front(); } - enum Mode get_mode() { + enum Mode get_mode() const { return mode_; }; - ListenPort *get_port(int _port) { + ListenPort *get_port(int _port) const { for (auto port : ports) { if (port->port == _port || _port == 0) { return port; From 3b50c710c2410376ab2e3a52a099cfb56ed5a380 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 1 Apr 2024 17:36:25 +0800 Subject: [PATCH 035/103] Optimize 7 --- examples/thread/thread_server.php | 11 +- ext-src/php_swoole_library.h | 312 +++++++++++----------- ext-src/php_swoole_server.h | 1 + ext-src/stubs/php_swoole_server.stub.php | 2 +- ext-src/stubs/php_swoole_server_arginfo.h | 10 +- ext-src/swoole_server.cc | 29 +- ext-src/swoole_thread.cc | 2 +- 7 files changed, 189 insertions(+), 178 deletions(-) diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php index c2d449d9873..dce29dde283 100644 --- a/examples/thread/thread_server.php +++ b/examples/thread/thread_server.php @@ -4,10 +4,13 @@ 'worker_num' => 2, // 'task_worker_num' => 3, 'enable_coroutine' => false, + 'init_arguments' => function () use ($http) { + return [new Swoole\Thread\Map]; + } ]); $http->on('Request', function ($req, $resp) use ($http) { - var_dump($http); + $resp->end("tid=" . \Swoole\Thread::getId()); }); $http->addProcess(new \Swoole\Process(function () { @@ -19,6 +22,8 @@ var_dump(func_get_args()); }); -$http->start(function () use ($http) { - $http->map = new Swoole\Thread\Map(); +$http->on('WorkerStart', function ($serv, $wid) { + var_dump(\Swoole\Thread::getArguments()); }); + +$http->start(); diff --git a/ext-src/php_swoole_library.h b/ext-src/php_swoole_library.h index 6612eb89777..2cefd9c2947 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: 3bc066dd5d5f3498f7bb2ef8c5a7408d0cd33149 */ +/* $Id: b1dfd92c6bcb71413e561d75fe860bfb3ca38434 */ #ifndef SWOOLE_LIBRARY_H #define SWOOLE_LIBRARY_H @@ -8425,178 +8425,180 @@ static const char* swoole_library_source_core_server_helper = " public const STATS_TIMER_INTERVAL_TIME = 1000;\n" "\n" " public const GLOBAL_OPTIONS = [\n" - " 'debug_mode' => true,\n" - " 'trace_flags' => true,\n" - " 'log_file' => true,\n" - " 'log_level' => true,\n" - " 'log_date_format' => true,\n" - " 'log_date_with_microseconds' => true,\n" - " 'log_rotation' => true,\n" - " 'display_errors' => true,\n" - " 'dns_server' => true,\n" - " 'socket_dns_timeout' => true,\n" - " 'socket_connect_timeout' => true,\n" - " 'socket_write_timeout' => true,\n" - " 'socket_send_timeout' => true,\n" - " 'socket_read_timeout' => true,\n" - " 'socket_recv_timeout' => true,\n" - " 'socket_buffer_size' => true,\n" - " 'socket_timeout' => true,\n" - " 'http2_header_table_size' => true,\n" - " 'http2_enable_push' => true,\n" + " 'debug_mode' => true,\n" + " 'trace_flags' => true,\n" + " 'log_file' => true,\n" + " 'log_level' => true,\n" + " 'log_date_format' => true,\n" + " 'log_date_with_microseconds' => true,\n" + " 'log_rotation' => true,\n" + " 'display_errors' => true,\n" + " 'dns_server' => true,\n" + " 'socket_dns_timeout' => true,\n" + " 'socket_connect_timeout' => true,\n" + " 'socket_write_timeout' => true,\n" + " 'socket_send_timeout' => true,\n" + " 'socket_read_timeout' => true,\n" + " 'socket_recv_timeout' => true,\n" + " 'socket_buffer_size' => true,\n" + " 'socket_timeout' => true,\n" + " 'http2_header_table_size' => true,\n" + " 'http2_enable_push' => true,\n" " 'http2_max_concurrent_streams' => true,\n" - " 'http2_init_window_size' => true,\n" - " 'http2_max_frame_size' => true,\n" - " 'http2_max_header_list_size' => true,\n" + " 'http2_init_window_size' => true,\n" + " 'http2_max_frame_size' => true,\n" + " 'http2_max_header_list_size' => true,\n" " ];\n" "\n" " public const SERVER_OPTIONS = [\n" - " 'chroot' => true,\n" - " 'user' => true,\n" - " 'group' => true,\n" - " 'daemonize' => true,\n" - " 'pid_file' => true,\n" - " 'reactor_num' => true,\n" - " 'single_thread' => true,\n" - " 'worker_num' => true,\n" - " 'max_wait_time' => true,\n" - " 'max_queued_bytes' => true,\n" - " 'max_concurrency' => true,\n" - " 'worker_max_concurrency' => true,\n" - " 'enable_coroutine' => true,\n" - " 'send_timeout' => true,\n" - " 'dispatch_mode' => true,\n" - " 'send_yield' => true,\n" - " 'dispatch_func' => true,\n" - " 'discard_timeout_request' => true,\n" - " 'enable_unsafe_event' => true,\n" - " 'enable_delay_receive' => true,\n" - " 'enable_reuse_port' => true,\n" - " 'task_use_object' => true,\n" - " 'task_object' => true,\n" - " 'event_object' => true,\n" - " 'task_enable_coroutine' => true,\n" - " 'task_worker_num' => true,\n" - " 'task_ipc_mode' => true,\n" - " 'task_tmpdir' => true,\n" - " 'task_max_request' => true,\n" - " 'task_max_request_grace' => true,\n" - " 'max_connection' => true,\n" - " 'max_conn' => true,\n" - " 'start_session_id' => true,\n" - " 'heartbeat_check_interval' => true,\n" - " 'heartbeat_idle_time' => true,\n" - " 'max_request' => true,\n" - " 'max_request_grace' => true,\n" - " 'reload_async' => true,\n" - " 'open_cpu_affinity' => true,\n" - " 'cpu_affinity_ignore' => true,\n" - " 'http_parse_cookie' => true,\n" - " 'http_parse_post' => true,\n" - " 'http_parse_files' => true,\n" - " 'http_compression' => true,\n" - " 'http_compression_level' => true,\n" - " 'compression_level' => true,\n" - " 'http_gzip_level' => true,\n" + " 'chroot' => true,\n" + " 'user' => true,\n" + " 'group' => true,\n" + " 'daemonize' => true,\n" + " 'pid_file' => true,\n" + " 'reactor_num' => true,\n" + " 'single_thread' => true,\n" + " 'worker_num' => true,\n" + " 'max_wait_time' => true,\n" + " 'max_queued_bytes' => true,\n" + " 'max_concurrency' => true,\n" + " 'worker_max_concurrency' => true,\n" + " 'enable_coroutine' => true,\n" + " 'send_timeout' => true,\n" + " 'dispatch_mode' => true,\n" + " 'send_yield' => true,\n" + " 'dispatch_func' => true,\n" + " 'discard_timeout_request' => true,\n" + " 'enable_unsafe_event' => true,\n" + " 'enable_delay_receive' => true,\n" + " 'enable_reuse_port' => true,\n" + " 'task_use_object' => true,\n" + " 'task_object' => true,\n" + " 'event_object' => true,\n" + " 'task_enable_coroutine' => true,\n" + " 'task_worker_num' => true,\n" + " 'task_ipc_mode' => true,\n" + " 'task_tmpdir' => true,\n" + " 'task_max_request' => true,\n" + " 'task_max_request_grace' => true,\n" + " 'max_connection' => true,\n" + " 'max_conn' => true,\n" + " 'start_session_id' => true,\n" + " 'heartbeat_check_interval' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'max_request' => true,\n" + " 'max_request_grace' => true,\n" + " 'reload_async' => true,\n" + " 'open_cpu_affinity' => true,\n" + " 'cpu_affinity_ignore' => true,\n" + " 'http_parse_cookie' => true,\n" + " 'http_parse_post' => true,\n" + " 'http_parse_files' => true,\n" + " 'http_compression' => true,\n" + " 'http_compression_level' => true,\n" + " 'compression_level' => true,\n" + " 'http_gzip_level' => true,\n" " 'http_compression_min_length' => true,\n" - " 'compression_min_length' => true,\n" - " 'websocket_compression' => true,\n" - " 'upload_tmp_dir' => true,\n" - " 'upload_max_filesize' => true,\n" - " 'enable_static_handler' => true,\n" - " 'document_root' => true,\n" - " 'http_autoindex' => true,\n" - " 'http_index_files' => true,\n" - " 'http_compression_types' => true,\n" - " 'compression_types' => true,\n" - " 'static_handler_locations' => true,\n" - " 'input_buffer_size' => true,\n" - " 'buffer_input_size' => true,\n" - " 'output_buffer_size' => true,\n" - " 'buffer_output_size' => true,\n" - " 'message_queue_key' => true,\n" + " 'compression_min_length' => true,\n" + " 'websocket_compression' => true,\n" + " 'upload_tmp_dir' => true,\n" + " 'upload_max_filesize' => true,\n" + " 'enable_static_handler' => true,\n" + " 'document_root' => true,\n" + " 'http_autoindex' => true,\n" + " 'http_index_files' => true,\n" + " 'http_compression_types' => true,\n" + " 'compression_types' => true,\n" + " 'static_handler_locations' => true,\n" + " 'input_buffer_size' => true,\n" + " 'buffer_input_size' => true,\n" + " 'output_buffer_size' => true,\n" + " 'buffer_output_size' => true,\n" + " 'message_queue_key' => true,\n" + " 'bootstrap' => true,\n" + " 'init_arguments' => true,\n" " ];\n" "\n" " public const PORT_OPTIONS = [\n" - " 'ssl_cert_file' => true,\n" - " 'ssl_key_file' => true,\n" - " 'backlog' => true,\n" - " 'socket_buffer_size' => true,\n" + " 'ssl_cert_file' => true,\n" + " 'ssl_key_file' => true,\n" + " 'backlog' => true,\n" + " 'socket_buffer_size' => true,\n" " 'kernel_socket_recv_buffer_size' => true,\n" " 'kernel_socket_send_buffer_size' => true,\n" - " 'heartbeat_idle_time' => true,\n" - " 'buffer_high_watermark' => true,\n" - " 'buffer_low_watermark' => true,\n" - " 'open_tcp_nodelay' => true,\n" - " 'tcp_defer_accept' => true,\n" - " 'open_tcp_keepalive' => true,\n" - " 'open_eof_check' => true,\n" - " 'open_eof_split' => true,\n" - " 'package_eof' => true,\n" - " 'open_http_protocol' => true,\n" - " 'open_websocket_protocol' => true,\n" - " 'websocket_subprotocol' => true,\n" - " 'open_websocket_close_frame' => true,\n" - " 'open_websocket_ping_frame' => true,\n" - " 'open_websocket_pong_frame' => true,\n" - " 'open_http2_protocol' => true,\n" - " 'open_mqtt_protocol' => true,\n" - " 'open_redis_protocol' => true,\n" - " 'max_idle_time' => true,\n" - " 'tcp_keepidle' => true,\n" - " 'tcp_keepinterval' => true,\n" - " 'tcp_keepcount' => true,\n" - " 'tcp_user_timeout' => true,\n" - " 'tcp_fastopen' => true,\n" - " 'open_length_check' => true,\n" - " 'package_length_type' => true,\n" - " 'package_length_offset' => true,\n" - " 'package_body_offset' => true,\n" - " 'package_body_start' => true,\n" - " 'package_length_func' => true,\n" - " 'package_max_length' => true,\n" - " 'ssl_compress' => true,\n" - " 'ssl_protocols' => true,\n" - " 'ssl_verify_peer' => true,\n" - " 'ssl_allow_self_signed' => true,\n" - " 'ssl_client_cert_file' => true,\n" - " 'ssl_verify_depth' => true,\n" - " 'ssl_prefer_server_ciphers' => true,\n" - " 'ssl_ciphers' => true,\n" - " 'ssl_ecdh_curve' => true,\n" - " 'ssl_dhparam' => true,\n" - " 'ssl_sni_certs' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'buffer_high_watermark' => true,\n" + " 'buffer_low_watermark' => true,\n" + " 'open_tcp_nodelay' => true,\n" + " 'tcp_defer_accept' => true,\n" + " 'open_tcp_keepalive' => true,\n" + " 'open_eof_check' => true,\n" + " 'open_eof_split' => true,\n" + " 'package_eof' => true,\n" + " 'open_http_protocol' => true,\n" + " 'open_websocket_protocol' => true,\n" + " 'websocket_subprotocol' => true,\n" + " 'open_websocket_close_frame' => true,\n" + " 'open_websocket_ping_frame' => true,\n" + " 'open_websocket_pong_frame' => true,\n" + " 'open_http2_protocol' => true,\n" + " 'open_mqtt_protocol' => true,\n" + " 'open_redis_protocol' => true,\n" + " 'max_idle_time' => true,\n" + " 'tcp_keepidle' => true,\n" + " 'tcp_keepinterval' => true,\n" + " 'tcp_keepcount' => true,\n" + " 'tcp_user_timeout' => true,\n" + " 'tcp_fastopen' => true,\n" + " 'open_length_check' => true,\n" + " 'package_length_type' => true,\n" + " 'package_length_offset' => true,\n" + " 'package_body_offset' => true,\n" + " 'package_body_start' => true,\n" + " 'package_length_func' => true,\n" + " 'package_max_length' => true,\n" + " 'ssl_compress' => true,\n" + " 'ssl_protocols' => true,\n" + " 'ssl_verify_peer' => true,\n" + " 'ssl_allow_self_signed' => true,\n" + " 'ssl_client_cert_file' => true,\n" + " 'ssl_verify_depth' => true,\n" + " 'ssl_prefer_server_ciphers' => true,\n" + " 'ssl_ciphers' => true,\n" + " 'ssl_ecdh_curve' => true,\n" + " 'ssl_dhparam' => true,\n" + " 'ssl_sni_certs' => true,\n" " ];\n" "\n" " public const AIO_OPTIONS = [\n" - " 'aio_core_worker_num' => true,\n" - " 'aio_worker_num' => true,\n" - " 'aio_max_wait_time' => true,\n" - " 'aio_max_idle_time' => true,\n" - " 'enable_signalfd' => true,\n" - " 'wait_signal' => true,\n" + " 'aio_core_worker_num' => true,\n" + " 'aio_worker_num' => true,\n" + " 'aio_max_wait_time' => true,\n" + " 'aio_max_idle_time' => true,\n" + " 'iouring_entries' => true,\n" + " 'enable_signalfd' => true,\n" + " 'wait_signal' => true,\n" " 'dns_cache_refresh_time' => true,\n" - " 'thread_num' => true,\n" - " 'min_thread_num' => true,\n" - " 'max_thread_num' => true,\n" - " 'socket_dontwait' => true,\n" - " 'dns_lookup_random' => true,\n" - " 'use_async_resolver' => true,\n" - " 'enable_coroutine' => true,\n" + " 'thread_num' => true,\n" + " 'min_thread_num' => true,\n" + " 'max_thread_num' => true,\n" + " 'socket_dontwait' => true,\n" + " 'dns_lookup_random' => true,\n" + " 'use_async_resolver' => true,\n" + " 'enable_coroutine' => true,\n" " ];\n" "\n" " public const COROUTINE_OPTIONS = [\n" - " 'max_coro_num' => true,\n" - " 'max_coroutine' => true,\n" - " 'enable_deadlock_check' => true,\n" - " 'hook_flags' => true,\n" + " 'max_coro_num' => true,\n" + " 'max_coroutine' => true,\n" + " 'enable_deadlock_check' => true,\n" + " 'hook_flags' => true,\n" " 'enable_preemptive_scheduler' => true,\n" - " 'c_stack_size' => true,\n" - " 'stack_size' => true,\n" - " 'name_resolver' => true,\n" - " 'dns_cache_expire' => true,\n" - " 'dns_cache_capacity' => true,\n" - " 'max_concurrency' => true,\n" + " 'c_stack_size' => true,\n" + " 'stack_size' => true,\n" + " 'name_resolver' => true,\n" + " 'dns_cache_expire' => true,\n" + " 'dns_cache_capacity' => true,\n" " ];\n" "\n" " public const HELPER_OPTIONS = [\n" diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index a5541c06baf..4fc2429069a 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -94,6 +94,7 @@ struct ServerProperty { struct ServerObject { Server *serv; ServerProperty *property; + zval init_arguments; zend_object std; zend_class_entry *get_ce() { diff --git a/ext-src/stubs/php_swoole_server.stub.php b/ext-src/stubs/php_swoole_server.stub.php index 2e0ab454b59..e9173e9e49d 100644 --- a/ext-src/stubs/php_swoole_server.stub.php +++ b/ext-src/stubs/php_swoole_server.stub.php @@ -10,7 +10,7 @@ public function listen(string $host, int $port, int $sock_type): false|Server\Po public function sendMessage(mixed $message, int $dst_worker_id): bool {} public function addProcess(\Swoole\Process $process): int {} public function addCommand(string $name, int $accepted_process_types, callable $callback): bool {} - public function start(?array $argv): bool {} + public function start(): bool {} public function stop(int $workerId = -1, bool $waitEvent = false): bool {} public function send(int|string $fd, string $send_data, int $serverSocket = -1): bool {} public function sendfile(int $conn_fd, string $filename, int $offset = 0, int $length = 0): bool {} diff --git a/ext-src/stubs/php_swoole_server_arginfo.h b/ext-src/stubs/php_swoole_server_arginfo.h index 79cb022b16e..5b1d2e569c8 100644 --- a/ext-src/stubs/php_swoole_server_arginfo.h +++ b/ext-src/stubs/php_swoole_server_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0389b504333cec64a55f31dad33ec84b88ceab06 */ + * Stub hash: fd753870dff9cec5f5a0e5eb825f550627c93416 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Server___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "\'0.0.0.0\'") @@ -45,8 +45,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_addCommand, ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_start, 0, 1, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO(0, argv, IS_ARRAY, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_start, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_stop, 0, 0, _IS_BOOL, 0) @@ -135,8 +134,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_reload, 0, 0 ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, only_reload_taskworker, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Server_shutdown, 0, 0, _IS_BOOL, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_Swoole_Server_shutdown arginfo_class_Swoole_Server_start ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Swoole_Server_heartbeat, 0, 0, MAY_BE_FALSE|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, ifCloseConnection, _IS_BOOL, 0, "true") @@ -197,7 +195,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Connection_Iterator_key arginfo_class_Swoole_Connection_Iterator_current -#define arginfo_class_Swoole_Connection_Iterator_valid arginfo_class_Swoole_Server_shutdown +#define arginfo_class_Swoole_Connection_Iterator_valid arginfo_class_Swoole_Server_start #define arginfo_class_Swoole_Connection_Iterator_count arginfo_class_Swoole_Server_getManagerPid diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 6fc6d0f58d7..88c3994c93e 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2367,6 +2367,10 @@ static PHP_METHOD(swoole_server, set) { } else { zend::object_set(ZEND_THIS, ZEND_STRL("bootstrap"), SG(request_info).path_translated); } + // thread arguments + if (php_swoole_array_get_value(vht, "init_arguments", ztmp)) { + server_object->init_arguments = *ztmp; + } if (serv->task_enable_coroutine && (serv->task_ipc_mode == Server::TASK_IPC_MSGQUEUE || serv->task_ipc_mode == Server::TASK_IPC_PREEMPTIVE)) { @@ -2594,8 +2598,6 @@ static PHP_METHOD(swoole_server, start) { #ifdef SW_THREAD if (serv->is_worker_thread()) { - zval *thread_argv = php_swoole_thread_get_arguments(); - zend::object_set(ZEND_THIS, ZEND_STRL("objects"), thread_argv); RETURN_BOOL(worker_thread_fn()); } #endif @@ -2617,27 +2619,27 @@ static PHP_METHOD(swoole_server, start) { RETURN_FALSE; } - zval *thread_argv = nullptr; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(thread_argv) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); #ifdef SW_THREAD zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); zend_string *bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); - zend_string *argv = php_swoole_thread_serialize(thread_argv); + zend_string *argv = nullptr; + zval thread_argv; + + if (!ZVAL_IS_NULL(&server_object->init_arguments)) { + call_user_function(NULL, NULL, &server_object->init_arguments, &thread_argv, 0, NULL); + argv = php_swoole_thread_serialize(&thread_argv); + } serv->worker_thread_start = [bootstrap, argv](const WorkerFn &fn) { worker_thread_fn = fn; zend_string *bootstrap_copy = zend_string_dup(bootstrap, 1); - zend_string *argv_copy = zend_string_dup(argv, 1); + zend_string *argv_copy = argv ? zend_string_dup(argv, 1) : nullptr; php_swoole_thread_start(bootstrap_copy, argv_copy); }; #endif - ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); server_object->register_callback(); server_object->on_before_start(); @@ -2647,7 +2649,10 @@ static PHP_METHOD(swoole_server, start) { #ifdef SW_THREAD zend_string_release(bootstrap); - zend_string_release(argv); + if (argv) { + zend_string_release(argv); + } + zval_ptr_dtor(&thread_argv); #endif RETURN_TRUE; diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index d8ad240c4d6..0f12f754018 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -849,7 +849,7 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv) { file_handle.primary_script = 1; zend_first_try { - if (ZSTR_LEN(file) == 0) { + if (argv == nullptr || ZSTR_LEN(argv) == 0) { array_init(&thread_argv); } else { php_swoole_thread_unserialize(argv, &thread_argv); From 4f992f722b08eef7ca782caeeb4ec07f98507e37 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 1 Apr 2024 19:51:57 +0800 Subject: [PATCH 036/103] Optimize 8 --- examples/thread/thread_server.php | 9 +- ext-src/swoole_server.cc | 2 + include/swoole.h | 1 + include/swoole_server.h | 14 +++- src/server/master.cc | 3 + src/server/worker_threads.cc | 132 ++++++++++++++++++++++++------ 6 files changed, 130 insertions(+), 31 deletions(-) diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php index dce29dde283..24c7b9c41e6 100644 --- a/examples/thread/thread_server.php +++ b/examples/thread/thread_server.php @@ -5,17 +5,18 @@ // 'task_worker_num' => 3, 'enable_coroutine' => false, 'init_arguments' => function () use ($http) { - return [new Swoole\Thread\Map]; + $map = new Swoole\Thread\Map; + return [$map]; } ]); $http->on('Request', function ($req, $resp) use ($http) { - $resp->end("tid=" . \Swoole\Thread::getId()); + $resp->end("tid=" . \Swoole\Thread::getId() . ', fd=' . $req->fd); }); $http->addProcess(new \Swoole\Process(function () { - echo "user process"; - sleep(100); + echo "user process, id=" . \Swoole\Thread::getId(); + sleep(2); })); $http->on('Task', function () { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 88c3994c93e..3c22b2f4e48 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2370,6 +2370,8 @@ static PHP_METHOD(swoole_server, set) { // thread arguments if (php_swoole_array_get_value(vht, "init_arguments", ztmp)) { server_object->init_arguments = *ztmp; + } else { + ZVAL_NULL(&server_object->init_arguments); } if (serv->task_enable_coroutine && diff --git a/include/swoole.h b/include/swoole.h index 9e31b9e8bce..0d9d153f10c 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -543,6 +543,7 @@ enum swProcessType { SW_PROCESS_MASTER = 1, SW_PROCESS_WORKER = 2, SW_PROCESS_MANAGER = 3, + SW_PROCESS_EVENTWORKER = 2, SW_PROCESS_TASKWORKER = 4, SW_PROCESS_USERWORKER = 5, }; diff --git a/include/swoole_server.h b/include/swoole_server.h index b40b00b0284..db6687b9ec2 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1100,7 +1100,11 @@ class Server { } bool is_worker() { - return SwooleG.process_type == SW_PROCESS_WORKER; +#ifdef SW_THREAD + return SwooleWG.worker->type == SW_PROCESS_EVENTWORKER; +#else + return SwooleG.process_type == SW_PROCESS_EVENTWORKER; +#endif } bool is_worker_thread() { @@ -1109,7 +1113,11 @@ class Server { bool is_task_worker() { +#ifdef SW_THREAD + return SwooleWG.worker->type == SW_PROCESS_TASKWORKER; +#else return SwooleG.process_type == SW_PROCESS_TASKWORKER; +#endif } bool is_manager() { @@ -1117,7 +1125,11 @@ class Server { } bool is_user_worker() { +#ifdef SW_THREAD + return SwooleWG.worker->type == SW_PROCESS_USERWORKER; +#else return SwooleG.process_type == SW_PROCESS_USERWORKER; +#endif } bool is_reactor_thread() { diff --git a/src/server/master.cc b/src/server/master.cc index 51da2a5b931..4673b716caf 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -467,6 +467,9 @@ int Server::start_master_thread() { } void Server::store_listen_socket() { +#ifdef SW_THREAD + std::unique_lock _lock(lock_); +#endif for (auto ls : ports) { int sockfd = ls->socket->fd; // save server socket to connection_list diff --git a/src/server/worker_threads.cc b/src/server/worker_threads.cc index c22ac074b88..cfdc0f52091 100644 --- a/src/server/worker_threads.cc +++ b/src/server/worker_threads.cc @@ -17,9 +17,110 @@ #include "swoole_server.h" #include "swoole_memory.h" +#include + namespace swoole { using network::Socket; +struct WorkerThreads { + std::vector threads_; + std::mutex lock_; + std::condition_variable cv_; + std::queue queue_; + Server *server_; + + WorkerThreads(Server *server) { + server_ = server; + threads_.resize(server_->task_worker_num + server_->worker_num + server_->get_user_worker_num()); + } + + ~WorkerThreads() { + for (auto &thread : threads_) { + thread.join(); + } + } + + void worker_exit(Worker *worker) { + std::unique_lock _lock(lock_); + queue_.push(worker); + cv_.notify_one(); + } + + template + void create_thread(int i, _Callable fn) { + if (threads_[i].joinable()) { + threads_[i].join(); + } + threads_[i] = std::thread(fn); + } + + void spawn_event_worker(int i) { + create_thread(i, [=]() { + SwooleTG.type = Server::THREAD_WORKER; + SwooleTG.id = i; + Worker *worker = server_->get_worker(i); + worker->type = SW_PROCESS_WORKER; + server_->worker_thread_start( + [=](void) -> bool { return server_->worker_main_loop(&server_->gs->event_workers, worker) == SW_OK; }); + worker_exit(worker); + }); + } + + void spawn_task_worker(int i) { + create_thread(i, [=]() { + SwooleTG.type = Server::THREAD_WORKER; + SwooleTG.id = i; + Worker *worker = server_->get_worker(i); + worker->type = SW_PROCESS_TASKWORKER; + server_->worker_thread_start([=](void) -> bool { + return server_->gs->task_workers.main_loop(&server_->gs->task_workers, worker) == SW_OK; + }); + worker_exit(worker); + }); + } + + void spawn_user_worker(int i) { + create_thread(i, [=]() { + Worker *worker = server_->user_worker_list.at(i - server_->task_worker_num - server_->worker_num); + SwooleTG.type = Server::THREAD_WORKER; + worker->type = SW_PROCESS_USERWORKER; + SwooleTG.id = i; + server_->worker_thread_start([=](void) -> bool { + server_->onUserWorkerStart(server_, worker); + return SW_OK; + }); + worker_exit(worker); + }); + } + + void wait() { + while (server_->running) { + std::unique_lock _lock(lock_); + if (!queue_.empty()) { + Worker *exited_worker = queue_.front(); + queue_.pop(); + switch (exited_worker->type) { + case SW_PROCESS_EVENTWORKER: + spawn_event_worker(exited_worker->id); + break; + case SW_PROCESS_TASKWORKER: + spawn_task_worker(exited_worker->id); + break; + case SW_PROCESS_USERWORKER: + spawn_user_worker(exited_worker->id); + break; + default: + abort(); + break; + } + _lock.unlock(); + } else { + cv_.wait(_lock); + } + } + } +}; + int Server::start_worker_threads() { single_thread = 1; @@ -92,48 +193,27 @@ int Server::start_worker_threads() { return SW_ERR; } - std::vector threads; - threads.resize(task_worker_num + worker_num + get_user_worker_num()); + WorkerThreads worker_threads(this); if (task_worker_num > 0) { SW_LOOP_N(task_worker_num) { - threads[worker_num + i] = std::thread([=]() { - SwooleTG.type = Server::THREAD_WORKER; - SwooleTG.id = worker_num + i; - Worker *worker = gs->task_workers.get_worker(i); - worker_thread_start( - [=](void) -> bool { return gs->task_workers.main_loop(&gs->task_workers, worker) == SW_OK; }); - }); + worker_threads.spawn_task_worker(worker_num + i); } } SW_LOOP_N(worker_num) { - threads[i] = std::thread([=]() { - SwooleTG.type = Server::THREAD_WORKER; - SwooleTG.id = i; - Worker *worker = get_worker(i); - worker_thread_start([=](void) -> bool { return worker_main_loop(&gs->event_workers, worker) == SW_OK; }); - }); + worker_threads.spawn_event_worker(i); } if (!user_worker_list.empty()) { int i = 0; for (auto worker : user_worker_list) { - threads[task_worker_num + worker_num + i] = std::thread([=]() { - SwooleTG.type = Server::THREAD_WORKER; - SwooleTG.id = task_worker_num + worker_num + i; - worker_thread_start([=](void) -> bool { - onUserWorkerStart(this, worker); - return SW_OK; - }); - }); + worker_threads.spawn_user_worker(task_worker_num + worker_num + i); i++; } } - for (auto &thread : threads) { - thread.join(); - } + worker_threads.wait(); return SW_OK; } From 0631581d25442f257839b732f219e192fe166c92 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 2 Apr 2024 17:23:46 +0800 Subject: [PATCH 037/103] Optimize 10 --- src/server/master.cc | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/server/master.cc b/src/server/master.cc index 4673b716caf..62fddbd412c 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -27,10 +27,6 @@ using swoole::network::Socket; swoole::Server *g_server_instance = nullptr; -#ifdef SW_THREAD -static std::vector listen_ports; -#endif - namespace swoole { static void Server_signal_handler(int sig); @@ -151,7 +147,7 @@ int Server::accept_command_result(Reactor *reactor, Event *event) { int Server::accept_connection(Reactor *reactor, Event *event) { Server *serv = (Server *) reactor->ptr; - ListenPort *listen_host = serv->get_port_by_server_fd(event->fd); + ListenPort *listen_host = (ListenPort *) event->socket->object; for (int i = 0; i < SW_ACCEPT_MAX_COUNT; i++) { Socket *sock = event->socket->accept(); @@ -478,6 +474,7 @@ void Server::store_listen_socket() { connection_list[sockfd].socket_type = ls->type; connection_list[sockfd].object = ls; connection_list[sockfd].info.assign(ls->type, ls->host, ls->port); + ls->socket->object = ls; if (sockfd >= 0) { set_minfd(sockfd); set_maxfd(sockfd); @@ -1745,17 +1742,6 @@ int Server::add_systemd_socket() { } static bool Server_create_socket(ListenPort *ls) { -#ifdef SW_THREAD - std::unique_lock _lock(sw_thread_lock); - if (listen_ports.size() > 0) { - for (auto _lp : listen_ports) { - if (_lp->type == ls->type && _lp->port == ls->port && _lp->host == ls->host) { - ls->socket = _lp->socket->dup(); - return true; - } - } - } -#endif ls->socket = make_socket( ls->type, ls->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); if (!ls->socket) { @@ -1775,9 +1761,6 @@ static bool Server_create_socket(ListenPort *ls) { ls->socket->info.assign(ls->type, ls->host, ls->port); -#ifdef SW_THREAD - listen_ports.push_back(ls); -#endif return true; } From 8378690a61a360c85965c1ee1c1753f57d75fa56 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 2 Apr 2024 19:04:37 +0800 Subject: [PATCH 038/103] Optimize 11, add Server::get_worker_id() --- include/swoole_server.h | 8 ++++++++ src/server/base.cc | 8 ++++---- src/server/process.cc | 6 +++--- src/server/reactor_process.cc | 14 ++++++-------- src/server/task_worker.cc | 4 ++-- src/server/worker.cc | 4 +++- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/include/swoole_server.h b/include/swoole_server.h index db6687b9ec2..c7c3e59e315 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -973,6 +973,14 @@ class Server { return gs->manager_pid; } + WorkerId get_worker_id() { +#ifdef SW_THREAD + return SwooleTG.id; +#else + return SwooleG.process_id; +#endif + } + void store_listen_socket(); void store_pipe_fd(UnixSocket *p); diff --git a/src/server/base.cc b/src/server/base.cc index bdb5ef37e47..e9537d7497c 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -89,7 +89,7 @@ bool BaseFactory::end(SessionId session_id, int flags) { _send.info.fd = session_id; _send.info.len = 0; _send.info.type = SW_SERVER_EVENT_CLOSE; - _send.info.reactor_id = SwooleG.process_id; + _send.info.reactor_id = server_->get_worker_id(); Session *session = server_->get_session(session_id); if (!session->fd) { @@ -100,7 +100,7 @@ bool BaseFactory::end(SessionId session_id, int flags) { return false; } - if (session->reactor_id != SwooleG.process_id) { + if (session->reactor_id != server_->get_worker_id()) { Worker *worker = server_->get_worker(session->reactor_id); if (worker->pipe_master->send_async((const char *) &_send.info, sizeof(_send.info)) < 0) { swoole_sys_warning("failed to send %lu bytes to pipe_master", sizeof(_send.info)); @@ -167,8 +167,8 @@ bool BaseFactory::finish(SendData *data) { SessionId session_id = data->info.fd; Session *session = server_->get_session(session_id); - if (session->reactor_id != SwooleG.process_id) { - swoole_trace("session->reactor_id=%d, SwooleG.process_id=%d", session->reactor_id, SwooleG.process_id); + if (session->reactor_id != server_->get_worker_id()) { + swoole_trace("session->reactor_id=%d, SwooleG.process_id=%d", session->reactor_id, server_->get_worker_id()); Worker *worker = server_->gs->event_workers.get_worker(session->reactor_id); EventData proxy_msg{}; diff --git a/src/server/process.cc b/src/server/process.cc index e95c35e7d71..7a7983dee99 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -128,7 +128,7 @@ static bool inline process_is_supported_send_yield(Server *serv, Connection *con if (!serv->is_hash_dispatch_mode()) { return false; } else { - return serv->schedule_worker(conn->fd, nullptr) == (int) SwooleG.process_id; + return serv->schedule_worker(conn->fd, nullptr) == (int) serv->get_worker_id(); } } @@ -185,7 +185,7 @@ bool ProcessFactory::finish(SendData *resp) { memcpy(&task, resp, sizeof(SendData)); task.info.fd = session_id; task.info.reactor_id = conn->reactor_id; - task.info.server_fd = SwooleG.process_id; + task.info.server_fd = server_->get_worker_id(); swoole_trace("worker_id=%d, type=%d", SwooleG.process_id, task.info.type); @@ -227,7 +227,7 @@ bool ProcessFactory::end(SessionId session_id, int flags) { if (conn->close_actively) { bool hash = server_->is_hash_dispatch_mode(); int worker_id = hash ? server_->schedule_worker(conn->fd, nullptr) : conn->fd % server_->worker_num; - if (server_->is_worker() && (!hash || worker_id == (int) SwooleG.process_id)) { + if (server_->is_worker() && (!hash || worker_id == (int) server_->get_worker_id())) { goto _close; } worker = server_->get_worker(worker_id); diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index c74f9ddd3c8..c8f3855633e 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -205,9 +205,7 @@ int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { SwooleTG.timer->reinit(reactor); } -#ifndef SW_THREAD - Server::worker_signal_init(); -#endif + worker_signal_init(); for (auto ls : serv->ports) { #ifdef HAVE_REUSEPORT @@ -227,11 +225,11 @@ int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { reactor->ptr = serv; reactor->max_socket = serv->get_max_connection(); - reactor->close = Server::close_connection; + reactor->close = close_connection; // set event handler // connect - reactor->set_handler(SW_FD_STREAM_SERVER, Server::accept_connection); + reactor->set_handler(SW_FD_STREAM_SERVER, accept_connection); // close reactor->default_error_handler = ReactorProcess_onClose; // pipe @@ -269,7 +267,7 @@ int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { } } - if ((serv->master_timer = swoole_timer_add(1000L, true, Server::timer_callback, serv)) == nullptr) { + if ((serv->master_timer = swoole_timer_add(1000L, true, timer_callback, serv)) == nullptr) { _fail: swoole_event_free(); return SW_ERR; @@ -298,11 +296,11 @@ int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { /** * call internal serv hooks */ - if (serv->isset_hook(Server::HOOK_WORKER_CLOSE)) { + if (serv->isset_hook(HOOK_WORKER_CLOSE)) { void *hook_args[2]; hook_args[0] = serv; hook_args[1] = (void *) (uintptr_t) worker->id; - serv->call_hook(Server::HOOK_WORKER_CLOSE, hook_args); + serv->call_hook(HOOK_WORKER_CLOSE, hook_args); } swoole_event_free(); diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 6923e3a832f..eaa0b5e8ed6 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -100,8 +100,8 @@ static int TaskWorker_onTask(ProcessPool *pool, EventData *task) { bool Server::task_pack(EventData *task, const void *_data, size_t _length) { task->info.type = SW_SERVER_EVENT_TASK; task->info.fd = SwooleG.current_task_id++; - task->info.reactor_id = SwooleG.process_id; - task->info.time = swoole::microtime(); + task->info.reactor_id = get_worker_id(); + task->info.time = microtime(); if (_length < SW_IPC_MAX_SIZE - sizeof(task->info)) { memcpy(task->data, _data, _length); diff --git a/src/server/worker.cc b/src/server/worker.cc index 5875af005c2..f4335df4ef2 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -33,6 +33,7 @@ static int Worker_onPipeReceive(Reactor *reactor, Event *event); static void Worker_reactor_try_to_exit(Reactor *reactor); void Server::worker_signal_init(void) { +#ifndef SW_THREAD swoole_signal_set(SIGHUP, nullptr); swoole_signal_set(SIGPIPE, SIG_IGN); swoole_signal_set(SIGUSR1, nullptr); @@ -44,6 +45,7 @@ void Server::worker_signal_init(void) { #ifdef SIGRTMIN swoole_signal_set(SIGRTMIN, Server::worker_signal_handler); #endif +#endif } void Server::worker_signal_handler(int signo) { @@ -215,7 +217,7 @@ void Server::worker_accept_event(DataHead *info) { } void Server::worker_start_callback(Worker *worker) { - if (SwooleG.process_id >= worker_num) { + if (get_worker_id() >= worker_num) { SwooleG.process_type = SW_PROCESS_TASKWORKER; } else { SwooleG.process_type = SW_PROCESS_WORKER; From 01275cf1cce3762ab68f1ceb8ced8ecca4f8f4be Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 3 Apr 2024 11:37:07 +0800 Subject: [PATCH 039/103] Optimize 12 --- core-tests/src/server/server.cpp | 2 +- ext-src/swoole_process.cc | 6 ++-- ext-src/swoole_process_pool.cc | 6 ++-- ext-src/swoole_server.cc | 12 ++++---- include/swoole.h | 47 +++++++++++++++++++++++++++++--- include/swoole_server.h | 8 ------ src/os/process_pool.cc | 8 +++--- src/server/base.cc | 6 ++-- src/server/manager.cc | 6 ++-- src/server/master.cc | 10 +++---- src/server/process.cc | 6 ++-- src/server/reactor_process.cc | 5 ++-- src/server/task_worker.cc | 4 +-- src/server/worker.cc | 12 ++++---- src/server/worker_threads.cc | 6 ---- 15 files changed, 84 insertions(+), 60 deletions(-) diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index c66e88a64aa..a8de5731ed6 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -678,7 +678,7 @@ TEST(server, task_worker4) { serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); sleep(1); - EventData *task_result = &(serv->task_result[SwooleG.process_id]); + EventData *task_result = &(serv->task_result[sw_get_process_id()]); sw_memset_zero(task_result, sizeof(*task_result)); memset(&buf.info, 0, sizeof(buf.info)); buf.info.len = strlen(packet); diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 830ad8f9ab5..a3bddb31f5d 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -602,8 +602,8 @@ void php_swoole_process_clean() { } } - if (SwooleG.process_type != SW_PROCESS_USERWORKER) { - SwooleG.process_type = 0; + if (sw_get_process_type() != SW_PROCESS_USERWORKER) { + sw_set_process_type(0); } } @@ -644,7 +644,7 @@ int php_swoole_process_start(Worker *process, zval *zobject) { } php_swoole_process_clean(); - SwooleG.process_id = process->id; + sw_set_process_id(process->id); SwooleWG.worker = process; zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("pid"), process->pid); diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index fd7db5e7cfb..e660ad3dab0 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -567,7 +567,7 @@ static PHP_METHOD(swoole_process_pool, getProcess) { php_swoole_error(E_WARNING, "invalid worker_id[%ld]", worker_id); RETURN_FALSE; } else if (worker_id < 0) { - worker_id = SwooleG.process_id; + worker_id = sw_get_process_id(); } zval *zworkers = @@ -584,11 +584,11 @@ static PHP_METHOD(swoole_process_pool, getProcess) { *worker = current_pool->workers[worker_id]; object_init_ex(zprocess, swoole_process_ce); - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("id"), SwooleG.process_id); + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("id"), sw_get_process_id()); zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("pid"), worker->pid); if (current_pool->ipc_mode == SW_IPC_UNIXSOCK) { // current process - if (worker->id == SwooleG.process_id) { + if (worker->id == sw_get_process_id()) { worker->pipe_current = worker->pipe_worker; } else { worker->pipe_current = worker->pipe_master; diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 3c22b2f4e48..937492c3f32 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -3039,9 +3039,9 @@ static PHP_METHOD(swoole_server, taskwait) { } uint64_t notify; - EventData *task_result = &(serv->task_result[SwooleG.process_id]); + EventData *task_result = &(serv->task_result[sw_get_process_id()]); sw_memset_zero(task_result, sizeof(*task_result)); - Pipe *pipe = serv->task_notify_pipes.at(SwooleG.process_id).get(); + Pipe *pipe = serv->task_notify_pipes.at(sw_get_process_id()).get(); network::Socket *task_notify_socket = pipe->get_socket(false); // clear history task @@ -3121,10 +3121,10 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { int list_of_id[SW_MAX_CONCURRENT_TASK] = {}; uint64_t notify; - EventData *task_result = &(serv->task_result[SwooleG.process_id]); + EventData *task_result = &(serv->task_result[sw_get_process_id()]); sw_memset_zero(task_result, sizeof(*task_result)); - Pipe *pipe = serv->task_notify_pipes.at(SwooleG.process_id).get(); - Worker *worker = serv->get_worker(SwooleG.process_id); + Pipe *pipe = serv->task_notify_pipes.at(sw_get_process_id()).get(); + Worker *worker = serv->get_worker(sw_get_process_id()); File fp = swoole::make_tmpfile(); if (!fp.ready()) { @@ -3442,7 +3442,7 @@ static PHP_METHOD(swoole_server, sendMessage) { Z_PARAM_LONG(worker_id) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if ((serv->is_worker() || serv->is_task_worker()) && worker_id == SwooleG.process_id) { + if ((serv->is_worker() || serv->is_task_worker()) && worker_id == sw_get_process_id()) { php_swoole_fatal_error(E_WARNING, "can't send messages to self"); RETURN_FALSE; } diff --git a/include/swoole.h b/include/swoole.h index 0d9d153f10c..c9fe58d4cf1 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -185,6 +185,13 @@ extern std::mutex sw_thread_lock; #define SW_THREAD_LOCAL #endif +/** + * API naming rules + * ----------------------------------- + * - starts with swoole_, means it is ready or has been used as an external API + * - starts with sw_, internal use only + */ + /*-----------------------------------Memory------------------------------------*/ void *sw_malloc(size_t size); void sw_free(void *ptr); @@ -328,9 +335,9 @@ static inline const char *swoole_strnstr(const char *haystack, } static inline const char *swoole_strncasestr(const char *haystack, - uint32_t haystack_length, - const char *needle, - uint32_t needle_length) { + uint32_t haystack_length, + const char *needle, + uint32_t needle_length) { assert(needle_length > 0); uint32_t i; @@ -728,7 +735,7 @@ struct Global { uchar use_async_resolver : 1; uchar use_name_resolver : 1; - int process_type; + uint8_t process_type; uint32_t process_id; TaskId current_task_id; pid_t pid; @@ -858,3 +865,35 @@ static sw_inline swoole::MemoryPool *sw_mem_pool() { static sw_inline const swoole::Allocator *sw_std_allocator() { return &SwooleG.std_allocator; } + +static sw_inline swoole::WorkerId sw_get_process_id() { +#ifdef SW_THREAD + return SwooleTG.id; +#else + return SwooleG.process_id; +#endif +} + +static sw_inline void sw_set_process_id(swoole::WorkerId id) { +#ifdef SW_THREAD + SwooleTG.id = id; +#else + SwooleG.process_id = id; +#endif +} + +static sw_inline void sw_set_process_type(int type) { +#ifdef SW_THREAD + SwooleTG.type = type; +#else + SwooleG.process_type = type; +#endif +} + +static sw_inline int sw_get_process_type() { +#ifdef SW_THREAD + return SwooleTG.type; +#else + return SwooleG.process_type; +#endif +} diff --git a/include/swoole_server.h b/include/swoole_server.h index c7c3e59e315..db6687b9ec2 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -973,14 +973,6 @@ class Server { return gs->manager_pid; } - WorkerId get_worker_id() { -#ifdef SW_THREAD - return SwooleTG.id; -#else - return SwooleG.process_id; -#endif - } - void store_listen_socket(); void store_pipe_fd(UnixSocket *p); diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 8c6212ea0ae..8ef0f2324c5 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -233,7 +233,7 @@ int ProcessPool::start() { running = started = true; master_pid = getpid(); reload_workers = new Worker[worker_num](); - SwooleG.process_type = SW_PROCESS_MASTER; + sw_set_process_type(SW_PROCESS_MASTER); if (async) { main_loop = ProcessPool_worker_loop_async; @@ -458,8 +458,8 @@ pid_t ProcessPool::spawn(Worker *worker) { // child case 0: worker->pid = SwooleG.pid; - SwooleG.process_id = worker->id; - SwooleG.process_type = SW_PROCESS_WORKER; + sw_set_process_type(SW_PROCESS_WORKER); + sw_set_process_id(worker->id); if (async) { if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) { exit(254); @@ -811,7 +811,7 @@ bool ProcessPool::detach() { WorkerStopMessage msg; msg.pid = getpid(); - msg.worker_id = SwooleG.process_id; + msg.worker_id = sw_get_process_id(); if (push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) { return false; diff --git a/src/server/base.cc b/src/server/base.cc index e9537d7497c..39a66833fe3 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -89,7 +89,7 @@ bool BaseFactory::end(SessionId session_id, int flags) { _send.info.fd = session_id; _send.info.len = 0; _send.info.type = SW_SERVER_EVENT_CLOSE; - _send.info.reactor_id = server_->get_worker_id(); + _send.info.reactor_id = sw_get_process_id(); Session *session = server_->get_session(session_id); if (!session->fd) { @@ -100,7 +100,7 @@ bool BaseFactory::end(SessionId session_id, int flags) { return false; } - if (session->reactor_id != server_->get_worker_id()) { + if (session->reactor_id != sw_get_process_id()) { Worker *worker = server_->get_worker(session->reactor_id); if (worker->pipe_master->send_async((const char *) &_send.info, sizeof(_send.info)) < 0) { swoole_sys_warning("failed to send %lu bytes to pipe_master", sizeof(_send.info)); @@ -167,7 +167,7 @@ bool BaseFactory::finish(SendData *data) { SessionId session_id = data->info.fd; Session *session = server_->get_session(session_id); - if (session->reactor_id != server_->get_worker_id()) { + if (session->reactor_id != sw_get_process_id()) { swoole_trace("session->reactor_id=%d, SwooleG.process_id=%d", session->reactor_id, server_->get_worker_id()); Worker *worker = server_->gs->event_workers.get_worker(session->reactor_id); EventData proxy_msg{}; diff --git a/src/server/manager.cc b/src/server/manager.cc index 5c0bc36d09f..c7d76871543 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -114,7 +114,7 @@ int Server::start_manager_process() { } auto fn = [this](void) { - SwooleG.process_type = SW_PROCESS_MANAGER; + sw_set_process_type(SW_PROCESS_MANAGER); gs->manager_pid = SwooleG.pid = getpid(); if (task_worker_num > 0) { @@ -630,8 +630,8 @@ pid_t Server::spawn_user_worker(Worker *worker) { } // child else if (pid == 0) { - SwooleG.process_type = SW_PROCESS_USERWORKER; - SwooleG.process_id = worker->id; + sw_set_process_type(SW_PROCESS_USERWORKER); + sw_set_process_id(worker->id); SwooleWG.worker = worker; worker->pid = SwooleG.pid; onUserWorkerStart(this, worker); diff --git a/src/server/master.cc b/src/server/master.cc index 62fddbd412c..d150de7f067 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -419,7 +419,7 @@ int Server::start_master_thread() { init_signal_handler(); SwooleG.pid = getpid(); - SwooleG.process_type = SW_PROCESS_MASTER; + sw_set_process_type(SW_PROCESS_MASTER); reactor->ptr = this; reactor->set_handler(SW_FD_STREAM_SERVER, Server::accept_connection); @@ -564,14 +564,14 @@ void Server::destroy_worker(Worker *worker) { * [Worker] */ void Server::init_worker(Worker *worker) { -#ifdef HAVE_CPU_AFFINITY +#if defined(HAVE_CPU_AFFINITY) && !defined(SW_THREAD) if (open_cpu_affinity) { cpu_set_t cpu_set; CPU_ZERO(&cpu_set); if (cpu_affinity_available_num) { - CPU_SET(cpu_affinity_available[SwooleG.process_id % cpu_affinity_available_num], &cpu_set); + CPU_SET(cpu_affinity_available[sw_get_process_id() % cpu_affinity_available_num], &cpu_set); } else { - CPU_SET(SwooleG.process_id % SW_CPU_NUM, &cpu_set); + CPU_SET(sw_get_process_id() % SW_CPU_NUM, &cpu_set); } if (swoole_set_cpu_affinity(&cpu_set) < 0) { swoole_sys_warning("swoole_set_cpu_affinity() failed"); @@ -1917,7 +1917,7 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f int fd = _socket->fd; Connection *connection = &(connection_list[fd]); - ReactorId reactor_id = is_base_mode() ? SwooleG.process_id : fd % reactor_num; + ReactorId reactor_id = is_base_mode() ? sw_get_process_id() : fd % reactor_num; *connection = {}; sw_spinlock(&gs->spinlock); diff --git a/src/server/process.cc b/src/server/process.cc index 7a7983dee99..a1ffcd34261 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -128,7 +128,7 @@ static bool inline process_is_supported_send_yield(Server *serv, Connection *con if (!serv->is_hash_dispatch_mode()) { return false; } else { - return serv->schedule_worker(conn->fd, nullptr) == (int) serv->get_worker_id(); + return serv->schedule_worker(conn->fd, nullptr) == (int) sw_get_process_id(); } } @@ -185,7 +185,7 @@ bool ProcessFactory::finish(SendData *resp) { memcpy(&task, resp, sizeof(SendData)); task.info.fd = session_id; task.info.reactor_id = conn->reactor_id; - task.info.server_fd = server_->get_worker_id(); + task.info.server_fd = sw_get_process_id(); swoole_trace("worker_id=%d, type=%d", SwooleG.process_id, task.info.type); @@ -227,7 +227,7 @@ bool ProcessFactory::end(SessionId session_id, int flags) { if (conn->close_actively) { bool hash = server_->is_hash_dispatch_mode(); int worker_id = hash ? server_->schedule_worker(conn->fd, nullptr) : conn->fd % server_->worker_num; - if (server_->is_worker() && (!hash || worker_id == (int) server_->get_worker_id())) { + if (server_->is_worker() && (!hash || worker_id == (int) sw_get_process_id())) { goto _close; } worker = server_->get_worker(worker_id); diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index c8f3855633e..5257135ae60 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -179,11 +179,10 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; - - SwooleG.process_type = SW_PROCESS_WORKER; SwooleG.pid = getpid(); + sw_set_process_type(SW_PROCESS_WORKER); + sw_set_process_id(worker->id); - SwooleG.process_id = worker->id; if (serv->max_request > 0) { SwooleWG.run_always = false; } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index eaa0b5e8ed6..4116e0e6549 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -100,7 +100,7 @@ static int TaskWorker_onTask(ProcessPool *pool, EventData *task) { bool Server::task_pack(EventData *task, const void *_data, size_t _length) { task->info.type = SW_SERVER_EVENT_TASK; task->info.fd = SwooleG.current_task_id++; - task->info.reactor_id = get_worker_id(); + task->info.reactor_id = sw_get_process_id(); task->info.time = microtime(); if (_length < SW_IPC_MAX_SIZE - sizeof(task->info)) { @@ -172,7 +172,7 @@ static void TaskWorker_signal_init(ProcessPool *pool) { static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; - SwooleG.process_id = worker->id; + sw_set_process_id(worker->id); /** * Make the task worker support asynchronous diff --git a/src/server/worker.cc b/src/server/worker.cc index f4335df4ef2..aaf39a70bda 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -217,10 +217,10 @@ void Server::worker_accept_event(DataHead *info) { } void Server::worker_start_callback(Worker *worker) { - if (get_worker_id() >= worker_num) { - SwooleG.process_type = SW_PROCESS_TASKWORKER; + if (sw_get_process_id() >= worker_num) { + sw_set_process_type(SW_PROCESS_TASKWORKER); } else { - SwooleG.process_type = SW_PROCESS_WORKER; + sw_set_process_type(SW_PROCESS_WORKER); } int is_root = !geteuid(); @@ -375,7 +375,7 @@ void Server::stop_async_worker(Worker *worker) { static void Worker_reactor_try_to_exit(Reactor *reactor) { Server *serv; - if (SwooleG.process_type == SW_PROCESS_TASKWORKER) { + if (sw_get_process_type() == SW_PROCESS_TASKWORKER) { ProcessPool *pool = (ProcessPool *) reactor->ptr; serv = (Server *) pool->ptr; } else { @@ -428,8 +428,8 @@ void Server::drain_worker_pipe() { * main loop [Worker] */ int Server::start_event_worker(Worker *worker) { - // worker_id - SwooleG.process_id = worker->id; + sw_set_process_id(worker->id); + sw_set_process_type(SW_PROCESS_EVENTWORKER); init_worker(worker); diff --git a/src/server/worker_threads.cc b/src/server/worker_threads.cc index cfdc0f52091..b38e11ddf5f 100644 --- a/src/server/worker_threads.cc +++ b/src/server/worker_threads.cc @@ -56,8 +56,6 @@ struct WorkerThreads { void spawn_event_worker(int i) { create_thread(i, [=]() { - SwooleTG.type = Server::THREAD_WORKER; - SwooleTG.id = i; Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_WORKER; server_->worker_thread_start( @@ -68,8 +66,6 @@ struct WorkerThreads { void spawn_task_worker(int i) { create_thread(i, [=]() { - SwooleTG.type = Server::THREAD_WORKER; - SwooleTG.id = i; Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_TASKWORKER; server_->worker_thread_start([=](void) -> bool { @@ -82,8 +78,6 @@ struct WorkerThreads { void spawn_user_worker(int i) { create_thread(i, [=]() { Worker *worker = server_->user_worker_list.at(i - server_->task_worker_num - server_->worker_num); - SwooleTG.type = Server::THREAD_WORKER; - worker->type = SW_PROCESS_USERWORKER; SwooleTG.id = i; server_->worker_thread_start([=](void) -> bool { server_->onUserWorkerStart(server_, worker); From df62cc31b0a571e3f6f7dc26445a3bdeed00944d Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 3 Apr 2024 12:03:47 +0800 Subject: [PATCH 040/103] Optimize 13 --- examples/thread/thread_server.php | 2 +- ext-src/swoole_process.cc | 3 ++- include/swoole_server.h | 35 +++++++++++++------------------ src/server/worker_threads.cc | 11 ++++++++-- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php index 24c7b9c41e6..e16ef182101 100644 --- a/examples/thread/thread_server.php +++ b/examples/thread/thread_server.php @@ -16,7 +16,7 @@ $http->addProcess(new \Swoole\Process(function () { echo "user process, id=" . \Swoole\Thread::getId(); - sleep(2); + sleep(2000); })); $http->on('Task', function () { diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index a3bddb31f5d..3c0323f7333 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -601,10 +601,11 @@ void php_swoole_process_clean() { signal_fci_caches[i] = nullptr; } } - +#ifndef SW_THREAD if (sw_get_process_type() != SW_PROCESS_USERWORKER) { sw_set_process_type(0); } +#endif } void php_swoole_process_rshutdown() { diff --git a/include/swoole_server.h b/include/swoole_server.h index db6687b9ec2..97756282da1 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -506,7 +506,6 @@ class Server { THREAD_MASTER = 1, THREAD_REACTOR = 2, THREAD_HEARTBEAT = 3, - THREAD_WORKER = 5, }; enum DispatchMode { @@ -1100,40 +1099,36 @@ class Server { } bool is_worker() { -#ifdef SW_THREAD - return SwooleWG.worker->type == SW_PROCESS_EVENTWORKER; -#else - return SwooleG.process_type == SW_PROCESS_EVENTWORKER; -#endif + return sw_get_process_type() == SW_PROCESS_EVENTWORKER; } - bool is_worker_thread() { - return SwooleTG.type == THREAD_WORKER; - } - - bool is_task_worker() { -#ifdef SW_THREAD - return SwooleWG.worker->type == SW_PROCESS_TASKWORKER; -#else - return SwooleG.process_type == SW_PROCESS_TASKWORKER; -#endif + return sw_get_process_type() == SW_PROCESS_TASKWORKER; } bool is_manager() { - return SwooleG.process_type == SW_PROCESS_MANAGER; + return sw_get_process_type() == SW_PROCESS_MANAGER; } bool is_user_worker() { + return sw_get_process_type() == SW_PROCESS_USERWORKER; + } + + bool is_worker_thread() { #ifdef SW_THREAD - return SwooleWG.worker->type == SW_PROCESS_USERWORKER; + return sw_get_process_type() == SW_PROCESS_EVENTWORKER || sw_get_process_type() == SW_PROCESS_TASKWORKER || + sw_get_process_type() == SW_PROCESS_USERWORKER; #else - return SwooleG.process_type == SW_PROCESS_USERWORKER; + return false; #endif } bool is_reactor_thread() { - return SwooleG.process_type == SW_PROCESS_MASTER && SwooleTG.type == Server::THREAD_REACTOR; +#ifdef SW_THREAD + return false; +#else + return sw_get_process_type() == SW_PROCESS_MASTER && SwooleTG.type == Server::THREAD_REACTOR; +#endif } bool isset_hook(enum HookType type) { diff --git a/src/server/worker_threads.cc b/src/server/worker_threads.cc index b38e11ddf5f..799bc9d8438 100644 --- a/src/server/worker_threads.cc +++ b/src/server/worker_threads.cc @@ -56,8 +56,10 @@ struct WorkerThreads { void spawn_event_worker(int i) { create_thread(i, [=]() { + sw_set_process_type(SW_PROCESS_EVENTWORKER); + sw_set_process_id(i); Worker *worker = server_->get_worker(i); - worker->type = SW_PROCESS_WORKER; + worker->type = SW_PROCESS_EVENTWORKER; server_->worker_thread_start( [=](void) -> bool { return server_->worker_main_loop(&server_->gs->event_workers, worker) == SW_OK; }); worker_exit(worker); @@ -66,6 +68,8 @@ struct WorkerThreads { void spawn_task_worker(int i) { create_thread(i, [=]() { + sw_set_process_type(SW_PROCESS_TASKWORKER); + sw_set_process_id(i); Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_TASKWORKER; server_->worker_thread_start([=](void) -> bool { @@ -78,7 +82,9 @@ struct WorkerThreads { void spawn_user_worker(int i) { create_thread(i, [=]() { Worker *worker = server_->user_worker_list.at(i - server_->task_worker_num - server_->worker_num); - SwooleTG.id = i; + sw_set_process_type(SW_PROCESS_USERWORKER); + sw_set_process_id(i); + worker->type = SW_PROCESS_USERWORKER; server_->worker_thread_start([=](void) -> bool { server_->onUserWorkerStart(server_, worker); return SW_OK; @@ -117,6 +123,7 @@ struct WorkerThreads { int Server::start_worker_threads() { single_thread = 1; + sw_set_process_type(SW_PROCESS_MANAGER); // listen TCP if (have_stream_sock == 1) { From 053a7d492016e276fd5fbad55b611f328ae9cb8f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sun, 7 Apr 2024 11:33:21 +0800 Subject: [PATCH 041/103] Optimize 14 --- core-tests/src/server/server.cpp | 2 +- examples/thread/thread_server.php | 7 ++- ext-src/swoole_process.cc | 2 +- ext-src/swoole_process_pool.cc | 6 +- ext-src/swoole_server.cc | 22 ++++--- include/swoole.h | 81 ++++++++++++++------------ include/swoole_server.h | 56 ++++++++++-------- src/os/process_pool.cc | 8 +-- src/server/base.cc | 45 ++++++++++++-- src/server/manager.cc | 6 +- src/server/master.cc | 97 +++++++++---------------------- src/server/process.cc | 60 ++++++++++++++----- src/server/reactor_process.cc | 18 +----- src/server/reactor_thread.cc | 72 +++++++++-------------- src/server/static_handler.cc | 4 +- src/server/task_worker.cc | 4 +- src/server/worker.cc | 12 ++-- src/server/worker_threads.cc | 48 ++++++++++++--- 18 files changed, 298 insertions(+), 252 deletions(-) diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index a8de5731ed6..0b2f5574787 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -678,7 +678,7 @@ TEST(server, task_worker4) { serv->gs->task_workers.dispatch(&buf, &_dst_worker_id); sleep(1); - EventData *task_result = &(serv->task_result[sw_get_process_id()]); + EventData *task_result = &(serv->task_result[swoole_get_process_id()]); sw_memset_zero(task_result, sizeof(*task_result)); memset(&buf.info, 0, sizeof(buf.info)); buf.info.len = strlen(packet); diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php index e16ef182101..bafd0fce162 100644 --- a/examples/thread/thread_server.php +++ b/examples/thread/thread_server.php @@ -11,7 +11,8 @@ ]); $http->on('Request', function ($req, $resp) use ($http) { - $resp->end("tid=" . \Swoole\Thread::getId() . ', fd=' . $req->fd); +// $resp->end("tid=" . \Swoole\Thread::getId() . ', fd=' . $req->fd); + $resp->end('hello world'); }); $http->addProcess(new \Swoole\Process(function () { @@ -27,4 +28,8 @@ var_dump(\Swoole\Thread::getArguments()); }); +$http->on('WorkerStop', function ($serv, $wid) { + var_dump('stop: T' . \Swoole\Thread::getId()); +}); + $http->start(); diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 3c0323f7333..6d76b46ae21 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -645,7 +645,7 @@ int php_swoole_process_start(Worker *process, zval *zobject) { } php_swoole_process_clean(); - sw_set_process_id(process->id); + swoole_set_process_id(process->id); SwooleWG.worker = process; zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("pid"), process->pid); diff --git a/ext-src/swoole_process_pool.cc b/ext-src/swoole_process_pool.cc index e660ad3dab0..f14004f8ff1 100644 --- a/ext-src/swoole_process_pool.cc +++ b/ext-src/swoole_process_pool.cc @@ -567,7 +567,7 @@ static PHP_METHOD(swoole_process_pool, getProcess) { php_swoole_error(E_WARNING, "invalid worker_id[%ld]", worker_id); RETURN_FALSE; } else if (worker_id < 0) { - worker_id = sw_get_process_id(); + worker_id = swoole_get_process_id(); } zval *zworkers = @@ -584,11 +584,11 @@ static PHP_METHOD(swoole_process_pool, getProcess) { *worker = current_pool->workers[worker_id]; object_init_ex(zprocess, swoole_process_ce); - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("id"), sw_get_process_id()); + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("id"), swoole_get_process_id()); zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("pid"), worker->pid); if (current_pool->ipc_mode == SW_IPC_UNIXSOCK) { // current process - if (worker->id == sw_get_process_id()) { + if (worker->id == swoole_get_process_id()) { worker->pipe_current = worker->pipe_worker; } else { worker->pipe_current = worker->pipe_master; diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 937492c3f32..5be3f6a719b 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -99,7 +99,7 @@ void php_swoole_server_rshutdown() { Server *serv = sw_server(); serv->drain_worker_pipe(); - if (serv->is_started() && !serv->is_user_worker()) { + if (serv->is_started() && !serv->is_user_worker() && !serv->is_worker_thread()) { if (php_swoole_is_fatal_error()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, @@ -1900,7 +1900,11 @@ static PHP_METHOD(swoole_server, __construct) { size_t host_len = 0; zend_long sock_type = SW_SOCK_TCP; zend_long serv_port = 0; +#ifdef SW_THREAD + zend_long serv_mode = Server::MODE_THREAD; +#else zend_long serv_mode = Server::MODE_BASE; +#endif // only cli env if (!SWOOLE_G(cli)) { @@ -1917,13 +1921,13 @@ static PHP_METHOD(swoole_server, __construct) { Z_PARAM_LONG(sock_type) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS) { + if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS && serv_mode != Server::MODE_THREAD) { zend_throw_error(NULL, "invalid $mode parameters %d", (int) serv_mode); RETURN_FALSE; } #ifdef SW_THREAD - if (sw_server() || sw_server()->is_worker_thread()) { + if (sw_server() && sw_server()->is_worker_thread()) { server_ctor(ZEND_THIS, sw_server()); return; } @@ -3039,9 +3043,9 @@ static PHP_METHOD(swoole_server, taskwait) { } uint64_t notify; - EventData *task_result = &(serv->task_result[sw_get_process_id()]); + EventData *task_result = &(serv->task_result[swoole_get_process_id()]); sw_memset_zero(task_result, sizeof(*task_result)); - Pipe *pipe = serv->task_notify_pipes.at(sw_get_process_id()).get(); + Pipe *pipe = serv->task_notify_pipes.at(swoole_get_process_id()).get(); network::Socket *task_notify_socket = pipe->get_socket(false); // clear history task @@ -3121,10 +3125,10 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { int list_of_id[SW_MAX_CONCURRENT_TASK] = {}; uint64_t notify; - EventData *task_result = &(serv->task_result[sw_get_process_id()]); + EventData *task_result = &(serv->task_result[swoole_get_process_id()]); sw_memset_zero(task_result, sizeof(*task_result)); - Pipe *pipe = serv->task_notify_pipes.at(sw_get_process_id()).get(); - Worker *worker = serv->get_worker(sw_get_process_id()); + Pipe *pipe = serv->task_notify_pipes.at(swoole_get_process_id()).get(); + Worker *worker = serv->get_worker(swoole_get_process_id()); File fp = swoole::make_tmpfile(); if (!fp.ready()) { @@ -3442,7 +3446,7 @@ static PHP_METHOD(swoole_server, sendMessage) { Z_PARAM_LONG(worker_id) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if ((serv->is_worker() || serv->is_task_worker()) && worker_id == sw_get_process_id()) { + if ((serv->is_worker() || serv->is_task_worker()) && worker_id == swoole_get_process_id()) { php_swoole_fatal_error(E_WARNING, "can't send messages to self"); RETURN_FALSE; } diff --git a/include/swoole.h b/include/swoole.h index c9fe58d4cf1..67352d602c9 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -680,6 +680,10 @@ struct RecvData { struct ThreadGlobal { uint16_t id; uint8_t type; +#ifdef SW_THREAD + uint8_t process_type; + uint32_t process_id; +#endif String *buffer_stack; Reactor *reactor; Timer *timer; @@ -795,20 +799,56 @@ static inline void swoole_set_last_error(int error) { SwooleTG.error = error; } -static inline int swoole_get_last_error() { +static inline int swoole_get_last_error(void) { return SwooleTG.error; } -static inline int swoole_get_thread_id() { +static inline int swoole_get_thread_id(void) { return SwooleTG.id; } -static inline int swoole_get_process_type() { - return SwooleG.process_type; +static inline int swoole_get_thread_type(void) { + return SwooleTG.type; } -static inline int swoole_get_process_id() { +static inline void swoole_set_thread_id(uint16_t id) { + SwooleTG.id = id; +} + +static inline void swoole_set_thread_type(uint8_t type) { + SwooleTG.type = type; +} + +static inline swoole::WorkerId swoole_get_process_id(void) { +#ifdef SW_THREAD + return SwooleTG.process_id; +#else return SwooleG.process_id; +#endif +} + +static inline void swoole_set_process_id(swoole::WorkerId id) { +#ifdef SW_THREAD + SwooleTG.process_id = id; +#else + SwooleG.process_id = id; +#endif +} + +static inline void swoole_set_process_type(int type) { +#ifdef SW_THREAD + SwooleTG.process_type = type; +#else + SwooleG.process_type = type; +#endif +} + +static inline int swoole_get_process_type(void) { +#ifdef SW_THREAD + return SwooleTG.process_type; +#else + return SwooleG.process_type; +#endif } static inline uint32_t swoole_pagesize() { @@ -866,34 +906,3 @@ static sw_inline const swoole::Allocator *sw_std_allocator() { return &SwooleG.std_allocator; } -static sw_inline swoole::WorkerId sw_get_process_id() { -#ifdef SW_THREAD - return SwooleTG.id; -#else - return SwooleG.process_id; -#endif -} - -static sw_inline void sw_set_process_id(swoole::WorkerId id) { -#ifdef SW_THREAD - SwooleTG.id = id; -#else - SwooleG.process_id = id; -#endif -} - -static sw_inline void sw_set_process_type(int type) { -#ifdef SW_THREAD - SwooleTG.type = type; -#else - SwooleG.process_type = type; -#endif -} - -static sw_inline int sw_get_process_type() { -#ifdef SW_THREAD - return SwooleTG.type; -#else - return SwooleG.process_type; -#endif -} diff --git a/include/swoole_server.h b/include/swoole_server.h index 97756282da1..29cedeebad3 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -416,7 +416,7 @@ class Factory { class BaseFactory : public Factory { public: - BaseFactory(Server *server) : Factory(server) {} + BaseFactory(Server *server); ~BaseFactory(); bool start() override; bool shutdown() override; @@ -427,9 +427,6 @@ class BaseFactory : public Factory { }; class ProcessFactory : public Factory { - private: - std::vector> pipes; - public: ProcessFactory(Server *server); ~ProcessFactory(); @@ -441,6 +438,14 @@ class ProcessFactory : public Factory { bool end(SessionId sesion_id, int flags) override; }; +class ThreadFactory : public BaseFactory { + public: + ThreadFactory(Server *server); + ~ThreadFactory(); + bool start() override; + bool shutdown() override; +}; + enum ServerEventType { // recv data payload SW_SERVER_EVENT_RECV_DATA, @@ -493,6 +498,7 @@ class Server { enum Mode { MODE_BASE = 1, MODE_PROCESS = 2, + MODE_THREAD = 3, }; enum TaskIpcMode { @@ -731,6 +737,7 @@ class Server { Manager *manager = nullptr; std::vector ports; + std::vector> worker_pipes; ListenPort *get_primary_port() { return ports.front(); @@ -931,6 +938,14 @@ class Server { void add_http_compression_type(const std::string &type); int create(); + Factory *create_base_factory(); + Factory *create_thread_factory(); + Factory *create_process_factory(); + bool create_worker_pipes(); + void destroy_base_factory(); + void destroy_thread_factory(); + void destroy_process_factory(); + int start(); bool reload(bool reload_all_workers); bool shutdown(); @@ -1012,6 +1027,10 @@ class Server { return mode_ == MODE_BASE; } + bool is_thread_mode() { + return mode_ == MODE_THREAD; + } + bool is_enable_coroutine() { if (is_task_worker()) { return task_enable_coroutine; @@ -1095,40 +1114,31 @@ class Server { } bool is_master() { - return SwooleG.process_type == SW_PROCESS_MASTER; + return swoole_get_process_type() == SW_PROCESS_MASTER; } bool is_worker() { - return sw_get_process_type() == SW_PROCESS_EVENTWORKER; + return swoole_get_process_type() == SW_PROCESS_EVENTWORKER; } bool is_task_worker() { - return sw_get_process_type() == SW_PROCESS_TASKWORKER; + return swoole_get_process_type() == SW_PROCESS_TASKWORKER; } bool is_manager() { - return sw_get_process_type() == SW_PROCESS_MANAGER; + return swoole_get_process_type() == SW_PROCESS_MANAGER; } bool is_user_worker() { - return sw_get_process_type() == SW_PROCESS_USERWORKER; + return swoole_get_process_type() == SW_PROCESS_USERWORKER; } bool is_worker_thread() { -#ifdef SW_THREAD - return sw_get_process_type() == SW_PROCESS_EVENTWORKER || sw_get_process_type() == SW_PROCESS_TASKWORKER || - sw_get_process_type() == SW_PROCESS_USERWORKER; -#else - return false; -#endif + return is_thread_mode() && is_reactor_thread(); } bool is_reactor_thread() { -#ifdef SW_THREAD - return false; -#else - return sw_get_process_type() == SW_PROCESS_MASTER && SwooleTG.type == Server::THREAD_REACTOR; -#endif + return swoole_get_thread_type() == Server::THREAD_REACTOR; } bool isset_hook(enum HookType type) { @@ -1414,14 +1424,10 @@ class Server { int start_check(); void check_port_type(ListenPort *ls); void destroy(); - void destroy_reactor_threads(); - void destroy_reactor_processes(); - int create_reactor_processes(); - int create_reactor_threads(); int start_reactor_threads(); int start_reactor_processes(); int start_worker_threads(); - int start_master_thread(); + int start_master_thread(Reactor *reactor); int start_event_worker(Worker *worker); void start_heartbeat_thread(); void join_reactor_thread(); diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 8ef0f2324c5..a43ef844229 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -233,7 +233,7 @@ int ProcessPool::start() { running = started = true; master_pid = getpid(); reload_workers = new Worker[worker_num](); - sw_set_process_type(SW_PROCESS_MASTER); + swoole_set_process_type(SW_PROCESS_MASTER); if (async) { main_loop = ProcessPool_worker_loop_async; @@ -458,8 +458,8 @@ pid_t ProcessPool::spawn(Worker *worker) { // child case 0: worker->pid = SwooleG.pid; - sw_set_process_type(SW_PROCESS_WORKER); - sw_set_process_id(worker->id); + swoole_set_process_type(SW_PROCESS_WORKER); + swoole_set_process_id(worker->id); if (async) { if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) { exit(254); @@ -811,7 +811,7 @@ bool ProcessPool::detach() { WorkerStopMessage msg; msg.pid = getpid(); - msg.worker_id = sw_get_process_id(); + msg.worker_id = swoole_get_process_id(); if (push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) { return false; diff --git a/src/server/base.cc b/src/server/base.cc index 39a66833fe3..928fe632766 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -18,6 +18,43 @@ namespace swoole { +Factory *Server::create_base_factory() { + reactor_num = worker_num; + connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); + if (connection_list == nullptr) { + swoole_sys_warning("calloc[2](%d) failed", (int) (max_connection * sizeof(Connection))); + return nullptr; + } + gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); + if (gs->connection_nums == nullptr) { + swoole_error("sw_shm_calloc(%ld) for gs->connection_nums failed", worker_num * sizeof(sw_atomic_t)); + return nullptr; + } + + for (auto port : ports) { + port->gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); + if (port->gs->connection_nums == nullptr) { + swoole_error("sw_shm_calloc(%ld) for port->connection_nums failed", worker_num * sizeof(sw_atomic_t)); + return nullptr; + } + } + + return new BaseFactory(this); +} + +void Server::destroy_base_factory() { + sw_free(connection_list); + sw_shm_free((void *) gs->connection_nums); + for (auto port : ports) { + sw_shm_free((void *) port->gs->connection_nums); + } + gs->connection_nums = nullptr; +} + +BaseFactory::BaseFactory(Server *server) : Factory(server) {} + +BaseFactory::~BaseFactory() {} + bool BaseFactory::start() { SwooleWG.run_always = true; return true; @@ -89,7 +126,7 @@ bool BaseFactory::end(SessionId session_id, int flags) { _send.info.fd = session_id; _send.info.len = 0; _send.info.type = SW_SERVER_EVENT_CLOSE; - _send.info.reactor_id = sw_get_process_id(); + _send.info.reactor_id = swoole_get_process_id(); Session *session = server_->get_session(session_id); if (!session->fd) { @@ -100,7 +137,7 @@ bool BaseFactory::end(SessionId session_id, int flags) { return false; } - if (session->reactor_id != sw_get_process_id()) { + if (session->reactor_id != swoole_get_process_id()) { Worker *worker = server_->get_worker(session->reactor_id); if (worker->pipe_master->send_async((const char *) &_send.info, sizeof(_send.info)) < 0) { swoole_sys_warning("failed to send %lu bytes to pipe_master", sizeof(_send.info)); @@ -167,7 +204,7 @@ bool BaseFactory::finish(SendData *data) { SessionId session_id = data->info.fd; Session *session = server_->get_session(session_id); - if (session->reactor_id != sw_get_process_id()) { + if (session->reactor_id != swoole_get_process_id()) { swoole_trace("session->reactor_id=%d, SwooleG.process_id=%d", session->reactor_id, server_->get_worker_id()); Worker *worker = server_->gs->event_workers.get_worker(session->reactor_id); EventData proxy_msg{}; @@ -194,6 +231,4 @@ bool BaseFactory::finish(SendData *data) { } } -BaseFactory::~BaseFactory() {} - } // namespace swoole diff --git a/src/server/manager.cc b/src/server/manager.cc index c7d76871543..143ab6d15aa 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -114,7 +114,7 @@ int Server::start_manager_process() { } auto fn = [this](void) { - sw_set_process_type(SW_PROCESS_MANAGER); + swoole_set_process_type(SW_PROCESS_MANAGER); gs->manager_pid = SwooleG.pid = getpid(); if (task_worker_num > 0) { @@ -630,8 +630,8 @@ pid_t Server::spawn_user_worker(Worker *worker) { } // child else if (pid == 0) { - sw_set_process_type(SW_PROCESS_USERWORKER); - sw_set_process_id(worker->id); + swoole_set_process_type(SW_PROCESS_USERWORKER); + swoole_set_process_id(worker->id); SwooleWG.worker = worker; worker->pid = SwooleG.pid; onUserWorkerStart(this, worker); diff --git a/src/server/master.cc b/src/server/master.cc index d150de7f067..de634a6d965 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -398,7 +398,7 @@ int Server::start_check() { /** * OpenSSL thread-safe */ - if (is_process_mode() && !single_thread) { + if ((is_process_mode() && !single_thread) || is_thread_mode()) { swoole_ssl_init_thread_safety(); } #endif @@ -406,11 +406,9 @@ int Server::start_check() { return SW_OK; } -int Server::start_master_thread() { - SwooleTG.type = THREAD_MASTER; - SwooleTG.id = single_thread ? 0 : reactor_num; - - Reactor *reactor = sw_reactor(); +int Server::start_master_thread(Reactor *reactor) { + swoole_set_thread_type(THREAD_MASTER); + swoole_set_thread_id(single_thread ? 0 : reactor_num); if (SwooleTG.timer && SwooleTG.timer->get_reactor() == nullptr) { SwooleTG.timer->reinit(reactor); @@ -419,7 +417,7 @@ int Server::start_master_thread() { init_signal_handler(); SwooleG.pid = getpid(); - sw_set_process_type(SW_PROCESS_MASTER); + swoole_set_process_type(SW_PROCESS_MASTER); reactor->ptr = this; reactor->set_handler(SW_FD_STREAM_SERVER, Server::accept_connection); @@ -443,7 +441,9 @@ int Server::start_master_thread() { #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) SW_START_SLEEP; #else - pthread_barrier_wait(&gs->manager_barrier); + if (is_process_mode()) { + pthread_barrier_wait(&gs->manager_barrier); + } #endif #else SW_START_SLEEP; @@ -463,9 +463,6 @@ int Server::start_master_thread() { } void Server::store_listen_socket() { -#ifdef SW_THREAD - std::unique_lock _lock(lock_); -#endif for (auto ls : ports) { int sockfd = ls->socket->fd; // save server socket to connection_list @@ -564,21 +561,6 @@ void Server::destroy_worker(Worker *worker) { * [Worker] */ void Server::init_worker(Worker *worker) { -#if defined(HAVE_CPU_AFFINITY) && !defined(SW_THREAD) - if (open_cpu_affinity) { - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); - if (cpu_affinity_available_num) { - CPU_SET(cpu_affinity_available[sw_get_process_id() % cpu_affinity_available_num], &cpu_set); - } else { - CPU_SET(sw_get_process_id() % SW_CPU_NUM, &cpu_set); - } - if (swoole_set_cpu_affinity(&cpu_set) < 0) { - swoole_sys_warning("swoole_set_cpu_affinity() failed"); - } - } -#endif - if (max_request < 1) { SwooleWG.run_always = true; } else { @@ -587,7 +569,6 @@ void Server::init_worker(Worker *worker) { SwooleWG.max_request += swoole_system_random(1, max_request_grace); } } - worker->start_time = ::time(nullptr); worker->request_count = 0; } @@ -701,11 +682,7 @@ int Server::start() { } int ret; if (is_base_mode()) { -#ifdef SW_THREAD - ret = start_worker_threads(); -#else ret = start_reactor_processes(); -#endif } else { ret = start_reactor_threads(); } @@ -766,14 +743,6 @@ int Server::create() { return SW_ERR; } - if (is_base_mode()) { - gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); - if (gs->connection_nums == nullptr) { - swoole_error("sw_shm_calloc(%ld) for gs->connection_nums failed", worker_num * sizeof(sw_atomic_t)); - return SW_ERR; - } - } - if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE)) { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE, this); } @@ -793,13 +762,6 @@ int Server::create() { int index = 0; for (auto port : ports) { port->gs = &port_gs_list[index++]; - if (is_base_mode()) { - port->gs->connection_nums = (sw_atomic_t *) sw_shm_calloc(worker_num, sizeof(sw_atomic_t)); - if (port->gs->connection_nums == nullptr) { - swoole_error("sw_shm_calloc(%ld) for port->connection_nums failed", worker_num * sizeof(sw_atomic_t)); - return SW_ERR; - } - } } if (enable_static_handler and locations == nullptr) { @@ -856,17 +818,19 @@ int Server::create() { return SW_ERR; } - int retval; if (is_base_mode()) { - factory = new BaseFactory(this); - retval = create_reactor_processes(); + factory = create_base_factory(); + } else if (is_thread_mode()) { + factory = create_thread_factory(); } else { - factory = new ProcessFactory(this); - retval = create_reactor_threads(); + factory = create_process_factory(); + } + if (!factory) { + return SW_ERR; } #ifdef HAVE_PTHREAD_BARRIER - if (is_process_mode()) { + if (is_process_mode() || is_thread_mode()) { pthread_barrier_init(&reactor_thread_barrier, nullptr, reactor_num + 1); #if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)) pthread_barrierattr_setpshared(&gs->manager_barrier_attr, PTHREAD_PROCESS_SHARED); @@ -879,7 +843,7 @@ int Server::create() { swoole_call_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE, this); } - return retval; + return SW_OK; } void Server::clear_timer() { @@ -1010,11 +974,6 @@ void Server::destroy() { if (onShutdown) { onShutdown(this); } - if (is_base_mode()) { - destroy_reactor_processes(); - } else { - destroy_reactor_threads(); - } SW_LOOP_N(SW_MAX_HOOK_TYPE) { if (hooks[i]) { std::list *l = reinterpret_cast *>(hooks[i]); @@ -1032,23 +991,21 @@ void Server::destroy() { } #endif - for (auto port : ports) { - if (port->gs->connection_nums) { - sw_shm_free((void *) port->gs->connection_nums); - } + if (is_base_mode()) { + destroy_base_factory(); + } else if (is_thread_mode()) { + destroy_thread_factory(); + } else { + destroy_process_factory(); } sw_shm_free(session_list); sw_shm_free(port_gs_list); sw_shm_free(workers); - if (gs->connection_nums) { - sw_shm_free((void *) gs->connection_nums); - } session_list = nullptr; port_gs_list = nullptr; workers = nullptr; - gs->connection_nums = nullptr; delete factory; factory = nullptr; @@ -1602,7 +1559,9 @@ void Server::init_signal_handler() { // for test swoole_signal_set(SIGVTALRM, Server_signal_handler); - set_minfd(SwooleG.signal_fd); + if (SwooleG.signal_fd > 0) { + set_minfd(SwooleG.signal_fd); + } } void Server::timer_callback(Timer *timer, TimerNode *tnode) { @@ -1917,7 +1876,7 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f int fd = _socket->fd; Connection *connection = &(connection_list[fd]); - ReactorId reactor_id = is_base_mode() ? sw_get_process_id() : fd % reactor_num; + ReactorId reactor_id = is_base_mode() ? swoole_get_process_id() : fd % reactor_num; *connection = {}; sw_spinlock(&gs->spinlock); @@ -1949,7 +1908,7 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f // TCP Nodelay if (ls->open_tcp_nodelay && (ls->type == SW_SOCK_TCP || ls->type == SW_SOCK_TCP6)) { - if (ls->socket->set_tcp_nodelay() != 0) { + if (_socket->set_tcp_nodelay() != 0) { swoole_sys_warning("setsockopt(TCP_NODELAY) failed"); } _socket->enable_tcp_nodelay = true; diff --git a/src/server/process.cc b/src/server/process.cc index a1ffcd34261..872977cd34c 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -14,16 +14,42 @@ +----------------------------------------------------------------------+ */ -#include - #include "swoole_server.h" namespace swoole { using network::Socket; +Factory *Server::create_process_factory() { + /** + * init reactor thread pool + */ + reactor_threads = new ReactorThread[reactor_num](); + /** + * alloc the memory for connection_list + */ + connection_list = (Connection *) sw_shm_calloc(max_connection, sizeof(Connection)); + if (connection_list == nullptr) { + swoole_error("calloc[1] failed"); + return nullptr; + } + reactor_pipe_num = worker_num / reactor_num; + return new ProcessFactory(this); +} + +void Server::destroy_process_factory() { + sw_shm_free(connection_list); + delete[] reactor_threads; + + if (gs->event_workers.message_box) { + gs->event_workers.message_box->destroy(); + } +} + ProcessFactory::ProcessFactory(Server *server) : Factory(server) {} +ProcessFactory::~ProcessFactory() {} + bool ProcessFactory::shutdown() { int status; @@ -38,27 +64,31 @@ bool ProcessFactory::shutdown() { return SW_OK; } -ProcessFactory::~ProcessFactory() {} - -bool ProcessFactory::start() { - SW_LOOP_N(server_->worker_num) { +bool Server::create_worker_pipes() { + SW_LOOP_N(worker_num) { auto _sock = new UnixSocket(true, SOCK_DGRAM); if (!_sock->ready()) { delete _sock; return false; } - pipes.emplace_back(_sock); - server_->workers[i].pipe_master = _sock->get_socket(true); - server_->workers[i].pipe_worker = _sock->get_socket(false); - server_->workers[i].pipe_object = _sock; + worker_pipes.emplace_back(_sock); + workers[i].pipe_master = _sock->get_socket(true); + workers[i].pipe_worker = _sock->get_socket(false); + workers[i].pipe_object = _sock; } - server_->init_ipc_max_size(); - if (server_->create_pipe_buffers() < 0) { + init_ipc_max_size(); + if (create_pipe_buffers() < 0) { return false; } + return true; +} +bool ProcessFactory::start() { + if (!server_->create_worker_pipes()) { + return false; + } return server_->start_manager_process() == SW_OK; } @@ -128,7 +158,7 @@ static bool inline process_is_supported_send_yield(Server *serv, Connection *con if (!serv->is_hash_dispatch_mode()) { return false; } else { - return serv->schedule_worker(conn->fd, nullptr) == (int) sw_get_process_id(); + return serv->schedule_worker(conn->fd, nullptr) == (int) swoole_get_process_id(); } } @@ -185,7 +215,7 @@ bool ProcessFactory::finish(SendData *resp) { memcpy(&task, resp, sizeof(SendData)); task.info.fd = session_id; task.info.reactor_id = conn->reactor_id; - task.info.server_fd = sw_get_process_id(); + task.info.server_fd = swoole_get_process_id(); swoole_trace("worker_id=%d, type=%d", SwooleG.process_id, task.info.type); @@ -227,7 +257,7 @@ bool ProcessFactory::end(SessionId session_id, int flags) { if (conn->close_actively) { bool hash = server_->is_hash_dispatch_mode(); int worker_id = hash ? server_->schedule_worker(conn->fd, nullptr) : conn->fd % server_->worker_num; - if (server_->is_worker() && (!hash || worker_id == (int) sw_get_process_id())) { + if (server_->is_worker() && (!hash || worker_id == (int) swoole_get_process_id())) { goto _close; } worker = server_->get_worker(worker_id); diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 5257135ae60..158f7a450fa 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -33,20 +33,6 @@ static bool Server_is_single(Server *serv) { serv->user_worker_list.empty()); } -int Server::create_reactor_processes() { - reactor_num = worker_num; - connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); - if (connection_list == nullptr) { - swoole_sys_warning("calloc[2](%d) failed", (int) (max_connection * sizeof(Connection))); - return SW_ERR; - } - return SW_OK; -} - -void Server::destroy_reactor_processes() { - sw_free(connection_list); -} - int Server::start_reactor_processes() { single_thread = 1; @@ -180,8 +166,8 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; SwooleG.pid = getpid(); - sw_set_process_type(SW_PROCESS_WORKER); - sw_set_process_id(worker->id); + swoole_set_process_type(SW_PROCESS_WORKER); + swoole_set_process_id(worker->id); if (serv->max_request > 0) { SwooleWG.run_always = false; diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index d1dbaaaac71..10175355eb1 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -630,23 +630,6 @@ static int ReactorThread_onWrite(Reactor *reactor, Event *ev) { return SW_OK; } -int Server::create_reactor_threads() { - /** - * init reactor thread pool - */ - reactor_threads = new ReactorThread[reactor_num](); - /** - * alloc the memory for connection_list - */ - connection_list = (Connection *) sw_shm_calloc(max_connection, sizeof(Connection)); - if (connection_list == nullptr) { - swoole_error("calloc[1] failed"); - return SW_ERR; - } - reactor_pipe_num = worker_num / reactor_num; - return SW_OK; -} - /** * [master] */ @@ -687,7 +670,21 @@ int Server::start_reactor_threads() { } SW_LOOP_N(reactor_num) { - get_thread(i)->thread = std::thread(ReactorThread_loop, this, i); + if (is_thread_mode()) { + get_thread(i)->thread = std::thread([=]() { + swoole_set_process_id(i); + swoole_set_process_type(SW_PROCESS_EVENTWORKER); + swoole_set_thread_id(i); + swoole_set_thread_type(Server::THREAD_REACTOR); + SwooleWG.worker = get_worker(i); + worker_thread_start([=](void) -> bool { + ReactorThread_loop(this, i); + return true; + }); + }); + } else { + get_thread(i)->thread = std::thread(ReactorThread_loop, this, i); + } } _init_master_thread: @@ -699,7 +696,7 @@ int Server::start_reactor_threads() { start_heartbeat_thread(); } - return start_master_thread(); + return start_master_thread(reactor); } int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { @@ -745,6 +742,9 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { } serv->init_reactor(reactor); + if (serv->is_thread_mode()) { + serv->init_worker(serv->get_worker(reactor_id)); + } int max_pipe_fd = serv->get_worker(serv->worker_num - 1)->pipe_master->fd + 2; pipe_sockets = (Socket *) sw_calloc(max_pipe_fd, sizeof(Socket)); @@ -803,28 +803,14 @@ static void ReactorThread_loop(Server *serv, int reactor_id) { return; } + if (serv->is_thread_mode()) { + serv->call_worker_start_callback(serv->get_worker(reactor_id)); + } + ReactorThread *thread = serv->get_thread(reactor_id); thread->id = reactor_id; Reactor *reactor = sw_reactor(); -#ifdef HAVE_CPU_AFFINITY - // cpu affinity setting - if (serv->open_cpu_affinity) { - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); - - if (serv->cpu_affinity_available_num) { - CPU_SET(serv->cpu_affinity_available[reactor_id % serv->cpu_affinity_available_num], &cpu_set); - } else { - CPU_SET(reactor_id % SW_CPU_NUM, &cpu_set); - } - - if (0 != pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set)) { - swoole_sys_warning("pthread_setaffinity_np() failed"); - } - } -#endif - if (thread->init(serv, reactor, reactor_id) < 0) { return; } @@ -837,6 +823,9 @@ static void ReactorThread_loop(Server *serv, int reactor_id) { #endif // main loop swoole_event_wait(); + if (serv->is_thread_mode()) { + serv->worker_stop_callback(serv->get_worker(reactor_id)); + } sw_free(thread->pipe_sockets); if (thread->pipe_command) { thread->pipe_command->fd = -1; @@ -947,15 +936,6 @@ void Server::join_reactor_thread() { } } -void Server::destroy_reactor_threads() { - sw_shm_free(connection_list); - delete[] reactor_threads; - - if (gs->event_workers.message_box) { - gs->event_workers.message_box->destroy(); - } -} - void Server::start_heartbeat_thread() { heartbeat_thread = std::thread([this]() { swoole_signal_block_all(); diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index 50bce294e30..b5b45af2562 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -327,7 +327,7 @@ void StaticHandler::parse_range(const char *range, const char *if_range) { } while (*p >= '0' && *p <= '9') { - if (start >= cutoff && (start > cutoff || (size_t)(*p - '0') > cutlim)) { + if (start >= cutoff && (start > cutoff || (size_t) (*p - '0') > cutlim)) { status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; return; } @@ -364,7 +364,7 @@ void StaticHandler::parse_range(const char *range, const char *if_range) { } while (*p >= '0' && *p <= '9') { - if (end >= cutoff && (end > cutoff || (size_t)(*p - '0') > cutlim)) { + if (end >= cutoff && (end > cutoff || (size_t) (*p - '0') > cutlim)) { status_code = SW_HTTP_RANGE_NOT_SATISFIABLE; return; } diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 4116e0e6549..b0faef13736 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -100,7 +100,7 @@ static int TaskWorker_onTask(ProcessPool *pool, EventData *task) { bool Server::task_pack(EventData *task, const void *_data, size_t _length) { task->info.type = SW_SERVER_EVENT_TASK; task->info.fd = SwooleG.current_task_id++; - task->info.reactor_id = sw_get_process_id(); + task->info.reactor_id = swoole_get_process_id(); task->info.time = microtime(); if (_length < SW_IPC_MAX_SIZE - sizeof(task->info)) { @@ -172,7 +172,7 @@ static void TaskWorker_signal_init(ProcessPool *pool) { static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; - sw_set_process_id(worker->id); + swoole_set_process_id(worker->id); /** * Make the task worker support asynchronous diff --git a/src/server/worker.cc b/src/server/worker.cc index aaf39a70bda..0c615035900 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -217,10 +217,10 @@ void Server::worker_accept_event(DataHead *info) { } void Server::worker_start_callback(Worker *worker) { - if (sw_get_process_id() >= worker_num) { - sw_set_process_type(SW_PROCESS_TASKWORKER); + if (swoole_get_process_id() >= worker_num) { + swoole_set_process_type(SW_PROCESS_TASKWORKER); } else { - sw_set_process_type(SW_PROCESS_WORKER); + swoole_set_process_type(SW_PROCESS_WORKER); } int is_root = !geteuid(); @@ -375,7 +375,7 @@ void Server::stop_async_worker(Worker *worker) { static void Worker_reactor_try_to_exit(Reactor *reactor) { Server *serv; - if (sw_get_process_type() == SW_PROCESS_TASKWORKER) { + if (swoole_get_process_type() == SW_PROCESS_TASKWORKER) { ProcessPool *pool = (ProcessPool *) reactor->ptr; serv = (Server *) pool->ptr; } else { @@ -428,8 +428,8 @@ void Server::drain_worker_pipe() { * main loop [Worker] */ int Server::start_event_worker(Worker *worker) { - sw_set_process_id(worker->id); - sw_set_process_type(SW_PROCESS_EVENTWORKER); + swoole_set_process_id(worker->id); + swoole_set_process_type(SW_PROCESS_EVENTWORKER); init_worker(worker); diff --git a/src/server/worker_threads.cc b/src/server/worker_threads.cc index 799bc9d8438..e6cfaed2b6f 100644 --- a/src/server/worker_threads.cc +++ b/src/server/worker_threads.cc @@ -22,6 +22,38 @@ namespace swoole { using network::Socket; +Factory *Server::create_thread_factory() { + reactor_num = worker_num; + connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); + if (connection_list == nullptr) { + swoole_sys_warning("calloc[2](%d) failed", (int) (max_connection * sizeof(Connection))); + return nullptr; + } + /** + * init reactor thread pool + */ + reactor_threads = new ReactorThread[reactor_num](); + reactor_pipe_num = 1; + return new ThreadFactory(this); +} + +void Server::destroy_thread_factory() { + sw_free(connection_list); + delete[] reactor_threads; +} + +ThreadFactory::ThreadFactory(Server *server) : BaseFactory(server) {} + +bool ThreadFactory::start() { + return server_->create_worker_pipes(); +} + +bool ThreadFactory::shutdown() { + return true; +} + +ThreadFactory::~ThreadFactory() {} + struct WorkerThreads { std::vector threads_; std::mutex lock_; @@ -46,7 +78,7 @@ struct WorkerThreads { cv_.notify_one(); } - template + template void create_thread(int i, _Callable fn) { if (threads_[i].joinable()) { threads_[i].join(); @@ -56,8 +88,8 @@ struct WorkerThreads { void spawn_event_worker(int i) { create_thread(i, [=]() { - sw_set_process_type(SW_PROCESS_EVENTWORKER); - sw_set_process_id(i); + swoole_set_process_type(SW_PROCESS_EVENTWORKER); + swoole_set_process_id(i); Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_EVENTWORKER; server_->worker_thread_start( @@ -68,8 +100,8 @@ struct WorkerThreads { void spawn_task_worker(int i) { create_thread(i, [=]() { - sw_set_process_type(SW_PROCESS_TASKWORKER); - sw_set_process_id(i); + swoole_set_process_type(SW_PROCESS_TASKWORKER); + swoole_set_process_id(i); Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_TASKWORKER; server_->worker_thread_start([=](void) -> bool { @@ -82,8 +114,8 @@ struct WorkerThreads { void spawn_user_worker(int i) { create_thread(i, [=]() { Worker *worker = server_->user_worker_list.at(i - server_->task_worker_num - server_->worker_num); - sw_set_process_type(SW_PROCESS_USERWORKER); - sw_set_process_id(i); + swoole_set_process_type(SW_PROCESS_USERWORKER); + swoole_set_process_id(i); worker->type = SW_PROCESS_USERWORKER; server_->worker_thread_start([=](void) -> bool { server_->onUserWorkerStart(server_, worker); @@ -123,7 +155,7 @@ struct WorkerThreads { int Server::start_worker_threads() { single_thread = 1; - sw_set_process_type(SW_PROCESS_MANAGER); + swoole_set_process_type(SW_PROCESS_MANAGER); // listen TCP if (have_stream_sock == 1) { From 7f63f02bb93c7bfffc059f06fa07d1d41e2ad2bb Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sun, 7 Apr 2024 16:16:18 +0800 Subject: [PATCH 042/103] rename --- src/server/{worker_threads.cc => thread.cc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/server/{worker_threads.cc => thread.cc} (100%) diff --git a/src/server/worker_threads.cc b/src/server/thread.cc similarity index 100% rename from src/server/worker_threads.cc rename to src/server/thread.cc From aeeb79f4f89c3ea512d126b967f46cd1eaa647e1 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sun, 7 Apr 2024 19:14:06 +0800 Subject: [PATCH 043/103] Refactor --- ext-src/swoole_server.cc | 3 +- include/swoole_server.h | 48 +++--- src/server/manager.cc | 159 +++---------------- src/server/master.cc | 7 +- src/server/process.cc | 126 +++++++++++++++ src/server/reactor_thread.cc | 22 +-- src/server/thread.cc | 295 +++++++++++++++-------------------- 7 files changed, 313 insertions(+), 347 deletions(-) diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 5be3f6a719b..dc1aa86acf1 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2604,7 +2604,8 @@ static PHP_METHOD(swoole_server, start) { #ifdef SW_THREAD if (serv->is_worker_thread()) { - RETURN_BOOL(worker_thread_fn()); + worker_thread_fn(); + return; } #endif diff --git a/include/swoole_server.h b/include/swoole_server.h index 29cedeebad3..0595e21ca3f 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -46,6 +46,7 @@ #include #include #include +#include //------------------------------------Server------------------------------------------- namespace swoole { @@ -57,7 +58,7 @@ struct Request; class Server; struct Manager; -typedef std::function WorkerFn; +typedef std::function WorkerFn; struct Session { SessionId id; @@ -430,6 +431,13 @@ class ProcessFactory : public Factory { public: ProcessFactory(Server *server); ~ProcessFactory(); + pid_t spawn_event_worker(Worker *worker); + pid_t spawn_user_worker(Worker *worker); + pid_t spawn_task_worker(Worker *worker); + void kill_user_workers(); + void kill_event_workers(); + void kill_task_workers(); + void check_worker_exit_status(Worker *worker, const ExitStatus &exit_status); bool start() override; bool shutdown() override; bool dispatch(SendData *) override; @@ -439,9 +447,23 @@ class ProcessFactory : public Factory { }; class ThreadFactory : public BaseFactory { + private: + std::vector threads_; + std::mutex lock_; + std::condition_variable cv_; + std::queue queue_; + Worker manager; + template + void create_thread(int i, _Callable fn); + void at_thread_exit(Worker *worker); public: ThreadFactory(Server *server); ~ThreadFactory(); + void spawn_event_worker(int i); + void spawn_task_worker(int i); + void spawn_user_worker(int i); + void spawn_manager_thread(int i); + void wait(); bool start() override; bool shutdown() override; }; @@ -512,6 +534,7 @@ class Server { THREAD_MASTER = 1, THREAD_REACTOR = 2, THREAD_HEARTBEAT = 3, + THREAD_WORKER = 4, }; enum DispatchMode { @@ -1134,7 +1157,7 @@ class Server { } bool is_worker_thread() { - return is_thread_mode() && is_reactor_thread(); + return is_thread_mode() && swoole_get_thread_type() == Server::THREAD_WORKER; } bool is_reactor_thread() { @@ -1370,24 +1393,11 @@ class Server { } } - /** - * [Manager] - */ - pid_t spawn_event_worker(Worker *worker); - pid_t spawn_user_worker(Worker *worker); - pid_t spawn_task_worker(Worker *worker); - - void kill_user_workers(); - void kill_event_workers(); - void kill_task_workers(); - static int wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status); static void read_worker_message(ProcessPool *pool, EventData *msg); void drain_worker_pipe(); - void check_worker_exit_status(Worker *worker, const ExitStatus &exit_status); - /** * [Worker] */ @@ -1399,9 +1409,14 @@ class Server { static int worker_main_loop(ProcessPool *pool, Worker *worker); static void worker_signal_handler(int signo); static void worker_signal_init(void); + static void reactor_thread_main_loop(Server *serv, int reactor_id); static bool task_pack(EventData *task, const void *data, size_t data_len); static bool task_unpack(EventData *task, String *buffer, PacketPtr *packet); + int start_master_thread(Reactor *reactor); + int start_event_worker(Worker *worker); + void start_heartbeat_thread(); + private: enum Mode mode_; Connection *connection_list = nullptr; @@ -1427,9 +1442,6 @@ class Server { int start_reactor_threads(); int start_reactor_processes(); int start_worker_threads(); - int start_master_thread(Reactor *reactor); - int start_event_worker(Worker *worker); - void start_heartbeat_thread(); void join_reactor_thread(); TimerCallback get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn); diff --git a/src/server/manager.cc b/src/server/manager.cc index 143ab6d15aa..ce75338f144 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -114,6 +114,7 @@ int Server::start_manager_process() { } auto fn = [this](void) { + ProcessFactory *_factory = dynamic_cast(factory); swoole_set_process_type(SW_PROCESS_MANAGER); gs->manager_pid = SwooleG.pid = getpid(); @@ -126,7 +127,7 @@ int Server::start_manager_process() { SW_LOOP_N(worker_num) { Worker *worker = get_worker(i); - if (spawn_event_worker(worker) < 0) { + if (_factory->spawn_event_worker(worker) < 0) { swoole_sys_error("failed to fork event worker"); return; } @@ -134,7 +135,7 @@ int Server::start_manager_process() { if (!user_worker_list.empty()) { for (auto worker : user_worker_list) { - if (spawn_user_worker(worker) < 0) { + if (_factory->spawn_user_worker(worker) < 0) { swoole_sys_error("failed to fork user worker"); return; } @@ -156,22 +157,6 @@ int Server::start_manager_process() { return SW_OK; } -void Server::check_worker_exit_status(Worker *worker, const ExitStatus &exit_status) { - if (exit_status.get_status() != 0) { - swoole_warning("worker(pid=%d, id=%d) abnormal exit, status=%d, signal=%d" - "%s", - exit_status.get_pid(), - worker->id, - exit_status.get_code(), - exit_status.get_signal(), - exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : ""); - - if (onWorkerError != nullptr) { - onWorkerError(this, worker, exit_status); - } - } -} - void Manager::wait(Server *_server) { server_ = _server; server_->manager = this; @@ -228,6 +213,8 @@ void Manager::wait(Server *_server) { swoole_timer_add((long) (_server->manager_alarm * 1000), true, timer_callback, _server); } + ProcessFactory *_factory = dynamic_cast(_server->factory); + while (_server->running) { ExitStatus exit_status = wait_process(); const auto errnoAfterWait = errno; @@ -244,10 +231,10 @@ void Manager::wait(Server *_server) { WorkerStopMessage worker_stop_msg; memcpy(&worker_stop_msg, msg.data, sizeof(worker_stop_msg)); if (worker_stop_msg.worker_id >= _server->worker_num) { - _server->spawn_task_worker(_server->get_worker(worker_stop_msg.worker_id)); + _factory->spawn_task_worker(_server->get_worker(worker_stop_msg.worker_id)); } else { Worker *worker = _server->get_worker(worker_stop_msg.worker_id); - _server->spawn_event_worker(worker); + _factory->spawn_event_worker(worker); } } pool->read_message = false; @@ -339,10 +326,10 @@ void Manager::wait(Server *_server) { } // check the process return code and signal - _server->check_worker_exit_status(worker, exit_status); + _factory->check_worker_exit_status(worker, exit_status); do { - if (_server->spawn_event_worker(worker) < 0) { + if (_factory->spawn_event_worker(worker) < 0) { SW_START_SLEEP; continue; } @@ -353,8 +340,8 @@ void Manager::wait(Server *_server) { if (_server->gs->task_workers.map_) { auto iter = _server->gs->task_workers.map_->find(exit_status.get_pid()); if (iter != _server->gs->task_workers.map_->end()) { - _server->check_worker_exit_status(iter->second, exit_status); - _server->spawn_task_worker(iter->second); + _factory->check_worker_exit_status(iter->second, exit_status); + _factory->spawn_task_worker(iter->second); } } // user process @@ -414,9 +401,9 @@ void Manager::wait(Server *_server) { */ alarm(_server->max_wait_time * 2); } - _server->kill_event_workers(); - _server->kill_task_workers(); - _server->kill_user_workers(); + _factory->kill_event_workers(); + _factory->kill_task_workers(); + _factory->kill_user_workers(); // force kill if (_server->max_wait_time) { alarm(0); @@ -493,16 +480,18 @@ int Server::wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status) return SW_ERR; } while (0); - serv->check_worker_exit_status(exit_worker, exit_status); + ProcessFactory *_factory = dynamic_cast(serv->factory); + + _factory->check_worker_exit_status(exit_worker, exit_status); pid_t new_process_pid = -1; switch (worker_type) { case SW_PROCESS_TASKWORKER: - new_process_pid = serv->spawn_task_worker(exit_worker); + new_process_pid = _factory->spawn_task_worker(exit_worker); break; case SW_PROCESS_USERWORKER: - new_process_pid = serv->spawn_user_worker(exit_worker); + new_process_pid = _factory->spawn_user_worker(exit_worker); break; default: /* never here */ @@ -543,116 +532,6 @@ void Server::read_worker_message(ProcessPool *pool, EventData *msg) { serv->message_bus.write(serv->get_command_reply_socket(), &task); } -/** - * kill and wait all user process - */ -void Server::kill_user_workers() { - if (user_worker_map.empty()) { - return; - } - - for (auto &kv : user_worker_map) { - swoole_kill(kv.second->pid, SIGTERM); - } - - for (auto &kv : user_worker_map) { - int __stat_loc; - if (swoole_waitpid(kv.second->pid, &__stat_loc, 0) < 0) { - swoole_sys_warning("waitpid(%d) failed", kv.second->pid); - } - } -} - -/** - * [Manager] kill and wait all event worker process - */ -void Server::kill_event_workers() { - int status; - - if (worker_num == 0) { - return; - } - - SW_LOOP_N(worker_num) { - swoole_trace("kill worker#%d[pid=%d]", workers[i].id, workers[i].pid); - swoole_kill(workers[i].pid, SIGTERM); - } - SW_LOOP_N(worker_num) { - swoole_trace("wait worker#%d[pid=%d]", workers[i].id, workers[i].pid); - if (swoole_waitpid(workers[i].pid, &status, 0) < 0) { - swoole_sys_warning("waitpid(%d) failed", workers[i].pid); - } - } -} - -/** - * [Manager] kill and wait task worker process - */ -void Server::kill_task_workers() { - if (task_worker_num == 0) { - return; - } - gs->task_workers.shutdown(); -} - -pid_t Server::spawn_event_worker(Worker *worker) { - pid_t pid = swoole_fork(0); - - if (pid < 0) { - swoole_sys_warning("failed to fork event worker"); - return SW_ERR; - } else if (pid == 0) { - worker->pid = SwooleG.pid; - } else { - worker->pid = pid; - return pid; - } - - if (is_base_mode()) { - gs->connection_nums[worker->id] = 0; - gs->event_workers.main_loop(&gs->event_workers, worker); - } else { - start_event_worker(worker); - } - - exit(0); - return 0; -} - -pid_t Server::spawn_user_worker(Worker *worker) { - pid_t pid = swoole_fork(0); - if (worker->pid) { - user_worker_map.erase(worker->pid); - } - if (pid < 0) { - swoole_sys_warning("Fork Worker failed"); - return SW_ERR; - } - // child - else if (pid == 0) { - swoole_set_process_type(SW_PROCESS_USERWORKER); - swoole_set_process_id(worker->id); - SwooleWG.worker = worker; - worker->pid = SwooleG.pid; - onUserWorkerStart(this, worker); - exit(0); - } - // parent - else { - /** - * worker: local memory - * user_workers: shared memory - */ - get_worker(worker->id)->pid = worker->pid = pid; - user_worker_map.emplace(std::make_pair(pid, worker)); - return pid; - } -} - -pid_t Server::spawn_task_worker(Worker *worker) { - return gs->task_workers.spawn(worker); -} - bool Server::reload(bool reload_all_workers) { if (gs->manager_pid == 0) { return false; diff --git a/src/server/master.cc b/src/server/master.cc index de634a6d965..371b114f005 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -683,8 +683,13 @@ int Server::start() { int ret; if (is_base_mode()) { ret = start_reactor_processes(); - } else { + } else if (is_process_mode()) { ret = start_reactor_threads(); + } else if (is_thread_mode()) { + ret = start_worker_threads(); + } else { + abort(); + return SW_ERR; } // failed to start if (ret < 0) { diff --git a/src/server/process.cc b/src/server/process.cc index 872977cd34c..fd5dd77d331 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -50,6 +50,132 @@ ProcessFactory::ProcessFactory(Server *server) : Factory(server) {} ProcessFactory::~ProcessFactory() {} +/** + * kill and wait all user process + */ +void ProcessFactory::kill_user_workers() { + if (server_->user_worker_map.empty()) { + return; + } + + for (auto &kv : server_->user_worker_map) { + swoole_kill(kv.second->pid, SIGTERM); + } + + for (auto &kv : server_->user_worker_map) { + int __stat_loc; + if (swoole_waitpid(kv.second->pid, &__stat_loc, 0) < 0) { + swoole_sys_warning("waitpid(%d) failed", kv.second->pid); + } + } +} + +/** + * [Manager] kill and wait all event worker process + */ +void ProcessFactory::kill_event_workers() { + int status; + + if (server_->worker_num == 0) { + return; + } + + SW_LOOP_N(server_->worker_num) { + swoole_trace("kill worker#%d[pid=%d]", workers[i].id, workers[i].pid); + swoole_kill(server_->workers[i].pid, SIGTERM); + } + SW_LOOP_N(server_->worker_num) { + swoole_trace("wait worker#%d[pid=%d]", workers[i].id, workers[i].pid); + if (swoole_waitpid(server_->workers[i].pid, &status, 0) < 0) { + swoole_sys_warning("waitpid(%d) failed", server_->workers[i].pid); + } + } +} + +/** + * [Manager] kill and wait task worker process + */ +void ProcessFactory::kill_task_workers() { + if (server_->task_worker_num == 0) { + return; + } + server_->gs->task_workers.shutdown(); +} + +pid_t ProcessFactory::spawn_event_worker(Worker *worker) { + pid_t pid = swoole_fork(0); + + if (pid < 0) { + swoole_sys_warning("failed to fork event worker"); + return SW_ERR; + } else if (pid == 0) { + worker->pid = SwooleG.pid; + } else { + worker->pid = pid; + return pid; + } + + if (server_->is_base_mode()) { + server_->gs->connection_nums[worker->id] = 0; + server_->gs->event_workers.main_loop(&server_->gs->event_workers, worker); + } else { + server_->start_event_worker(worker); + } + + exit(0); + return 0; +} + +pid_t ProcessFactory::spawn_user_worker(Worker *worker) { + pid_t pid = swoole_fork(0); + if (worker->pid) { + server_->user_worker_map.erase(worker->pid); + } + if (pid < 0) { + swoole_sys_warning("Fork Worker failed"); + return SW_ERR; + } + // child + else if (pid == 0) { + swoole_set_process_type(SW_PROCESS_USERWORKER); + swoole_set_process_id(worker->id); + SwooleWG.worker = worker; + worker->pid = SwooleG.pid; + server_->onUserWorkerStart(server_, worker); + exit(0); + } + // parent + else { + /** + * worker: local memory + * user_workers: shared memory + */ + server_->get_worker(worker->id)->pid = worker->pid = pid; + server_->user_worker_map.emplace(std::make_pair(pid, worker)); + return pid; + } +} + +pid_t ProcessFactory::spawn_task_worker(Worker *worker) { + return server_->gs->task_workers.spawn(worker); +} + +void ProcessFactory::check_worker_exit_status(Worker *worker, const ExitStatus &exit_status) { + if (exit_status.get_status() != 0) { + swoole_warning("worker(pid=%d, id=%d) abnormal exit, status=%d, signal=%d" + "%s", + exit_status.get_pid(), + worker->id, + exit_status.get_code(), + exit_status.get_signal(), + exit_status.get_signal() == SIGSEGV ? SwooleG.bug_report_message.c_str() : ""); + + if (server_->onWorkerError != nullptr) { + server_->onWorkerError(server_, worker, exit_status); + } + } +} + bool ProcessFactory::shutdown() { int status; diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 10175355eb1..01d8558e21c 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -26,7 +26,6 @@ using std::unordered_map; namespace swoole { using namespace network; -static void ReactorThread_loop(Server *serv, int reactor_id); static int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev); static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev); static int ReactorThread_onRead(Reactor *reactor, Event *ev); @@ -670,21 +669,7 @@ int Server::start_reactor_threads() { } SW_LOOP_N(reactor_num) { - if (is_thread_mode()) { - get_thread(i)->thread = std::thread([=]() { - swoole_set_process_id(i); - swoole_set_process_type(SW_PROCESS_EVENTWORKER); - swoole_set_thread_id(i); - swoole_set_thread_type(Server::THREAD_REACTOR); - SwooleWG.worker = get_worker(i); - worker_thread_start([=](void) -> bool { - ReactorThread_loop(this, i); - return true; - }); - }); - } else { - get_thread(i)->thread = std::thread(ReactorThread_loop, this, i); - } + get_thread(i)->thread = std::thread(reactor_thread_main_loop, this, i); } _init_master_thread: @@ -791,10 +776,7 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { return SW_OK; } -/** - * ReactorThread main Loop - */ -static void ReactorThread_loop(Server *serv, int reactor_id) { +void Server::reactor_thread_main_loop(Server *serv, int reactor_id) { SwooleTG.id = reactor_id; SwooleTG.type = Server::THREAD_REACTOR; swoole_thread_init(); diff --git a/src/server/thread.cc b/src/server/thread.cc index e6cfaed2b6f..ae6f5d276a8 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -17,8 +17,6 @@ #include "swoole_server.h" #include "swoole_memory.h" -#include - namespace swoole { using network::Socket; @@ -29,9 +27,6 @@ Factory *Server::create_thread_factory() { swoole_sys_warning("calloc[2](%d) failed", (int) (max_connection * sizeof(Connection))); return nullptr; } - /** - * init reactor thread pool - */ reactor_threads = new ReactorThread[reactor_num](); reactor_pipe_num = 1; return new ThreadFactory(this); @@ -42,7 +37,9 @@ void Server::destroy_thread_factory() { delete[] reactor_threads; } -ThreadFactory::ThreadFactory(Server *server) : BaseFactory(server) {} +ThreadFactory::ThreadFactory(Server *server) : BaseFactory(server) { + threads_.resize(server_->task_worker_num + server_->worker_num + server_->get_user_worker_num() + 1); +} bool ThreadFactory::start() { return server_->create_worker_pipes(); @@ -52,202 +49,166 @@ bool ThreadFactory::shutdown() { return true; } -ThreadFactory::~ThreadFactory() {} - -struct WorkerThreads { - std::vector threads_; - std::mutex lock_; - std::condition_variable cv_; - std::queue queue_; - Server *server_; - - WorkerThreads(Server *server) { - server_ = server; - threads_.resize(server_->task_worker_num + server_->worker_num + server_->get_user_worker_num()); +ThreadFactory::~ThreadFactory() { + for (auto &thread : threads_) { + thread.join(); } +} - ~WorkerThreads() { - for (auto &thread : threads_) { - thread.join(); - } - } +void ThreadFactory::at_thread_exit(Worker *worker) { + std::unique_lock _lock(lock_); + queue_.push(worker); + cv_.notify_one(); +} - void worker_exit(Worker *worker) { - std::unique_lock _lock(lock_); - queue_.push(worker); - cv_.notify_one(); +template +void ThreadFactory::create_thread(int i, _Callable fn) { + if (threads_[i].joinable()) { + threads_[i].join(); } + threads_[i] = std::thread(fn); +} - template - void create_thread(int i, _Callable fn) { - if (threads_[i].joinable()) { - threads_[i].join(); - } - threads_[i] = std::thread(fn); - } +void ThreadFactory::spawn_event_worker(int i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_EVENTWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + Worker *worker = server_->get_worker(i); + SwooleWG.worker = worker; + worker->type = SW_PROCESS_EVENTWORKER; + server_->worker_thread_start([=](void) -> void { Server::reactor_thread_main_loop(server_, i); }); + at_thread_exit(worker); + }); +} - void spawn_event_worker(int i) { - create_thread(i, [=]() { - swoole_set_process_type(SW_PROCESS_EVENTWORKER); - swoole_set_process_id(i); - Worker *worker = server_->get_worker(i); - worker->type = SW_PROCESS_EVENTWORKER; - server_->worker_thread_start( - [=](void) -> bool { return server_->worker_main_loop(&server_->gs->event_workers, worker) == SW_OK; }); - worker_exit(worker); - }); - } +void ThreadFactory::spawn_task_worker(int i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_TASKWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + Worker *worker = server_->get_worker(i); + worker->type = SW_PROCESS_TASKWORKER; + server_->worker_thread_start( + [=](void) { server_->gs->task_workers.main_loop(&server_->gs->task_workers, worker); }); + at_thread_exit(worker); + }); +} - void spawn_task_worker(int i) { - create_thread(i, [=]() { - swoole_set_process_type(SW_PROCESS_TASKWORKER); - swoole_set_process_id(i); - Worker *worker = server_->get_worker(i); - worker->type = SW_PROCESS_TASKWORKER; - server_->worker_thread_start([=](void) -> bool { - return server_->gs->task_workers.main_loop(&server_->gs->task_workers, worker) == SW_OK; - }); - worker_exit(worker); - }); - } +void ThreadFactory::spawn_user_worker(int i) { + create_thread(i, [=]() { + Worker *worker = server_->user_worker_list.at(i - server_->task_worker_num - server_->worker_num); + swoole_set_process_type(SW_PROCESS_USERWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + worker->type = SW_PROCESS_USERWORKER; + server_->worker_thread_start([=](void) { server_->onUserWorkerStart(server_, worker); }); + at_thread_exit(worker); + }); +} - void spawn_user_worker(int i) { - create_thread(i, [=]() { - Worker *worker = server_->user_worker_list.at(i - server_->task_worker_num - server_->worker_num); - swoole_set_process_type(SW_PROCESS_USERWORKER); - swoole_set_process_id(i); - worker->type = SW_PROCESS_USERWORKER; - server_->worker_thread_start([=](void) -> bool { - server_->onUserWorkerStart(server_, worker); - return SW_OK; - }); - worker_exit(worker); +void ThreadFactory::spawn_manager_thread(int i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_MANAGER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + manager.id = i; + manager.type = SW_PROCESS_MANAGER; + server_->worker_thread_start([=](void) { + if (server_->onManagerStart) { + server_->onManagerStart(server_); + } + wait(); + if (server_->onManagerStop) { + server_->onManagerStop(server_); + } }); - } + at_thread_exit(&manager); + }); +} - void wait() { - while (server_->running) { - std::unique_lock _lock(lock_); - if (!queue_.empty()) { - Worker *exited_worker = queue_.front(); - queue_.pop(); - switch (exited_worker->type) { - case SW_PROCESS_EVENTWORKER: - spawn_event_worker(exited_worker->id); - break; - case SW_PROCESS_TASKWORKER: - spawn_task_worker(exited_worker->id); - break; - case SW_PROCESS_USERWORKER: - spawn_user_worker(exited_worker->id); - break; - default: - abort(); - break; - } - _lock.unlock(); - } else { - cv_.wait(_lock); +void ThreadFactory::wait() { + while (server_->running) { + std::unique_lock _lock(lock_); + if (!queue_.empty()) { + Worker *exited_worker = queue_.front(); + queue_.pop(); + switch (exited_worker->type) { + case SW_PROCESS_EVENTWORKER: + spawn_event_worker(exited_worker->id); + break; + case SW_PROCESS_TASKWORKER: + spawn_task_worker(exited_worker->id); + break; + case SW_PROCESS_USERWORKER: + spawn_user_worker(exited_worker->id); + break; + case SW_PROCESS_MANAGER: + swoole_warning("Fatal Error: manager process exit"); + break; + default: + abort(); + break; } + _lock.unlock(); + } else { + cv_.wait(_lock); } } -}; +} int Server::start_worker_threads() { - single_thread = 1; - swoole_set_process_type(SW_PROCESS_MANAGER); - - // listen TCP - if (have_stream_sock == 1) { - for (auto ls : ports) { - if (ls->is_dgram()) { - continue; - } -#ifdef HAVE_REUSEPORT - if (enable_reuse_port) { - if (::close(ls->socket->fd) < 0) { - swoole_sys_warning("close(%d) failed", ls->socket->fd); - } - delete ls->socket; - ls->socket = nullptr; - continue; - } else -#endif - { - // listen server socket - if (ls->listen() < 0) { - return SW_ERR; - } - } - } - } - - ProcessPool *pool = &gs->event_workers; - *pool = {}; - if (pool->create(worker_num, 0, SW_IPC_UNIXSOCK) < 0) { - return SW_ERR; - } - pool->set_max_request(max_request, max_request_grace); - /** - * store to ProcessPool object + * heartbeat thread */ - gs->event_workers.ptr = this; - gs->event_workers.max_wait_time = max_wait_time; - gs->event_workers.use_msgqueue = 0; - gs->event_workers.main_loop = worker_main_loop; - memcpy(workers, gs->event_workers.workers, sizeof(*workers) * worker_num); - gs->event_workers.workers = workers; - - SW_LOOP_N(worker_num) { - gs->event_workers.workers[i].pool = &gs->event_workers; - gs->event_workers.workers[i].id = i; - gs->event_workers.workers[i].type = SW_PROCESS_WORKER; - } - - init_ipc_max_size(); - if (create_pipe_buffers() < 0) { - return SW_ERR; - } - - SW_LOOP_N(worker_num) { - create_worker(get_worker(i)); + if (heartbeat_check_interval >= 1) { + start_heartbeat_thread(); } - if (gs->event_workers.create_message_box(SW_MESSAGE_BOX_SIZE) == SW_ERR) { - return SW_ERR; - } - - if (task_worker_num > 0 && create_task_workers() < 0) { - return SW_ERR; - } - - if (get_user_worker_num() > 0 && create_user_workers() < 0) { - return SW_ERR; - } - - WorkerThreads worker_threads(this); + ThreadFactory *_factory = dynamic_cast(factory); if (task_worker_num > 0) { SW_LOOP_N(task_worker_num) { - worker_threads.spawn_task_worker(worker_num + i); + _factory->spawn_task_worker(worker_num + i); } } SW_LOOP_N(worker_num) { - worker_threads.spawn_event_worker(i); + _factory->spawn_event_worker(i); } if (!user_worker_list.empty()) { int i = 0; for (auto worker : user_worker_list) { - worker_threads.spawn_user_worker(task_worker_num + worker_num + i); + _factory->spawn_user_worker(task_worker_num + worker_num + i); i++; } } - worker_threads.wait(); + int manager_thread_id = task_worker_num + worker_num + get_user_worker_num(); + _factory->spawn_manager_thread(manager_thread_id); - return SW_OK; + if (swoole_event_init(0) < 0) { + return SW_ERR; + } + Reactor *reactor = sw_reactor(); + for (auto iter = ports.begin(); iter != ports.end(); iter++) { + auto port = *iter; + if (port->is_dgram()) { + continue; + } + if (port->listen() < 0) { + swoole_event_free(); + return SW_ERR; + } + reactor->add(port->socket, SW_EVENT_READ); + } + SwooleTG.id = reactor->id = manager_thread_id + 1; + store_listen_socket(); + return start_master_thread(reactor); } } // namespace swoole From b2fe9712eac8a9c3a5852e268a3cd934e800123b Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 8 Apr 2024 11:40:30 +0800 Subject: [PATCH 044/103] Refactor 2 --- examples/thread/thread_server.php | 12 +++++++----- ext-src/swoole_admin_server.cc | 2 +- ext-src/swoole_http_server.cc | 8 ++++---- ext-src/swoole_process.cc | 2 +- ext-src/swoole_server.cc | 22 +++++++++++----------- include/swoole_process_pool.h | 20 +++++++++++--------- include/swoole_server.h | 4 ++++ scripts/make.sh | 1 + src/core/log.cc | 4 ++-- src/os/process_pool.cc | 2 +- src/server/base.cc | 25 +++++++++++++++++++------ src/server/master.cc | 16 ++++++++-------- src/server/process.cc | 6 +++--- src/server/reactor_process.cc | 8 ++++---- src/server/task_worker.cc | 16 ++++++++-------- src/server/thread.cc | 2 +- src/server/worker.cc | 26 +++++++++++++------------- 17 files changed, 99 insertions(+), 77 deletions(-) diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php index bafd0fce162..e2bfc76e13c 100644 --- a/examples/thread/thread_server.php +++ b/examples/thread/thread_server.php @@ -1,9 +1,11 @@ set([ - 'worker_num' => 2, + 'worker_num' => 4, // 'task_worker_num' => 3, 'enable_coroutine' => false, +// 'trace_flags' => SWOOLE_TRACE_SERVER, +// 'log_level' => SWOOLE_LOG_TRACE, 'init_arguments' => function () use ($http) { $map = new Swoole\Thread\Map; return [$map]; @@ -15,10 +17,10 @@ $resp->end('hello world'); }); -$http->addProcess(new \Swoole\Process(function () { - echo "user process, id=" . \Swoole\Thread::getId(); - sleep(2000); -})); +//$http->addProcess(new \Swoole\Process(function () { +// echo "user process, id=" . \Swoole\Thread::getId(); +// sleep(2000); +//})); $http->on('Task', function () { var_dump(func_get_args()); diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index 05110efe92c..7c322443532 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -421,7 +421,7 @@ static std::string handle_get_connections(Server *serv, const std::string &msg) if (serv->is_process_mode() && conn->reactor_id != SwooleTG.id) { return; } - if (serv->is_base_mode() && SwooleWG.worker && conn->reactor_id != SwooleWG.worker->id) { + if (serv->is_base_mode() && sw_worker() && conn->reactor_id != sw_worker()->id) { return; } list.push_back(get_connection_info(serv, conn)); diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 2d87a67bdb2..c4a4a85e2ea 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -394,10 +394,10 @@ bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { ctx->onBeforeRequest = nullptr; ctx->onAfterResponse = swoole_http_server_onAfterResponse; Server *serv = (Server *) ctx->private_data; - SwooleWG.worker->concurrency++; + sw_worker()->concurrency++; sw_atomic_add_fetch(&serv->gs->concurrency, 1); swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); - if (SwooleWG.worker->concurrency > serv->worker_max_concurrency) { + if (sw_worker()->concurrency > serv->worker_max_concurrency) { swoole_trace_log(SW_TRACE_COROUTINE, "exceed worker_max_concurrency[%u] limit, request[%p] queued", serv->worker_max_concurrency, @@ -412,13 +412,13 @@ bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { void swoole_http_server_onAfterResponse(HttpContext *ctx) { ctx->onAfterResponse = nullptr; Server *serv = (Server *) ctx->private_data; - SwooleWG.worker->concurrency--; + sw_worker()->concurrency--; sw_atomic_sub_fetch(&serv->gs->concurrency, 1); swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); if (!queued_http_contexts.empty()) { HttpContext *ctx = queued_http_contexts.front(); swoole_trace( - "[POP 1] concurrency=%u, ctx=%p, request=%p", SwooleWG.worker->concurrency, ctx, ctx->request.zobject); + "[POP 1] concurrency=%u, ctx=%p, request=%p", sw_worker()->concurrency, ctx, ctx->request.zobject); queued_http_contexts.pop(); swoole_event_defer( [](void *private_data) { diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 6d76b46ae21..1ffebed1adc 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -646,7 +646,7 @@ int php_swoole_process_start(Worker *process, zval *zobject) { php_swoole_process_clean(); swoole_set_process_id(process->id); - SwooleWG.worker = process; + g_worker_instance = process; zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("pid"), process->pid); if (process->pipe_current) { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index dc1aa86acf1..7a3862f8f5f 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1535,10 +1535,10 @@ static void php_swoole_server_onAfterReload(Server *serv) { } static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { - if (SwooleWG.shutdown) { + if (worker->shutdown) { return; } - SwooleWG.shutdown = true; + worker->shutdown = true; zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); @@ -2898,10 +2898,10 @@ static PHP_METHOD(swoole_server, stats) { add_assoc_long_ex(return_value, ZEND_STRL("min_fd"), serv->gs->min_fd); add_assoc_long_ex(return_value, ZEND_STRL("max_fd"), serv->gs->max_fd); - if (SwooleWG.worker) { - add_assoc_long_ex(return_value, ZEND_STRL("worker_request_count"), SwooleWG.worker->request_count); - add_assoc_long_ex(return_value, ZEND_STRL("worker_response_count"), SwooleWG.worker->response_count); - add_assoc_long_ex(return_value, ZEND_STRL("worker_dispatch_count"), SwooleWG.worker->dispatch_count); + if (sw_worker()) { + add_assoc_long_ex(return_value, ZEND_STRL("worker_request_count"), sw_worker()->request_count); + add_assoc_long_ex(return_value, ZEND_STRL("worker_response_count"), sw_worker()->response_count); + add_assoc_long_ex(return_value, ZEND_STRL("worker_dispatch_count"), sw_worker()->dispatch_count); } if (serv->task_ipc_mode > Server::TASK_IPC_UNIXSOCK && serv->gs->task_workers.queue) { @@ -3802,7 +3802,7 @@ static PHP_METHOD(swoole_server, getWorkerId) { if (!serv->is_worker() && !serv->is_task_worker()) { RETURN_FALSE; } else { - RETURN_LONG(SwooleWG.worker->id); + RETURN_LONG(sw_worker()->id); } } @@ -3820,7 +3820,7 @@ static PHP_METHOD(swoole_server, getWorkerStatus) { Worker *worker; if (worker_id == -1) { - worker = SwooleWG.worker; + worker = sw_worker(); } else { worker = serv->get_worker(worker_id); } @@ -3838,7 +3838,7 @@ static PHP_METHOD(swoole_server, getWorkerPid) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &worker_id) == FAILURE) { RETURN_FALSE; } - Worker *worker = worker_id < 0 ? SwooleWG.worker : serv->get_worker(worker_id); + Worker *worker = worker_id < 0 ? sw_worker() : serv->get_worker(worker_id); if (!worker) { RETURN_FALSE; } @@ -3885,7 +3885,7 @@ static PHP_METHOD(swoole_server, stop) { } zend_bool wait_reactor = 0; - zend_long worker_id = SwooleWG.worker->id; + zend_long worker_id = sw_worker()->id; ZEND_PARSE_PARAMETERS_START(0, 2) Z_PARAM_OPTIONAL @@ -3893,7 +3893,7 @@ static PHP_METHOD(swoole_server, stop) { Z_PARAM_BOOL(wait_reactor) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - if (worker_id == SwooleWG.worker->id && wait_reactor == 0) { + if (worker_id == sw_worker()->id && wait_reactor == 0) { if (SwooleTG.reactor != nullptr) { SwooleTG.reactor->defer( [](void *data) { diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index eec5ac30154..9623d89fc2e 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -102,14 +102,6 @@ static inline ExitStatus wait_process(pid_t _pid, int options) { struct ProcessPool; struct Worker; -struct WorkerGlobal { - bool run_always; - bool shutdown; - uint32_t max_request; - Worker *worker; - time_t exit_time; -}; - struct Worker { pid_t pid; WorkerId id; @@ -120,6 +112,11 @@ struct Worker { bool redirect_stdin; bool redirect_stderr; + bool run_always; + bool shutdown; + uint32_t max_request; + time_t exit_time; + /** * worker status, IDLE or BUSY */ @@ -325,5 +322,10 @@ static sw_inline int swoole_kill(pid_t __pid, int __sig) { return kill(__pid, __sig); } -extern SW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG; // Worker Global Variable typedef swoole::ProtocolType swProtocolType; + +extern SW_THREAD_LOCAL swoole::Worker *g_worker_instance; + +static inline swoole::Worker *sw_worker() { + return g_worker_instance; +} diff --git a/include/swoole_server.h b/include/swoole_server.h index 0595e21ca3f..a58d6562692 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1089,6 +1089,10 @@ class Server { #endif } + bool if_forward_message(Session *session) { + return session->reactor_id != swoole_get_process_id(); + } + Worker *get_worker(uint16_t worker_id) { // Event Worker if (worker_id < worker_num) { diff --git a/scripts/make.sh b/scripts/make.sh index fd46c8c9d90..2ba131ef928 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -8,6 +8,7 @@ COMPILE_PARAMS="--enable-openssl \ --enable-cares \ --enable-swoole-pgsql \ --enable-swoole-thread \ +--enable-trace-log \ --with-swoole-odbc=unixODBC,/usr \ --enable-swoole-sqlite" diff --git a/src/core/log.cc b/src/core/log.cc index 987c9725cc7..f2a58ef3c4e 100644 --- a/src/core/log.cc +++ b/src/core/log.cc @@ -317,11 +317,11 @@ void Logger::put(int level, const char *content, size_t length) { n = sw_snprintf(log_str, SW_LOG_BUFFER_SIZE, - "[%.*s %c%d.%d]\t%s\t%.*s\n", + "[%.*s %c%ld.%d]\t%s\t%.*s\n", static_cast(l_data_str), date_str, process_flag, - SwooleG.pid, + pthread_self(), process_id, level_str, static_cast(length), diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index a43ef844229..055eebdd584 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -536,7 +536,7 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker out.mtype = worker->id + 1; } - while (pool->running && !SwooleWG.shutdown && task_n > 0) { + while (pool->running && !worker->shutdown && task_n > 0) { /** * fetch task */ diff --git a/src/server/base.cc b/src/server/base.cc index 928fe632766..11995ae07fa 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -56,7 +56,7 @@ BaseFactory::BaseFactory(Server *server) : Factory(server) {} BaseFactory::~BaseFactory() {} bool BaseFactory::start() { - SwooleWG.run_always = true; + sw_worker()->run_always = true; return true; } @@ -91,8 +91,14 @@ bool BaseFactory::dispatch(SendData *task) { } } - server_->message_bus.pass(task); - server_->worker_accept_event(&server_->message_bus.get_buffer()->info); +#ifdef SW_THREAD + MessageBus *bus = sw_likely(server_->is_thread_mode()) ? &server_->get_thread(swoole_get_thread_id())->message_bus + : &server_->message_bus; +#else + MessageBus *bus = &server_->message_bus; +#endif + bus->pass(task); + server_->worker_accept_event(&bus->get_buffer()->info); return true; } @@ -137,7 +143,11 @@ bool BaseFactory::end(SessionId session_id, int flags) { return false; } - if (session->reactor_id != swoole_get_process_id()) { + if (server_->if_forward_message(session)) { + swoole_trace_log(SW_TRACE_SERVER, "session_id=%ld, fd=%d, session->reactor_id=%d", + session_id, + session->fd, + session->reactor_id); Worker *worker = server_->get_worker(session->reactor_id); if (worker->pipe_master->send_async((const char *) &_send.info, sizeof(_send.info)) < 0) { swoole_sys_warning("failed to send %lu bytes to pipe_master", sizeof(_send.info)); @@ -204,8 +214,11 @@ bool BaseFactory::finish(SendData *data) { SessionId session_id = data->info.fd; Session *session = server_->get_session(session_id); - if (session->reactor_id != swoole_get_process_id()) { - swoole_trace("session->reactor_id=%d, SwooleG.process_id=%d", session->reactor_id, server_->get_worker_id()); + if (server_->if_forward_message(session)) { + swoole_trace_log(SW_TRACE_SERVER, "session_id=%ld, fd=%d, session->reactor_id=%d", + session_id, + session->fd, + session->reactor_id); Worker *worker = server_->gs->event_workers.get_worker(session->reactor_id); EventData proxy_msg{}; diff --git a/src/server/master.cc b/src/server/master.cc index 371b114f005..d8b1300e3f5 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -562,11 +562,11 @@ void Server::destroy_worker(Worker *worker) { */ void Server::init_worker(Worker *worker) { if (max_request < 1) { - SwooleWG.run_always = true; + worker->run_always = true; } else { - SwooleWG.max_request = max_request; + worker->max_request = max_request; if (max_request_grace > 0) { - SwooleWG.max_request += swoole_system_random(1, max_request_grace); + worker->max_request += swoole_system_random(1, max_request_grace); } } worker->start_time = ::time(nullptr); @@ -878,7 +878,7 @@ bool Server::shutdown() { } } else { gs->event_workers.running = 0; - stop_async_worker(SwooleWG.worker); + stop_async_worker(sw_worker()); return true; } } @@ -1051,7 +1051,7 @@ bool Server::command(WorkerId process_id, if (is_process_mode() && !is_master()) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "command() can only be used in master process"); return false; - } else if (is_base_mode() && SwooleWG.worker->id != 0) { + } else if (is_base_mode() && sw_worker()->id != 0) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_INVALID_PARAMS, "command() can only be used in worker process 0"); return false; } @@ -1185,8 +1185,8 @@ bool Server::send(SessionId session_id, const void *data, uint32_t length) { sw_atomic_fetch_add(&port->gs->response_count, 1); sw_atomic_fetch_add(&port->gs->total_send_bytes, length); } - if (SwooleWG.worker) { - SwooleWG.worker->response_count++; + if (sw_worker()) { + sw_worker()->response_count++; } return true; } @@ -1302,7 +1302,7 @@ int Server::send_to_connection(SendData *_send) { assert(fd % reactor_num == SwooleTG.id); } - if (is_base_mode() && conn->overflow) { + if (!is_process_mode() && conn->overflow) { if (send_yield) { swoole_set_last_error(SW_ERROR_OUTPUT_SEND_YIELD); } else { diff --git a/src/server/process.cc b/src/server/process.cc index fd5dd77d331..ee5f092f0b9 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -81,11 +81,11 @@ void ProcessFactory::kill_event_workers() { } SW_LOOP_N(server_->worker_num) { - swoole_trace("kill worker#%d[pid=%d]", workers[i].id, workers[i].pid); + swoole_trace_log(SW_TRACE_SERVER, "kill worker#%d[pid=%d]", server_->workers[i].id, server_->workers[i].pid); swoole_kill(server_->workers[i].pid, SIGTERM); } SW_LOOP_N(server_->worker_num) { - swoole_trace("wait worker#%d[pid=%d]", workers[i].id, workers[i].pid); + swoole_trace_log(SW_TRACE_SERVER, "wait worker#%d[pid=%d]", server_->workers[i].id, server_->workers[i].pid); if (swoole_waitpid(server_->workers[i].pid, &status, 0) < 0) { swoole_sys_warning("waitpid(%d) failed", server_->workers[i].pid); } @@ -139,7 +139,7 @@ pid_t ProcessFactory::spawn_user_worker(Worker *worker) { else if (pid == 0) { swoole_set_process_type(SW_PROCESS_USERWORKER); swoole_set_process_id(worker->id); - SwooleWG.worker = worker; + g_worker_instance = worker; worker->pid = SwooleG.pid; server_->onUserWorkerStart(server_, worker); exit(0); diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 158f7a450fa..81ba448366f 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -145,7 +145,7 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { break; } case SW_SERVER_EVENT_COMMAND_REQUEST: { - serv->call_command_handler(serv->message_bus, SwooleWG.worker->id, serv->get_worker(0)->pipe_master); + serv->call_command_handler(serv->message_bus, sw_worker()->id, serv->get_worker(0)->pipe_master); break; } case SW_SERVER_EVENT_COMMAND_RESPONSE: { @@ -170,10 +170,10 @@ int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { swoole_set_process_id(worker->id); if (serv->max_request > 0) { - SwooleWG.run_always = false; + worker->run_always = false; } - SwooleWG.max_request = serv->max_request; - SwooleWG.worker = worker; + worker->max_request = serv->max_request; + g_worker_instance = worker; SwooleTG.id = 0; serv->init_worker(worker); diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index b0faef13736..03d3912bd6e 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -72,7 +72,7 @@ static int TaskWorker_call_command_handler(ProcessPool *pool, EventData *req) { SendData task{}; task.info.fd = req->info.fd; - task.info.reactor_id = SwooleWG.worker->id; + task.info.reactor_id = sw_worker()->id; task.info.server_fd = -1; task.info.type = SW_SERVER_EVENT_COMMAND_RESPONSE; task.info.len = result.length(); @@ -173,6 +173,7 @@ static void TaskWorker_signal_init(ProcessPool *pool) { static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; swoole_set_process_id(worker->id); + g_worker_instance = worker; /** * Make the task worker support asynchronous @@ -193,16 +194,15 @@ static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { worker->start_time = ::time(nullptr); worker->request_count = 0; - SwooleWG.worker = worker; - SwooleWG.worker->status = SW_WORKER_IDLE; + worker->status = SW_WORKER_IDLE; /** * task_max_request */ if (pool->max_request > 0) { - SwooleWG.run_always = false; - SwooleWG.max_request = pool->get_max_request(); + worker->run_always = false; + worker->max_request = pool->get_max_request(); } else { - SwooleWG.run_always = true; + worker->run_always = true; } } @@ -218,7 +218,7 @@ static void TaskWorker_onStop(ProcessPool *pool, Worker *worker) { static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) { EventData task; ProcessPool *pool = (ProcessPool *) reactor->ptr; - Worker *worker = SwooleWG.worker; + Worker *worker = sw_worker(); Server *serv = (Server *) pool->ptr; if (event->socket->read(&task, sizeof(task)) > 0) { @@ -227,7 +227,7 @@ static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) { worker->status = SW_WORKER_IDLE; worker->request_count++; // maximum number of requests, process will exit. - if (!SwooleWG.run_always && worker->request_count >= SwooleWG.max_request) { + if (!worker->run_always && worker->request_count >= worker->max_request) { serv->stop_async_worker(worker); } return retval; diff --git a/src/server/thread.cc b/src/server/thread.cc index ae6f5d276a8..e08bfd5a30c 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -76,7 +76,7 @@ void ThreadFactory::spawn_event_worker(int i) { swoole_set_process_id(i); swoole_set_thread_id(i); Worker *worker = server_->get_worker(i); - SwooleWG.worker = worker; + g_worker_instance = worker; worker->type = SW_PROCESS_EVENTWORKER; server_->worker_thread_start([=](void) -> void { Server::reactor_thread_main_loop(server_, i); }); at_thread_exit(worker); diff --git a/src/server/worker.cc b/src/server/worker.cc index 0c615035900..4aeaac68252 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -24,7 +24,7 @@ #include "swoole_msg_queue.h" #include "swoole_coroutine.h" -SW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG = {}; +SW_THREAD_LOCAL swoole::Worker *g_worker_instance; namespace swoole { using namespace network; @@ -49,18 +49,18 @@ void Server::worker_signal_init(void) { } void Server::worker_signal_handler(int signo) { - if (!SwooleG.running || !sw_server()) { + if (!SwooleG.running || !sw_server() || !sw_worker()) { return; } switch (signo) { case SIGTERM: // Event worker if (swoole_event_is_available()) { - sw_server()->stop_async_worker(SwooleWG.worker); + sw_server()->stop_async_worker(sw_worker()); } // Task worker else { - SwooleWG.shutdown = true; + sw_worker()->shutdown = true; } break; // for test @@ -121,7 +121,7 @@ static sw_inline void Worker_do_task(Server *serv, Worker *worker, DataHead *inf } void Server::worker_accept_event(DataHead *info) { - Worker *worker = SwooleWG.worker; + Worker *worker = sw_worker(); // worker busy worker->status = SW_WORKER_BUSY; @@ -211,7 +211,7 @@ void Server::worker_accept_event(DataHead *info) { worker->status = SW_WORKER_IDLE; // maximum number of requests, process will exit. - if (!SwooleWG.run_always && worker->request_count >= SwooleWG.max_request) { + if (!worker->run_always && worker->request_count >= worker->max_request) { stop_async_worker(worker); } } @@ -276,7 +276,7 @@ void Server::worker_start_callback(Worker *worker) { sw_logger()->reopen(); } - SwooleWG.worker = worker; + g_worker_instance = worker; worker->status = SW_WORKER_IDLE; if (is_process_mode()) { @@ -322,9 +322,9 @@ void Server::stop_async_worker(Worker *worker) { } // Separated from the event worker process pool - worker = (Worker *) sw_malloc(sizeof(*worker)); - *worker = *SwooleWG.worker; - SwooleWG.worker = worker; + Worker *worker_copy = (Worker *) sw_malloc(sizeof(*worker)); + *worker_copy = *worker; + g_worker_instance = worker_copy; if (worker->pipe_worker && !worker->pipe_worker->removed) { reactor->remove_read_event(worker->pipe_worker); @@ -365,7 +365,7 @@ void Server::stop_async_worker(Worker *worker) { reactor->set_wait_exit(true); reactor->set_end_callback(Reactor::PRIORITY_TRY_EXIT, Worker_reactor_try_to_exit); - SwooleWG.exit_time = ::time(nullptr); + worker->exit_time = ::time(nullptr); Worker_reactor_try_to_exit(reactor); if (!reactor->running) { @@ -389,11 +389,11 @@ static void Worker_reactor_try_to_exit(Reactor *reactor) { break; } else { if (serv->onWorkerExit && call_worker_exit_func == 0) { - serv->onWorkerExit(serv, SwooleWG.worker); + serv->onWorkerExit(serv, sw_worker()); call_worker_exit_func = 1; continue; } - int remaining_time = serv->max_wait_time - (::time(nullptr) - SwooleWG.exit_time); + int remaining_time = serv->max_wait_time - (::time(nullptr) - sw_worker()->exit_time); if (remaining_time <= 0) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, "worker exit timeout, forced termination"); From 993bcdb5e2b392933abdfce3d0688152c0d3d70e Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 8 Apr 2024 11:51:49 +0800 Subject: [PATCH 045/103] revert , format --- scripts/make.sh | 1 - src/core/base64.cc | 2 +- src/core/log.cc | 4 ++-- src/core/string.cc | 13 ++++++++++--- src/os/msg_queue.cc | 1 - src/os/process_pool.cc | 2 +- src/os/unix_socket.cc | 5 ++--- src/server/base.cc | 18 ++++++++++-------- src/server/thread.cc | 10 +++++----- 9 files changed, 31 insertions(+), 25 deletions(-) diff --git a/scripts/make.sh b/scripts/make.sh index 2ba131ef928..fd46c8c9d90 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -8,7 +8,6 @@ COMPILE_PARAMS="--enable-openssl \ --enable-cares \ --enable-swoole-pgsql \ --enable-swoole-thread \ ---enable-trace-log \ --with-swoole-odbc=unixODBC,/usr \ --enable-swoole-sqlite" diff --git a/src/core/base64.cc b/src/core/base64.cc index dd9b452b353..0c430bfbf24 100644 --- a/src/core/base64.cc +++ b/src/core/base64.cc @@ -131,4 +131,4 @@ size_t base64_decode(const char *in, size_t inlen, char *out) { return j; } -} +} // namespace swoole diff --git a/src/core/log.cc b/src/core/log.cc index f2a58ef3c4e..987c9725cc7 100644 --- a/src/core/log.cc +++ b/src/core/log.cc @@ -317,11 +317,11 @@ void Logger::put(int level, const char *content, size_t length) { n = sw_snprintf(log_str, SW_LOG_BUFFER_SIZE, - "[%.*s %c%ld.%d]\t%s\t%.*s\n", + "[%.*s %c%d.%d]\t%s\t%.*s\n", static_cast(l_data_str), date_str, process_flag, - pthread_self(), + SwooleG.pid, process_id, level_str, static_cast(length), diff --git a/src/core/string.cc b/src/core/string.cc index fab2edb13df..e6b50065fc0 100644 --- a/src/core/string.cc +++ b/src/core/string.cc @@ -180,7 +180,12 @@ ssize_t String::split(const char *delimiter, size_t delimiter_length, const Stri off_t _offset = offset; size_t ret; - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[0] count=%d, length=%ld, size=%ld, offset=%jd", count, length, size, (intmax_t) offset); + swoole_trace_log(SW_TRACE_EOF_PROTOCOL, + "#[0] count=%d, length=%ld, size=%ld, offset=%jd", + count, + length, + size, + (intmax_t) offset); while (delimiter_addr) { size_t _length = delimiter_addr - start_addr + delimiter_length; @@ -207,9 +212,11 @@ ssize_t String::split(const char *delimiter, size_t delimiter_length, const Stri ret = start_addr - str - _offset; if (ret > 0 && ret < length) { - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[5] count=%d, remaining_length=%zu", count, (size_t) (length - offset)); + swoole_trace_log( + SW_TRACE_EOF_PROTOCOL, "#[5] count=%d, remaining_length=%zu", count, (size_t) (length - offset)); } else if (ret >= length) { - swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[3] length=%ld, size=%zu, offset=%jd", length, size, (intmax_t) offset); + swoole_trace_log( + SW_TRACE_EOF_PROTOCOL, "#[3] length=%ld, size=%zu, offset=%jd", length, size, (intmax_t) offset); } return ret; diff --git a/src/os/msg_queue.cc b/src/os/msg_queue.cc index f40e2c1a151..89d98448a07 100644 --- a/src/os/msg_queue.cc +++ b/src/os/msg_queue.cc @@ -86,7 +86,6 @@ bool MsgQueue::push(QueueNode *in, size_t mdata_length) { } swoole_set_last_error(errno); break; - } return false; } diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 055eebdd584..7d7b668fd03 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -581,7 +581,7 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker continue; } - if (n != (ssize_t)(out.buf.info.len + sizeof(out.buf.info))) { + if (n != (ssize_t) (out.buf.info.len + sizeof(out.buf.info))) { swoole_warning("bad task packet, The received data-length[%ld] is inconsistent with the packet-length[%ld]", n, out.buf.info.len + sizeof(out.buf.info)); diff --git a/src/os/unix_socket.cc b/src/os/unix_socket.cc index 08b34422dfb..c1db228dedd 100644 --- a/src/os/unix_socket.cc +++ b/src/os/unix_socket.cc @@ -18,8 +18,7 @@ #include "swoole_socket.h" namespace swoole { -UnixSocket::UnixSocket(bool blocking, int _protocol) : - SocketPair(blocking), protocol_(_protocol) { +UnixSocket::UnixSocket(bool blocking, int _protocol) : SocketPair(blocking), protocol_(_protocol) { if (socketpair(AF_UNIX, protocol_, 0, socks) < 0) { swoole_sys_warning("socketpair() failed"); return; @@ -39,4 +38,4 @@ bool UnixSocket::set_buffer_size(size_t _size) { } return true; } -} +} // namespace swoole diff --git a/src/server/base.cc b/src/server/base.cc index 11995ae07fa..36231fe9d76 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -144,10 +144,11 @@ bool BaseFactory::end(SessionId session_id, int flags) { } if (server_->if_forward_message(session)) { - swoole_trace_log(SW_TRACE_SERVER, "session_id=%ld, fd=%d, session->reactor_id=%d", - session_id, - session->fd, - session->reactor_id); + swoole_trace_log(SW_TRACE_SERVER, + "session_id=%ld, fd=%d, session->reactor_id=%d", + session_id, + session->fd, + session->reactor_id); Worker *worker = server_->get_worker(session->reactor_id); if (worker->pipe_master->send_async((const char *) &_send.info, sizeof(_send.info)) < 0) { swoole_sys_warning("failed to send %lu bytes to pipe_master", sizeof(_send.info)); @@ -215,10 +216,11 @@ bool BaseFactory::finish(SendData *data) { Session *session = server_->get_session(session_id); if (server_->if_forward_message(session)) { - swoole_trace_log(SW_TRACE_SERVER, "session_id=%ld, fd=%d, session->reactor_id=%d", - session_id, - session->fd, - session->reactor_id); + swoole_trace_log(SW_TRACE_SERVER, + "session_id=%ld, fd=%d, session->reactor_id=%d", + session_id, + session->fd, + session->reactor_id); Worker *worker = server_->gs->event_workers.get_worker(session->reactor_id); EventData proxy_msg{}; diff --git a/src/server/thread.cc b/src/server/thread.cc index e08bfd5a30c..b7764f7a055 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -78,7 +78,7 @@ void ThreadFactory::spawn_event_worker(int i) { Worker *worker = server_->get_worker(i); g_worker_instance = worker; worker->type = SW_PROCESS_EVENTWORKER; - server_->worker_thread_start([=](void) -> void { Server::reactor_thread_main_loop(server_, i); }); + server_->worker_thread_start([=]() { Server::reactor_thread_main_loop(server_, i); }); at_thread_exit(worker); }); } @@ -91,8 +91,8 @@ void ThreadFactory::spawn_task_worker(int i) { swoole_set_thread_id(i); Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_TASKWORKER; - server_->worker_thread_start( - [=](void) { server_->gs->task_workers.main_loop(&server_->gs->task_workers, worker); }); + auto pool = &server_->gs->task_workers; + server_->worker_thread_start([=]() { pool->main_loop(pool, worker); }); at_thread_exit(worker); }); } @@ -105,7 +105,7 @@ void ThreadFactory::spawn_user_worker(int i) { swoole_set_process_id(i); swoole_set_thread_id(i); worker->type = SW_PROCESS_USERWORKER; - server_->worker_thread_start([=](void) { server_->onUserWorkerStart(server_, worker); }); + server_->worker_thread_start([=]() { server_->onUserWorkerStart(server_, worker); }); at_thread_exit(worker); }); } @@ -118,7 +118,7 @@ void ThreadFactory::spawn_manager_thread(int i) { swoole_set_thread_id(i); manager.id = i; manager.type = SW_PROCESS_MANAGER; - server_->worker_thread_start([=](void) { + server_->worker_thread_start([=]() { if (server_->onManagerStart) { server_->onManagerStart(server_); } From e824c8691239d3b8405708d7eea9bf18d8509d44 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 8 Apr 2024 16:51:13 +0800 Subject: [PATCH 046/103] onPipeMessage/onTask/onFinish, fix message bus --- examples/thread/argv.php | 14 ++++++++++++++ examples/thread/thread_server.php | 31 +++++++++++++++++++++++++------ ext-src/swoole_server.cc | 6 ++++-- include/swoole_process_pool.h | 1 + include/swoole_server.h | 10 +++++++++- src/os/process_pool.cc | 21 +++++++++++++-------- src/server/base.cc | 9 ++------- src/server/reactor_process.cc | 2 +- src/server/reactor_thread.cc | 10 +++++++++- src/server/task_worker.cc | 4 ++++ src/server/thread.cc | 30 ++++++++++++++++++++++++------ src/server/worker.cc | 25 +++++++++++++------------ 12 files changed, 119 insertions(+), 44 deletions(-) create mode 100644 examples/thread/argv.php diff --git a/examples/thread/argv.php b/examples/thread/argv.php new file mode 100644 index 00000000000..b489c93a18b --- /dev/null +++ b/examples/thread/argv.php @@ -0,0 +1,14 @@ +join(); +} else { + var_dump($args[0], $args[1], $args[2]); + sleep(1); +} diff --git a/examples/thread/thread_server.php b/examples/thread/thread_server.php index e2bfc76e13c..bfd00fa37cf 100644 --- a/examples/thread/thread_server.php +++ b/examples/thread/thread_server.php @@ -1,9 +1,10 @@ set([ - 'worker_num' => 4, -// 'task_worker_num' => 3, - 'enable_coroutine' => false, + 'worker_num' => 2, + 'task_worker_num' => 3, + 'enable_coroutine' => true, + 'hook_flags' => SWOOLE_HOOK_ALL, // 'trace_flags' => SWOOLE_TRACE_SERVER, // 'log_level' => SWOOLE_LOG_TRACE, 'init_arguments' => function () use ($http) { @@ -14,20 +15,38 @@ $http->on('Request', function ($req, $resp) use ($http) { // $resp->end("tid=" . \Swoole\Thread::getId() . ', fd=' . $req->fd); + if ($req->server['request_uri'] == '/task') { + $http->task(['code' => uniqid()]); + } elseif ($req->server['request_uri'] == '/msg') { + $dstWorkerId = random_int(0, 4); + if ($dstWorkerId != $http->getWorkerId()) { + $http->sendMessage('hello ' . base64_encode(random_bytes(16)), $dstWorkerId); + echo "[worker#" . $http->getWorkerId() . "]\tsend pipe message to " . $dstWorkerId . "\n"; + } + } $resp->end('hello world'); }); +$http->on('pipeMessage', function ($http, $srcWorkerId, $msg) { + echo "[worker#" . $http->getWorkerId() . "]\treceived pipe message[$msg] from " . $srcWorkerId . "\n"; +}); + //$http->addProcess(new \Swoole\Process(function () { // echo "user process, id=" . \Swoole\Thread::getId(); // sleep(2000); //})); -$http->on('Task', function () { - var_dump(func_get_args()); +$http->on('Task', function ($server, $taskId, $srcWorkerId, $data) { + var_dump($taskId, $srcWorkerId, $data); + return ['result' => uniqid()]; +}); + +$http->on('Finish', function ($server, $taskId, $data) { + var_dump($taskId, $data); }); $http->on('WorkerStart', function ($serv, $wid) { - var_dump(\Swoole\Thread::getArguments()); + var_dump(\Swoole\Thread::getArguments(), $wid); }); $http->on('WorkerStop', function ($serv, $wid) { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 7a3862f8f5f..510a0b4c337 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -698,7 +698,7 @@ void php_swoole_get_recv_data(Server *serv, zval *zdata, RecvData *req) { } else { if (req->info.flags & SW_EVENT_DATA_OBJ_PTR) { zend::assign_zend_string_by_val(zdata, (char *) data, length); - serv->message_bus.move_packet(); + serv->get_worker_message_bus()->move_packet(); } else if (req->info.flags & SW_EVENT_DATA_POP_PTR) { String *recv_buffer = serv->get_recv_buffer(serv->get_connection_by_session_id(req->info.fd)->socket); zend::assign_zend_string_by_val(zdata, recv_buffer->pop(serv->recv_buffer_size), length); @@ -834,7 +834,7 @@ void ServerObject::on_before_start() { serv->message_bus.set_allocator(sw_zend_string_allocator()); - if (serv->is_base_mode()) { + if (serv->is_base_mode() || serv->is_thread_mode()) { serv->recv_buffer_allocator = sw_zend_string_allocator(); } @@ -1493,6 +1493,8 @@ static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { PHPCoroutine::disable_hook(); } + serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator()); + zval args[2]; args[0] = *zserv; ZVAL_LONG(&args[1], worker->id); diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 9623d89fc2e..be7d514cad9 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -285,6 +285,7 @@ struct ProcessPool { int get_max_request(); bool detach(); int wait(); + int start_check(); int start(); void shutdown(); bool reload(); diff --git a/include/swoole_server.h b/include/swoole_server.h index a58d6562692..7f8ce3c590a 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1030,6 +1030,14 @@ class Server { return buffer; } + MessageBus *get_worker_message_bus() { +#ifdef SW_THREAD + return sw_likely(is_thread_mode()) ? &get_thread(swoole_get_thread_id())->message_bus : &message_bus; +#else + return &message_bus; +#endif + } + uint32_t get_worker_buffer_num() { return is_base_mode() ? 1 : reactor_num + dgram_port_num; } @@ -1408,11 +1416,11 @@ class Server { void worker_start_callback(Worker *worker); void worker_stop_callback(Worker *worker); void worker_accept_event(DataHead *info); + void worker_signal_init(void); std::function worker_thread_start; static int worker_main_loop(ProcessPool *pool, Worker *worker); static void worker_signal_handler(int signo); - static void worker_signal_init(void); static void reactor_thread_main_loop(Server *serv, int reactor_id); static bool task_pack(EventData *task, const void *data, size_t data_len); static bool task_unpack(EventData *task, String *buffer, PacketPtr *packet); diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 7d7b668fd03..546a7801d00 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -220,16 +220,12 @@ void ProcessPool::set_protocol(enum ProtocolType _protocol_type) { protocol_type_ = _protocol_type; } -/** - * start workers - */ -int ProcessPool::start() { +int ProcessPool::start_check() { if (ipc_mode == SW_IPC_SOCKET && (stream_info_ == nullptr || stream_info_->socket == 0)) { swoole_warning("must first listen to an tcp port"); return SW_ERR; } - uint32_t i; running = started = true; master_pid = getpid(); reload_workers = new Worker[worker_num](); @@ -239,7 +235,7 @@ int ProcessPool::start() { main_loop = ProcessPool_worker_loop_async; } - for (i = 0; i < worker_num; i++) { + SW_LOOP_N(worker_num) { workers[i].pool = this; workers[i].id = start_id + i; workers[i].type = type; @@ -251,12 +247,21 @@ int ProcessPool::start() { } } - for (i = 0; i < worker_num; i++) { + return SW_OK; +} + +/** + * start workers + */ +int ProcessPool::start() { + if (start_check() < 0) { + return SW_ERR; + } + SW_LOOP_N(worker_num) { if (spawn(&(workers[i])) < 0) { return SW_ERR; } } - return SW_OK; } diff --git a/src/server/base.cc b/src/server/base.cc index 36231fe9d76..3fcd489e4a6 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -91,12 +91,7 @@ bool BaseFactory::dispatch(SendData *task) { } } -#ifdef SW_THREAD - MessageBus *bus = sw_likely(server_->is_thread_mode()) ? &server_->get_thread(swoole_get_thread_id())->message_bus - : &server_->message_bus; -#else - MessageBus *bus = &server_->message_bus; -#endif + auto bus = server_->get_worker_message_bus(); bus->pass(task); server_->worker_accept_event(&bus->get_buffer()->info); @@ -225,7 +220,7 @@ bool BaseFactory::finish(SendData *data) { EventData proxy_msg{}; if (data->info.type == SW_SERVER_EVENT_SEND_DATA) { - if (!server_->message_bus.write(worker->pipe_master, data)) { + if (!server_->get_worker_message_bus()->write(worker->pipe_master, data)) { swoole_sys_warning("failed to send %u bytes to pipe_master", data->info.len); return false; } diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 81ba448366f..d531ca5f60e 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -190,7 +190,7 @@ int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { SwooleTG.timer->reinit(reactor); } - worker_signal_init(); + serv->worker_signal_init(); for (auto ls : serv->ports) { #ifdef HAVE_REUSEPORT diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 01d8558e21c..8f17bc98b25 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -355,6 +355,10 @@ static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) { return SW_OK; } else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) { ReactorThread_shutdown(reactor); + } else if (resp->info.type == SW_SERVER_EVENT_FINISH) { + serv->onFinish(serv, (EventData *) resp); + } else if (resp->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { + serv->onPipeMessage(serv, (EventData *) resp); } else if (resp->info.type == SW_SERVER_EVENT_CLOSE_FORCE) { SessionId session_id = resp->info.fd; Connection *conn = serv->get_connection_verify_no_ssl(session_id); @@ -728,7 +732,11 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { serv->init_reactor(reactor); if (serv->is_thread_mode()) { - serv->init_worker(serv->get_worker(reactor_id)); + Worker *worker = serv->get_worker(reactor_id); + serv->init_worker(worker); + worker->pipe_worker->set_nonblock(); + worker->pipe_worker->buffer_size = UINT_MAX; + reactor->add(worker->pipe_worker, SW_EVENT_READ); } int max_pipe_fd = serv->get_worker(serv->worker_num - 1)->pipe_master->fd + 2; diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 03d3912bd6e..9fd07ceecff 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -160,6 +160,10 @@ bool Server::task_unpack(EventData *task, String *buffer, PacketPtr *packet) { } static void TaskWorker_signal_init(ProcessPool *pool) { + Server *serv = (Server *) pool->ptr; + if (serv->is_thread_mode()) { + return; + } swoole_signal_set(SIGHUP, nullptr); swoole_signal_set(SIGPIPE, nullptr); swoole_signal_set(SIGUSR1, Server::worker_signal_handler); diff --git a/src/server/thread.cc b/src/server/thread.cc index b7764f7a055..8fff6d202b9 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -42,7 +42,17 @@ ThreadFactory::ThreadFactory(Server *server) : BaseFactory(server) { } bool ThreadFactory::start() { - return server_->create_worker_pipes(); + if (!server_->create_worker_pipes()) { + return false; + } + if (server_->task_worker_num > 0 && + (server_->create_task_workers() < 0 || server_->gs->task_workers.start_check() < 0)) { + return false; + } + if (server_->get_user_worker_num() > 0 && server_->create_user_workers() < 0) { + return false; + } + return true; } bool ThreadFactory::shutdown() { @@ -91,8 +101,17 @@ void ThreadFactory::spawn_task_worker(int i) { swoole_set_thread_id(i); Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_TASKWORKER; + worker->status = SW_WORKER_IDLE; auto pool = &server_->gs->task_workers; - server_->worker_thread_start([=]() { pool->main_loop(pool, worker); }); + server_->worker_thread_start([=]() { + if (pool->onWorkerStart != nullptr) { + pool->onWorkerStart(pool, worker); + } + pool->main_loop(pool, worker); + if (pool->onWorkerStop != nullptr) { + pool->onWorkerStop(pool, worker); + } + }); at_thread_exit(worker); }); } @@ -127,7 +146,9 @@ void ThreadFactory::spawn_manager_thread(int i) { server_->onManagerStop(server_); } }); - at_thread_exit(&manager); + if (server_->running) { + swoole_warning("Fatal Error: manager thread exits abnormally"); + } }); } @@ -147,9 +168,6 @@ void ThreadFactory::wait() { case SW_PROCESS_USERWORKER: spawn_user_worker(exited_worker->id); break; - case SW_PROCESS_MANAGER: - swoole_warning("Fatal Error: manager process exit"); - break; default: abort(); break; diff --git a/src/server/worker.cc b/src/server/worker.cc index 4aeaac68252..6c6fd27aa68 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -33,7 +33,9 @@ static int Worker_onPipeReceive(Reactor *reactor, Event *event); static void Worker_reactor_try_to_exit(Reactor *reactor); void Server::worker_signal_init(void) { -#ifndef SW_THREAD + if (is_thread_mode()) { + return; + } swoole_signal_set(SIGHUP, nullptr); swoole_signal_set(SIGPIPE, SIG_IGN); swoole_signal_set(SIGUSR1, nullptr); @@ -45,7 +47,6 @@ void Server::worker_signal_init(void) { #ifdef SIGRTMIN swoole_signal_set(SIGRTMIN, Server::worker_signal_handler); #endif -#endif } void Server::worker_signal_handler(int signo) { @@ -109,7 +110,7 @@ typedef std::function TaskCallback; static sw_inline void Worker_do_task(Server *serv, Worker *worker, DataHead *info, const TaskCallback &callback) { RecvData recv_data; - auto packet = serv->message_bus.get_packet(); + auto packet = serv->get_worker_message_bus()->get_packet(); recv_data.info = *info; recv_data.info.len = packet.length; recv_data.data = packet.data; @@ -130,7 +131,7 @@ void Server::worker_accept_event(DataHead *info) { Connection *conn = get_connection_verify(info->fd); if (conn) { if (info->len > 0) { - auto packet = message_bus.get_packet(); + auto packet = get_worker_message_bus()->get_packet(); sw_atomic_fetch_sub(&conn->recv_queued_bytes, packet.length); swoole_trace_log(SW_TRACE_SERVER, "[Worker] session_id=%ld, len=%lu, qb=%d", @@ -166,7 +167,7 @@ void Server::worker_accept_event(DataHead *info) { if (info->len > 0) { Connection *conn = get_connection_verify_no_ssl(info->fd); if (conn) { - auto packet = message_bus.get_packet(); + auto packet = get_worker_message_bus()->get_packet(); conn->ssl_client_cert = new String(packet.data, packet.length); conn->ssl_client_cert_pid = SwooleG.pid; } @@ -191,11 +192,11 @@ void Server::worker_accept_event(DataHead *info) { break; } case SW_SERVER_EVENT_FINISH: { - onFinish(this, (EventData *) message_bus.get_buffer()); + onFinish(this, (EventData *) get_worker_message_bus()->get_buffer()); break; } case SW_SERVER_EVENT_PIPE_MESSAGE: { - onPipeMessage(this, (EventData *) message_bus.get_buffer()); + onPipeMessage(this, (EventData *) get_worker_message_bus()->get_buffer()); break; } case SW_SERVER_EVENT_COMMAND_REQUEST: { @@ -296,10 +297,10 @@ void Server::worker_stop_callback(Worker *worker) { if (onWorkerStop) { onWorkerStop(this, worker); } - if (!message_bus.empty()) { + if (!get_worker_message_bus()->empty()) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA, "unprocessed data in the worker process buffer"); - message_bus.clear(); + get_worker_message_bus()->clear(); } } @@ -506,14 +507,14 @@ ssize_t Server::send_to_worker_from_worker(Worker *dst_worker, const void *buf, */ static int Worker_onPipeReceive(Reactor *reactor, Event *event) { Server *serv = (Server *) reactor->ptr; - PipeBuffer *pipe_buffer = serv->message_bus.get_buffer(); + PipeBuffer *pipe_buffer = serv->get_worker_message_bus()->get_buffer(); - if (serv->message_bus.read(event->socket) <= 0) { + if (serv->get_worker_message_bus()->read(event->socket) <= 0) { return SW_OK; } serv->worker_accept_event(&pipe_buffer->info); - serv->message_bus.pop(); + serv->get_worker_message_bus()->pop(); return SW_OK; } From 5d0df0ec930987a37c8669d61e6f39cf029af145 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 9 Apr 2024 12:19:26 +0800 Subject: [PATCH 047/103] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c5f0cb5e523..6c68311c15d 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,9 @@ docker run --rm phpswoole/swoole "php --ri swoole" > For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image). +## Documentation + + ### HTTP Service ```php $http = new Swoole\Http\Server('127.0.0.1', 9501); @@ -115,7 +118,6 @@ Co\run(function() { + __IDE Helper & API__: + __Twitter__: + __Discord__: -+ __中文文档__: + __中文社区__: ## 💎 Awesome Swoole From 319bba53d289cd3c1e6b9a3f25deb25c07d7f54d Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 9 Apr 2024 14:42:24 +0800 Subject: [PATCH 048/103] Added pty support for proc_open function, fix #5275 (#5290) * support pty, fix #5275 * fix * fix 2 * fix * fix BC * fix tests --- ext-src/php_swoole_cxx.h | 1 + ext-src/swoole_runtime.cc | 8 + src/coroutine/hook.cc | 8 +- thirdparty/php/standard/proc_open.cc | 1346 +++++++++++++++++------- thirdparty/php/standard/proc_open.h | 65 +- thirdparty/php/streams/plain_wrapper.c | 35 +- 6 files changed, 1040 insertions(+), 423 deletions(-) diff --git a/ext-src/php_swoole_cxx.h b/ext-src/php_swoole_cxx.h index 82419ca788e..3cb912b49e7 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -145,6 +145,7 @@ SW_API php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, int type, int protocol STREAMS_DC); +SW_API php_stream *php_swoole_create_stream_from_pipe(int fd, const char *mode, const char *persistent_id STREAMS_DC); SW_API php_stream_ops *php_swoole_get_ori_php_stream_stdio_ops(); SW_API void php_swoole_register_rshutdown_callback(swoole::Callback cb, void *private_data); diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 99ec24c22cc..c41912d304c 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -2006,6 +2006,14 @@ php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, i return stream; } +php_stream *php_swoole_create_stream_from_pipe(int fd, const char *mode, const char *persistent_id STREAMS_DC) { +#if PHP_VERSION_ID >= 80200 + return _sw_php_stream_fopen_from_fd(fd, mode, persistent_id, false STREAMS_CC); +#else + return _sw_php_stream_fopen_from_fd(fd, mode, persistent_id STREAMS_CC); +#endif +} + php_stream_ops *php_swoole_get_ori_php_stream_stdio_ops() { return &ori_php_stream_stdio_ops; } diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 96525bbd13f..3e2c5e7249a 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -213,11 +213,11 @@ int swoole_coroutine_socket_create(int fd) { int _fd = socket->get_fd(); if (sw_unlikely(_fd < 0)) { return -1; - } else { - std::unique_lock _lock(socket_map_lock); - socket_map[fd] = socket; - return 0; } + socket->get_socket()->set_nonblock(); + std::unique_lock _lock(socket_map_lock); + socket_map[fd] = socket; + return 0; } int swoole_coroutine_socket_unwrap(int fd) { diff --git a/thirdparty/php/standard/proc_open.cc b/thirdparty/php/standard/proc_open.cc index ccdafefaa30..b3e50ec9d89 100644 --- a/thirdparty/php/standard/proc_open.cc +++ b/thirdparty/php/standard/proc_open.cc @@ -1,13 +1,11 @@ /* - +----------------------------------------------------------------------+ - | PHP Version 7 | +----------------------------------------------------------------------+ | Copyright (c) The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | + | https://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | @@ -24,25 +22,50 @@ using swoole::Coroutine; using swoole::PHPCoroutine; using swoole::coroutine::Socket; -#if HAVE_SYS_STAT_H -#include +#ifdef HAVE_SYS_WAIT_H +#include #endif -#if HAVE_FCNTL_H + +#ifdef HAVE_FCNTL_H #include #endif +#ifdef HAVE_PTY_H +#include +#elif defined(__FreeBSD__) +/* FreeBSD defines `openpty` in */ +#include +#elif defined(__NetBSD__) || defined(__DragonFly__) +/* On recent NetBSD/DragonFlyBSD releases the emalloc, estrdup ... calls had been introduced in libutil */ +#if defined(__NetBSD__) +#include +#else +#include +#endif +extern int openpty(int *, int *, char *, struct termios *, struct winsize *); +#elif defined(__sun) +#include +#else +/* Mac OS X (and some BSDs) define `openpty` in */ +#include +#endif + static int le_proc_open; static const char *le_proc_name = "process/coroutine"; -/* {{{ _php_array_to_envp */ -static proc_co_env_t _php_array_to_envp(zval *environment) { +/* {{{ _php_array_to_envp + * Process the `environment` argument to `proc_open` + * Convert into data structures which can be passed to underlying OS APIs like `exec` on POSIX or + * `CreateProcessW` on Win32 */ +static sw_php_process_env _php_array_to_envp(zval *environment) { zval *element; - proc_co_env_t env; - zend_string *key, *str; + sw_php_process_env env; +#ifndef PHP_WIN32 char **ep; +#endif char *p; - size_t cnt, sizeenv = 0; - HashTable *env_hash; + size_t sizeenv = 0; + HashTable *env_hash; /* temporary PHP array used as helper */ memset(&env, 0, sizeof(env)); @@ -50,10 +73,12 @@ static proc_co_env_t _php_array_to_envp(zval *environment) { return env; } - cnt = zend_hash_num_elements(Z_ARRVAL_P(environment)); + uint32_t cnt = zend_hash_num_elements(Z_ARRVAL_P(environment)); if (cnt < 1) { +#ifndef PHP_WIN32 env.envarray = (char **) ecalloc(1, sizeof(char *)); +#endif env.envp = (char *) ecalloc(4, 1); return env; } @@ -61,12 +86,15 @@ static proc_co_env_t _php_array_to_envp(zval *environment) { ALLOC_HASHTABLE(env_hash); zend_hash_init(env_hash, cnt, NULL, NULL, 0); + void *_key, *_str; + zend_string *key, *str; /* first, we have to get the size of all the elements in the hash */ - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(environment), key, element) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(environment), _key, element) { + key = (zend_string *) _key; str = zval_get_string(element); if (ZSTR_LEN(str) == 0) { - zend_string_release(str); + zend_string_release_ex(str, 0); continue; } @@ -81,15 +109,18 @@ static proc_co_env_t _php_array_to_envp(zval *environment) { } ZEND_HASH_FOREACH_END(); +#ifndef PHP_WIN32 ep = env.envarray = (char **) ecalloc(cnt + 1, sizeof(char *)); +#endif p = env.envp = (char *) ecalloc(sizeenv + 4, 1); - void *v1, *v2; - ZEND_HASH_FOREACH_STR_KEY_PTR(env_hash, v1, v2) { - key = (zend_string *) v1; - str = (zend_string *) v2; + ZEND_HASH_FOREACH_STR_KEY_PTR(env_hash, _key, _str) { + key = (zend_string *) _key; + str = (zend_string *) _str; +#ifndef PHP_WIN32 *ep = p; ++ep; +#endif if (key) { memcpy(p, ZSTR_VAL(key), ZSTR_LEN(key)); @@ -100,11 +131,11 @@ static proc_co_env_t _php_array_to_envp(zval *environment) { memcpy(p, ZSTR_VAL(str), ZSTR_LEN(str)); p += ZSTR_LEN(str); *p++ = '\0'; - zend_string_release(str); + zend_string_release_ex(str, 0); } ZEND_HASH_FOREACH_END(); - assert((uint32_t)(p - env.envp) <= sizeenv); + assert((uint32_t) (p - env.envp) <= sizeenv); zend_hash_destroy(env_hash); FREE_HASHTABLE(env_hash); @@ -113,28 +144,28 @@ static proc_co_env_t _php_array_to_envp(zval *environment) { } /* }}} */ -/* {{{ _php_free_envp */ -static void _php_free_envp(proc_co_env_t env, int is_persistent) { +/* {{{ _php_free_envp + * Free the structures allocated by `_php_array_to_envp` */ +static void _php_free_envp(sw_php_process_env env) { if (env.envarray) { - pefree(env.envarray, is_persistent); + efree(env.envarray); } if (env.envp) { - pefree(env.envp, is_persistent); + efree(env.envp); } } /* }}} */ static void proc_co_rsrc_dtor(zend_resource *rsrc) { - proc_co_t *proc = (proc_co_t *) rsrc->ptr; - int i; + sw_php_process_handle *proc = (sw_php_process_handle *) rsrc->ptr; int wstatus = 0; /* Close all handles to avoid a deadlock */ - for (i = 0; i < proc->npipes; i++) { - if (proc->pipes[i] != 0) { + for (int i = 0; i < proc->npipes; i++) { + if (proc->pipes[i] != NULL) { GC_DELREF(proc->pipes[i]); zend_list_close(proc->pipes[i]); - proc->pipes[i] = 0; + proc->pipes[i] = NULL; } } @@ -147,74 +178,82 @@ static void proc_co_rsrc_dtor(zend_resource *rsrc) { *proc->wstatus = wstatus; } - _php_free_envp(proc->env, proc->is_persistent); + _php_free_envp(proc->env); efree(proc->pipes); - efree(proc->command); + zend_string_release_ex(proc->command, false); efree(proc); } +/* }}} */ +/* {{{ PHP_MINIT_FUNCTION(proc_open) */ void swoole_proc_open_init(int module_number) { le_proc_open = zend_register_list_destructors_ex(proc_co_rsrc_dtor, NULL, le_proc_name, module_number); } +/* }}} */ -/* {{{ proto bool proc_terminate(resource process [, int signal]) - kill a process opened by proc_open */ +/* {{{ Kill a process opened by `proc_open` */ PHP_FUNCTION(swoole_proc_terminate) { zval *zproc; - proc_co_t *proc; + sw_php_process_handle *proc; zend_long sig_no = SIGTERM; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_RESOURCE(zproc) Z_PARAM_OPTIONAL Z_PARAM_LONG(sig_no) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + ZEND_PARSE_PARAMETERS_END(); - if ((proc = (proc_co_t *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) { - RETURN_FALSE; + proc = (sw_php_process_handle *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open); + if (proc == NULL) { + RETURN_THROWS(); } +#ifdef PHP_WIN32 + RETURN_BOOL(TerminateProcess(proc->childHandle, 255)); +#else RETURN_BOOL(kill(proc->child, sig_no) == 0); +#endif } /* }}} */ +/* {{{ Close a process opened by `proc_open` */ PHP_FUNCTION(swoole_proc_close) { zval *zproc; - proc_co_t *proc; int wstatus = 0; + sw_php_process_handle *proc; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_RESOURCE(zproc) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + ZEND_PARSE_PARAMETERS_END(); - if ((proc = (proc_co_t *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) { - RETURN_FALSE; + if ((proc = (sw_php_process_handle *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) { + RETURN_THROWS(); } - proc->wstatus = &wstatus; zend_list_close(Z_RES_P(zproc)); RETURN_LONG(wstatus); } +/* }}} */ +/* {{{ Get information about a process opened by `proc_open` */ PHP_FUNCTION(swoole_proc_get_status) { zval *zproc; - proc_co_t *proc; + sw_php_process_handle *proc; int wstatus; pid_t wait_pid; - int running = 1, signaled = 0, stopped = 0; + bool running = 1, signaled = 0, stopped = 0; int exitcode = -1, termsig = 0, stopsig = 0; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_RESOURCE(zproc) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + ZEND_PARSE_PARAMETERS_END(); - if ((proc = (proc_co_t *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) { - RETURN_FALSE; + if ((proc = (sw_php_process_handle *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) { + RETURN_THROWS(); } array_init(return_value); - - add_assoc_string(return_value, "command", proc->command); + add_assoc_str(return_value, "command", zend_string_copy(proc->command)); add_assoc_long(return_value, "pid", (zend_long) proc->child); errno = 0; @@ -228,7 +267,6 @@ PHP_FUNCTION(swoole_proc_get_status) { if (WIFSIGNALED(wstatus)) { running = 0; signaled = 1; - termsig = WTERMSIG(wstatus); } if (WIFSTOPPED(wstatus)) { @@ -236,6 +274,8 @@ PHP_FUNCTION(swoole_proc_get_status) { stopsig = WSTOPSIG(wstatus); } } else if (wait_pid == -1) { + /* The only error which could occur here is ECHILD, which means that the PID we were + * looking for either does not exist or is not a child of this process */ running = 0; } @@ -250,18 +290,50 @@ PHP_FUNCTION(swoole_proc_get_status) { } /* }}} */ -#define DESC_PIPE 1 -#define DESC_FILE 2 -#define DESC_REDIRECT 3 -#define DESC_PARENT_MODE_WRITE 8 - -struct php_proc_open_descriptor_item { - int index; /* desired fd number in child process */ - int parentend, childend; /* fds for pipes in parent/child */ - int mode; /* mode for proc_open code */ - int mode_flags; /* mode flags for opening fds */ -}; -/* }}} */ +#ifdef PHP_WIN32 + +/* We use this to allow child processes to inherit handles + * One static instance can be shared and used for all calls to `proc_open`, since the values are + * never changed */ +SECURITY_ATTRIBUTES php_proc_open_security = { + .nLength = sizeof(SECURITY_ATTRIBUTES), .lpSecurityDescriptor = NULL, .bInheritHandle = TRUE}; + +#define pipe(pair) (CreatePipe(&pair[0], &pair[1], &php_proc_open_security, 0) ? 0 : -1) + +#define COMSPEC_NT "cmd.exe" + +static inline HANDLE dup_handle(HANDLE src, BOOL inherit, BOOL closeorig) { + HANDLE copy, self = GetCurrentProcess(); + + if (!DuplicateHandle( + self, src, self, ©, 0, inherit, DUPLICATE_SAME_ACCESS | (closeorig ? DUPLICATE_CLOSE_SOURCE : 0))) + return NULL; + return copy; +} + +static inline HANDLE dup_fd_as_handle(int fd) { + return dup_handle((HANDLE) _get_osfhandle(fd), TRUE, FALSE); +} + +#define close_descriptor(fd) CloseHandle(fd) +#else /* !PHP_WIN32 */ +#define close_descriptor(fd) close(fd) +#endif + +/* Determines the type of a descriptor item. */ +typedef enum _descriptor_type { DESCRIPTOR_TYPE_STD, DESCRIPTOR_TYPE_PIPE, DESCRIPTOR_TYPE_SOCKET } descriptor_type; + +/* One instance of this struct is created for each item in `$descriptorspec` argument to `proc_open` + * They are used within `proc_open` and freed before it returns */ +typedef struct _descriptorspec_item { + int index; /* desired FD # in child process */ + descriptor_type type; + php_file_descriptor_t childend; /* FD # opened for use in child + * (will be copied to `index` in child) */ + php_file_descriptor_t parentend; /* FD # opened for use in parent + * (for pipes only; will be 0 otherwise) */ + int mode_flags; /* mode for opening FDs: r/o, r/w, binary (on Win32), etc */ +} descriptorspec_item; static zend_string *get_valid_arg_string(zval *zv, int elem_num) { zend_string *str = zval_get_string(zv); @@ -269,8 +341,14 @@ static zend_string *get_valid_arg_string(zval *zv, int elem_num) { return NULL; } + if (elem_num == 1 && ZSTR_LEN(str) == 0) { + zend_value_error("First element must contain a non-empty program name"); + zend_string_release(str); + return NULL; + } + if (strlen(ZSTR_VAL(str)) != ZSTR_LEN(str)) { - php_error_docref(NULL, E_WARNING, "Command array element %d contains a null byte", elem_num); + zend_value_error("Command array element %d contains a null byte", elem_num); zend_string_release(str); return NULL; } @@ -278,76 +356,627 @@ static zend_string *get_valid_arg_string(zval *zv, int elem_num) { return str; } -/* {{{ proto resource proc_open(string|array command, array descriptorspec, array &pipes [, string cwd [, array env [, - array other_options]]]) Run a process with more control over it's file descriptors */ +#ifdef PHP_WIN32 +static void append_backslashes(smart_str *str, size_t num_bs) { + for (size_t i = 0; i < num_bs; i++) { + smart_str_appendc(str, '\\'); + } +} + +/* See https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments */ +static void append_win_escaped_arg(smart_str *str, zend_string *arg) { + size_t num_bs = 0; + + smart_str_appendc(str, '"'); + for (size_t i = 0; i < ZSTR_LEN(arg); ++i) { + char c = ZSTR_VAL(arg)[i]; + if (c == '\\') { + num_bs++; + continue; + } + + if (c == '"') { + /* Backslashes before " need to be doubled. */ + num_bs = num_bs * 2 + 1; + } + append_backslashes(str, num_bs); + smart_str_appendc(str, c); + num_bs = 0; + } + append_backslashes(str, num_bs * 2); + smart_str_appendc(str, '"'); +} + +static zend_string *create_win_command_from_args(HashTable *args) { + smart_str str = {0}; + zval *arg_zv; + bool is_prog_name = 1; + int elem_num = 0; + + ZEND_HASH_FOREACH_VAL(args, arg_zv) { + zend_string *arg_str = get_valid_arg_string(arg_zv, ++elem_num); + if (!arg_str) { + smart_str_free(&str); + return NULL; + } + + if (!is_prog_name) { + smart_str_appendc(&str, ' '); + } + + append_win_escaped_arg(&str, arg_str); + + is_prog_name = 0; + zend_string_release(arg_str); + } + ZEND_HASH_FOREACH_END(); + smart_str_0(&str); + return str.s; +} + +/* Get a boolean option from the `other_options` array which can be passed to `proc_open`. + * (Currently, all options apply on Windows only.) */ +static bool get_option(zval *other_options, char *opt_name, size_t opt_name_len) { + HashTable *opt_ary = Z_ARRVAL_P(other_options); + zval *item = zend_hash_str_find_deref(opt_ary, opt_name, opt_name_len); + return item != NULL && (Z_TYPE_P(item) == IS_TRUE || ((Z_TYPE_P(item) == IS_LONG) && Z_LVAL_P(item))); +} + +/* Initialize STARTUPINFOW struct, used on Windows when spawning a process. + * Ref: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow */ +static void init_startup_info(STARTUPINFOW *si, descriptorspec_item *descriptors, int ndesc) { + memset(si, 0, sizeof(STARTUPINFOW)); + si->cb = sizeof(STARTUPINFOW); + si->dwFlags = STARTF_USESTDHANDLES; + + si->hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si->hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si->hStdError = GetStdHandle(STD_ERROR_HANDLE); + + /* redirect stdin/stdout/stderr if requested */ + for (int i = 0; i < ndesc; i++) { + switch (descriptors[i].index) { + case 0: + si->hStdInput = descriptors[i].childend; + break; + case 1: + si->hStdOutput = descriptors[i].childend; + break; + case 2: + si->hStdError = descriptors[i].childend; + break; + } + } +} + +static void init_process_info(PROCESS_INFORMATION *pi) { + memset(&pi, 0, sizeof(pi)); +} + +static zend_result convert_command_to_use_shell(wchar_t **cmdw, size_t cmdw_len) { + size_t len = sizeof(COMSPEC_NT) + sizeof(" /s /c ") + cmdw_len + 3; + wchar_t *cmdw_shell = (wchar_t *) malloc(len * sizeof(wchar_t)); + + if (cmdw_shell == NULL) { + php_error_docref(NULL, E_WARNING, "Command conversion failed"); + return FAILURE; + } + + if (_snwprintf(cmdw_shell, len, L"%hs /s /c \"%s\"", COMSPEC_NT, *cmdw) == -1) { + free(cmdw_shell); + php_error_docref(NULL, E_WARNING, "Command conversion failed"); + return FAILURE; + } + + free(*cmdw); + *cmdw = cmdw_shell; + + return SUCCESS; +} +#endif + +/* Convert command parameter array passed as first argument to `proc_open` into command string */ +static zend_string *get_command_from_array(HashTable *array, char ***argv, int num_elems) { + zval *arg_zv; + zend_string *command = NULL; + int i = 0; + + *argv = (char **) safe_emalloc(sizeof(char *), num_elems + 1, 0); + + ZEND_HASH_FOREACH_VAL(array, arg_zv) { + zend_string *arg_str = get_valid_arg_string(arg_zv, i + 1); + if (!arg_str) { + /* Terminate with NULL so exit_fail code knows how many entries to free */ + (*argv)[i] = NULL; + if (command != NULL) { + zend_string_release_ex(command, false); + } + return NULL; + } + + if (i == 0) { + command = zend_string_copy(arg_str); + } + + (*argv)[i++] = (char *) estrdup(ZSTR_VAL(arg_str)); + zend_string_release(arg_str); + } + ZEND_HASH_FOREACH_END(); + + (*argv)[i] = NULL; + return command; +} + +static descriptorspec_item *alloc_descriptor_array(HashTable *descriptorspec) { + uint32_t ndescriptors = zend_hash_num_elements(descriptorspec); + return (descriptorspec_item *) ecalloc(sizeof(descriptorspec_item), ndescriptors); +} + +static zend_string *get_string_parameter(zval *array, int index, char *param_name) { + zval *array_item; + if ((array_item = zend_hash_index_find(Z_ARRVAL_P(array), index)) == NULL) { + zend_value_error("Missing %s", param_name); + return NULL; + } + return zval_try_get_string(array_item); +} + +static zend_result set_proc_descriptor_to_blackhole(descriptorspec_item *desc) { +#ifdef PHP_WIN32 + desc->childend = CreateFileA( + "nul", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (desc->childend == NULL) { + php_error_docref(NULL, E_WARNING, "Failed to open nul"); + return FAILURE; + } +#else + desc->childend = open("/dev/null", O_RDWR); + if (desc->childend < 0) { + php_error_docref(NULL, E_WARNING, "Failed to open /dev/null: %s", strerror(errno)); + return FAILURE; + } +#endif + return SUCCESS; +} + +static zend_result set_proc_descriptor_to_pty(descriptorspec_item *desc, int *master_fd, int *slave_fd) { +#ifdef HAVE_OPENPTY + /* All FDs set to PTY in the child process will go to the slave end of the same PTY. + * Likewise, all the corresponding entries in `$pipes` in the parent will all go to the master + * end of the same PTY. + * If this is the first descriptorspec set to 'pty', find an available PTY and get master and + * slave FDs. */ + if (*master_fd == -1) { + if (openpty(master_fd, slave_fd, NULL, NULL, NULL)) { + php_error_docref(NULL, E_WARNING, "Could not open PTY (pseudoterminal): %s", strerror(errno)); + return FAILURE; + } + } + + desc->type = DESCRIPTOR_TYPE_PIPE; + desc->childend = dup(*slave_fd); + desc->parentend = dup(*master_fd); + desc->mode_flags = O_RDWR; + return SUCCESS; +#else + php_error_docref(NULL, E_WARNING, "PTY (pseudoterminal) not supported on this system"); + return FAILURE; +#endif +} + +/* Mark the descriptor close-on-exec, so it won't be inherited by children */ +static php_file_descriptor_t make_descriptor_cloexec(php_file_descriptor_t fd) { +#ifdef PHP_WIN32 + return dup_handle(fd, FALSE, TRUE); +#else +#if defined(F_SETFD) && defined(FD_CLOEXEC) + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + return fd; +#endif +} + +static zend_result set_proc_descriptor_to_pipe(descriptorspec_item *desc, zend_string *zmode) { + php_file_descriptor_t newpipe[2]; + + if (pipe(newpipe) != 0) { + php_error_docref(NULL, E_WARNING, "Unable to create pipe %s", strerror(errno)); + return FAILURE; + } + + desc->type = DESCRIPTOR_TYPE_PIPE; + + if (strncmp(ZSTR_VAL(zmode), "w", 1) != 0) { + desc->parentend = newpipe[1]; + desc->childend = newpipe[0]; + desc->mode_flags = O_WRONLY; + } else { + desc->parentend = newpipe[0]; + desc->childend = newpipe[1]; + desc->mode_flags = O_RDONLY; + } + + desc->parentend = make_descriptor_cloexec(desc->parentend); + +#ifdef PHP_WIN32 + if (ZSTR_LEN(zmode) >= 2 && ZSTR_VAL(zmode)[1] == 'b') desc->mode_flags |= O_BINARY; +#endif + + return SUCCESS; +} + +#ifdef PHP_WIN32 +#define create_socketpair(socks) socketpair_win32(AF_INET, SOCK_STREAM, 0, (socks), 0) +#else +#define create_socketpair(socks) socketpair(AF_UNIX, SOCK_STREAM, 0, (socks)) +#endif + +static zend_result set_proc_descriptor_to_socket(descriptorspec_item *desc) { + php_socket_t sock[2]; + + if (create_socketpair(sock)) { + zend_string *err = php_socket_error_str(php_socket_errno()); + php_error_docref(NULL, E_WARNING, "Unable to create socket pair: %s", ZSTR_VAL(err)); + zend_string_release(err); + return FAILURE; + } + + desc->type = DESCRIPTOR_TYPE_SOCKET; + desc->parentend = make_descriptor_cloexec((php_file_descriptor_t) sock[0]); + + /* Pass sock[1] to child because it will never use overlapped IO on Windows. */ + desc->childend = (php_file_descriptor_t) sock[1]; + + return SUCCESS; +} + +static zend_result set_proc_descriptor_to_file(descriptorspec_item *desc, + zend_string *file_path, + zend_string *file_mode) { + php_socket_t fd; + + /* try a wrapper */ + php_stream *stream = + php_stream_open_wrapper(ZSTR_VAL(file_path), ZSTR_VAL(file_mode), REPORT_ERRORS | STREAM_WILL_CAST, NULL); + if (stream == NULL) { + return FAILURE; + } + + /* force into an fd */ + if (php_stream_cast(stream, PHP_STREAM_CAST_RELEASE | PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS) == FAILURE) { + return FAILURE; + } + +#ifdef PHP_WIN32 + desc->childend = dup_fd_as_handle((int) fd); + _close((int) fd); + + /* Simulate the append mode by fseeking to the end of the file + * This introduces a potential race condition, but it is the best we can do */ + if (strchr(ZSTR_VAL(file_mode), 'a')) { + SetFilePointer(desc->childend, 0, NULL, FILE_END); + } +#else + desc->childend = fd; +#endif + return SUCCESS; +} + +static zend_result dup_proc_descriptor(php_file_descriptor_t from, php_file_descriptor_t *to, zend_ulong nindex) { +#ifdef PHP_WIN32 + *to = dup_handle(from, TRUE, FALSE); + if (*to == NULL) { + php_error_docref(NULL, E_WARNING, "Failed to dup() for descriptor " ZEND_LONG_FMT, nindex); + return FAILURE; + } +#else + *to = dup(from); + if (*to < 0) { + php_error_docref( + NULL, E_WARNING, "Failed to dup() for descriptor " ZEND_LONG_FMT ": %s", nindex, strerror(errno)); + return FAILURE; + } +#endif + return SUCCESS; +} + +static zend_result redirect_proc_descriptor( + descriptorspec_item *desc, int target, descriptorspec_item *descriptors, int ndesc, int nindex) { + php_file_descriptor_t redirect_to = PHP_INVALID_FD; + + for (int i = 0; i < ndesc; i++) { + if (descriptors[i].index == target) { + redirect_to = descriptors[i].childend; + break; + } + } + + if (redirect_to == PHP_INVALID_FD) { /* Didn't find the index we wanted */ + if (target < 0 || target > 2) { + php_error_docref(NULL, E_WARNING, "Redirection target %d not found", target); + return FAILURE; + } + + /* Support referring to a stdin/stdout/stderr pipe adopted from the parent, + * which happens whenever an explicit override is not provided. */ +#ifndef PHP_WIN32 + redirect_to = target; +#else + switch (target) { + case 0: + redirect_to = GetStdHandle(STD_INPUT_HANDLE); + break; + case 1: + redirect_to = GetStdHandle(STD_OUTPUT_HANDLE); + break; + case 2: + redirect_to = GetStdHandle(STD_ERROR_HANDLE); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } +#endif + } + + return dup_proc_descriptor(redirect_to, &desc->childend, nindex); +} + +/* Process one item from `$descriptorspec` argument to `proc_open` */ +static zend_result set_proc_descriptor_from_array( + zval *descitem, descriptorspec_item *descriptors, int ndesc, int nindex, int *pty_master_fd, int *pty_slave_fd) { + zend_string *ztype = get_string_parameter(descitem, 0, "handle qualifier"); + if (!ztype) { + return FAILURE; + } + + zend_string *zmode = NULL, *zfile = NULL; + zend_result retval = FAILURE; + +#if 0 + if (zend_string_equals_literal(ztype, "pipe")) { + /* Set descriptor to pipe */ + zmode = get_string_parameter(descitem, 1, "mode parameter for 'pipe'"); + if (zmode == NULL) { + goto finish; + } + retval = set_proc_descriptor_to_pipe(&descriptors[ndesc], zmode); + } else +#endif + if (zend_string_equals_literal(ztype, "socket") || zend_string_equals_literal(ztype, "pipe")) { + /* Set descriptor to socketpair */ + retval = set_proc_descriptor_to_socket(&descriptors[ndesc]); + } else if (zend_string_equals(ztype, ZSTR_KNOWN(ZEND_STR_FILE))) { + /* Set descriptor to file */ + if ((zfile = get_string_parameter(descitem, 1, "file name parameter for 'file'")) == NULL) { + goto finish; + } + if ((zmode = get_string_parameter(descitem, 2, "mode parameter for 'file'")) == NULL) { + goto finish; + } + retval = set_proc_descriptor_to_file(&descriptors[ndesc], zfile, zmode); + } else if (zend_string_equals_literal(ztype, "redirect")) { + /* Redirect descriptor to whatever another descriptor is set to */ + zval *ztarget = zend_hash_index_find_deref(Z_ARRVAL_P(descitem), 1); + if (!ztarget) { + zend_value_error("Missing redirection target"); + goto finish; + } + if (Z_TYPE_P(ztarget) != IS_LONG) { + zend_value_error("Redirection target must be of type int, %s given", zend_zval_type_name(ztarget)); + goto finish; + } + + retval = redirect_proc_descriptor(&descriptors[ndesc], (int) Z_LVAL_P(ztarget), descriptors, ndesc, nindex); + } else if (zend_string_equals(ztype, ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE))) { + /* Set descriptor to blackhole (discard all data written) */ + retval = set_proc_descriptor_to_blackhole(&descriptors[ndesc]); + } else if (zend_string_equals_literal(ztype, "pty")) { + /* Set descriptor to slave end of PTY */ + retval = set_proc_descriptor_to_pty(&descriptors[ndesc], pty_master_fd, pty_slave_fd); + } else { + php_error_docref(NULL, E_WARNING, "%s is not a valid descriptor spec/mode", ZSTR_VAL(ztype)); + goto finish; + } + +finish: + if (zmode) zend_string_release(zmode); + if (zfile) zend_string_release(zfile); + zend_string_release(ztype); + return retval; +} + +static zend_result set_proc_descriptor_from_resource(zval *resource, descriptorspec_item *desc, int nindex) { + /* Should be a stream - try and dup the descriptor */ + php_stream *stream = (php_stream *) zend_fetch_resource(Z_RES_P(resource), "stream", php_file_le_stream()); + if (stream == NULL) { + return FAILURE; + } + + php_socket_t fd; + zend_result status = (zend_result) php_stream_cast(stream, PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS); + if (status == FAILURE) { + return FAILURE; + } + +#ifdef PHP_WIN32 + php_file_descriptor_t fd_t = (php_file_descriptor_t) _get_osfhandle(fd); +#else + php_file_descriptor_t fd_t = fd; +#endif + return dup_proc_descriptor(fd_t, &desc->childend, nindex); +} + +#ifndef PHP_WIN32 +#if defined(USE_POSIX_SPAWN) +static zend_result close_parentends_of_pipes(posix_spawn_file_actions_t *actions, + descriptorspec_item *descriptors, + int ndesc) { + int r; + for (int i = 0; i < ndesc; i++) { + if (descriptors[i].type != DESCRIPTOR_TYPE_STD) { + r = posix_spawn_file_actions_addclose(actions, descriptors[i].parentend); + if (r != 0) { + php_error_docref( + NULL, E_WARNING, "Cannot close file descriptor %d: %s", descriptors[i].parentend, strerror(r)); + return FAILURE; + } + } + if (descriptors[i].childend != descriptors[i].index) { + r = posix_spawn_file_actions_adddup2(actions, descriptors[i].childend, descriptors[i].index); + if (r != 0) { + php_error_docref(NULL, + E_WARNING, + "Unable to copy file descriptor %d (for pipe) into " + "file descriptor %d: %s", + descriptors[i].childend, + descriptors[i].index, + strerror(r)); + return FAILURE; + } + r = posix_spawn_file_actions_addclose(actions, descriptors[i].childend); + if (r != 0) { + php_error_docref( + NULL, E_WARNING, "Cannot close file descriptor %d: %s", descriptors[i].childend, strerror(r)); + return FAILURE; + } + } + } + + return SUCCESS; +} +#else +static zend_result close_parentends_of_pipes(descriptorspec_item *descriptors, int ndesc) { + /* We are running in child process + * Close the 'parent end' of pipes which were opened before forking/spawning + * Also, dup() the child end of all pipes as necessary so they will use the FD + * number which the user requested */ + for (int i = 0; i < ndesc; i++) { + if (descriptors[i].type != DESCRIPTOR_TYPE_STD) { + close(descriptors[i].parentend); + } + if (descriptors[i].childend != descriptors[i].index) { + if (dup2(descriptors[i].childend, descriptors[i].index) < 0) { + php_error_docref(NULL, + E_WARNING, + "Unable to copy file descriptor %d (for pipe) into " + "file descriptor %d: %s", + descriptors[i].childend, + descriptors[i].index, + strerror(errno)); + return FAILURE; + } + close(descriptors[i].childend); + } + } + + return SUCCESS; +} +#endif +#endif + +static void close_all_descriptors(descriptorspec_item *descriptors, int ndesc) { + for (int i = 0; i < ndesc; i++) { + close_descriptor(descriptors[i].childend); + if (descriptors[i].parentend) close_descriptor(descriptors[i].parentend); + } +} + +static void efree_argv(char **argv) { + if (argv) { + char **arg = argv; + while (*arg != NULL) { + efree(*arg); + arg++; + } + efree(argv); + } +} + +/* {{{ Execute a command, with specified files used for input/output */ PHP_FUNCTION(swoole_proc_open) { - zval *command_zv; - char *command = NULL, *cwd = NULL; - size_t cwd_len = 0; - zval *descriptorspec; - zval *pipes; - zval *environment = NULL; - zval *other_options = NULL; - proc_co_env_t env; + zend_string *command_str; + HashTable *command_ht; + HashTable *descriptorspec; /* Mandatory argument */ + zval *pipes; /* Mandatory argument */ + char *cwd = NULL; /* Optional argument */ + size_t cwd_len = 0; /* Optional argument */ + zval *environment = NULL, *other_options = NULL; /* Optional arguments */ + + sw_php_process_env env; int ndesc = 0; int i; zval *descitem = NULL; zend_string *str_index; zend_ulong nindex; - struct php_proc_open_descriptor_item *descriptors = NULL; - int ndescriptors_array; - + descriptorspec_item *descriptors = NULL; +#ifdef PHP_WIN32 + PROCESS_INFORMATION pi; + HANDLE childHandle; + STARTUPINFOW si; + BOOL newprocok; + DWORD dwCreateFlags = 0; + UINT old_error_mode; + char cur_cwd[MAXPATHLEN]; + wchar_t *cmdw = NULL, *cwdw = NULL, *envpw = NULL; + size_t cmdw_len; + bool suppress_errors = 0; + bool bypass_shell = 0; + bool blocking_pipes = 0; + bool create_process_group = 0; + bool create_new_console = 0; +#else char **argv = NULL; - - pid_t child; - proc_co_t *proc; - int is_persistent = 0; /* TODO: ensure that persistent procs will work */ +#endif + int pty_master_fd = -1, pty_slave_fd = -1; + php_process_id_t child; + sw_php_process_handle *proc; ZEND_PARSE_PARAMETERS_START(3, 6) - Z_PARAM_ZVAL(command_zv) - Z_PARAM_ARRAY(descriptorspec) + Z_PARAM_ARRAY_HT_OR_STR(command_ht, command_str) + Z_PARAM_ARRAY_HT(descriptorspec) Z_PARAM_ZVAL(pipes) Z_PARAM_OPTIONAL - Z_PARAM_STRING_EX(cwd, cwd_len, 1, 0) - Z_PARAM_ARRAY_EX(environment, 1, 0) - Z_PARAM_ARRAY_EX(other_options, 1, 0) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + Z_PARAM_STRING_OR_NULL(cwd, cwd_len) + Z_PARAM_ARRAY_OR_NULL(environment) + Z_PARAM_ARRAY_OR_NULL(other_options) + ZEND_PARSE_PARAMETERS_END(); memset(&env, 0, sizeof(env)); - if (Z_TYPE_P(command_zv) == IS_ARRAY) { - zval *arg_zv; - uint32_t num_elems = zend_hash_num_elements(Z_ARRVAL_P(command_zv)); + if (command_ht) { + uint32_t num_elems = zend_hash_num_elements(command_ht); if (num_elems == 0) { - php_error_docref(NULL, E_WARNING, "Command array must have at least one element"); - RETURN_FALSE; + zend_argument_value_error(1, "must have at least one element"); + RETURN_THROWS(); } - argv = (char **) safe_emalloc(sizeof(char *), num_elems + 1, 0); - i = 0; - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(command_zv), arg_zv) { - zend_string *arg_str = get_valid_arg_string(arg_zv, i + 1); - if (!arg_str) { - argv[i] = NULL; - goto exit_fail; - } - - if (i == 0) { - command = pestrdup(ZSTR_VAL(arg_str), is_persistent); - } +#ifdef PHP_WIN32 + /* Automatically bypass shell if command is given as an array */ + bypass_shell = 1; + command_str = create_win_command_from_args(command_ht); +#else + command_str = get_command_from_array(command_ht, &argv, num_elems); +#endif - argv[i++] = estrdup(ZSTR_VAL(arg_str)); - zend_string_release(arg_str); + if (!command_str) { +#ifndef PHP_WIN32 + efree_argv(argv); +#endif + RETURN_FALSE; } - ZEND_HASH_FOREACH_END(); - argv[i] = NULL; - - /* As the array is non-empty, we should have found a command. */ - ZEND_ASSERT(command); } else { - convert_to_string(command_zv); - command = pestrdup(Z_STRVAL_P(command_zv), is_persistent); + zend_string_addref(command_str); + } + +#ifdef PHP_WIN32 + if (other_options) { + suppress_errors = get_option(other_options, "suppress_errors", strlen("suppress_errors")); + /* TODO: Deprecate in favor of array command? */ + bypass_shell = bypass_shell || get_option(other_options, "bypass_shell", strlen("bypass_shell")); + blocking_pipes = get_option(other_options, "blocking_pipes", strlen("blocking_pipes")); + create_process_group = get_option(other_options, "create_process_group", strlen("create_process_group")); + create_new_console = get_option(other_options, "create_new_console", strlen("create_new_console")); } +#endif php_swoole_check_reactor(); if (php_swoole_signal_isset_handler(SIGCHLD)) { @@ -355,251 +984,157 @@ PHP_FUNCTION(swoole_proc_open) { RETURN_FALSE; } - Coroutine::get_current_safe(); + swoole::Coroutine::get_current_safe(); if (environment) { env = _php_array_to_envp(environment); - } else { - memset(&env, 0, sizeof(env)); } - ndescriptors_array = zend_hash_num_elements(Z_ARRVAL_P(descriptorspec)); - - descriptors = (struct php_proc_open_descriptor_item *) safe_emalloc( - sizeof(struct php_proc_open_descriptor_item), ndescriptors_array, 0); - - memset(descriptors, 0, sizeof(struct php_proc_open_descriptor_item) * ndescriptors_array); - - /* walk the descriptor spec and set up files/pipes */ - ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(descriptorspec), nindex, str_index, descitem) { - zval *ztype; + descriptors = alloc_descriptor_array(descriptorspec); + /* Walk the descriptor spec and set up files/pipes */ + ZEND_HASH_FOREACH_KEY_VAL(descriptorspec, nindex, str_index, descitem) { if (str_index) { - php_swoole_fatal_error(E_WARNING, "descriptor spec must be an integer indexed array"); + zend_argument_value_error(2, "must be an integer indexed array"); goto exit_fail; } descriptors[ndesc].index = (int) nindex; + ZVAL_DEREF(descitem); if (Z_TYPE_P(descitem) == IS_RESOURCE) { - /* should be a stream - try and dup the descriptor */ - php_stream *stream; - php_socket_t fd; - - php_stream_from_zval(stream, descitem); - - if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS)) { + if (set_proc_descriptor_from_resource(descitem, &descriptors[ndesc], ndesc) == FAILURE) { goto exit_fail; } - - descriptors[ndesc].childend = dup(fd); - if (descriptors[ndesc].childend < 0) { - php_swoole_fatal_error(E_WARNING, - "unable to dup File-Handle for descriptor " ZEND_ULONG_FMT " - %s", - nindex, - strerror(errno)); + } else if (Z_TYPE_P(descitem) == IS_ARRAY) { + if (set_proc_descriptor_from_array( + descitem, descriptors, ndesc, (int) nindex, &pty_master_fd, &pty_slave_fd) == FAILURE) { goto exit_fail; } + } else { + php_swoole_fatal_error(E_WARNING, "Descriptor item must be either an array or a File-Handle"); + goto exit_fail; + } + ndesc++; + } + ZEND_HASH_FOREACH_END(); + +#ifdef PHP_WIN32 + if (cwd == NULL) { + char *getcwd_result = VCWD_GETCWD(cur_cwd, MAXPATHLEN); + if (!getcwd_result) { + php_error_docref(NULL, E_WARNING, "Cannot get current directory"); + goto exit_fail; + } + cwd = cur_cwd; + } + cwdw = php_win32_cp_any_to_w(cwd); + if (!cwdw) { + php_error_docref(NULL, E_WARNING, "CWD conversion failed"); + goto exit_fail; + } - descriptors[ndesc].mode = DESC_FILE; + init_startup_info(&si, descriptors, ndesc); + init_process_info(&pi); - } else if (Z_TYPE_P(descitem) != IS_ARRAY) { - php_swoole_fatal_error(E_WARNING, "Descriptor item must be either an array or a File-Handle"); + if (suppress_errors) { + old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + } + + dwCreateFlags = NORMAL_PRIORITY_CLASS; + if (strcmp(sapi_module.name, "cli") != 0) { + dwCreateFlags |= CREATE_NO_WINDOW; + } + if (create_process_group) { + dwCreateFlags |= CREATE_NEW_PROCESS_GROUP; + } + if (create_new_console) { + dwCreateFlags |= CREATE_NEW_CONSOLE; + } + envpw = php_win32_cp_env_any_to_w(env.envp); + if (envpw) { + dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT; + } else { + if (env.envp) { + php_error_docref(NULL, E_WARNING, "ENV conversion failed"); goto exit_fail; - } else { - if ((ztype = zend_hash_index_find(Z_ARRVAL_P(descitem), 0)) != NULL) { - convert_to_string_ex(ztype); - } else { - php_swoole_fatal_error(E_WARNING, "Missing handle qualifier in array"); - goto exit_fail; - } + } + } - if (strcmp(Z_STRVAL_P(ztype), "pipe") == 0) { - php_file_descriptor_t newpipe[2]; - zval *zmode; - - if ((zmode = zend_hash_index_find(Z_ARRVAL_P(descitem), 1)) != NULL) { - convert_to_string_ex(zmode); - } else { - php_swoole_fatal_error(E_WARNING, "Missing mode parameter for 'pipe'"); - goto exit_fail; - } - - descriptors[ndesc].mode = DESC_PIPE; - - if (0 != socketpair(AF_UNIX, SOCK_STREAM, 0, newpipe)) { - php_swoole_fatal_error(E_WARNING, "unable to create pipe %s", strerror(errno)); - goto exit_fail; - } - - if (strncmp(Z_STRVAL_P(zmode), "w", 1) != 0) { - descriptors[ndesc].parentend = newpipe[1]; - descriptors[ndesc].childend = newpipe[0]; - descriptors[ndesc].mode |= DESC_PARENT_MODE_WRITE; - } else { - descriptors[ndesc].parentend = newpipe[0]; - descriptors[ndesc].childend = newpipe[1]; - } - descriptors[ndesc].mode_flags = descriptors[ndesc].mode & DESC_PARENT_MODE_WRITE ? O_WRONLY : O_RDONLY; - - } else if (strcmp(Z_STRVAL_P(ztype), "file") == 0) { - zval *zfile, *zmode; - php_socket_t fd; - php_stream *stream; - - descriptors[ndesc].mode = DESC_FILE; - - if ((zfile = zend_hash_index_find(Z_ARRVAL_P(descitem), 1)) != NULL) { - if (!try_convert_to_string(zfile)) { - goto exit_fail; - } - } else { - php_swoole_fatal_error(E_WARNING, "Missing file name parameter for 'file'"); - goto exit_fail; - } - - if ((zmode = zend_hash_index_find(Z_ARRVAL_P(descitem), 2)) != NULL) { - if (!try_convert_to_string(zmode)) { - goto exit_fail; - } - } else { - php_swoole_fatal_error(E_WARNING, "Missing mode parameter for 'file'"); - goto exit_fail; - } - - /* try a wrapper */ - stream = php_stream_open_wrapper( - Z_STRVAL_P(zfile), Z_STRVAL_P(zmode), REPORT_ERRORS | STREAM_WILL_CAST, NULL); - - /* force into an fd */ - if (stream == NULL || - FAILURE == php_stream_cast( - stream, PHP_STREAM_CAST_RELEASE | PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS)) { - goto exit_fail; - } - - descriptors[ndesc].childend = fd; - } else if (strcmp(Z_STRVAL_P(ztype), "redirect") == 0) { - zval *ztarget = zend_hash_index_find_deref(Z_ARRVAL_P(descitem), 1); - struct php_proc_open_descriptor_item *target = NULL; - php_file_descriptor_t childend; - - if (!ztarget) { - php_error_docref(NULL, E_WARNING, "Missing redirection target"); - goto exit_fail; - } - if (Z_TYPE_P(ztarget) != IS_LONG) { - php_error_docref(NULL, E_WARNING, "Redirection target must be an integer"); - goto exit_fail; - } - - for (i = 0; i < ndesc; i++) { - if (descriptors[i].index == Z_LVAL_P(ztarget)) { - target = &descriptors[i]; - break; - } - } - if (target) { - childend = target->childend; - } else { - if (Z_LVAL_P(ztarget) < 0 || Z_LVAL_P(ztarget) > 2) { - php_error_docref( - NULL, E_WARNING, "Redirection target " ZEND_LONG_FMT " not found", Z_LVAL_P(ztarget)); - goto exit_fail; - } - - /* Support referring to a stdin/stdout/stderr pipe adopted from the parent, - * which happens whenever an explicit override is not provided. */ -#ifndef PHP_WIN32 - childend = Z_LVAL_P(ztarget); -#else - switch (Z_LVAL_P(ztarget)) { - case 0: - childend = GetStdHandle(STD_INPUT_HANDLE); - break; - case 1: - childend = GetStdHandle(STD_OUTPUT_HANDLE); - break; - case 2: - childend = GetStdHandle(STD_ERROR_HANDLE); - break; - EMPTY_SWITCH_DEFAULT_CASE() - } -#endif - } + cmdw = php_win32_cp_conv_any_to_w(ZSTR_VAL(command_str), ZSTR_LEN(command_str), &cmdw_len); + if (!cmdw) { + php_error_docref(NULL, E_WARNING, "Command conversion failed"); + goto exit_fail; + } -#ifdef PHP_WIN32 - descriptors[ndesc].childend = dup_handle(childend, TRUE, FALSE); - if (descriptors[ndesc].childend == NULL) { - php_error_docref(NULL, E_WARNING, "Failed to dup() for descriptor " ZEND_LONG_FMT, nindex); - goto exit_fail; - } -#else - descriptors[ndesc].childend = dup(childend); - if (descriptors[ndesc].childend < 0) { - php_error_docref(NULL, - E_WARNING, - "Failed to dup() for descriptor " ZEND_LONG_FMT " - %s", - nindex, - strerror(errno)); - goto exit_fail; - } -#endif - descriptors[ndesc].mode = DESC_REDIRECT; - } else if (strcmp(Z_STRVAL_P(ztype), "null") == 0) { -#ifndef PHP_WIN32 - descriptors[ndesc].childend = open("/dev/null", O_RDWR); - if (descriptors[ndesc].childend < 0) { - php_error_docref(NULL, E_WARNING, "Failed to open /dev/null - %s", strerror(errno)); - goto exit_fail; - } -#else - descriptors[ndesc].childend = CreateFileA("nul", - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - 0, - NULL); - if (descriptors[ndesc].childend == NULL) { - php_error_docref(NULL, E_WARNING, "Failed to open nul"); - goto exit_fail; - } -#endif - descriptors[ndesc].mode = DESC_FILE; - } else if (strcmp(Z_STRVAL_P(ztype), "pty") == 0) { - php_swoole_fatal_error(E_WARNING, "pty pseudo terminal not supported on this system"); - goto exit_fail; - } else { - php_swoole_fatal_error(E_WARNING, "%s is not a valid descriptor spec/mode", Z_STRVAL_P(ztype)); - goto exit_fail; - } + if (!bypass_shell) { + if (convert_command_to_use_shell(&cmdw, cmdw_len) == FAILURE) { + goto exit_fail; } - ndesc++; } - ZEND_HASH_FOREACH_END(); + newprocok = CreateProcessW( + NULL, cmdw, &php_proc_open_security, &php_proc_open_security, TRUE, dwCreateFlags, envpw, cwdw, &si, &pi); - /* the unix way */ + if (suppress_errors) { + SetErrorMode(old_error_mode); + } + + if (newprocok == FALSE) { + DWORD dw = GetLastError(); + close_all_descriptors(descriptors, ndesc); + php_error_docref(NULL, E_WARNING, "CreateProcess failed, error code: %u", dw); + goto exit_fail; + } + + childHandle = pi.hProcess; + child = pi.dwProcessId; + CloseHandle(pi.hThread); +#elif defined(USE_POSIX_SPAWN) + posix_spawn_file_actions_t factions; + int r; + posix_spawn_file_actions_init(&factions); + + if (close_parentends_of_pipes(&factions, descriptors, ndesc) == FAILURE) { + posix_spawn_file_actions_destroy(&factions); + close_all_descriptors(descriptors, ndesc); + goto exit_fail; + } + + if (cwd) { + r = posix_spawn_file_actions_addchdir_np(&factions, cwd); + if (r != 0) { + php_error_docref(NULL, E_WARNING, "posix_spawn_file_actions_addchdir_np() failed: %s", strerror(r)); + } + } + + if (argv) { + r = posix_spawnp(&child, ZSTR_VAL(command_str), &factions, NULL, argv, (env.envarray ? env.envarray : environ)); + } else { + r = posix_spawn(&child, + "/bin/sh", + &factions, + NULL, + (char *const[]){"sh", "-c", ZSTR_VAL(command_str), NULL}, + env.envarray ? env.envarray : environ); + } + posix_spawn_file_actions_destroy(&factions); + if (r != 0) { + close_all_descriptors(descriptors, ndesc); + php_error_docref(NULL, E_WARNING, "posix_spawn() failed: %s", strerror(r)); + goto exit_fail; + } +#elif HAVE_FORK + /* the Unix way */ child = swoole_fork(SW_FORK_EXEC); if (child == 0) { - /* this is the child process */ - - /* close those descriptors that we just opened for the parent stuff, - * dup new descriptors into required descriptors and close the original - * cruft */ - for (i = 0; i < ndesc; i++) { - switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) { - case DESC_PIPE: - close(descriptors[i].parentend); - break; - } - if (dup2(descriptors[i].childend, descriptors[i].index) < 0) { - perror("dup2"); - } - if (descriptors[i].childend != descriptors[i].index) { - close(descriptors[i].childend); - } + /* This is the child process */ + + if (close_parentends_of_pipes(descriptors, ndesc) == FAILURE) { + /* We are already in child process and can't do anything to make + * `proc_open` return an error in the parent + * All we can do is exit with a non-zero (error) exit code */ + _exit(127); } if (cwd) { @@ -611,107 +1146,130 @@ PHP_FUNCTION(swoole_proc_open) { if (env.envarray) { environ = env.envarray; } - execvp(command, argv); + execvp(ZSTR_VAL(command_str), argv); } else { if (env.envarray) { - execle("/bin/sh", "sh", "-c", command, NULL, env.envarray); + execle("/bin/sh", "sh", "-c", ZSTR_VAL(command_str), NULL, env.envarray); } else { - execl("/bin/sh", "sh", "-c", command, NULL); + execl("/bin/sh", "sh", "-c", ZSTR_VAL(command_str), NULL); } } - _exit(127); + /* If execvp/execle/execl are successful, we will never reach here + * Display error and exit with non-zero (error) status code */ + php_error_docref(NULL, E_WARNING, "Exec failed: %s", strerror(errno)); + _exit(127); } else if (child < 0) { - /* failed to fork() */ - - /* clean up all the descriptors */ - for (i = 0; i < ndesc; i++) { - close(descriptors[i].childend); - if (descriptors[i].parentend) { - close(descriptors[i].parentend); - } - } - - php_swoole_fatal_error(E_WARNING, "fork failed - %s", strerror(errno)); - + /* Failed to fork() */ + close_all_descriptors(descriptors, ndesc); + php_error_docref(NULL, E_WARNING, "Fork failed: %s", strerror(errno)); goto exit_fail; } +#else +#error You lose (configure should not have let you get here) +#endif + + /* We forked/spawned and this is the parent */ - /* we forked/spawned and this is the parent */ pipes = zend_try_array_init(pipes); if (!pipes) { goto exit_fail; } - proc = (proc_co_t *) pemalloc(sizeof(proc_co_t), is_persistent); - proc->is_persistent = is_persistent; + proc = (sw_php_process_handle *) emalloc(sizeof(sw_php_process_handle)); + proc->command = zend_string_copy(command_str); proc->wstatus = nullptr; proc->running = true; - proc->command = command; proc->pipes = (zend_resource **) emalloc(sizeof(zend_resource *) * ndesc); proc->npipes = ndesc; proc->child = child; proc->env = env; - /* clean up all the child ends and then open streams on the parent - * ends, where appropriate */ + /* Clean up all the child ends and then open streams on the parent + * ends, where appropriate */ for (i = 0; i < ndesc; i++) { php_stream *stream = NULL; - close(descriptors[i].childend); - - switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) { - case DESC_PIPE: - stream = php_swoole_create_stream_from_socket(descriptors[i].parentend, AF_UNIX, SOCK_STREAM, 0 STREAMS_CC); - /* mark the descriptor close-on-exec, so that it won't be inherited by potential other children */ - fcntl(descriptors[i].parentend, F_SETFD, FD_CLOEXEC); - if (stream) { - zval retfp; + close_descriptor(descriptors[i].childend); - /* nasty hack; don't copy it */ - stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + if (descriptors[i].type == DESCRIPTOR_TYPE_PIPE) { + char *mode_string = NULL; - php_stream_to_zval(stream, &retfp); - (void) add_index_zval(pipes, descriptors[i].index, &retfp); - - proc->pipes[i] = Z_RES(retfp); - Z_ADDREF(retfp); + switch (descriptors[i].mode_flags) { +#ifdef PHP_WIN32 + case O_WRONLY | O_BINARY: + mode_string = "wb"; + break; + case O_RDONLY | O_BINARY: + mode_string = "rb"; + break; +#endif + case O_WRONLY: + mode_string = "w"; + break; + case O_RDONLY: + mode_string = "r"; + break; + case O_RDWR: + mode_string = "r+"; + break; } - break; - default: + +#ifdef PHP_WIN32 + stream = php_stream_fopen_from_fd( + _open_osfhandle((intptr_t) descriptors[i].parentend, descriptors[i].mode_flags), mode_string, NULL); + php_stream_set_option(stream, PHP_STREAM_OPTION_PIPE_BLOCKING, blocking_pipes, NULL); +#else + stream = php_swoole_create_stream_from_pipe(descriptors[i].parentend, mode_string, NULL STREAMS_CC); +#endif + } else if (descriptors[i].type == DESCRIPTOR_TYPE_SOCKET) { + stream = php_swoole_create_stream_from_socket( + (php_socket_t) descriptors[i].parentend, AF_UNIX, SOCK_STREAM, 0 STREAMS_CC); + } else { proc->pipes[i] = NULL; } - } - if (argv) { - char **arg = argv; - while (*arg != NULL) { - efree(*arg); - arg++; + if (stream) { + zval retfp; + + /* nasty hack; don't copy it */ + stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + + php_stream_to_zval(stream, &retfp); + add_index_zval(pipes, descriptors[i].index, &retfp); + + proc->pipes[i] = Z_RES(retfp); + Z_ADDREF(retfp); } - efree(argv); } - efree(descriptors); - ZVAL_RES(return_value, zend_register_resource(proc, le_proc_open)); - return; + if (1) { + RETVAL_RES(zend_register_resource(proc, le_proc_open)); + } else { + exit_fail: + _php_free_envp(env); + RETVAL_FALSE; + } -exit_fail: - if (descriptors) { - efree(descriptors); + zend_string_release_ex(command_str, false); +#ifdef PHP_WIN32 + free(cwdw); + free(cmdw); + free(envpw); +#else + efree_argv(argv); +#endif +#ifdef HAVE_OPENPTY + if (pty_master_fd != -1) { + close(pty_master_fd); } - _php_free_envp(env, is_persistent); - if (command) { - pefree(command, is_persistent); + if (pty_slave_fd != -1) { + close(pty_slave_fd); } - if (argv) { - char **arg = argv; - while (*arg != NULL) { - efree(*arg); - arg++; - } - efree(argv); +#endif + if (descriptors) { + efree(descriptors); } - RETURN_FALSE; } /* }}} */ + diff --git a/thirdparty/php/standard/proc_open.h b/thirdparty/php/standard/proc_open.h index 393c72591c5..5747f5968b8 100644 --- a/thirdparty/php/standard/proc_open.h +++ b/thirdparty/php/standard/proc_open.h @@ -1,31 +1,64 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ + */ + #include "php_swoole_cxx.h" -extern "C" -{ +SW_EXTERN_C_BEGIN +void swoole_proc_open_init(int module_number); PHP_FUNCTION(swoole_proc_open); PHP_FUNCTION(swoole_proc_close); PHP_FUNCTION(swoole_proc_get_status); PHP_FUNCTION(swoole_proc_terminate); -} +SW_EXTERN_C_END +#ifdef PHP_WIN32 +typedef HANDLE php_file_descriptor_t; +typedef DWORD php_process_id_t; +#define PHP_INVALID_FD INVALID_HANDLE_VALUE +#else typedef int php_file_descriptor_t; +typedef pid_t php_process_id_t; +#define PHP_INVALID_FD (-1) +#endif -void swoole_proc_open_init(int module_number); - -struct proc_co_env_t -{ +/* Environment block under Win32 is a NUL terminated sequence of NUL terminated + * name=value strings. + * Under Unix, it is an argv style array. */ +typedef struct { char *envp; +#ifndef PHP_WIN32 char **envarray; -}; +#endif +} sw_php_process_env; -struct proc_co_t -{ - pid_t child; +typedef struct { bool running; - int npipes; int *wstatus; + php_process_id_t child; +#ifdef PHP_WIN32 + HANDLE childHandle; +#endif + int npipes; zend_resource **pipes; - char *command; - int is_persistent; - proc_co_env_t env; -}; + zend_string *command; + sw_php_process_env env; +#if HAVE_SYS_WAIT_H + /* We can only request the status once before it becomes unavailable. + * Cache the result so we can request it multiple times. */ + int cached_exit_wait_status_value; + bool has_cached_exit_wait_status; +#endif +} sw_php_process_handle; diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index 4762dfd66a5..5776deb3039 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -170,7 +170,8 @@ typedef struct { unsigned is_pipe_blocking:1; /* allow blocking read() on pipes, currently Windows only */ unsigned no_forced_fstat:1; /* Use fstat cache even if forced */ unsigned is_seekable:1; /* don't try and seek, if not set */ - unsigned _reserved:26; + unsigned can_poll:1; + unsigned _reserved:25; int lock_flag; /* stores the lock state */ zend_string *temp_name; /* if non-null, this is the path to a temporary file that @@ -234,11 +235,15 @@ static php_stream *_sw_php_stream_fopen_from_fd_int(int fd, const char *mode, co return php_stream_alloc_rel(&sw_php_stream_stdio_ops, self, persistent_id, mode); } -static void detect_is_seekable(php_stdio_stream_data *self) { +static void _sw_detect_is_seekable(php_stdio_stream_data *self) { #if defined(S_ISFIFO) && defined(S_ISCHR) if (self->fd >= 0 && do_fstat(self, 0) == 0) { self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode)); self->is_pipe = S_ISFIFO(self->sb.st_mode); + self->can_poll = S_ISFIFO(self->sb.st_mode) || S_ISSOCK(self->sb.st_mode) || S_ISCHR(self->sb.st_mode); + if (self->can_poll) { + swoole_coroutine_socket_create(self->fd); + } } #elif defined(PHP_WIN32) zend_uintptr_t handle = _get_osfhandle(self->fd); @@ -267,7 +272,7 @@ static php_stream *_sw_php_stream_fopen_from_fd(int fd, const char *mode, const if (stream) { php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract; - detect_is_seekable(self); + _sw_detect_is_seekable(self); if (!self->is_seekable) { stream->flags |= PHP_STREAM_FLAG_NO_SEEK; stream->position = -1; @@ -306,7 +311,10 @@ static php_stream_size_t sw_php_stdiop_write(php_stream *stream, const char *buf } bytes_written = _write(data->fd, buf, (unsigned int)count); #else - ssize_t bytes_written = write(data->fd, buf, count); + php_stdio_stream_data *self = (php_stdio_stream_data*) stream->abstract; + ssize_t bytes_written = self->can_poll ? + swoole_coroutine_write(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)) : + write(data->fd, buf, count); #endif if (bytes_written < 0) { if (PHP_IS_TRANSIENT_ERROR(errno)) { @@ -368,13 +376,18 @@ static php_stream_size_t sw_php_stdiop_read(php_stream *stream, char *buf, size_ } } #endif - ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); + php_stdio_stream_data *self = (php_stdio_stream_data*) stream->abstract; + ret = self->can_poll ? + swoole_coroutine_read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)) : + read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); if (ret == (ssize_t) -1 && errno == EINTR) { /* Read was interrupted, retry once, - If read still fails, giveup with feof==0 - so script can retry if desired */ - ret = read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); + If read still fails, giveup with feof==0 + so script can retry if desired */ + ret = self->can_poll ? + swoole_coroutine_read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)) : + read(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count)); } if (ret < 0) { @@ -451,7 +464,11 @@ static int sw_php_stdiop_close(php_stream *stream, int close_handle) { if ((data->lock_flag & LOCK_EX) || (data->lock_flag & LOCK_SH)) { swoole_coroutine_flock_ex(stream->orig_path, data->fd, LOCK_UN); } - ret = close_file(data->fd); + if (data->can_poll) { + ret = swoole_coroutine_close(data->fd); + } else { + ret = close_file(data->fd); + } data->fd = -1; } else { return 0; /* everything should be closed already -> success */ From ed3af5be3b12a44ed2f5e2daa1ef05b605190c46 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 10 Apr 2024 10:48:22 +0800 Subject: [PATCH 049/103] Update boost asm (#5291) * update boost asm * Fix error --- config.m4 | 7 + include/swoole_asm_context.h | 20 +- include/swoole_coroutine_context.h | 5 + src/coroutine/context.cc | 17 +- thirdparty/boost/asm/combined.S | 3 + .../boost/asm/jump_arm64_aapcs_elf_gas.S | 37 +-- .../boost/asm/jump_arm64_aapcs_macho_gas.S | 37 +-- .../boost/asm/jump_loongarch64_sysv_elf_gas.S | 121 +++++++++ .../boost/asm/jump_mips64_n64_elf_gas.S | 29 +- .../boost/asm/jump_ppc64_sysv_elf_gas.S | 224 ++++++---------- .../boost/asm/jump_ppc64_sysv_macho_gas.S | 212 ++++++--------- .../boost/asm/jump_ppc64_sysv_xcoff_gas.S | 253 ++++++++++-------- .../boost/asm/jump_riscv64_sysv_elf_gas.S | 17 +- .../boost/asm/jump_x86_64_sysv_elf_gas.S | 142 ++++++---- .../boost/asm/jump_x86_64_sysv_macho_gas.S | 84 +++--- .../boost/asm/make_arm64_aapcs_elf_gas.S | 6 +- .../boost/asm/make_arm64_aapcs_macho_gas.S | 10 +- .../boost/asm/make_loongarch64_sysv_elf_gas.S | 72 +++++ .../boost/asm/make_mips64_n64_elf_gas.S | 1 + .../boost/asm/make_ppc64_sysv_elf_gas.S | 60 ++--- .../boost/asm/make_ppc64_sysv_macho_gas.S | 53 ++-- .../boost/asm/make_ppc64_sysv_xcoff_gas.S | 110 +++++++- .../boost/asm/make_x86_64_sysv_elf_gas.S | 91 ++++++- .../boost/asm/make_x86_64_sysv_macho_gas.S | 31 ++- 24 files changed, 950 insertions(+), 692 deletions(-) create mode 100644 thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S create mode 100644 thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S diff --git a/config.m4 b/config.m4 index c3c0c3fb0ae..a0ded1fa414 100644 --- a/config.m4 +++ b/config.m4 @@ -1099,6 +1099,7 @@ EOF [mips64*], [SW_CPU="mips64"], [mips*], [SW_CPU="mips32"], [riscv64*], [SW_CPU="riscv64"], + [loongarch64*], [SW_CPU="loongarch64"], [ SW_USE_ASM_CONTEXT="no" ] @@ -1160,6 +1161,12 @@ EOF else SW_USE_ASM_CONTEXT="no" fi + elif test "$SW_CPU" = "loongarch64"; then + if test "$SW_OS" = "LINUX"; then + SW_CONTEXT_ASM_FILE="loongarch64_sysv_elf_gas.S" + else + SW_USE_ASM_CONTEXT="no" + fi else SW_USE_ASM_CONTEXT="no" fi diff --git a/include/swoole_asm_context.h b/include/swoole_asm_context.h index 3ba14af5930..7db0fbd9452 100644 --- a/include/swoole_asm_context.h +++ b/include/swoole_asm_context.h @@ -29,6 +29,11 @@ SW_EXTERN_C_BEGIN typedef void *fcontext_t; +struct transfer_t { + fcontext_t fctx; + void * data; +}; + #ifdef __GNUC__ #define SW_GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) #else @@ -41,8 +46,19 @@ typedef void *fcontext_t; #define SW_INDIRECT_RETURN #endif -intptr_t swoole_jump_fcontext(fcontext_t *ofc, fcontext_t nfc, intptr_t vp, bool preserve_fpu = false); -SW_INDIRECT_RETURN fcontext_t swoole_make_fcontext(void *sp, size_t size, void (*fn)(intptr_t)); +#undef SWOOLE_CONTEXT_CALLDECL +#if (defined(i386) || defined(__i386__) || defined(__i386) \ + || defined(__i486__) || defined(__i586__) || defined(__i686__) \ + || defined(__X86__) || defined(_X86_) || defined(__THW_INTEL__) \ + || defined(__I86__) || defined(__INTEL__) || defined(__IA32__) \ + || defined(_M_IX86) || defined(_I86_)) && defined(BOOST_WINDOWS) +# define SWOOLE_CONTEXT_CALLDECL __cdecl +#else +# define SWOOLE_CONTEXT_CALLDECL +#endif + +transfer_t SWOOLE_CONTEXT_CALLDECL swoole_jump_fcontext(fcontext_t const to, void * vp); +fcontext_t SWOOLE_CONTEXT_CALLDECL swoole_make_fcontext(void *stack, size_t stack_size, void (* fn)(transfer_t)); SW_EXTERN_C_END diff --git a/include/swoole_coroutine_context.h b/include/swoole_coroutine_context.h index de3727444ba..ce9d43833e8 100644 --- a/include/swoole_coroutine_context.h +++ b/include/swoole_coroutine_context.h @@ -42,6 +42,7 @@ typedef ucontext_t coroutine_context_t; #elif defined(USE_ASM_CONTEXT) typedef fcontext_t coroutine_context_t; +typedef transfer_t coroutine_transfer_t; #endif typedef std::function CoroutineFunc; @@ -89,7 +90,11 @@ class Context { void *private_data_; bool end_; +#if USE_UCONTEXT static void context_func(void *arg); +#else + static void context_func(transfer_t arg); +#endif }; } // namespace coroutine diff --git a/src/coroutine/context.cc b/src/coroutine/context.cc index fdca66e0e68..31045cddd3d 100644 --- a/src/coroutine/context.cc +++ b/src/coroutine/context.cc @@ -68,7 +68,7 @@ Context::Context(size_t stack_size, CoroutineFunc fn, void *private_data) ctx_.uc_link = nullptr; makecontext(&ctx_, (void (*)(void)) & context_func, 1, this); #else - ctx_ = swoole_make_fcontext(sp, stack_size_, (void (*)(intptr_t)) & context_func); + ctx_ = swoole_make_fcontext(sp, stack_size_, (void (*)(transfer_t)) & context_func); swap_ctx_ = nullptr; #endif @@ -123,7 +123,8 @@ bool Context::swap_in() { #if USE_UCONTEXT return 0 == swapcontext(&swap_ctx_, &ctx_); #else - swoole_jump_fcontext(&swap_ctx_, ctx_, (intptr_t) this, true); + coroutine_transfer_t transfer_data = swoole_jump_fcontext(ctx_, (void *) this); + ctx_ = transfer_data.fctx; return true; #endif } @@ -132,13 +133,21 @@ bool Context::swap_out() { #if USE_UCONTEXT return 0 == swapcontext(&ctx_, &swap_ctx_); #else - swoole_jump_fcontext(&ctx_, swap_ctx_, (intptr_t) this, true); + coroutine_transfer_t transfer_data = swoole_jump_fcontext(swap_ctx_, (void *) this); + swap_ctx_ = transfer_data.fctx; return true; #endif } -void Context::context_func(void *arg) { +void Context::context_func( +#if USE_UCONTEXT + void *arg) { auto *_this = (Context *) arg; +#else + transfer_t arg) { + auto *_this = (Context *) arg.data; + _this->swap_ctx_ = arg.fctx; +#endif _this->fn_(_this->private_data_); _this->end_ = true; _this->swap_out(); diff --git a/thirdparty/boost/asm/combined.S b/thirdparty/boost/asm/combined.S index 4fdba6b03bc..3aeb528fd97 100644 --- a/thirdparty/boost/asm/combined.S +++ b/thirdparty/boost/asm/combined.S @@ -8,6 +8,9 @@ #elif defined(__arm64__) #include "make_arm64_aapcs_elf_gas.S" #include "jump_arm64_aapcs_elf_gas.S" + #elif defined(__loongarch64) + #include "make_loongarch64_sysv_elf_gas.S" + #include "jump_loongarch64_sysv_elf_gas.S" #else #error "No arch's" #endif diff --git a/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S b/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S index 4780fb86fd2..47282c18e96 100644 --- a/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S @@ -1,5 +1,5 @@ /* - Copyright Edward Nevill 2015 + Copyright Edward Nevill + Oliver Kowalke 2015 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -51,7 +51,7 @@ * * *******************************************************/ -.cpu generic+fp+simd +.file "jump_arm64_aapcs_elf_gas.S" .text .align 2 .global swoole_jump_fcontext @@ -60,23 +60,12 @@ swoole_jump_fcontext: # prepare stack for GP + FPU sub sp, sp, #0xb0 -# Because gcc may save integer registers in fp registers across a -# function call we cannot skip saving the fp registers. -# -# Do not reinstate this test unless you fully understand what you -# are doing. -# -# # test if fpu env should be preserved -# cmp w3, #0 -# b.eq 1f - # save d8 - d15 stp d8, d9, [sp, #0x00] stp d10, d11, [sp, #0x10] stp d12, d13, [sp, #0x20] stp d14, d15, [sp, #0x30] -1: # save x19-x30 stp x19, x20, [sp, #0x40] stp x21, x22, [sp, #0x50] @@ -88,17 +77,11 @@ swoole_jump_fcontext: # save LR as PC str x30, [sp, #0xa0] - # store RSP (pointing to context-data) in first argument (x0). - # STR cannot have sp as a target register + # store RSP (pointing to context-data) in X0 mov x4, sp - str x4, [x0] - - # restore RSP (pointing to context-data) from A2 (x1) - mov sp, x1 -# # test if fpu env should be preserved -# cmp w3, #0 -# b.eq 2f + # restore RSP (pointing to context-data) from X1 + mov sp, x0 # load d8 - d15 ldp d8, d9, [sp, #0x00] @@ -106,7 +89,6 @@ swoole_jump_fcontext: ldp d12, d13, [sp, #0x20] ldp d14, d15, [sp, #0x30] -2: # load x19-x30 ldp x19, x20, [sp, #0x40] ldp x21, x22, [sp, #0x50] @@ -115,9 +97,10 @@ swoole_jump_fcontext: ldp x27, x28, [sp, #0x80] ldp x29, x30, [sp, #0x90] - # use third arg as return value after jump - # and as first arg in context function - mov x0, x2 + # return transfer_t from jump + # pass transfer_t as first arg in context function + # X0 == FCTX, X1 == DATA + mov x0, x4 # load pc ldr x4, [sp, #0xa0] @@ -127,7 +110,5 @@ swoole_jump_fcontext: ret x4 .size swoole_jump_fcontext,.-swoole_jump_fcontext -#ifndef __NetBSD__ # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S b/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S index 2cac1274264..dc544e026e4 100644 --- a/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/jump_arm64_aapcs_macho_gas.S @@ -1,3 +1,9 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ /******************************************************* * * * ------------------------------------------------- * @@ -52,20 +58,12 @@ _swoole_jump_fcontext: ; prepare stack for GP + FPU sub sp, sp, #0xb0 -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - ; test if fpu env should be preserved - cmp w3, #0 - b.eq 1f - ; save d8 - d15 stp d8, d9, [sp, #0x00] stp d10, d11, [sp, #0x10] stp d12, d13, [sp, #0x20] stp d14, d15, [sp, #0x30] -1: -#endif - ; save x19-x30 stp x19, x20, [sp, #0x40] stp x21, x22, [sp, #0x50] @@ -77,18 +75,11 @@ _swoole_jump_fcontext: ; save LR as PC str lr, [sp, #0xa0] - ; store RSP (pointing to context-data) in first argument (x0). - ; STR cannot have sp as a target register + ; store RSP (pointing to context-data) in X0 mov x4, sp - str x4, [x0] - ; restore RSP (pointing to context-data) from A2 (x1) - mov sp, x1 - -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - ; test if fpu env should be preserved - cmp w3, #0 - b.eq 2f + ; restore RSP (pointing to context-data) from X1 + mov sp, x0 ; load d8 - d15 ldp d8, d9, [sp, #0x00] @@ -96,9 +87,6 @@ _swoole_jump_fcontext: ldp d12, d13, [sp, #0x20] ldp d14, d15, [sp, #0x30] -2: -#endif - ; load x19-x30 ldp x19, x20, [sp, #0x40] ldp x21, x22, [sp, #0x50] @@ -107,9 +95,10 @@ _swoole_jump_fcontext: ldp x27, x28, [sp, #0x80] ldp fp, lr, [sp, #0x90] - ; use third arg as return value after jump - ; and as first arg in context function - mov x0, x2 + ; return transfer_t from jump + ; pass transfer_t as first arg in context function + ; X0 == FCTX, X1 == DATA + mov x0, x4 ; load pc ldr x4, [sp, #0xa0] diff --git a/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S new file mode 100644 index 00000000000..89a08821ca6 --- /dev/null +++ b/thirdparty/boost/asm/jump_loongarch64_sysv_elf_gas.S @@ -0,0 +1,121 @@ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | FS0 | FS1 | FS2 | FS3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | FS4 | FS5 | FS6 | FS7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | S8 | FP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "jump_loongarch64_sysv_elf_gas.S" +.text +.globl swoole_jump_fcontext +.align 2 +.type swoole_jump_fcontext,@function +swoole_jump_fcontext: + # reserve space on stack + addi.d $sp, $sp, -160 + + # save fs0 - fs7 + fst.d $fs0, $sp, 0 + fst.d $fs1, $sp, 8 + fst.d $fs2, $sp, 16 + fst.d $fs3, $sp, 24 + fst.d $fs4, $sp, 32 + fst.d $fs5, $sp, 40 + fst.d $fs6, $sp, 48 + fst.d $fs7, $sp, 56 + + # save s0 - s8, fp, ra + st.d $s0, $sp, 64 + st.d $s1, $sp, 72 + st.d $s2, $sp, 80 + st.d $s3, $sp, 88 + st.d $s4, $sp, 96 + st.d $s5, $sp, 104 + st.d $s6, $sp, 112 + st.d $s7, $sp, 120 + st.d $s8, $sp, 128 + st.d $fp, $sp, 136 + st.d $ra, $sp, 144 + + # save RA as PC + st.d $ra, $sp, 152 + + # store SP (pointing to context-data) in A2 + move $a2, $sp + + # restore SP (pointing to context-data) from A0 + move $sp, $a0 + + # load fs0 - fs7 + fld.d $fs0, $sp, 0 + fld.d $fs1, $sp, 8 + fld.d $fs2, $sp, 16 + fld.d $fs3, $sp, 24 + fld.d $fs4, $sp, 32 + fld.d $fs5, $sp, 40 + fld.d $fs6, $sp, 48 + fld.d $fs7, $sp, 56 + + #load s0 - s7 + ld.d $s0, $sp, 64 + ld.d $s1, $sp, 72 + ld.d $s2, $sp, 80 + ld.d $s3, $sp, 88 + ld.d $s4, $sp, 96 + ld.d $s5, $sp, 104 + ld.d $s6, $sp, 112 + ld.d $s7, $sp, 120 + ld.d $s8, $sp, 128 + ld.d $fp, $sp, 136 + ld.d $ra, $sp, 144 + + # return transfer_t from jump + # pass transfer_t as first arg in context function + # a0 == FCTX, a1 == DATA + move $a0, $a2 + + # load PC + ld.d $a2, $sp, 152 + + # restore stack + addi.d $sp, $sp, 160 + + # jump to context + jr $a2 +.size swoole_jump_fcontext, .-swoole_jump_fcontext + +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S b/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S index 2c21bdaa97c..edff6ec0501 100644 --- a/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S +++ b/thirdparty/boost/asm/jump_mips64_n64_elf_gas.S @@ -5,10 +5,6 @@ http://www.boost.org/LICENSE_1_0.txt) */ -/* - "backported" version of original jump_mips64_n64_elf_gas.S -*/ - /******************************************************* * * * ------------------------------------------------- * @@ -49,6 +45,7 @@ * * * *****************************************************/ +.file "jump_mips64_n64_elf_gas.S" .text .globl swoole_jump_fcontext .align 3 @@ -71,9 +68,6 @@ swoole_jump_fcontext: sd $ra, 152($sp) # save RA as PC #if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 1f - s.d $f24, 0($sp) # save F24 s.d $f25, 8($sp) # save F25 s.d $f26, 16($sp) # save F26 @@ -82,20 +76,15 @@ swoole_jump_fcontext: s.d $f29, 40($sp) # save F29 s.d $f30, 48($sp) # save F30 s.d $f31, 56($sp) # save F31 -1: #endif - # store SP (pointing to context-data) in A0 - sd $sp, ($a0) + # store SP (pointing to old context-data) in v0 as return + move $v0, $sp - # restore SP (pointing to context-data) from A1 - move $sp, $a1 + # get SP (pointing to new context-data) from a0 param + move $sp, $a0 #if defined(__mips_hard_float) - # test if fpu env should be preserved - beqz $a3, 2f - - l.d $f24, 0($sp) # restore F24 l.d $f25, 8($sp) # restore F25 l.d $f26, 16($sp) # restore F26 @@ -104,7 +93,6 @@ swoole_jump_fcontext: l.d $f29, 40($sp) # restore F29 l.d $f30, 48($sp) # restore F30 l.d $f31, 56($sp) # restore F31 -2: #endif ld $s0, 64($sp) # restore S0 @@ -124,10 +112,8 @@ swoole_jump_fcontext: # adjust stack daddiu $sp, $sp, 160 - # use third arg as return value after jump - move $v0, $a2 - # use third arg as first arg in context function - move $a0, $a2 + move $a0, $v0 # move old sp from v0 to a0 as param + move $v1, $a1 # move *data from a1 to v1 as return # jump to context jr $t9 @@ -136,4 +122,3 @@ swoole_jump_fcontext: /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits - diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S index 5cc97550b61..a90ffbe1681 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S @@ -12,82 +12,61 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | TOC | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | TOC | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | TOC saved | FCTX | DATA | | * * ------------------------------------------------- * * * *******************************************************/ +.file "jump_ppc64_sysv_elf_gas.S" .globl swoole_jump_fcontext #if _CALL_ELF == 2 .text @@ -118,143 +97,118 @@ swoole_jump_fcontext: # endif #endif # reserve space on stack - subi %r1, %r1, 328 + subi %r1, %r1, 184 #if _CALL_ELF != 2 - std %r2, 152(%r1) # save TOC + std %r2, 0(%r1) # save TOC +#endif + std %r14, 8(%r1) # save R14 + std %r15, 16(%r1) # save R15 + std %r16, 24(%r1) # save R16 + std %r17, 32(%r1) # save R17 + std %r18, 40(%r1) # save R18 + std %r19, 48(%r1) # save R19 + std %r20, 56(%r1) # save R20 + std %r21, 64(%r1) # save R21 + std %r22, 72(%r1) # save R22 + std %r23, 80(%r1) # save R23 + std %r24, 88(%r1) # save R24 + std %r25, 96(%r1) # save R25 + std %r26, 104(%r1) # save R26 + std %r27, 112(%r1) # save R27 + std %r28, 120(%r1) # save R28 + std %r29, 128(%r1) # save R29 + std %r30, 136(%r1) # save R30 + std %r31, 144(%r1) # save R31 +#if _CALL_ELF != 2 + std %r3, 152(%r1) # save hidden #endif - std %r14, 160(%r1) # save R14 - std %r15, 168(%r1) # save R15 - std %r16, 176(%r1) # save R16 - std %r17, 184(%r1) # save R17 - std %r18, 192(%r1) # save R18 - std %r19, 200(%r1) # save R19 - std %r20, 208(%r1) # save R20 - std %r21, 216(%r1) # save R21 - std %r22, 224(%r1) # save R22 - std %r23, 232(%r1) # save R23 - std %r24, 240(%r1) # save R24 - std %r25, 248(%r1) # save R25 - std %r26, 256(%r1) # save R26 - std %r27, 264(%r1) # save R27 - std %r28, 272(%r1) # save R28 - std %r29, 280(%r1) # save R29 - std %r30, 288(%r1) # save R30 - std %r31, 296(%r1) # save R31 # save CR mfcr %r0 - std %r0, 304(%r1) + std %r0, 160(%r1) # save LR mflr %r0 - std %r0, 312(%r1) + std %r0, 168(%r1) # save LR as PC - std %r0, 320(%r1) - - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 1f - - stfd %f14, 0(%r1) # save F14 - stfd %f15, 8(%r1) # save F15 - stfd %f16, 16(%r1) # save F16 - stfd %f17, 24(%r1) # save F17 - stfd %f18, 32(%r1) # save F18 - stfd %f19, 40(%r1) # save F19 - stfd %f20, 48(%r1) # save F20 - stfd %f21, 56(%r1) # save F21 - stfd %f22, 64(%r1) # save F22 - stfd %f23, 72(%r1) # save F23 - stfd %f24, 80(%r1) # save F24 - stfd %f25, 88(%r1) # save F25 - stfd %f26, 96(%r1) # save F26 - stfd %f27, 104(%r1) # save F27 - stfd %f28, 112(%r1) # save F28 - stfd %f29, 120(%r1) # save F29 - stfd %f30, 128(%r1) # save F30 - stfd %f31, 136(%r1) # save F31 - mffs %f0 # load FPSCR - stfd %f0, 144(%r1) # save FPSCR + std %r0, 176(%r1) -1: - # store RSP (pointing to context-data) in R3 - std %r1, 0(%r3) + # store RSP (pointing to context-data) in R6 + mr %r6, %r1 +#if _CALL_ELF == 2 + # restore RSP (pointing to context-data) from R3 + mr %r1, %r3 +#else # restore RSP (pointing to context-data) from R4 mr %r1, %r4 - # test if fpu env should be preserved - cmpwi cr7, %r6, 0 - beq cr7, 2f - - lfd %f14, 0(%r1) # restore F14 - lfd %f15, 8(%r1) # restore F15 - lfd %f16, 16(%r1) # restore F16 - lfd %f17, 24(%r1) # restore F17 - lfd %f18, 32(%r1) # restore F18 - lfd %f19, 40(%r1) # restore F19 - lfd %f20, 48(%r1) # restore F20 - lfd %f21, 56(%r1) # restore F21 - lfd %f22, 64(%r1) # restore F22 - lfd %f23, 72(%r1) # restore F23 - lfd %f24, 80(%r1) # restore F24 - lfd %f25, 88(%r1) # restore F25 - lfd %f26, 96(%r1) # restore F26 - lfd %f27, 104(%r1) # restore F27 - lfd %f28, 112(%r1) # restore F28 - lfd %f29, 120(%r1) # restore F29 - lfd %f30, 128(%r1) # restore F30 - lfd %f31, 136(%r1) # restore F31 - lfd %f0, 144(%r1) # load FPSCR - mtfsf 0xff, %f0 # restore FPSCR - -2: + ld %r2, 0(%r1) # restore TOC +#endif + ld %r14, 8(%r1) # restore R14 + ld %r15, 16(%r1) # restore R15 + ld %r16, 24(%r1) # restore R16 + ld %r17, 32(%r1) # restore R17 + ld %r18, 40(%r1) # restore R18 + ld %r19, 48(%r1) # restore R19 + ld %r20, 56(%r1) # restore R20 + ld %r21, 64(%r1) # restore R21 + ld %r22, 72(%r1) # restore R22 + ld %r23, 80(%r1) # restore R23 + ld %r24, 88(%r1) # restore R24 + ld %r25, 96(%r1) # restore R25 + ld %r26, 104(%r1) # restore R26 + ld %r27, 112(%r1) # restore R27 + ld %r28, 120(%r1) # restore R28 + ld %r29, 128(%r1) # restore R29 + ld %r30, 136(%r1) # restore R30 + ld %r31, 144(%r1) # restore R31 #if _CALL_ELF != 2 - ld %r2, 152(%r1) # restore TOC + ld %r3, 152(%r1) # restore hidden #endif - ld %r14, 160(%r1) # restore R14 - ld %r15, 168(%r1) # restore R15 - ld %r16, 176(%r1) # restore R16 - ld %r17, 184(%r1) # restore R17 - ld %r18, 192(%r1) # restore R18 - ld %r19, 200(%r1) # restore R19 - ld %r20, 208(%r1) # restore R20 - ld %r21, 216(%r1) # restore R21 - ld %r22, 224(%r1) # restore R22 - ld %r23, 232(%r1) # restore R23 - ld %r24, 240(%r1) # restore R24 - ld %r25, 248(%r1) # restore R25 - ld %r26, 256(%r1) # restore R26 - ld %r27, 264(%r1) # restore R27 - ld %r28, 272(%r1) # restore R28 - ld %r29, 280(%r1) # restore R29 - ld %r30, 288(%r1) # restore R30 - ld %r31, 296(%r1) # restore R31 # restore CR - ld %r0, 304(%r1) + ld %r0, 160(%r1) mtcr %r0 # restore LR - ld %r0, 312(%r1) + ld %r0, 168(%r1) mtlr %r0 # load PC - ld %r12, 320(%r1) + ld %r12, 176(%r1) # restore CTR mtctr %r12 # adjust stack - addi %r1, %r1, 328 + addi %r1, %r1, 184 - # use third arg as return value after jump - # use third arg as first arg in context function - mr %r3, %r5 +#if _CALL_ELF == 2 + # copy transfer_t into transfer_fn arg registers + mr %r3, %r6 + # arg pointer already in %r4 # jump to context bctr -#if _CALL_ELF == 2 .size swoole_jump_fcontext, .-swoole_jump_fcontext #else + # zero in r3 indicates first jump to context-function + cmpdi %r3, 0 + beq use_entry_arg + + # return transfer_t + std %r6, 0(%r3) + std %r5, 8(%r3) + + # jump to context + bctr + +use_entry_arg: + # copy transfer_t into transfer_fn arg registers + mr %r3, %r6 + mr %r4, %r5 + + # jump to context + bctr # ifdef _CALL_LINUX .size .swoole_jump_fcontext, .-.L.swoole_jump_fcontext # else @@ -263,7 +217,5 @@ swoole_jump_fcontext: #endif -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S index d8d9aa4f513..abea7940628 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_macho_gas.S @@ -12,215 +12,153 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | R13 | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | FCTX | DATA | | | * * ------------------------------------------------- * * * *******************************************************/ .text .align 2 -.globl swoole_jump_fcontext +.globl _swoole_jump_fcontext _swoole_jump_fcontext: ; reserve space on stack - subi r1, r1, 328 - - std r13, 152(r1) ; save R13 - std r14, 160(r1) ; save R14 - std r15, 168(r1) ; save R15 - std r16, 176(r1) ; save R16 - std r17, 184(r1) ; save R17 - std r18, 192(r1) ; save R18 - std r19, 200(r1) ; save R19 - std r20, 208(r1) ; save R20 - std r21, 216(r1) ; save R21 - std r22, 224(r1) ; save R22 - std r23, 232(r1) ; save R23 - std r24, 240(r1) ; save R24 - std r25, 248(r1) ; save R25 - std r26, 256(r1) ; save R26 - std r27, 264(r1) ; save R27 - std r28, 272(r1) ; save R28 - std r29, 280(r1) ; save R29 - std r30, 288(r1) ; save R30 - std r31, 296(r1) ; save R31 + subi r1, r1, 184 + + std r14, 8(r1) ; save R14 + std r15, 16(r1) ; save R15 + std r16, 24(r1) ; save R16 + std r17, 32(r1) ; save R17 + std r18, 40(r1) ; save R18 + std r19, 48(r1) ; save R19 + std r20, 56(r1) ; save R20 + std r21, 64(r1) ; save R21 + std r22, 72(r1) ; save R22 + std r23, 80(r1) ; save R23 + std r24, 88(r1) ; save R24 + std r25, 96(r1) ; save R25 + std r26, 104(r1) ; save R26 + std r27, 112(r1) ; save R27 + std r28, 120(r1) ; save R28 + std r29, 128(r1) ; save R29 + std r30, 136(r1) ; save R30 + std r31, 144(r1) ; save R31 + std r3, 152(r1) ; save hidden ; save CR mfcr r0 - std r0, 304(r1) + std r0, 160(r1) ; save LR mflr r0 - std r0, 312(r1) + std r0, 168(r1) ; save LR as PC - std r0, 320(r1) - - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l1 - - stfd f14, 0(r1) ; save F14 - stfd f15, 8(r1) ; save F15 - stfd f16, 16(r1) ; save F16 - stfd f17, 24(r1) ; save F17 - stfd f18, 32(r1) ; save F18 - stfd f19, 40(r1) ; save F19 - stfd f20, 48(r1) ; save F20 - stfd f21, 56(r1) ; save F21 - stfd f22, 64(r1) ; save F22 - stfd f23, 72(r1) ; save F23 - stfd f24, 80(r1) ; save F24 - stfd f25, 88(r1) ; save F25 - stfd f26, 96(r1) ; save F26 - stfd f27, 104(r1) ; save F27 - stfd f28, 112(r1) ; save F28 - stfd f29, 120(r1) ; save F29 - stfd f30, 128(r1) ; save F30 - stfd f31, 136(r1) ; save F31 - mffs f0 ; load FPSCR - stfd f0, 144(r1) ; save FPSCR - -l1: - ; store RSP (pointing to context-data) in R3 - stw r1, 0(r3) + std r0, 176(r1) + + ; store RSP (pointing to context-data) in R6 + mr r6, r1 ; restore RSP (pointing to context-data) from R4 mr r1, r4 - ; test if fpu env should be preserved - cmpwi cr7, r6, 0 - beq cr7, l2 - - lfd f14, 0(r1) ; restore F14 - lfd f15, 8(r1) ; restore F15 - lfd f16, 16(r1) ; restore F16 - lfd f17, 24(r1) ; restore F17 - lfd f18, 32(r1) ; restore F18 - lfd f19, 40(r1) ; restore F19 - lfd f20, 48(r1) ; restore F20 - lfd f21, 56(r1) ; restore F21 - lfd f22, 64(r1) ; restore F22 - lfd f23, 72(r1) ; restore F23 - lfd f24, 80(r1) ; restore F24 - lfd f25, 88(r1) ; restore F25 - lfd f26, 96(r1) ; restore F26 - lfd f27, 104(r1) ; restore F27 - lfd f28, 112(r1) ; restore F28 - lfd f29, 120(r1) ; restore F29 - lfd f30, 128(r1) ; restore F30 - lfd f31, 136(r1) ; restore F31 - lfd f0, 144(r1) ; load FPSCR - mtfsf 0xff, f0 ; restore FPSCR - -2: - ld r13, 152(r1) ; restore R13 - ld r14, 160(r1) ; restore R14 - ld r15, 168(r1) ; restore R15 - ld r16, 176(r1) ; restore R16 - ld r17, 184(r1) ; restore R17 - ld r18, 192(r1) ; restore R18 - ld r19, 200(r1) ; restore R19 - ld r20, 208(r1) ; restore R20 - ld r21, 216(r1) ; restore R21 - ld r22, 224(r1) ; restore R22 - ld r23, 232(r1) ; restore R23 - ld r24, 240(r1) ; restore R24 - ld r25, 248(r1) ; restore R25 - ld r26, 256(r1) ; restore R26 - ld r27, 264(r1) ; restore R27 - ld r28, 272(r1) ; restore R28 - ld r29, 280(r1) ; restore R29 - ld r30, 288(r1) ; restore R30 - ld r31, 296(r1) ; restore R31 + ld r14, 8(r1) ; restore R14 + ld r15, 16(r1) ; restore R15 + ld r16, 24(r1) ; restore R16 + ld r17, 32(r1) ; restore R17 + ld r18, 40(r1) ; restore R18 + ld r19, 48(r1) ; restore R19 + ld r20, 56(r1) ; restore R20 + ld r21, 64(r1) ; restore R21 + ld r22, 72(r1) ; restore R22 + ld r23, 80(r1) ; restore R23 + ld r24, 88(r1) ; restore R24 + ld r25, 96(r1) ; restore R25 + ld r26, 104(r1) ; restore R26 + ld r27, 112(r1) ; restore R27 + ld r28, 120(r1) ; restore R28 + ld r29, 128(r1) ; restore R29 + ld r30, 136(r1) ; restore R30 + ld r31, 144(r1) ; restore R31 + ld r3, 152(r1) ; restore hidden ; restore CR - ld r0, 304(r1) + ld r0, 160(r1) mtcr r0 ; restore LR - ld r0, 312(r1) + ld r0, 168(r1) mtlr r0 ; load PC - ld r0, 320(r1) + ld r12, 176(r1) ; restore CTR - mtctr r0 + mtctr r12 ; adjust stack - addi r1, r1, 328 + addi r1, r1, 184 + + ; zero in r3 indicates first jump to context-function + cmpdi r3, 0 + beq use_entry_arg + + ; return transfer_t + std r6, 0(r3) + std r5, 8(r3) + + ; jump to context + bctr - ; use third arg as return value after jump - ; use third arg as first arg in context function - mr r3, r5 +use_entry_arg: + ; copy transfer_t into transfer_fn arg registers + mr r3, r6 + mr r4, r5 ; jump to context bctr diff --git a/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S b/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S index e00720b0a5e..a125f681b5e 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_xcoff_gas.S @@ -1,134 +1,173 @@ -.align 2 -.globl .swoole_jump_fcontext + +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + *******************************************************/ + + .file "jump_ppc64_sysv_xcoff_gas.S" + .toc + .csect .text[PR], 5 + .align 2 + .globl swoole_jump_fcontext[DS] + .globl .swoole_jump_fcontext + .csect swoole_jump_fcontext[DS], 3 +swoole_jump_fcontext: + .llong .swoole_jump_fcontext[PR], TOC[tc0], 0 + .csect .text[PR], 5 .swoole_jump_fcontext: # reserve space on stack - subi 1, 1, 328 - - std 13, 152(1) # save R13 - std 14, 160(1) # save R14 - std 15, 168(1) # save R15 - std 16, 176(1) # save R16 - std 17, 184(1) # save R17 - std 18, 192(1) # save R18 - std 19, 200(1) # save R19 - std 20, 208(1) # save R20 - std 21, 216(1) # save R21 - std 22, 224(1) # save R22 - std 23, 232(1) # save R23 - std 24, 240(1) # save R24 - std 25, 248(1) # save R25 - std 26, 256(1) # save R26 - std 27, 264(1) # save R27 - std 28, 272(1) # save R28 - std 29, 280(1) # save R29 - std 30, 288(1) # save R30 - std 31, 296(1) # save R31 + subi 1, 1, 184 + + std 2, 0(1) # save TOC + std 14, 8(1) # save R14 + std 15, 16(1) # save R15 + std 16, 24(1) # save R16 + std 17, 32(1) # save R17 + std 18, 40(1) # save R18 + std 19, 48(1) # save R19 + std 20, 56(1) # save R20 + std 21, 64(1) # save R21 + std 22, 72(1) # save R22 + std 23, 80(1) # save R23 + std 24, 88(1) # save R24 + std 25, 96(1) # save R25 + std 26, 104(1) # save R26 + std 27, 112(1) # save R27 + std 28, 120(1) # save R28 + std 29, 128(1) # save R29 + std 30, 136(1) # save R30 + std 31, 144(1) # save R31 + std 3, 152(1) # save hidden # save CR mfcr 0 - std 0, 304(1) + std 0, 160(1) # save LR mflr 0 - std 0, 312(1) + std 0, 168(1) # save LR as PC - std 0, 320(1) - - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label1 - - stfd 14, 0(1) # save F14 - stfd 15, 8(1) # save F15 - stfd 16, 16(1) # save F16 - stfd 17, 24(1) # save F17 - stfd 18, 32(1) # save F18 - stfd 19, 40(1) # save F19 - stfd 20, 48(1) # save F20 - stfd 21, 56(1) # save F21 - stfd 22, 64(1) # save F22 - stfd 23, 72(1) # save F23 - stfd 24, 80(1) # save F24 - stfd 25, 88(1) # save F25 - stfd 26, 96(1) # save F26 - stfd 27, 104(1) # save F27 - stfd 28, 112(1) # save F28 - stfd 29, 120(1) # save F29 - stfd 30, 128(1) # save F30 - stfd 31, 136(1) # save F31 - mffs 0 # load FPSCR - stfd 0, 144(1) # save FPSCR - -label1: - # store RSP (pointing to context-data) in R3 - stw 1, 0(3) + std 0, 176(1) + + # store RSP (pointing to context-data) in R6 + mr 6, 1 # restore RSP (pointing to context-data) from R4 mr 1, 4 - # test if fpu env should be preserved - cmpwi 7, 6, 0 - beq 7, label2 - - lfd 14, 0(1) # restore F14 - lfd 15, 8(1) # restore F15 - lfd 16, 16(1) # restore F16 - lfd 17, 24(1) # restore F17 - lfd 18, 32(1) # restore F18 - lfd 19, 40(1) # restore F19 - lfd 20, 48(1) # restore F20 - lfd 21, 56(1) # restore F21 - lfd 22, 64(1) # restore F22 - lfd 23, 72(1) # restore F23 - lfd 24, 80(1) # restore F24 - lfd 25, 88(1) # restore F25 - lfd 26, 96(1) # restore F26 - lfd 27, 104(1) # restore F27 - lfd 28, 112(1) # restore F28 - lfd 29, 120(1) # restore F29 - lfd 30, 128(1) # restore F30 - lfd 31, 136(1) # restore F31 - lfd 0, 144(1) # load FPSCR - mtfsf 0xff, 0 # restore FPSCR - -label2: - ld 13, 152(1) # restore R13 - ld 14, 160(1) # restore R14 - ld 15, 168(1) # restore R15 - ld 16, 176(1) # restore R16 - ld 17, 184(1) # restore R17 - ld 18, 192(1) # restore R18 - ld 19, 200(1) # restore R19 - ld 20, 208(1) # restore R20 - ld 21, 216(1) # restore R21 - ld 22, 224(1) # restore R22 - ld 23, 232(1) # restore R23 - ld 24, 240(1) # restore R24 - ld 25, 248(1) # restore R25 - ld 26, 256(1) # restore R26 - ld 27, 264(1) # restore R27 - ld 28, 272(1) # restore R28 - ld 29, 280(1) # restore R29 - ld 30, 288(1) # restore R30 - ld 31, 296(1) # restore R31 + ld 2, 0(1) # restore TOC + ld 14, 8(1) # restore R14 + ld 15, 16(1) # restore R15 + ld 16, 24(1) # restore R16 + ld 17, 32(1) # restore R17 + ld 18, 40(1) # restore R18 + ld 19, 48(1) # restore R19 + ld 20, 56(1) # restore R20 + ld 21, 64(1) # restore R21 + ld 22, 72(1) # restore R22 + ld 23, 80(1) # restore R23 + ld 24, 88(1) # restore R24 + ld 25, 96(1) # restore R25 + ld 26, 104(1) # restore R26 + ld 27, 112(1) # restore R27 + ld 28, 120(1) # restore R28 + ld 29, 128(1) # restore R29 + ld 30, 136(1) # restore R30 + ld 31, 144(1) # restore R31 + ld 3, 152(1) # restore hidden # restore CR - ld 0, 304(1) + ld 0, 160(1) mtcr 0 # restore LR - ld 0, 312(1) + ld 0, 168(1) mtlr 0 # load PC - ld 0, 320(1) + ld 0, 176(1) # restore CTR mtctr 0 # adjust stack - addi 1, 1, 328 + addi 1, 1, 184 + + # zero in r3 indicates first jump to context-function + cmpdi 3, 0 + beq use_entry_arg + + # return transfer_t + std 6, 0(3) + std 5, 8(3) + + # jump to context + bctr - # use third arg as return value after jump - # use third arg as first arg in context function - mr 3, 5 +use_entry_arg: + # copy transfer_t into transfer_fn arg registers + mr 3, 6 + mr 4, 5 # jump to context bctr diff --git a/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S index 23f66d60b2a..a2f9a2f3bb3 100644 --- a/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_riscv64_sysv_elf_gas.S @@ -66,8 +66,6 @@ swoole_jump_fcontext: # prepare stack for GP + FPU addi sp, sp, -0xd0 - beqz a3, .L1 - # save fs0 - fs11 fsd fs0, 0x00(sp) fsd fs1, 0x08(sp) @@ -81,7 +79,6 @@ swoole_jump_fcontext: fsd fs9, 0x48(sp) fsd fs10, 0x50(sp) fsd fs11, 0x58(sp) -.L1: # save s0-s11, ra sd s0, 0x60(sp) @@ -101,13 +98,12 @@ swoole_jump_fcontext: # save RA as PC sd ra, 0xc8(sp) - # store SP (pointing to context-data) in A0 - sd sp, (a0) + # store SP (pointing to context-data) in A2 + mv a2, sp - # restore SP (pointing to context-data) from A1 - mv sp, a1 + # restore SP (pointing to context-data) from A0 + mv sp, a0 - beqz a3, .L2 # load fs0 - fs11 fld fs0, 0x00(sp) fld fs1, 0x08(sp) @@ -121,7 +117,6 @@ swoole_jump_fcontext: fld fs9, 0x48(sp) fld fs10, 0x50(sp) fld fs11, 0x58(sp) -.L2: # load s0-s11,ra ld s0, 0x60(sp) @@ -138,7 +133,9 @@ swoole_jump_fcontext: ld s11, 0xb8(sp) ld ra, 0xc0(sp) - # use A2 as return value + # return transfer_t from jump + # pass transfer_t as first arg in context function + # a0 == FCTX, a1 == DATA mv a0, a2 # load pc diff --git a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S index 64193d5a5fd..7b80132b67e 100644 --- a/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_x86_64_sysv_elf_gas.S @@ -12,95 +12,131 @@ * ---------------------------------------------------------------------------------- * * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * | fc_mxcsr|fc_x87_cw| guard | R12 | R13 | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ---------------------------------------------------------------------------------- * * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * * ---------------------------------------------------------------------------------- * - * | R15 | RBX | RBP | RIP | * + * | R14 | R15 | RBX | RBP | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ---------------------------------------------------------------------------------- * * | 0x40 | 0x44 | | * * ---------------------------------------------------------------------------------- * - * | EXIT | | * + * | RIP | | * * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ -#ifdef __CET__ -#include -#else -#define _CET_ENDBR -#endif +# if defined __CET__ +# include +# define SWOOLE_SHSTK_ENABLED (__CET__ & 0x2) +# define SWOOLE_CONTEXT_SHADOW_STACK (SWOOLE_SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# else +# define _CET_ENDBR +# endif +.file "jump_x86_64_sysv_elf_gas.S" .text .globl swoole_jump_fcontext .type swoole_jump_fcontext,@function .align 16 swoole_jump_fcontext: _CET_ENDBR - pushq %rbp /* save RBP */ - pushq %rbx /* save RBX */ - pushq %r15 /* save R15 */ - pushq %r14 /* save R14 */ - pushq %r13 /* save R13 */ - pushq %r12 /* save R12 */ - - /* prepare stack for FPU */ - leaq -0x8(%rsp), %rsp + leaq -0x40(%rsp), %rsp /* prepare stack */ - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 1f +#if !defined(SWOOLE_USE_TSX) + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ +#endif - /* save MMX control- and status-word */ - stmxcsr (%rsp) - /* save x87 control-word */ - fnstcw 0x4(%rsp) +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + movq %fs:0x28, %rcx /* read stack guard from TLS record */ + movq %rcx, 0x8(%rsp) /* save stack guard */ +#endif -1: - /* store RSP (pointing to context-data) in RDI */ - movq %rsp, (%rdi) + movq %r12, 0x10(%rsp) /* save R12 */ + movq %r13, 0x18(%rsp) /* save R13 */ + movq %r14, 0x20(%rsp) /* save R14 */ + movq %r15, 0x28(%rsp) /* save R15 */ + movq %rbx, 0x30(%rsp) /* save RBX */ + movq %rbp, 0x38(%rsp) /* save RBP */ - /* restore RSP (pointing to context-data) from RSI */ - movq %rsi, %rsp +#if SWOOLE_CONTEXT_SHADOW_STACK + /* grow the stack to reserve space for shadow stack pointer(SSP) */ + leaq -0x8(%rsp), %rsp + /* read the current SSP and store it */ + rdsspq %rcx + movq %rcx, (%rsp) +#endif - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 2f + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax - /* restore MMX control- and status-word */ - ldmxcsr (%rsp) - /* restore x87 control-word */ - fldcw 0x4(%rsp) + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp -2: - /* prepare stack for FPU */ +#if SWOOLE_CONTEXT_SHADOW_STACK + /* first 8 bytes are SSP */ + movq (%rsp), %rcx leaq 0x8(%rsp), %rsp - popq %r12 /* restrore R12 */ - popq %r13 /* restrore R13 */ - popq %r14 /* restrore R14 */ - popq %r15 /* restrore R15 */ - popq %rbx /* restrore RBX */ - popq %rbp /* restrore RBP */ + /* Restore target(new) shadow stack */ + rstorssp -8(%rcx) + /* restore token for previous shadow stack is pushed */ + /* on previous shadow stack after saveprevssp */ + saveprevssp + + /* when return, swoole_jump_fcontext jump to restored return address */ + /* (r8) instead of RET. This miss of RET implies us to unwind */ + /* shadow stack accordingly. Otherwise mismatch occur */ + movq $1, %rcx + incsspq %rcx +#endif + + movq 0x40(%rsp), %r8 /* restore return-address */ - /* restore return-address */ - popq %r8 +#if !defined(SWOOLE_USE_TSX) + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ +#endif + +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + movq 0x8(%rsp), %rdx /* load stack guard */ + movq %rdx, %fs:0x28 /* restore stack guard to TLS record */ +#endif + + movq 0x10(%rsp), %r12 /* restore R12 */ + movq 0x18(%rsp), %r13 /* restore R13 */ + movq 0x20(%rsp), %r14 /* restore R14 */ + movq 0x28(%rsp), %r15 /* restore R15 */ + movq 0x30(%rsp), %rbx /* restore RBX */ + movq 0x38(%rsp), %rbp /* restore RBP */ + + leaq 0x48(%rsp), %rsp /* prepare stack */ - /* use third arg as return-value after jump */ - movq %rdx, %rax - /* use third arg as first arg in context function */ - movq %rdx, %rdi + /* return transfer_t from jump */ +#if !defined(_ILP32) + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx +#else + /* RAX == data:fctx */ + salq $32, %rsi + orq %rsi, %rax +#endif + /* pass transfer_t as first arg in context function */ +#if !defined(_ILP32) + /* RDI == fctx, RSI == data */ +#else + /* RDI == data:fctx */ +#endif + movq %rax, %rdi /* indirect jump to context */ jmp *%r8 .size swoole_jump_fcontext,.-swoole_jump_fcontext -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S index 1515a6e7f9f..0bf18bd763f 100644 --- a/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_x86_64_sysv_macho_gas.S @@ -21,13 +21,6 @@ * ---------------------------------------------------------------------------------- * * | R15 | RBX | RBP | RIP | * * ---------------------------------------------------------------------------------- * - * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * - * ---------------------------------------------------------------------------------- * - * | 0x40 | 0x44 | | * - * ---------------------------------------------------------------------------------- * - * | EXIT | | * - * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ @@ -35,59 +28,48 @@ .globl _swoole_jump_fcontext .align 8 _swoole_jump_fcontext: - pushq %rbp /* save RBP */ - pushq %rbx /* save RBX */ - pushq %r15 /* save R15 */ - pushq %r14 /* save R14 */ - pushq %r13 /* save R13 */ - pushq %r12 /* save R12 */ - - /* prepare stack for FPU */ - leaq -0x8(%rsp), %rsp - - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 1f + leaq -0x38(%rsp), %rsp /* prepare stack */ - /* save MMX control- and status-word */ - stmxcsr (%rsp) - /* save x87 control-word */ - fnstcw 0x4(%rsp) +#if !defined(SWOOLE_USE_TSX) + stmxcsr (%rsp) /* save MMX control- and status-word */ + fnstcw 0x4(%rsp) /* save x87 control-word */ +#endif -1: - /* store RSP (pointing to context-data) in RDI */ - movq %rsp, (%rdi) + movq %r12, 0x8(%rsp) /* save R12 */ + movq %r13, 0x10(%rsp) /* save R13 */ + movq %r14, 0x18(%rsp) /* save R14 */ + movq %r15, 0x20(%rsp) /* save R15 */ + movq %rbx, 0x28(%rsp) /* save RBX */ + movq %rbp, 0x30(%rsp) /* save RBP */ - /* restore RSP (pointing to context-data) from RSI */ - movq %rsi, %rsp + /* store RSP (pointing to context-data) in RAX */ + movq %rsp, %rax - /* test for flag preserve_fpu */ - cmp $0, %rcx - je 2f + /* restore RSP (pointing to context-data) from RDI */ + movq %rdi, %rsp - /* restore MMX control- and status-word */ - ldmxcsr (%rsp) - /* restore x87 control-word */ - fldcw 0x4(%rsp) + movq 0x38(%rsp), %r8 /* restore return-address */ -2: - /* prepare stack for FPU */ - leaq 0x8(%rsp), %rsp +#if !defined(SWOOLE_USE_TSX) + ldmxcsr (%rsp) /* restore MMX control- and status-word */ + fldcw 0x4(%rsp) /* restore x87 control-word */ +#endif - popq %r12 /* restrore R12 */ - popq %r13 /* restrore R13 */ - popq %r14 /* restrore R14 */ - popq %r15 /* restrore R15 */ - popq %rbx /* restrore RBX */ - popq %rbp /* restrore RBP */ + movq 0x8(%rsp), %r12 /* restore R12 */ + movq 0x10(%rsp), %r13 /* restore R13 */ + movq 0x18(%rsp), %r14 /* restore R14 */ + movq 0x20(%rsp), %r15 /* restore R15 */ + movq 0x28(%rsp), %rbx /* restore RBX */ + movq 0x30(%rsp), %rbp /* restore RBP */ - /* restore return-address */ - popq %r8 + leaq 0x40(%rsp), %rsp /* prepare stack */ - /* use third arg as return-value after jump */ - movq %rdx, %rax - /* use third arg as first arg in context function */ - movq %rdx, %rdi + /* return transfer_t from jump */ + /* RAX == fctx, RDX == data */ + movq %rsi, %rdx + /* pass transfer_t as first arg in context function */ + /* RDI == fctx, RSI == data */ + movq %rax, %rdi /* indirect jump to context */ jmp *%r8 diff --git a/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S b/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S index 1fc23f4e369..fd98d15984d 100644 --- a/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S +++ b/thirdparty/boost/asm/make_arm64_aapcs_elf_gas.S @@ -1,5 +1,5 @@ /* - Copyright Edward Nevill 2015 + Copyright Edward Nevill + Oliver Kowalke 2015 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -51,7 +51,7 @@ * * *******************************************************/ -.cpu generic+fp+simd +.file "make_arm64_aapcs_elf_gas.S" .text .align 2 .global swoole_make_fcontext @@ -81,7 +81,5 @@ finish: bl _exit .size swoole_make_fcontext,.-swoole_make_fcontext -#ifndef __NetBSD__ # Mark that we don't need executable stack. .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S b/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S index 556cc15e519..7977c0ee9bd 100644 --- a/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S +++ b/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S @@ -1,3 +1,9 @@ +/* + Copyright Edward Nevill + Oliver Kowalke 2015 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ /******************************************************* * * * ------------------------------------------------- * @@ -45,7 +51,6 @@ * * *******************************************************/ - .text .globl _swoole_make_fcontext .balign 16 @@ -61,9 +66,6 @@ _swoole_make_fcontext: ; store address as a PC to jump in str x2, [x0, #0xa0] - ; compute abs address of label finish - ; 0x0c = 3 instructions * size (4) before label 'finish' - adr x1, finish ; save address of finish as return-address for context-function diff --git a/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S b/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S new file mode 100644 index 00000000000..5359e0235e1 --- /dev/null +++ b/thirdparty/boost/asm/make_loongarch64_sysv_elf_gas.S @@ -0,0 +1,72 @@ +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 8 | 16 | 24 | * + * ------------------------------------------------- * + * | FS0 | FS1 | FS2 | FS3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 40 | 48 | 56 | * + * ------------------------------------------------- * + * | FS4 | FS5 | FS6 | FS7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 72 | 80 | 88 | * + * ------------------------------------------------- * + * | S0 | S1 | S2 | S3 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | S4 | S5 | S6 | S7 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | S8 | FP | RA | PC | * + * ------------------------------------------------- * + * * + * *****************************************************/ + +.file "make_loongarch64_sysv_elf_gas.S" +.text +.globl swoole_make_fcontext +.align 2 +.type swoole_make_fcontext,@function +swoole_make_fcontext: + # shift address in A0 to lower 16 byte boundary + bstrins.d $a0, $zero, 3, 0 + + # reserve space for context-data on context-stack + addi.d $a0, $a0, -160 + + # third arg of swoole_make_fcontext() == address of context-function + st.d $a2, $a0, 152 + + # save address of finish as return-address for context-function + # will be entered after context-function returns + la.local $a4, finish + st.d $a4, $a0, 144 + + # return pointer to context-data + jr $ra + +finish: + # exit code is zero + li.d $a0, 0 + # call _exit(0) + b %plt(_exit) + +.size swoole_make_fcontext, .-swoole_make_fcontext +/* Mark that we don't need executable stack. */ +.section .note.GNU-stack,"",%progbits diff --git a/thirdparty/boost/asm/make_mips64_n64_elf_gas.S b/thirdparty/boost/asm/make_mips64_n64_elf_gas.S index 888ddc26ca3..d3d46313b6b 100644 --- a/thirdparty/boost/asm/make_mips64_n64_elf_gas.S +++ b/thirdparty/boost/asm/make_mips64_n64_elf_gas.S @@ -45,6 +45,7 @@ * * * *****************************************************/ +.file "make_mips64_n64_elf_gas.S" .text .globl swoole_make_fcontext .align 3 diff --git a/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S index 71af0db1a52..59354f8dde5 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_elf_gas.S @@ -12,82 +12,61 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | TOC | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | TOC | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | TOC saved | FCTX | DATA | | * * ------------------------------------------------- * * * *******************************************************/ +.file "make_ppc64_sysv_elf_gas.S" .globl swoole_make_fcontext #if _CALL_ELF == 2 .text @@ -126,20 +105,29 @@ swoole_make_fcontext: # reserve space for context-data on context-stack # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi %r3, %r3, 392 + subi %r3, %r3, 248 # third arg of swoole_make_fcontext() == address of context-function # entry point (ELFv2) or descriptor (ELFv1) #if _CALL_ELF == 2 # save address of context-function entry point - std %r5, 320(%r3) + std %r5, 176(%r3) #else # save address of context-function entry point ld %r4, 0(%r5) - std %r4, 320(%r3) + std %r4, 176(%r3) # save TOC of context-function ld %r4, 8(%r5) - std %r4, 152(%r3) + std %r4, 0(%r3) +#endif + + # set back-chain to zero + li %r0, 0 + std %r0, 184(%r3) + +#if _CALL_ELF != 2 + # zero in r3 indicates first jump to context-function + std %r0, 152(%r3) #endif # load LR @@ -155,7 +143,7 @@ swoole_make_fcontext: mtlr %r0 # save address of finish as return-address for context-function # will be entered after context-function returns - std %r4, 312(%r3) + std %r4, 168(%r3) # restore return address from R6 mtlr %r6 @@ -185,7 +173,5 @@ finish: # endif #endif -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S index d656cab6554..717f3bb2cf2 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_macho_gas.S @@ -12,78 +12,56 @@ * ------------------------------------------------- * * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * * ------------------------------------------------- * - * | F14 | F15 | F16 | F17 | * + * | R13 | R14 | R15 | R16 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ------------------------------------------------- * * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * * ------------------------------------------------- * - * | F18 | F19 | F20 | F21 | * + * | R17 | R18 | R19 | R20 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ------------------------------------------------- * * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * * ------------------------------------------------- * - * | F22 | F23 | F24 | F25 | * + * | R21 | R22 | R23 | R24 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * * ------------------------------------------------- * * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * * ------------------------------------------------- * - * | F26 | F27 | F28 | F29 | * + * | R25 | R26 | R27 | R28 | * * ------------------------------------------------- * * ------------------------------------------------- * * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * * ------------------------------------------------- * * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * * ------------------------------------------------- * - * | F30 | F31 | fpscr | R13 | * + * | R29 | R30 | R31 | hidden | * * ------------------------------------------------- * * ------------------------------------------------- * * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * * ------------------------------------------------- * * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * * ------------------------------------------------- * - * | R14 | R15 | R16 | R17 | * + * | CR | LR | PC | back-chain| * * ------------------------------------------------- * * ------------------------------------------------- * * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * * ------------------------------------------------- * * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * * ------------------------------------------------- * - * | R18 | R19 | R20 | R21 | * + * | cr saved | lr saved | compiler | linker | * * ------------------------------------------------- * * ------------------------------------------------- * * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * * ------------------------------------------------- * * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * * ------------------------------------------------- * - * | R22 | R23 | R24 | R25 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | * - * ------------------------------------------------- * - * | 256 | 260 | 264 | 268 | 272 | 276 | 280 | 284 | * - * ------------------------------------------------- * - * | R26 | R27 | R28 | R29 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | * - * ------------------------------------------------- * - * | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | R30 | R31 | CR | LR | * - * ------------------------------------------------- * - * ------------------------------------------------- * - * | 80 | 81 | | * - * ------------------------------------------------- * - * | 320 | 324 | | * - * ------------------------------------------------- * - * | PC | | * + * | FCTX | DATA | | | * * ------------------------------------------------- * * * *******************************************************/ @@ -100,10 +78,19 @@ _swoole_make_fcontext: ; reserve space for context-data on context-stack ; including 64 byte of linkage + parameter area (R1 16 == 0) - subi r3, r3, 392 + subi r3, r3, 240 ; third arg of swoole_make_fcontext() == address of context-function - stw r5, 320(r3) + stw r5, 176(r3) + + ; set back-chain to zero + li r0, 0 + std r0, 184(r3) + + ; compute address of returned transfer_t + addi r0, r3, 224 + mr r4, r0 + std r4, 152(r3) ; load LR mflr r0 @@ -118,7 +105,7 @@ l1: mtlr r0 ; save address of finish as return-address for context-function ; will be entered after context-function returns - std r4, 312(r3) + std r4, 168(r3) ; restore return address from R6 mtlr r6 diff --git a/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S b/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S index b9dfb189767..58fd12eb916 100644 --- a/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S +++ b/thirdparty/boost/asm/make_ppc64_sysv_xcoff_gas.S @@ -1,22 +1,106 @@ - .globl swoole_make_fcontext[DS] - .globl .swoole_make_fcontext[PR] - .align 2 - .csect .swoole_make_fcontext[PR], 3 - .globl _swoole_make_fcontext -#._swoole_make_fcontext: +/* + Copyright Oliver Kowalke 2009. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +/******************************************************* + * * + * ------------------------------------------------- * + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * + * ------------------------------------------------- * + * | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | * + * ------------------------------------------------- * + * | TOC | R14 | R15 | R16 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * + * ------------------------------------------------- * + * | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | * + * ------------------------------------------------- * + * | R17 | R18 | R19 | R20 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * + * ------------------------------------------------- * + * | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | * + * ------------------------------------------------- * + * | R21 | R22 | R23 | R24 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | * + * ------------------------------------------------- * + * | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | * + * ------------------------------------------------- * + * | R25 | R26 | R27 | R28 | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | * + * ------------------------------------------------- * + * | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | * + * ------------------------------------------------- * + * | R29 | R30 | R31 | hidden | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | * + * ------------------------------------------------- * + * | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | * + * ------------------------------------------------- * + * | CR | LR | PC | back-chain| * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | * + * ------------------------------------------------- * + * | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | * + * ------------------------------------------------- * + * | cr saved | lr saved | compiler | linker | * + * ------------------------------------------------- * + * ------------------------------------------------- * + * | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | * + * ------------------------------------------------- * + * | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | * + * ------------------------------------------------- * + * | TOC saved | FCTX | DATA | | * + * ------------------------------------------------- * + * * + *******************************************************/ + + .file "make_ppc64_sysv_xcoff_gas.S" + .toc + .csect .text[PR], 5 + .align 2 + .globl swoole_make_fcontext[DS] + .globl .swoole_make_fcontext + .csect swoole_make_fcontext[DS], 3 +swoole_make_fcontext: + .llong .swoole_make_fcontext[PR], TOC[tc0], 0 + .csect .text[PR], 5 +.swoole_make_fcontext: # save return address into R6 mflr 6 # first arg of swoole_make_fcontext() == top address of context-function # shift address in R3 to lower 16 byte boundary - clrrwi 3, 3, 4 + clrrdi 3, 3, 4 # reserve space for context-data on context-stack # including 64 byte of linkage + parameter area (R1 % 16 == 0) - subi 3, 3, 392 + subi 3, 3, 248 + + # third arg of swoole_make_fcontext() == address of context-function descriptor + ld 4, 0(5) + std 4, 176(3) + # save TOC of context-function + ld 4, 8(5) + std 4, 0(3) + + # set back-chain to zero + li 0, 0 + std 0, 184(3) - # third arg of swoole_make_fcontext() == address of context-function - stw 5, 320(3) + # zero in r3 indicates first jump to context-function + std 0, 152(3) # load LR mflr 0 @@ -31,7 +115,7 @@ mtlr 0 # save address of finish as return-address for context-function # will be entered after context-function returns - stw 4, 312(3) + std 4, 168(3) # restore return address from R6 mtlr 6 @@ -42,9 +126,9 @@ # save return address into R0 mflr 0 # save return address on stack, set up stack frame - stw 0, 8(1) + std 0, 8(1) # allocate stack space, R1 % 16 == 0 - stwu 1, -32(1) + stdu 1, -32(1) # exit code is zero li 3, 0 diff --git a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S index 34ce3b26f4e..00674735f38 100644 --- a/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/make_x86_64_sysv_elf_gas.S @@ -12,36 +12,44 @@ * ---------------------------------------------------------------------------------- * * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * * ---------------------------------------------------------------------------------- * - * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * + * | fc_mxcsr|fc_x87_cw| guard | R12 | R13 | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * * ---------------------------------------------------------------------------------- * * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * * ---------------------------------------------------------------------------------- * - * | R15 | RBX | RBP | RIP | * + * | R14 | R15 | RBX | RBP | * * ---------------------------------------------------------------------------------- * * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * + * | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | * * ---------------------------------------------------------------------------------- * * | 0x40 | 0x44 | | * * ---------------------------------------------------------------------------------- * - * | EXIT | | * + * | RIP | | * * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ -#ifdef __CET__ -#include -#else -#define _CET_ENDBR -#endif +# if defined __CET__ +# include +# define SWOOLE_SHSTK_ENABLED (__CET__ & 0x2) +# define SWOOLE_CONTEXT_SHADOW_STACK (SWOOLE_SHSTK_ENABLED && SHADOW_STACK_SYSCALL) +# else +# define _CET_ENDBR +# endif +.file "make_x86_64_sysv_elf_gas.S" .text .globl swoole_make_fcontext .type swoole_make_fcontext,@function .align 16 swoole_make_fcontext: _CET_ENDBR +#if SWOOLE_CONTEXT_SHADOW_STACK + /* the new shadow stack pointer (SSP) */ + movq -0x8(%rdi), %r9 +#endif + /* first arg of swoole_make_fcontext() == top of context-stack */ movq %rdi, %rax @@ -49,26 +57,83 @@ swoole_make_fcontext: andq $-16, %rax /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. RIP + return-address for context-function */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x48(%rax), %rax /* third arg of swoole_make_fcontext() == address of context-function */ - movq %rdx, 0x38(%rax) + /* stored in RBX */ + movq %rdx, 0x30(%rax) /* save MMX control- and status-word */ stmxcsr (%rax) /* save x87 control-word */ fnstcw 0x4(%rax) +#if defined(SWOOLE_CONTEXT_TLS_STACK_PROTECTOR) + /* save stack guard */ + movq %fs:0x28, %rcx /* read stack guard from TLS record */ + movq %rcx, 0x8(%rsp) /* save stack guard */ +#endif + + /* compute abs address of label trampoline */ + leaq trampoline(%rip), %rcx + /* save address of trampoline as return-address for context-function */ + /* will be entered after calling jump_fcontext() first time */ + movq %rcx, 0x40(%rax) + /* compute abs address of label finish */ leaq finish(%rip), %rcx /* save address of finish as return-address for context-function */ /* will be entered after context-function returns */ - movq %rcx, 0x40(%rax) + movq %rcx, 0x38(%rax) + +#if SWOOLE_CONTEXT_SHADOW_STACK + /* Populate the shadow stack and normal stack */ + /* get original SSP */ + rdsspq %r8 + /* restore new shadow stack */ + rstorssp -0x8(%r9) + /* save the restore token on the original shadow stack */ + saveprevssp + /* push the address of "jmp trampoline" to the new shadow stack */ + /* as well as the stack */ + call 1f + jmp trampoline +1: + /* save address of "jmp trampoline" as return-address */ + /* for context-function */ + pop 0x38(%rax) + /* Get the new SSP. */ + rdsspq %r9 + /* restore original shadow stack */ + rstorssp -0x8(%r8) + /* save the restore token on the new shadow stack. */ + saveprevssp + + /* reserve space for the new SSP */ + leaq -0x8(%rax), %rax + /* save the new SSP to this fcontext */ + movq %r9, (%rax) +#endif ret /* return pointer to context-data */ +trampoline: + _CET_ENDBR + /* store return address on stack */ + /* fix stack alignment */ +#if SWOOLE_CONTEXT_SHADOW_STACK + /* save address of "jmp *%rbp" as return-address */ + /* on stack and shadow stack */ + call 2f + jmp *%rbp +2: +#else + push %rbp +#endif + /* jump to context-function */ + jmp *%rbx + finish: _CET_ENDBR /* exit code is zero */ @@ -78,7 +143,5 @@ finish: hlt .size swoole_make_fcontext,.-swoole_make_fcontext -#ifndef __NetBSD__ /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits -#endif diff --git a/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S b/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S index 79995c8833b..46f8bd15cc4 100644 --- a/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_x86_64_sysv_macho_gas.S @@ -21,13 +21,6 @@ * ---------------------------------------------------------------------------------- * * | R15 | RBX | RBP | RIP | * * ---------------------------------------------------------------------------------- * - * ---------------------------------------------------------------------------------- * - * | 16 | 17 | | * - * ---------------------------------------------------------------------------------- * - * | 0x40 | 0x44 | | * - * ---------------------------------------------------------------------------------- * - * | EXIT | | * - * ---------------------------------------------------------------------------------- * * * ****************************************************************************************/ @@ -39,30 +32,42 @@ _swoole_make_fcontext: movq %rdi, %rax /* shift address in RAX to lower 16 byte boundary */ - movabs $-16, %r8 - andq %r8, %rax + andq $-16, %rax /* reserve space for context-data on context-stack */ - /* size for fc_mxcsr .. RIP + return-address for context-function */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ - leaq -0x48(%rax), %rax + leaq -0x40(%rax), %rax /* third arg of swoole_make_fcontext() == address of context-function */ - movq %rdx, 0x38(%rax) + /* stored in RBX */ + movq %rdx, 0x28(%rax) /* save MMX control- and status-word */ stmxcsr (%rax) /* save x87 control-word */ fnstcw 0x4(%rax) + /* compute abs address of label trampoline */ + leaq trampoline(%rip), %rcx + /* save address of trampoline as return-address for context-function */ + /* will be entered after calling jump_fcontext() first time */ + movq %rcx, 0x38(%rax) + /* compute abs address of label finish */ leaq finish(%rip), %rcx /* save address of finish as return-address for context-function */ /* will be entered after context-function returns */ - movq %rcx, 0x40(%rax) + movq %rcx, 0x30(%rax) ret /* return pointer to context-data */ +trampoline: + /* store return address on stack */ + /* fix stack alignment */ + push %rbp + /* jump to context-function */ + jmp *%rbx + finish: /* exit code is zero */ xorq %rdi, %rdi From cc6cd14e8faacb8adb84259d1f0ed870623c745f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Apr 2024 14:44:48 +0800 Subject: [PATCH 050/103] fix tests --- ext-src/swoole_process.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 1ffebed1adc..1adfb84029d 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -602,8 +602,8 @@ void php_swoole_process_clean() { } } #ifndef SW_THREAD - if (sw_get_process_type() != SW_PROCESS_USERWORKER) { - sw_set_process_type(0); + if (swoole_get_process_type() != SW_PROCESS_USERWORKER) { + swoole_set_process_type(0); } #endif } From 907269ce6a18cccc5e0d07cb0d474c291390f11d Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 10 Apr 2024 16:39:17 +0800 Subject: [PATCH 051/103] fix tests --- CMakeLists.txt | 2 +- include/swoole_server.h | 14 +++++++------- scripts/make.sh | 8 +------- src/server/base.cc | 1 - src/server/manager.cc | 35 +++++++++++++++-------------------- src/server/master.cc | 1 + src/server/process.cc | 14 +++++++------- 7 files changed, 32 insertions(+), 43 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 08009e56302..848bb88b72a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ cmake_minimum_required(VERSION 2.8) file(READ ./config.h SWOOLE_CONFIG_FILE) set(CMAKE_MACOSX_RPATH 1) -set(SWOOLE_LINK_LIBRARIES pthread dl) +set(SWOOLE_LINK_LIBRARIES pthread dl uring) if (APPLE) set(CMAKE_SHARED_LINKER_FLAGS "-undefined dynamic_lookup") diff --git a/include/swoole_server.h b/include/swoole_server.h index 7f8ce3c590a..69df9407b9f 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -406,6 +406,13 @@ class Factory { Factory(Server *_server) { server_ = _server; } + pid_t spawn_event_worker(Worker *worker); + pid_t spawn_user_worker(Worker *worker); + pid_t spawn_task_worker(Worker *worker); + void kill_user_workers(); + void kill_event_workers(); + void kill_task_workers(); + void check_worker_exit_status(Worker *worker, const ExitStatus &exit_status); virtual ~Factory() {} virtual bool start() = 0; virtual bool shutdown() = 0; @@ -431,13 +438,6 @@ class ProcessFactory : public Factory { public: ProcessFactory(Server *server); ~ProcessFactory(); - pid_t spawn_event_worker(Worker *worker); - pid_t spawn_user_worker(Worker *worker); - pid_t spawn_task_worker(Worker *worker); - void kill_user_workers(); - void kill_event_workers(); - void kill_task_workers(); - void check_worker_exit_status(Worker *worker, const ExitStatus &exit_status); bool start() override; bool shutdown() override; bool dispatch(SendData *) override; diff --git a/scripts/make.sh b/scripts/make.sh index fd46c8c9d90..46e6aff78a9 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -7,7 +7,6 @@ COMPILE_PARAMS="--enable-openssl \ --enable-swoole-curl \ --enable-cares \ --enable-swoole-pgsql \ ---enable-swoole-thread \ --with-swoole-odbc=unixODBC,/usr \ --enable-swoole-sqlite" @@ -89,9 +88,4 @@ else fi make clean make -j ${CPU_COUNT} - -if [ "$(whoami)" = "root" ]; then - make install -else - sudo make install -fi +make install diff --git a/src/server/base.cc b/src/server/base.cc index 3fcd489e4a6..9ce1964e15b 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -56,7 +56,6 @@ BaseFactory::BaseFactory(Server *server) : Factory(server) {} BaseFactory::~BaseFactory() {} bool BaseFactory::start() { - sw_worker()->run_always = true; return true; } diff --git a/src/server/manager.cc b/src/server/manager.cc index ce75338f144..e100b894202 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -114,7 +114,6 @@ int Server::start_manager_process() { } auto fn = [this](void) { - ProcessFactory *_factory = dynamic_cast(factory); swoole_set_process_type(SW_PROCESS_MANAGER); gs->manager_pid = SwooleG.pid = getpid(); @@ -127,7 +126,7 @@ int Server::start_manager_process() { SW_LOOP_N(worker_num) { Worker *worker = get_worker(i); - if (_factory->spawn_event_worker(worker) < 0) { + if (factory->spawn_event_worker(worker) < 0) { swoole_sys_error("failed to fork event worker"); return; } @@ -135,7 +134,7 @@ int Server::start_manager_process() { if (!user_worker_list.empty()) { for (auto worker : user_worker_list) { - if (_factory->spawn_user_worker(worker) < 0) { + if (factory->spawn_user_worker(worker) < 0) { swoole_sys_error("failed to fork user worker"); return; } @@ -213,8 +212,6 @@ void Manager::wait(Server *_server) { swoole_timer_add((long) (_server->manager_alarm * 1000), true, timer_callback, _server); } - ProcessFactory *_factory = dynamic_cast(_server->factory); - while (_server->running) { ExitStatus exit_status = wait_process(); const auto errnoAfterWait = errno; @@ -231,10 +228,10 @@ void Manager::wait(Server *_server) { WorkerStopMessage worker_stop_msg; memcpy(&worker_stop_msg, msg.data, sizeof(worker_stop_msg)); if (worker_stop_msg.worker_id >= _server->worker_num) { - _factory->spawn_task_worker(_server->get_worker(worker_stop_msg.worker_id)); + _server->factory->spawn_task_worker(_server->get_worker(worker_stop_msg.worker_id)); } else { Worker *worker = _server->get_worker(worker_stop_msg.worker_id); - _factory->spawn_event_worker(worker); + _server->factory->spawn_event_worker(worker); } } pool->read_message = false; @@ -326,10 +323,10 @@ void Manager::wait(Server *_server) { } // check the process return code and signal - _factory->check_worker_exit_status(worker, exit_status); + _server->factory->check_worker_exit_status(worker, exit_status); do { - if (_factory->spawn_event_worker(worker) < 0) { + if (_server->factory->spawn_event_worker(worker) < 0) { SW_START_SLEEP; continue; } @@ -340,13 +337,13 @@ void Manager::wait(Server *_server) { if (_server->gs->task_workers.map_) { auto iter = _server->gs->task_workers.map_->find(exit_status.get_pid()); if (iter != _server->gs->task_workers.map_->end()) { - _factory->check_worker_exit_status(iter->second, exit_status); - _factory->spawn_task_worker(iter->second); + _server->factory->check_worker_exit_status(iter->second, exit_status); + _server->factory->spawn_task_worker(iter->second); } } // user process if (!_server->user_worker_map.empty()) { - Server::wait_other_worker(&_server->gs->event_workers, exit_status); + pool->onWorkerNotFound(&_server->gs->event_workers, exit_status); } if (exit_status.get_pid() == reload_worker_pid && pool->reloading) { pool->reload_worker_i++; @@ -401,9 +398,9 @@ void Manager::wait(Server *_server) { */ alarm(_server->max_wait_time * 2); } - _factory->kill_event_workers(); - _factory->kill_task_workers(); - _factory->kill_user_workers(); + _server->factory->kill_event_workers(); + _server->factory->kill_task_workers(); + _server->factory->kill_user_workers(); // force kill if (_server->max_wait_time) { alarm(0); @@ -480,18 +477,16 @@ int Server::wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status) return SW_ERR; } while (0); - ProcessFactory *_factory = dynamic_cast(serv->factory); - - _factory->check_worker_exit_status(exit_worker, exit_status); + serv->factory->check_worker_exit_status(exit_worker, exit_status); pid_t new_process_pid = -1; switch (worker_type) { case SW_PROCESS_TASKWORKER: - new_process_pid = _factory->spawn_task_worker(exit_worker); + new_process_pid = serv->factory->spawn_task_worker(exit_worker); break; case SW_PROCESS_USERWORKER: - new_process_pid = _factory->spawn_user_worker(exit_worker); + new_process_pid = serv->factory->spawn_user_worker(exit_worker); break; default: /* never here */ diff --git a/src/server/master.cc b/src/server/master.cc index d8b1300e3f5..e1925ce8840 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -564,6 +564,7 @@ void Server::init_worker(Worker *worker) { if (max_request < 1) { worker->run_always = true; } else { + worker->run_always = false; worker->max_request = max_request; if (max_request_grace > 0) { worker->max_request += swoole_system_random(1, max_request_grace); diff --git a/src/server/process.cc b/src/server/process.cc index ee5f092f0b9..9ab40c1696c 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -53,7 +53,7 @@ ProcessFactory::~ProcessFactory() {} /** * kill and wait all user process */ -void ProcessFactory::kill_user_workers() { +void Factory::kill_user_workers() { if (server_->user_worker_map.empty()) { return; } @@ -73,7 +73,7 @@ void ProcessFactory::kill_user_workers() { /** * [Manager] kill and wait all event worker process */ -void ProcessFactory::kill_event_workers() { +void Factory::kill_event_workers() { int status; if (server_->worker_num == 0) { @@ -95,14 +95,14 @@ void ProcessFactory::kill_event_workers() { /** * [Manager] kill and wait task worker process */ -void ProcessFactory::kill_task_workers() { +void Factory::kill_task_workers() { if (server_->task_worker_num == 0) { return; } server_->gs->task_workers.shutdown(); } -pid_t ProcessFactory::spawn_event_worker(Worker *worker) { +pid_t Factory::spawn_event_worker(Worker *worker) { pid_t pid = swoole_fork(0); if (pid < 0) { @@ -126,7 +126,7 @@ pid_t ProcessFactory::spawn_event_worker(Worker *worker) { return 0; } -pid_t ProcessFactory::spawn_user_worker(Worker *worker) { +pid_t Factory::spawn_user_worker(Worker *worker) { pid_t pid = swoole_fork(0); if (worker->pid) { server_->user_worker_map.erase(worker->pid); @@ -156,11 +156,11 @@ pid_t ProcessFactory::spawn_user_worker(Worker *worker) { } } -pid_t ProcessFactory::spawn_task_worker(Worker *worker) { +pid_t Factory::spawn_task_worker(Worker *worker) { return server_->gs->task_workers.spawn(worker); } -void ProcessFactory::check_worker_exit_status(Worker *worker, const ExitStatus &exit_status) { +void Factory::check_worker_exit_status(Worker *worker, const ExitStatus &exit_status) { if (exit_status.get_status() != 0) { swoole_warning("worker(pid=%d, id=%d) abnormal exit, status=%d, signal=%d" "%s", From caa4aeb9468a4b345268e54b0b0bf70ae342e2e1 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 11 Apr 2024 14:25:37 +0800 Subject: [PATCH 052/103] fix tests --- CMakeLists.txt | 2 +- tests/include/lib/src/DbWrapper.php | 20 +++++++++++-------- tests/include/lib/src/MysqlPool.php | 2 +- tests/swoole_channel_coro/pool.phpt | 7 ++++--- tests/swoole_coroutine/bug_2387.phpt | 3 ++- tests/swoole_coroutine/private_access.phpt | 20 ++++++++++--------- tests/swoole_feature/cross_close/redis.phpt | 3 ++- .../cross_close/redis_by_server.phpt | 3 ++- 8 files changed, 35 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 848bb88b72a..08009e56302 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ cmake_minimum_required(VERSION 2.8) file(READ ./config.h SWOOLE_CONFIG_FILE) set(CMAKE_MACOSX_RPATH 1) -set(SWOOLE_LINK_LIBRARIES pthread dl uring) +set(SWOOLE_LINK_LIBRARIES pthread dl) if (APPLE) set(CMAKE_SHARED_LINKER_FLAGS "-undefined dynamic_lookup") diff --git a/tests/include/lib/src/DbWrapper.php b/tests/include/lib/src/DbWrapper.php index 8309875b937..ad6921292e0 100644 --- a/tests/include/lib/src/DbWrapper.php +++ b/tests/include/lib/src/DbWrapper.php @@ -15,8 +15,14 @@ class DbWrapper public function connect($config) { - $mysql = new MySQL([]); - $res = $mysql->connect($config); + $mysql = new \mysqli(); + $res = $mysql->connect( + $config['host'], + $config['user'], + $config['password'], + $config['database'], + $config['port'], + ); if (false === $res) { throw new RuntimeException($mysql->connect_error, $mysql->errno); @@ -31,20 +37,18 @@ public function connect($config) public function __call($name, $arguments) { // $result = $this->mysql->{$name}(...$arguments); - $result = call_user_func_array([$this->mysql, $name], $arguments); + // $result = call_user_func_array([$this->mysql, $name], $arguments); $result = $this->mysql->query($arguments[0]); if (false === $result) { if (!$this->mysql->connected) { - $this->mysql->connect($this->config); - + $this->connect($this->config); return call_user_func_array([$this->mysql, $name], $arguments); } - - if (!empty($this->mysql->errno)) { //有错误码,则抛出弃常 + if (!empty($this->mysql->errno)) { throw new RuntimeException($this->mysql->error, $this->mysql->errno); } } - return $result; + return $result->fetch_all(); } } diff --git a/tests/include/lib/src/MysqlPool.php b/tests/include/lib/src/MysqlPool.php index 00f2484901c..153094157f3 100644 --- a/tests/include/lib/src/MysqlPool.php +++ b/tests/include/lib/src/MysqlPool.php @@ -53,7 +53,7 @@ public function put($mySQL) public function get() { /** - * @var \Swoole\Coroutine\Mysql $mysql + * @var mysqli */ $mysql = $this->pool->pop($this->config['pool_get_timeout']); if ($mysql === false) { diff --git a/tests/swoole_channel_coro/pool.phpt b/tests/swoole_channel_coro/pool.phpt index 45007d4dd71..e7622f05944 100644 --- a/tests/swoole_channel_coro/pool.phpt +++ b/tests/swoole_channel_coro/pool.phpt @@ -19,7 +19,7 @@ class RedisPool { $this->pool = new \Swoole\Coroutine\Channel($size); for ($i = 0; $i < $size; $i++) { - $redis = new Swoole\Coroutine\Redis(); + $redis = new \redis(); $res = $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); if ($res == false) { throw new \RuntimeException("failed to connect redis server."); @@ -29,12 +29,12 @@ class RedisPool } } - public function get(): \Swoole\Coroutine\Redis + public function get(): \redis { return $this->pool->pop(); } - public function put(\Swoole\Coroutine\Redis $redis) + public function put(\redis $redis) { $this->pool->push($redis); } @@ -47,6 +47,7 @@ class RedisPool } $count = 0; +\Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); go(function () { $pool = new RedisPool(); // max concurrency num is more than max connections diff --git a/tests/swoole_coroutine/bug_2387.phpt b/tests/swoole_coroutine/bug_2387.phpt index c705d2975a4..e0dffd0f898 100644 --- a/tests/swoole_coroutine/bug_2387.phpt +++ b/tests/swoole_coroutine/bug_2387.phpt @@ -33,7 +33,8 @@ $pm->childFunc = function () use ($pm) { $httpServer = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); $httpServer->set([ 'log_file' => '/dev/null', - 'worker_num' => 1 + 'worker_num' => 1, + 'hook_flags' => SWOOLE_HOOK_ALL, ]); $httpServer->on('WorkerStart', function (Swoole\Http\Server $server) use ($pm, $config) { try { diff --git a/tests/swoole_coroutine/private_access.phpt b/tests/swoole_coroutine/private_access.phpt index 143fcdd3fc7..bfe4f9d1dcc 100644 --- a/tests/swoole_coroutine/private_access.phpt +++ b/tests/swoole_coroutine/private_access.phpt @@ -19,6 +19,8 @@ class Bar public function foo() { + \Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); + go(function () { var_dump(self::$s_private); var_dump(self::$s_protect); @@ -47,16 +49,16 @@ class Bar var_dump($this->private); var_dump($this->protect); var_dump($this->public); - $mysql = new Swoole\Coroutine\MySQL; - $res = $mysql->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); + $mysql = new mysqli(); + $res = $mysql->connect( + MYSQL_SERVER_HOST, + MYSQL_SERVER_USER, + MYSQL_SERVER_PWD, + MYSQL_SERVER_DB, + MYSQL_SERVER_PORT, + ); Assert::assert($res); - $ret = $mysql->query('show tables', 1); + $ret = $mysql->query('show tables', 1)->fetch_all(); Assert::assert(is_array($ret)); Assert::assert(count($ret) > 0); var_dump(self::$s_private); diff --git a/tests/swoole_feature/cross_close/redis.phpt b/tests/swoole_feature/cross_close/redis.phpt index 49a9e30e6b9..640f2a259fc 100644 --- a/tests/swoole_feature/cross_close/redis.phpt +++ b/tests/swoole_feature/cross_close/redis.phpt @@ -8,7 +8,8 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new ProcessManager(); $pm->initRandomData(1); $pm->parentFunc = function () use ($pm) { - $redis = new Co\Redis; + Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); + $redis = new \redis; go(function () use ($pm, $redis) { $redis->connect('127.0.0.1', $pm->getFreePort()); go(function () use ($pm, $redis) { diff --git a/tests/swoole_feature/cross_close/redis_by_server.phpt b/tests/swoole_feature/cross_close/redis_by_server.phpt index ebe728b7d79..64f889fc803 100644 --- a/tests/swoole_feature/cross_close/redis_by_server.phpt +++ b/tests/swoole_feature/cross_close/redis_by_server.phpt @@ -8,8 +8,9 @@ require __DIR__ . '/../../include/bootstrap.php'; $pm = new ProcessManager(); $pm->initRandomData(1); $pm->parentFunc = function () use ($pm) { + Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); go(function () use ($pm) { - $redis = new Co\Redis; + $redis = new \redis; Assert::assert($redis->connect('127.0.0.1', $pm->getFreePort())); echo "GET\n"; Assert::assert(!$redis->get($pm->getRandomData())); From ab2eea886f5387f66d876817226a4e9572338ba4 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 11 Apr 2024 19:45:40 +0800 Subject: [PATCH 053/103] remove hiredis --- .github/workflows/framework.yml | 2 +- config.m4 | 14 ++------------ package.xml | 15 --------------- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index d5346886186..c78daf14448 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -102,7 +102,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: [ '8.0', '8.1', '8.2', '8.3' ] + php-version: [ '8.1', '8.2', '8.3' ] framework: [ 'Simps' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} - macOS steps: diff --git a/config.m4 b/config.m4 index a0ded1fa414..25172389285 100644 --- a/config.m4 +++ b/config.m4 @@ -110,7 +110,7 @@ PHP_ARG_ENABLE([thread-context], [whether to enable thread context], [AS_HELP_STRING([--enable-thread-context], [Use thread context])], [no], [no]) - + PHP_ARG_ENABLE([swoole-thread], [whether to enable swoole thread support], [AS_HELP_STRING([--enable-swoole-thread], @@ -1034,13 +1034,6 @@ EOF thirdparty/swoole_http_parser.c \ thirdparty/multipart_parser.c" - swoole_source_file="$swoole_source_file \ - thirdparty/hiredis/hiredis.c \ - thirdparty/hiredis/alloc.c \ - thirdparty/hiredis/net.c \ - thirdparty/hiredis/read.c \ - thirdparty/hiredis/sds.c" - if test "$PHP_NGHTTP2_DIR" = "no"; then PHP_ADD_INCLUDE([$ext_srcdir/thirdparty]) swoole_source_file="$swoole_source_file \ @@ -1191,7 +1184,6 @@ EOF PHP_ADD_INCLUDE([$ext_srcdir/include]) PHP_ADD_INCLUDE([$ext_srcdir/ext-src]) PHP_ADD_INCLUDE([$ext_srcdir/thirdparty]) - PHP_ADD_INCLUDE([$ext_srcdir/thirdparty/hiredis]) AC_MSG_CHECKING([swoole coverage]) if test "$PHP_SWOOLE_COVERAGE" != "no"; then @@ -1206,8 +1198,7 @@ EOF include/*.h \ stubs/*.h \ thirdparty/*.h \ - thirdparty/nghttp2/*.h \ - thirdparty/hiredis/*.h]) + thirdparty/nghttp2/*.h]) PHP_REQUIRE_CXX() @@ -1236,7 +1227,6 @@ EOF PHP_ADD_BUILD_DIR($ext_builddir/src/wrapper) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost/asm) - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/hiredis) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/sockets) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/standard) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/curl) diff --git a/package.xml b/package.xml index ac34fe5c845..0a9c5ed15f6 100644 --- a/package.xml +++ b/package.xml @@ -2407,21 +2407,6 @@ - - - - - - - - - - - - - - - From 9819341ee469104798492c28272f9789f467fd0c Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 11 Apr 2024 20:29:21 +0800 Subject: [PATCH 054/103] fix tests [3] --- ext-src/php_swoole.cc | 1 - ext-src/swoole_thread.cc | 1 + include/swoole.h | 1 + src/core/base.cc | 13 +++++++++---- src/os/async_thread.cc | 1 + src/server/reactor_thread.cc | 7 +++++-- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 2031c63fa45..1248481f1ff 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -896,7 +896,6 @@ PHP_MINFO_FUNCTION(swoole) { #ifdef SW_USE_TCMALLOC php_info_print_table_row(2, "tcmalloc", "enabled"); #endif - php_info_print_table_row(2, "async_redis", "enabled"); #ifdef SW_USE_PGSQL php_info_print_table_row(2, "coroutine_pgsql", "enabled"); #endif diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 0f12f754018..a5e7f18b930 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -866,6 +866,7 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv) { zend_string_release(file); zend_string_release(argv); ts_free_thread(); + swoole_thread_clean(); } static PHP_METHOD(swoole_thread, exec) { diff --git a/include/swoole.h b/include/swoole.h index 67352d602c9..20381909044 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -584,6 +584,7 @@ void swoole_clean(void); pid_t swoole_fork(int flags); pid_t swoole_fork_exec(const std::function &child_fn); void swoole_thread_init(void); +void swoole_thread_clean(void); void swoole_redirect_stdout(int new_fd); int swoole_shell_exec(const char *command, pid_t *pid, bool get_error_stream); int swoole_daemon(int nochdir, int noclose); diff --git a/src/core/base.cc b/src/core/base.cc index 40512b11153..a3ea0de9a9e 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -432,12 +432,17 @@ pid_t swoole_fork(int flags) { } void swoole_thread_init(void) { - SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); - ON_SCOPE_EXIT { + if (!SwooleTG.buffer_stack) { + SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); + } + swoole_signal_block_all(); +} + +void swoole_thread_clean(void) { + if (SwooleTG.buffer_stack) { delete SwooleTG.buffer_stack; SwooleTG.buffer_stack = nullptr; - }; - swoole_signal_block_all(); + } } void swoole_dump_ascii(const char *data, size_t size) { diff --git a/src/os/async_thread.cc b/src/os/async_thread.cc index ca173d42d02..8fca0974b9b 100644 --- a/src/os/async_thread.cc +++ b/src/os/async_thread.cc @@ -312,6 +312,7 @@ void ThreadPool::main_func(bool is_core_worker) { --n_waiting; } } + swoole_thread_clean(); } void ThreadPool::create_thread(const bool is_core_worker) { diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 8f17bc98b25..42fba7a6843 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -673,7 +673,11 @@ int Server::start_reactor_threads() { } SW_LOOP_N(reactor_num) { - get_thread(i)->thread = std::thread(reactor_thread_main_loop, this, i); + get_thread(i)->thread = std::thread([=]() { + swoole_thread_init(); + reactor_thread_main_loop(this, i); + swoole_thread_clean(); + }); } _init_master_thread: @@ -787,7 +791,6 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { void Server::reactor_thread_main_loop(Server *serv, int reactor_id) { SwooleTG.id = reactor_id; SwooleTG.type = Server::THREAD_REACTOR; - swoole_thread_init(); if (swoole_event_init(0) < 0) { return; From f57d0334a11d9d939cd88ef4aa7f801c6fd90e04 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 12 Apr 2024 16:33:20 +0800 Subject: [PATCH 055/103] fix tests [4] --- ext-src/swoole_server.cc | 5 + tests/swoole_pgsql_coro/bug_4911.phpt | 27 ---- tests/swoole_pgsql_coro/connect.phpt | 14 -- tests/swoole_pgsql_coro/connect_failed.phpt | 16 -- tests/swoole_pgsql_coro/error.phpt | 44 ------ tests/swoole_pgsql_coro/escape.phpt | 18 --- tests/swoole_pgsql_coro/fetch.phpt | 158 ------------------- tests/swoole_pgsql_coro/insert.phpt | 19 --- tests/swoole_pgsql_coro/lob.phpt | 110 ------------- tests/swoole_pgsql_coro/no_field_name.phpt | 22 --- tests/swoole_pgsql_coro/not_connected.phpt | 19 --- tests/swoole_pgsql_coro/prepare.phpt | 19 --- tests/swoole_pgsql_coro/query.phpt | 21 --- tests/swoole_server/bug_2308.phpt | 3 +- tests/swoole_server/force_reload.phpt | 8 +- tests/swoole_server/force_reload2.phpt | 2 +- tests/swoole_server/heartbeat_with_base.phpt | 18 +-- 17 files changed, 20 insertions(+), 503 deletions(-) delete mode 100644 tests/swoole_pgsql_coro/bug_4911.phpt delete mode 100644 tests/swoole_pgsql_coro/connect.phpt delete mode 100644 tests/swoole_pgsql_coro/connect_failed.phpt delete mode 100644 tests/swoole_pgsql_coro/error.phpt delete mode 100644 tests/swoole_pgsql_coro/escape.phpt delete mode 100644 tests/swoole_pgsql_coro/fetch.phpt delete mode 100644 tests/swoole_pgsql_coro/insert.phpt delete mode 100644 tests/swoole_pgsql_coro/lob.phpt delete mode 100644 tests/swoole_pgsql_coro/no_field_name.phpt delete mode 100644 tests/swoole_pgsql_coro/not_connected.phpt delete mode 100644 tests/swoole_pgsql_coro/prepare.phpt delete mode 100644 tests/swoole_pgsql_coro/query.phpt diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 510a0b4c337..7df775af885 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1943,6 +1943,11 @@ static PHP_METHOD(swoole_server, __construct) { serv = new Server((enum Server::Mode) serv_mode); + if (serv_mode == Server::MODE_BASE) { + serv->reactor_num = 1; + serv->worker_num = 1; + } + if (serv_port == 0 && strcasecmp(host, "SYSTEMD") == 0) { if (serv->add_systemd_socket() <= 0) { zend_throw_error(NULL, "failed to add systemd socket"); diff --git a/tests/swoole_pgsql_coro/bug_4911.phpt b/tests/swoole_pgsql_coro/bug_4911.phpt deleted file mode 100644 index a50da2bc3ae..00000000000 --- a/tests/swoole_pgsql_coro/bug_4911.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -swoole_pgsql_coro: bug 4911 https://github.com/swoole/swoole-src/issues/4911 ---SKIPIF-- - ---FILE-- -connect('host=pgsql;port=5432;dbname=test123123;user=root;password=root'); - echo $pgsql->error.PHP_EOL; - - $pgsql = new Swoole\Coroutine\PostgreSQL(); - $connected = $pgsql->connect('host=pgsql;port=5432;dbname=test;user=root123;password=root'); - echo $pgsql->error.PHP_EOL; - - $pgsql = new Swoole\Coroutine\PostgreSQL(); - $connected = $pgsql->connect('host=pgsql;port=5432;dbname=test;user=root;password='); - echo $pgsql->error.PHP_EOL; -}); -?> ---EXPECT-- -FATAL: database "test123123" does not exist - -FATAL: password authentication failed for user "root123" - -fe_sendauth: no password supplied diff --git a/tests/swoole_pgsql_coro/connect.phpt b/tests/swoole_pgsql_coro/connect.phpt deleted file mode 100644 index cb56c5d72a1..00000000000 --- a/tests/swoole_pgsql_coro/connect.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -swoole_pgsql_coro: connect ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/connect_failed.phpt b/tests/swoole_pgsql_coro/connect_failed.phpt deleted file mode 100644 index 85944d81a45..00000000000 --- a/tests/swoole_pgsql_coro/connect_failed.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -swoole_pgsql_coro: connect failed ---SKIPIF-- - ---FILE-- -connect('')); - - $connected = $pgsql->connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/error.phpt b/tests/swoole_pgsql_coro/error.phpt deleted file mode 100644 index 0bd49f35e67..00000000000 --- a/tests/swoole_pgsql_coro/error.phpt +++ /dev/null @@ -1,44 +0,0 @@ ---TEST-- -swoole_pgsql_coro: error ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->query('SELECT * FROM not_exists;'); - Assert::false($stmt, (string) $pgsql->error); - - $stmt = $pgsql->prepare('SELECT * FROM not_exists;'); - Assert::false($stmt, (string) $pgsql->error); - - $stmt = $pgsql->prepare("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ($1, $2, $3, $4, $5) RETURNING id"); - Assert::true(false !== $stmt, (string) $pgsql->error); - - $result = $stmt->affectedRows(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->numRows(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->fieldCount(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->fetchObject(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->fetchAssoc(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->fetchArray(); - Assert::false($result, (string) $stmt->error); - - $result = $stmt->fetchRow(); - Assert::false($result, (string) $stmt->error); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/escape.phpt b/tests/swoole_pgsql_coro/escape.phpt deleted file mode 100644 index 59dea5223f8..00000000000 --- a/tests/swoole_pgsql_coro/escape.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -swoole_pgsql_coro: escape ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $result = $pgsql->escape("' or 1=1 & 2"); - Assert::true(false !== $result, (string) $pgsql->error); - Assert::eq($result, "'' or 1=1 & 2"); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/fetch.phpt b/tests/swoole_pgsql_coro/fetch.phpt deleted file mode 100644 index cbfd629b70a..00000000000 --- a/tests/swoole_pgsql_coro/fetch.phpt +++ /dev/null @@ -1,158 +0,0 @@ ---TEST-- -swoole_pgsql_coro: fetch ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->query('SELECT * FROM weather;'); - Assert::true(false !== $stmt, (string) $pgsql->error); - - var_dump($stmt->fetchObject(0), $stmt->fetchObject(1)); - var_dump($stmt->fetchAssoc(0), $stmt->fetchAssoc(1)); - var_dump($stmt->fetchArray(0), $stmt->fetchArray(1)); - var_dump($stmt->fetchRow(0), $stmt->fetchRow(1)); -}); -?> ---EXPECTF-- -object(stdClass)#%d (6) { - ["id"]=> - int(1) - ["city"]=> - string(13) "San Francisco" - ["temp_lo"]=> - int(46) - ["temp_hi"]=> - int(50) - ["prcp"]=> - float(0.25) - ["date"]=> - string(10) "1994-11-27" -} -object(stdClass)#%d (6) { - ["id"]=> - int(2) - ["city"]=> - string(5) "Test2" - ["temp_lo"]=> - int(11) - ["temp_hi"]=> - int(22) - ["prcp"]=> - float(0.3) - ["date"]=> - string(10) "1994-11-28" -} -array(6) { - ["id"]=> - int(1) - ["city"]=> - string(13) "San Francisco" - ["temp_lo"]=> - int(46) - ["temp_hi"]=> - int(50) - ["prcp"]=> - float(0.25) - ["date"]=> - string(10) "1994-11-27" -} -array(6) { - ["id"]=> - int(2) - ["city"]=> - string(5) "Test2" - ["temp_lo"]=> - int(11) - ["temp_hi"]=> - int(22) - ["prcp"]=> - float(0.3) - ["date"]=> - string(10) "1994-11-28" -} -array(12) { - [0]=> - int(1) - ["id"]=> - int(1) - [1]=> - string(13) "San Francisco" - ["city"]=> - string(13) "San Francisco" - [2]=> - int(46) - ["temp_lo"]=> - int(46) - [3]=> - int(50) - ["temp_hi"]=> - int(50) - [4]=> - float(0.25) - ["prcp"]=> - float(0.25) - [5]=> - string(10) "1994-11-27" - ["date"]=> - string(10) "1994-11-27" -} -array(12) { - [0]=> - int(2) - ["id"]=> - int(2) - [1]=> - string(5) "Test2" - ["city"]=> - string(5) "Test2" - [2]=> - int(11) - ["temp_lo"]=> - int(11) - [3]=> - int(22) - ["temp_hi"]=> - int(22) - [4]=> - float(0.3) - ["prcp"]=> - float(0.3) - [5]=> - string(10) "1994-11-28" - ["date"]=> - string(10) "1994-11-28" -} -array(6) { - [0]=> - int(1) - [1]=> - string(13) "San Francisco" - [2]=> - int(46) - [3]=> - int(50) - [4]=> - float(0.25) - [5]=> - string(10) "1994-11-27" -} -array(6) { - [0]=> - int(2) - [1]=> - string(5) "Test2" - [2]=> - int(11) - [3]=> - int(22) - [4]=> - float(0.3) - [5]=> - string(10) "1994-11-28" -} diff --git a/tests/swoole_pgsql_coro/insert.phpt b/tests/swoole_pgsql_coro/insert.phpt deleted file mode 100644 index d82e898d4f0..00000000000 --- a/tests/swoole_pgsql_coro/insert.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_pgsql_coro: insert ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->query("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ('Shanghai', 88, 10, 0.75,'1993-11-27') RETURNING id"); - Assert::true(false !== $stmt, (string) $pgsql->error); - Assert::eq($stmt->numRows(), 1); - Assert::greaterThan($stmt->fetchAssoc()['id'], 1); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/lob.phpt b/tests/swoole_pgsql_coro/lob.phpt deleted file mode 100644 index 83d7fd3d141..00000000000 --- a/tests/swoole_pgsql_coro/lob.phpt +++ /dev/null @@ -1,110 +0,0 @@ ---TEST-- -swoole_pgsql_coro: lob ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->prepare("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ($1, $2, $3, $4, $5) RETURNING id"); - Assert::true(false !== $stmt, (string) $pgsql->error); - - $fp = fopen('php://memory', 'w+'); - Assert::true(!!$fp); - fwrite($fp, 'Wuxi'); - rewind($fp); - $result = $stmt->execute([$fp, rand(1000, 99999), 10, 0.75, '1993-11-23']); - fclose($fp); - Assert::true(false !== $result, (string) $pgsql->error); - $id = $stmt->fetchAssoc()['id'] ?? null; - Assert::greaterThanEq($id, 1); - $stmt2 = $pgsql->prepare('select * from weather where id = $1'); - Assert::true(false !== $stmt2, (string) $pgsql->error); - $stmt2->execute([$id]); - var_dump($stmt2->fetchAssoc()); - - $result = $pgsql->query('begin'); - Assert::notEq($result, false, (string) $pgsql->error); - $stmt = $pgsql->prepare("INSERT INTO oid(oid) VALUES ($1) RETURNING id"); - Assert::true(false !== $stmt, (string) $pgsql->error); - $oid = $pgsql->createLOB(); - Assert::integer($oid, (string) $pgsql->error); - $lob = $pgsql->openLOB($oid, 'wb'); - Assert::notEq($lob, false, (string) $pgsql->error); - fwrite($lob, 'Shanghai'); - $result = $stmt->execute([$lob]); - Assert::true(false !== $result, (string) $pgsql->error); - $result = $pgsql->query('commit'); - Assert::notEq($result, false, (string) $pgsql->error); - - $result = $pgsql->query('begin'); - Assert::notEq($result, false, (string) $pgsql->error); - $id = $stmt->fetchAssoc()['id'] ?? null; - Assert::greaterThanEq($id, 1); - $stmt2 = $pgsql->prepare('select * from oid where id = $1'); - Assert::true(false !== $stmt2, (string) $pgsql->error); - $stmt2->execute([$id]); - $row = $stmt2->fetchRow(0, SW_PGSQL_ASSOC); - $lob = $pgsql->openLOB($row['oid']); - Assert::notEq($lob, false, (string) $pgsql->error); - Assert::eq(fgets($lob), 'Shanghai'); - $result = $pgsql->query('commit'); - Assert::notEq($result, false, (string) $pgsql->error); - - $result = $pgsql->query('begin'); - Assert::notEq($result, false, (string) $pgsql->error); - $oid = $pgsql->createLOB(); - Assert::integer($oid, (string) $pgsql->error); - $lob = $pgsql->openLOB($oid, 'wb'); - Assert::notEq($lob, false, (string) $pgsql->error); - var_dump($lob); - fwrite($lob, 'test'); - $result = $pgsql->query('rollback'); - Assert::notEq($result, false, (string) $pgsql->error); - var_dump($lob); - - $result = $pgsql->query('begin'); - Assert::notEq($result, false, (string) $pgsql->error); - $oid = $pgsql->createLOB(); - Assert::integer($oid, (string) $pgsql->error); - $lob = $pgsql->openLOB($oid, 'wb'); - Assert::notEq($lob, false, (string) $pgsql->error); - var_dump($lob); - fwrite($lob, 'test'); - $result = $pgsql->query('commit'); - Assert::notEq($result, false, (string) $pgsql->error); - var_dump($lob); - - $result = $pgsql->query('begin'); - Assert::notEq($result, false, (string) $pgsql->error); - $lob = $pgsql->openLOB($oid, 'wb'); - Assert::notEq($lob, false, (string) $pgsql->error); - var_dump($lob); - var_dump(fgets($lob)); -}); -?> ---EXPECTF-- -array(6) { - ["id"]=> - int(%d) - ["city"]=> - string(4) "Wuxi" - ["temp_lo"]=> - int(%d) - ["temp_hi"]=> - int(10) - ["prcp"]=> - float(0.75) - ["date"]=> - string(10) "1993-11-23" -} -resource(%d) of type (stream) -resource(%d) of type (Unknown) -resource(%d) of type (stream) -resource(%d) of type (Unknown) -resource(%d) of type (stream) -string(4) "test" diff --git a/tests/swoole_pgsql_coro/no_field_name.phpt b/tests/swoole_pgsql_coro/no_field_name.phpt deleted file mode 100644 index 35c114c510e..00000000000 --- a/tests/swoole_pgsql_coro/no_field_name.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -swoole_pgsql_coro: no field name ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->query('SELECT 11, 22'); - Assert::true(false !== $stmt, (string) $pgsql->error); - - $arr = $stmt->fetchAll(); - Assert::isArray($arr); - Assert::eq($arr[0]['?column?'], 11); - Assert::eq($arr[0]['?column?1'], 22); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/not_connected.phpt b/tests/swoole_pgsql_coro/not_connected.phpt deleted file mode 100644 index f508c5c0564..00000000000 --- a/tests/swoole_pgsql_coro/not_connected.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_pgsql_coro: not connected ---SKIPIF-- - ---FILE-- -escape('')); - Assert::false($pgsql->escapeLiteral('')); - Assert::false($pgsql->escapeIdentifier('')); - Assert::false($pgsql->query('')); - Assert::false($pgsql->prepare('')); - Assert::false($pgsql->metaData('')); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/prepare.phpt b/tests/swoole_pgsql_coro/prepare.phpt deleted file mode 100644 index 8968244cc77..00000000000 --- a/tests/swoole_pgsql_coro/prepare.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -swoole_pgsql_coro: prepare ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->prepare("INSERT INTO weather(city, temp_lo, temp_hi, prcp, date) VALUES ($1, $2, $3, $4, $5) RETURNING id"); - Assert::true(false !== $stmt, (string) $pgsql->error); - $result = $stmt->execute(['Beijing', rand(1000, 99999), 10, 0.75, '1993-11-23']); - Assert::true(false !== $result, (string) $pgsql->error); -}); -?> ---EXPECT-- diff --git a/tests/swoole_pgsql_coro/query.phpt b/tests/swoole_pgsql_coro/query.phpt deleted file mode 100644 index 499f27bc6cc..00000000000 --- a/tests/swoole_pgsql_coro/query.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -swoole_pgsql_coro: query ---SKIPIF-- - ---FILE-- -connect(PGSQL_CONNECTION_STRING); - Assert::true($connected, (string) $pgsql->error); - - $stmt = $pgsql->query('SELECT * FROM weather;'); - Assert::true(false !== $stmt, (string) $pgsql->error); - - $arr = $stmt->fetchAll(); - Assert::isArray($arr); - Assert::eq($arr[0]['city'], 'San Francisco'); -}); -?> ---EXPECT-- diff --git a/tests/swoole_server/bug_2308.phpt b/tests/swoole_server/bug_2308.phpt index 233635d4dc0..253bb68defb 100644 --- a/tests/swoole_server/bug_2308.phpt +++ b/tests/swoole_server/bug_2308.phpt @@ -19,10 +19,11 @@ $pm->childFunc = function () use ($pm) { 'worker_num' => MAX_PROCESS_NUM, 'log_file' => '/dev/null', 'enable_coroutine' => false, + 'hook_flags' => SWOOLE_HOOK_ALL, ]); $server->on('start', function () { Swoole\Coroutine::create(function () { - $redis = new Swoole\Coroutine\Redis(); + $redis = new \Redis(); $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); $ret = $redis->set('foo', 'bar'); Assert::assert($ret); diff --git a/tests/swoole_server/force_reload.phpt b/tests/swoole_server/force_reload.phpt index ec2eecc4e1f..29e9284c982 100644 --- a/tests/swoole_server/force_reload.phpt +++ b/tests/swoole_server/force_reload.phpt @@ -64,7 +64,7 @@ Assert::eq($atomic->get(), WORKER_NUM * 2); [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=%d) exit timeout, force kill the process -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 +[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=%d) abnormal exit, status=0, signal=9 diff --git a/tests/swoole_server/force_reload2.phpt b/tests/swoole_server/force_reload2.phpt index 1144d868c26..0f1576122f6 100644 --- a/tests/swoole_server/force_reload2.phpt +++ b/tests/swoole_server/force_reload2.phpt @@ -62,7 +62,7 @@ $pm->run(); %d [%d] stop %d [%d] start [%s] WARNING Manager::kill_timeout_process() (ERRNO 9101): worker(pid=%d, id=0) exit timeout, force kill the process -[%s] WARNING Server::check_worker_exit_status(): worker(pid=%d, id=0) abnormal exit, status=0, signal=9 +[%s] WARNING Factory::check_worker_exit_status(): worker(pid=%d, id=0) abnormal exit, status=0, signal=9 %d [%d] start %d [%d] stop %d [%d] stop diff --git a/tests/swoole_server/heartbeat_with_base.phpt b/tests/swoole_server/heartbeat_with_base.phpt index c984de02a5c..a067505c6b4 100644 --- a/tests/swoole_server/heartbeat_with_base.phpt +++ b/tests/swoole_server/heartbeat_with_base.phpt @@ -8,13 +8,13 @@ skip_if_in_valgrind(); --FILE-- parentFunc = function ($pid) use ($pm) -{ +$pm->parentFunc = function ($pid) use ($pm) { $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); - if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) - { + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); } @@ -25,19 +25,17 @@ $pm->parentFunc = function ($pid) use ($pm) Swoole\Process::kill($pid); }; -$pm->childFunc = function () use ($pm) -{ +$pm->childFunc = function () use ($pm) { $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); $serv->set(array( 'heartbeat_check_interval' => 1, 'heartbeat_idle_time' => 2, + 'worker_num' => 1, )); - $serv->on("WorkerStart", function (Server $serv) use ($pm) - { + $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on('receive', function (Server $serv, $fd, $rid, $data) - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { }); $serv->start(); }; From f4cb996a949ed07b73dd6e409e0a34684fa6f7a6 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 12 Apr 2024 19:22:47 +0800 Subject: [PATCH 056/103] fix tests [5], revert SwooleWG --- ext-src/swoole_process.cc | 2 +- ext-src/swoole_server.cc | 6 +++--- include/swoole_process_pool.h | 18 ++++++++++------- include/swoole_server.h | 14 ++++++------- src/os/process_pool.cc | 5 ++++- src/server/master.cc | 8 ++++---- src/server/port.cc | 2 +- src/server/process.cc | 3 ++- src/server/reactor_process.cc | 5 ++--- src/server/task_worker.cc | 9 ++++----- src/server/thread.cc | 4 +++- src/server/worker.cc | 29 +++++++++++++-------------- tests/swoole_server/task/kill_01.phpt | 5 ++--- 13 files changed, 58 insertions(+), 52 deletions(-) diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 1adfb84029d..84883f00f90 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -646,7 +646,7 @@ int php_swoole_process_start(Worker *process, zval *zobject) { php_swoole_process_clean(); swoole_set_process_id(process->id); - g_worker_instance = process; + SwooleWG.worker = process; zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("pid"), process->pid); if (process->pipe_current) { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 7df775af885..7c49e908716 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1537,10 +1537,10 @@ static void php_swoole_server_onAfterReload(Server *serv) { } static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { - if (worker->shutdown) { + if (SwooleWG.shutdown) { return; } - worker->shutdown = true; + SwooleWG.shutdown = true; zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); @@ -3590,7 +3590,7 @@ static PHP_METHOD(swoole_server, getSocket) { RETURN_FALSE; } - ListenPort *lp = serv->get_port(port); + const ListenPort *lp = serv->get_port(port); php_socket *socket_object = php_swoole_convert_to_socket(lp->get_fd()); if (!socket_object) { diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index be7d514cad9..74f70754018 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -102,6 +102,15 @@ static inline ExitStatus wait_process(pid_t _pid, int options) { struct ProcessPool; struct Worker; +struct WorkerGlobal { + bool run_always; + bool shutdown; + uint32_t max_request; + Worker *worker; + Worker *worker_copy; + time_t exit_time; +}; + struct Worker { pid_t pid; WorkerId id; @@ -112,11 +121,6 @@ struct Worker { bool redirect_stdin; bool redirect_stderr; - bool run_always; - bool shutdown; - uint32_t max_request; - time_t exit_time; - /** * worker status, IDLE or BUSY */ @@ -325,8 +329,8 @@ static sw_inline int swoole_kill(pid_t __pid, int __sig) { typedef swoole::ProtocolType swProtocolType; -extern SW_THREAD_LOCAL swoole::Worker *g_worker_instance; +extern SW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG; static inline swoole::Worker *sw_worker() { - return g_worker_instance; + return SwooleWG.worker; } diff --git a/include/swoole_server.h b/include/swoole_server.h index 69df9407b9f..36f2373b7fd 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -343,20 +343,20 @@ struct ListenPort { network::Socket *get_socket() { return socket; } - int get_port() { + int get_port() const { return port; } - const char *get_host() { + const char *get_host() const { return host.c_str(); } - SocketType get_type() { + SocketType get_type() const { return type; } - int get_fd() { + int get_fd() const { return socket ? socket->fd : -1; } - size_t get_connection_num(); + size_t get_connection_num() const; }; struct ServerGS { @@ -770,7 +770,7 @@ class Server { return mode_; }; - ListenPort *get_port(int _port) const { + const ListenPort *get_port(int _port) const { for (auto port : ports) { if (port->port == _port || _port == 0) { return port; @@ -1393,7 +1393,7 @@ class Server { int schedule_worker(int fd, SendData *data); - size_t get_connection_num() { + size_t get_connection_num() const { if (gs->connection_nums) { size_t num = 0; for (uint32_t i = 0; i < worker_num; i++) { diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc index 546a7801d00..b04db48cc49 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -25,6 +25,8 @@ #include "swoole_process_pool.h" #include "swoole_client.h" +SW_THREAD_LOCAL swoole::WorkerGlobal SwooleWG = {}; + namespace swoole { using network::Socket; @@ -465,6 +467,7 @@ pid_t ProcessPool::spawn(Worker *worker) { worker->pid = SwooleG.pid; swoole_set_process_type(SW_PROCESS_WORKER); swoole_set_process_id(worker->id); + SwooleWG.worker = worker; if (async) { if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) { exit(254); @@ -541,7 +544,7 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker out.mtype = worker->id + 1; } - while (pool->running && !worker->shutdown && task_n > 0) { + while (pool->running && !SwooleWG.shutdown && task_n > 0) { /** * fetch task */ diff --git a/src/server/master.cc b/src/server/master.cc index e1925ce8840..b77fb64639e 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -562,12 +562,12 @@ void Server::destroy_worker(Worker *worker) { */ void Server::init_worker(Worker *worker) { if (max_request < 1) { - worker->run_always = true; + SwooleWG.run_always = true; } else { - worker->run_always = false; - worker->max_request = max_request; + SwooleWG.run_always = false; + SwooleWG.max_request = max_request; if (max_request_grace > 0) { - worker->max_request += swoole_system_random(1, max_request_grace); + SwooleWG.max_request += swoole_system_random(1, max_request_grace); } } worker->start_time = ::time(nullptr); diff --git a/src/server/port.cc b/src/server/port.cc index a302c869102..89902ae709e 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -780,7 +780,7 @@ const char *ListenPort::get_protocols() { } } -size_t ListenPort::get_connection_num() { +size_t ListenPort::get_connection_num() const { if (gs->connection_nums) { size_t num = 0; for (uint32_t i = 0; i < sw_server()->worker_num; i++) { diff --git a/src/server/process.cc b/src/server/process.cc index 9ab40c1696c..409111fa89d 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -110,6 +110,7 @@ pid_t Factory::spawn_event_worker(Worker *worker) { return SW_ERR; } else if (pid == 0) { worker->pid = SwooleG.pid; + SwooleWG.worker = worker; } else { worker->pid = pid; return pid; @@ -139,8 +140,8 @@ pid_t Factory::spawn_user_worker(Worker *worker) { else if (pid == 0) { swoole_set_process_type(SW_PROCESS_USERWORKER); swoole_set_process_id(worker->id); - g_worker_instance = worker; worker->pid = SwooleG.pid; + SwooleWG.worker = worker; server_->onUserWorkerStart(server_, worker); exit(0); } diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index d531ca5f60e..488a2dad93d 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -170,10 +170,9 @@ int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { swoole_set_process_id(worker->id); if (serv->max_request > 0) { - worker->run_always = false; + SwooleWG.run_always = false; } - worker->max_request = serv->max_request; - g_worker_instance = worker; + SwooleWG.max_request = serv->max_request; SwooleTG.id = 0; serv->init_worker(worker); diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 9fd07ceecff..b7046907294 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -177,7 +177,6 @@ static void TaskWorker_signal_init(ProcessPool *pool) { static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { Server *serv = (Server *) pool->ptr; swoole_set_process_id(worker->id); - g_worker_instance = worker; /** * Make the task worker support asynchronous @@ -203,10 +202,10 @@ static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { * task_max_request */ if (pool->max_request > 0) { - worker->run_always = false; - worker->max_request = pool->get_max_request(); + SwooleWG.run_always = false; + SwooleWG.max_request = pool->get_max_request(); } else { - worker->run_always = true; + SwooleWG.run_always = true; } } @@ -231,7 +230,7 @@ static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) { worker->status = SW_WORKER_IDLE; worker->request_count++; // maximum number of requests, process will exit. - if (!worker->run_always && worker->request_count >= worker->max_request) { + if (!SwooleWG.run_always && worker->request_count >= SwooleWG.max_request) { serv->stop_async_worker(worker); } return retval; diff --git a/src/server/thread.cc b/src/server/thread.cc index 8fff6d202b9..9566cfa60a5 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -86,8 +86,8 @@ void ThreadFactory::spawn_event_worker(int i) { swoole_set_process_id(i); swoole_set_thread_id(i); Worker *worker = server_->get_worker(i); - g_worker_instance = worker; worker->type = SW_PROCESS_EVENTWORKER; + SwooleWG.worker = worker; server_->worker_thread_start([=]() { Server::reactor_thread_main_loop(server_, i); }); at_thread_exit(worker); }); @@ -102,6 +102,7 @@ void ThreadFactory::spawn_task_worker(int i) { Worker *worker = server_->get_worker(i); worker->type = SW_PROCESS_TASKWORKER; worker->status = SW_WORKER_IDLE; + SwooleWG.worker = worker; auto pool = &server_->gs->task_workers; server_->worker_thread_start([=]() { if (pool->onWorkerStart != nullptr) { @@ -124,6 +125,7 @@ void ThreadFactory::spawn_user_worker(int i) { swoole_set_process_id(i); swoole_set_thread_id(i); worker->type = SW_PROCESS_USERWORKER; + SwooleWG.worker = worker; server_->worker_thread_start([=]() { server_->onUserWorkerStart(server_, worker); }); at_thread_exit(worker); }); diff --git a/src/server/worker.cc b/src/server/worker.cc index 6c6fd27aa68..d5e72958aca 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -24,8 +24,6 @@ #include "swoole_msg_queue.h" #include "swoole_coroutine.h" -SW_THREAD_LOCAL swoole::Worker *g_worker_instance; - namespace swoole { using namespace network; @@ -55,13 +53,10 @@ void Server::worker_signal_handler(int signo) { } switch (signo) { case SIGTERM: - // Event worker - if (swoole_event_is_available()) { + if (swoole_event_is_available()) { // Event worker sw_server()->stop_async_worker(sw_worker()); - } - // Task worker - else { - sw_worker()->shutdown = true; + } else { // Task worker + SwooleWG.shutdown = true; } break; // for test @@ -212,7 +207,7 @@ void Server::worker_accept_event(DataHead *info) { worker->status = SW_WORKER_IDLE; // maximum number of requests, process will exit. - if (!worker->run_always && worker->request_count >= worker->max_request) { + if (!SwooleWG.run_always && worker->request_count >= SwooleWG.max_request) { stop_async_worker(worker); } } @@ -277,7 +272,6 @@ void Server::worker_start_callback(Worker *worker) { sw_logger()->reopen(); } - g_worker_instance = worker; worker->status = SW_WORKER_IDLE; if (is_process_mode()) { @@ -302,6 +296,11 @@ void Server::worker_stop_callback(Worker *worker) { SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA, "unprocessed data in the worker process buffer"); get_worker_message_bus()->clear(); } + if (SwooleWG.worker_copy) { + delete SwooleWG.worker_copy; + SwooleWG.worker_copy = nullptr; + SwooleWG.worker = nullptr; + } } void Server::stop_async_worker(Worker *worker) { @@ -323,9 +322,9 @@ void Server::stop_async_worker(Worker *worker) { } // Separated from the event worker process pool - Worker *worker_copy = (Worker *) sw_malloc(sizeof(*worker)); - *worker_copy = *worker; - g_worker_instance = worker_copy; + SwooleWG.worker_copy = new Worker{}; + *SwooleWG.worker_copy = *worker; + SwooleWG.worker = worker; if (worker->pipe_worker && !worker->pipe_worker->removed) { reactor->remove_read_event(worker->pipe_worker); @@ -366,7 +365,7 @@ void Server::stop_async_worker(Worker *worker) { reactor->set_wait_exit(true); reactor->set_end_callback(Reactor::PRIORITY_TRY_EXIT, Worker_reactor_try_to_exit); - worker->exit_time = ::time(nullptr); + SwooleWG.exit_time = ::time(nullptr); Worker_reactor_try_to_exit(reactor); if (!reactor->running) { @@ -394,7 +393,7 @@ static void Worker_reactor_try_to_exit(Reactor *reactor) { call_worker_exit_func = 1; continue; } - int remaining_time = serv->max_wait_time - (::time(nullptr) - sw_worker()->exit_time); + int remaining_time = serv->max_wait_time - (::time(nullptr) - SwooleWG.exit_time); if (remaining_time <= 0) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, "worker exit timeout, forced termination"); diff --git a/tests/swoole_server/task/kill_01.phpt b/tests/swoole_server/task/kill_01.phpt index 7c5a83d0d09..e6e96938868 100644 --- a/tests/swoole_server/task/kill_01.phpt +++ b/tests/swoole_server/task/kill_01.phpt @@ -14,9 +14,8 @@ const PROC_NAME = 'swoole_unittest_server_task_worker'; $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($pm) { - for ($i = 0; $i < 5; $i++) - { - //杀死进程 + for ($i = 0; $i < 5; $i++) { + // 杀死进程 kill_process_by_name(PROC_NAME); usleep(10000); //判断进程是否存在 From acbd5be889af231cfc9f0511ed6ed62c09acbf86 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 12 Apr 2024 20:17:05 +0800 Subject: [PATCH 057/103] fix tests [6] --- src/server/manager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/manager.cc b/src/server/manager.cc index e100b894202..8d0f564ca78 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -343,7 +343,7 @@ void Manager::wait(Server *_server) { } // user process if (!_server->user_worker_map.empty()) { - pool->onWorkerNotFound(&_server->gs->event_workers, exit_status); + Server::wait_other_worker(&_server->gs->event_workers, exit_status); } if (exit_status.get_pid() == reload_worker_pid && pool->reloading) { pool->reload_worker_i++; From b78ef3e17103aaf70061fe9c805273c9ad42ecbc Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 12 Apr 2024 20:25:30 +0800 Subject: [PATCH 058/103] fix tests [7] --- src/server/reactor_process.cc | 4 +++- src/server/worker.cc | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 488a2dad93d..6ef8d84e678 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -91,7 +91,9 @@ int Server::start_reactor_processes() { } if (Server_is_single(this)) { - int retval = worker_main_loop(&gs->event_workers, &gs->event_workers.workers[0]); + Worker *worker = &gs->event_workers.workers[0]; + SwooleWG.worker = worker; + int retval = worker_main_loop(&gs->event_workers, worker); if (retval == SW_OK) { gs->event_workers.destroy(); } diff --git a/src/server/worker.cc b/src/server/worker.cc index d5e72958aca..585350ae08d 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -53,9 +53,9 @@ void Server::worker_signal_handler(int signo) { } switch (signo) { case SIGTERM: - if (swoole_event_is_available()) { // Event worker + if (swoole_event_is_available()) { // Event worker sw_server()->stop_async_worker(sw_worker()); - } else { // Task worker + } else { // Task worker SwooleWG.shutdown = true; } break; From 1d5046f92fdceba008a5104ca1d25c22f5141516 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 12 Apr 2024 20:41:18 +0800 Subject: [PATCH 059/103] fix tests [8] --- tests/include/config.php | 1 - tests/swoole_coroutine/check.phpt | 24 ------------------------ tests/swoole_global/serialize_deny.phpt | 16 ---------------- tests/swoole_http_server/bug_2444.phpt | 20 +++++++++----------- 4 files changed, 9 insertions(+), 52 deletions(-) diff --git a/tests/include/config.php b/tests/include/config.php index 65063cb916b..5c7cb0c6149 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -15,7 +15,6 @@ define('IS_PHPTESTSING', !!getenv('PHPT')); define('USE_VALGRIND', getenv('USE_ZEND_ALLOC') === '0'); define('HAS_SSL', defined("SWOOLE_SSL")); -define('HAS_ASYNC_REDIS', class_exists("Swoole\\Redis", false)); define('HAS_HTTP2', class_exists("Swoole\\Http2\\Request", false)); define('DEV_NULL', '/dev/null'); diff --git a/tests/swoole_coroutine/check.phpt b/tests/swoole_coroutine/check.phpt index 0c4e0b3432c..036829313dc 100644 --- a/tests/swoole_coroutine/check.phpt +++ b/tests/swoole_coroutine/check.phpt @@ -80,32 +80,8 @@ $map = [ (new Co\Http\Client('127.0.0.1', 1234))->get('/'); Assert::assert(0); // never here }, - function () { - (new Co\Mysql)->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB - ]); - Assert::assert(0); // never here - }, - function () { - (new Co\Redis)->connect('127.0.0.1', 6379); - Assert::assert(0); // never here - }, ]; -function pgsql_test() { - (new Co\Postgresql())->connect('host=127.0.0.1 port=12345 dbname=test user=root password=root'); - Assert::assert(0); // never here -} - -if (class_exists(Co\Postgresql::class)) { - $map[] = function () { - pgsql_test(); - }; -} if (class_exists(Co\Http2\Client::class)) { $map[] = function () { (new Co\Http2\Client('127.0.0.1', 1234))->connect(); diff --git a/tests/swoole_global/serialize_deny.phpt b/tests/swoole_global/serialize_deny.phpt index 82a24e8ec44..6d9154d7f38 100644 --- a/tests/swoole_global/serialize_deny.phpt +++ b/tests/swoole_global/serialize_deny.phpt @@ -34,22 +34,6 @@ go(function () { } catch (\Exception $exception) { Assert::same(strpos($exception->getMessage(), 'Serialization'), 0); } - try { - $hcc = new \Swoole\Coroutine\Mysql(); - serialize($hcc); - Assert::true(false, 'never here'); - } catch (\Exception $exception) { - Assert::same(strpos($exception->getMessage(), 'Serialization'), 0); - } - if (HAS_ASYNC_REDIS) { - try { - $hcc = new \Swoole\Coroutine\Redis(); - serialize($hcc); - Assert::true(false, 'never here'); - } catch (\Exception $exception) { - Assert::same(strpos($exception->getMessage(), 'Serialization'), 0); - } - } try { $hcc = new \Swoole\Table(1); serialize($hcc); diff --git a/tests/swoole_http_server/bug_2444.phpt b/tests/swoole_http_server/bug_2444.phpt index ff6357a7f2e..cd4b6067b4c 100644 --- a/tests/swoole_http_server/bug_2444.phpt +++ b/tests/swoole_http_server/bug_2444.phpt @@ -14,7 +14,7 @@ $pm->parentFunc = function () use ($pm) { }; $pm->childFunc = function () use ($pm) { $server = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); - $server->set(['log_file' => '/dev/null']); + $server->set(['log_file' => '/dev/null', 'hook_flags' => SWOOLE_HOOK_ALL]); $server->on('start', function () use ($pm) { $pm->wakeup(); }); @@ -30,18 +30,16 @@ $pm->childFunc = function () use ($pm) { return; } $cli->close(); - $db = new Swoole\Coroutine\Mysql(); - if (!Assert::assert($db->connect([ - 'host' => MYSQL_SERVER_HOST, - 'port' => MYSQL_SERVER_PORT, - 'user' => MYSQL_SERVER_USER, - 'password' => MYSQL_SERVER_PWD, - 'database' => MYSQL_SERVER_DB, - 'strict_type' => true - ]))) { + $db = new mysqli(); + $db->set_opt(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1); + if (!Assert::assert($db->connect(MYSQL_SERVER_HOST, + MYSQL_SERVER_USER, + MYSQL_SERVER_PWD, + MYSQL_SERVER_DB, + MYSQL_SERVER_PORT))) { goto _error; } - if (!Assert::assert($db->query('select 1')[0][1] === 1)) { + if (!Assert::assert($db->query('select 1')->fetch_all()[0][0] === 1)) { goto _error; } $db->close(); From d3eae1d2455f3066c5bc792de7227cf152325aeb Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 14:21:11 +0800 Subject: [PATCH 060/103] optimize tests --- .github/workflows/test-linux.yml | 1 + .github/workflows/test-thread.yml | 40 +++++++++++++++++++++++++++ ext-src/swoole_server.cc | 4 ++- scripts/docker-compile-with-thread.sh | 34 +++++++++++++++++++++++ scripts/docker-thread-route.sh | 21 ++++++++++++++ scripts/make.sh | 1 + scripts/route.sh | 17 +++++++++++- 7 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/test-thread.yml create mode 100755 scripts/docker-compile-with-thread.sh create mode 100755 scripts/docker-thread-route.sh diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index f7ed0167007..1e5fef9c955 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -5,6 +5,7 @@ on: [push, pull_request] jobs: test-linux: runs-on: ubuntu-latest + if: 0 strategy: fail-fast: false matrix: diff --git a/.github/workflows/test-thread.yml b/.github/workflows/test-thread.yml new file mode 100644 index 00000000000..64f4989a597 --- /dev/null +++ b/.github/workflows/test-thread.yml @@ -0,0 +1,40 @@ +name: test-linux + +on: [push, pull_request] + +jobs: + test-linux: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ['8.1', '8.2', '8.3'] + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: none + env: + phpts: ts + - name: Show machine information + run: | + date + env + uname -a + ulimit -a + php -v + php --ini + ls -al + pwd + echo "`git log -20 --pretty --oneline`" + echo "`git log -10 --stat --pretty --oneline`" + - name: Run pecl-install.sh + run: | + sudo ${{runner.workspace}}/swoole-src/scripts/pecl-install.sh + - name: Run Swoole test + run: | + export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_THREAD=1 + ${{runner.workspace}}/swoole-src/scripts/route.sh diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 7c49e908716..c050072ad2e 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -584,7 +584,9 @@ void php_swoole_server_minit(int module_number) { */ SW_REGISTER_LONG_CONSTANT("SWOOLE_BASE", swoole::Server::MODE_BASE); SW_REGISTER_LONG_CONSTANT("SWOOLE_PROCESS", swoole::Server::MODE_PROCESS); - +#ifdef SW_THREAD + SW_REGISTER_LONG_CONSTANT("SWOOLE_THREAD", swoole::Server::MODE_THREAD); +#endif /** * task ipc mode */ diff --git a/scripts/docker-compile-with-thread.sh b/scripts/docker-compile-with-thread.sh new file mode 100755 index 00000000000..99c32bc48b3 --- /dev/null +++ b/scripts/docker-compile-with-thread.sh @@ -0,0 +1,34 @@ +#!/bin/sh -e +__CURRENT__=$(pwd) +__DIR__=$(cd "$(dirname "$0")";pwd) + +sh library.sh + +if [ ! -f "/.dockerenv" ]; then + echo "" && echo "❌ This script is just for Docker!" + exit +fi + +cd "${__DIR__}" && cd .. +./scripts/clear.sh +phpize +./configure \ +--enable-openssl \ +--enable-sockets \ +--enable-mysqlnd \ +--enable-swoole-curl \ +--enable-cares \ +--enable-swoole-pgsql \ +--enable-swoole-thread \ +--with-swoole-odbc=unixODBC,/usr \ +--with-swoole-oracle=instantclient,/usr/local/instantclient \ +--enable-swoole-sqlite + +make -j$(cat /proc/cpuinfo | grep processor | wc -l) +make install +docker-php-ext-enable swoole +php -v +php -m +php --ri curl +php --ri swoole + diff --git a/scripts/docker-thread-route.sh b/scripts/docker-thread-route.sh new file mode 100755 index 00000000000..7a33f58ce12 --- /dev/null +++ b/scripts/docker-thread-route.sh @@ -0,0 +1,21 @@ +#!/bin/sh -e +__CURRENT__=$(pwd) +__DIR__=$(cd "$(dirname "$0")";pwd) + +# enter the dir +cd "${__DIR__}" + +# show system info +date && echo "" +uname -a && echo "" + +# show php info +php -v && echo "" + +# compile in docker +echo "" && echo "📦 Compile thread test in docker..." && echo "" +./docker-compile-with-thread.sh + +# run unit tests +echo "" && echo "📋 PHP unit tests in docker..." && echo "" +./run-tests.sh swoole-thread diff --git a/scripts/make.sh b/scripts/make.sh index 46e6aff78a9..0bddf4ea2d5 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -6,6 +6,7 @@ COMPILE_PARAMS="--enable-openssl \ --enable-mysqlnd \ --enable-swoole-curl \ --enable-cares \ +--enable-swoole-thread \ --enable-swoole-pgsql \ --with-swoole-odbc=unixODBC,/usr \ --enable-swoole-sqlite" diff --git a/scripts/route.sh b/scripts/route.sh index 07272ae87aa..aadbff9d8b9 100755 --- a/scripts/route.sh +++ b/scripts/route.sh @@ -90,6 +90,16 @@ run_tests_in_docker(){ fi } +run_thread_tests_in_docker(){ + docker exec swoole touch /.cienv && \ + docker exec swoole /swoole-src/scripts/docker-thread-route.sh + code=$? + if [ $code -ne 0 ]; then + echo "\n❌ Run thread tests failed! ExitCode: $code" + exit 1 + fi +} + remove_tests_resources(){ remove_docker_containers remove_data_files @@ -104,6 +114,11 @@ echo "📦 Start docker containers...\n" start_docker_containers # && trap "remove_tests_resources" echo "\n⏳ Run tests in docker...\n" -run_tests_in_docker + +if [ $SWOOLE_THREAD = 1 ]; then + run_thread_tests_in_docker +else + run_tests_in_docker +fi echo "\n🚀🚀🚀Completed successfully🚀🚀🚀\n" From a538cbc1b92a8ae36ed7d81b8c915decccdd2ca9 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 15:28:09 +0800 Subject: [PATCH 061/103] add thread test --- tests/include/skipif.inc | 6 ++++++ tests/swoole_thread/lock.phpt | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/swoole_thread/lock.phpt diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index faf324acd13..d33f93f8710 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -26,6 +26,12 @@ skip( (function () { global $argv; skip_if_php_version_lower_than('7.0'); + if (defined('SWOOLE_THREAD')) { + $targs = Swoole\Thread::getArguments(); + if ($targs) { + $argv = $targs[0]; + } + } if (!getenv('PHPT') && substr($argv[0], -4) === '.php') { skip('please read ' . dirname(dirname(__FILE__)) . '/README.md and try again'); } diff --git a/tests/swoole_thread/lock.phpt b/tests/swoole_thread/lock.phpt new file mode 100644 index 00000000000..1f25e946ae2 --- /dev/null +++ b/tests/swoole_thread/lock.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_thread: lock +--SKIPIF-- + +--FILE-- +lock(); + $thread = Thread::exec(__FILE__, $argv, $lock); + $lock->lock(); + echo "main thread\n"; + $thread->join(); +} else { + echo "child thread\n"; + $lock = $args[1]; + usleep(200_000); + $lock->unlock(); + exit(0); +} +?> +--EXPECTF-- +child thread +main thread + From bdfafccc53e5a139446a58061b4e49043fadf921 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 16:29:36 +0800 Subject: [PATCH 062/103] fix ci --- .github/workflows/test-thread.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-thread.yml b/.github/workflows/test-thread.yml index 64f4989a597..7782e1a3b0d 100644 --- a/.github/workflows/test-thread.yml +++ b/.github/workflows/test-thread.yml @@ -1,4 +1,4 @@ -name: test-linux +name: test-thread on: [push, pull_request] @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.1', '8.2', '8.3'] + php: ['8.1-zts', '8.2-zts', '8.3-zts'] steps: - uses: actions/checkout@v4 - name: Setup PHP From b6b529f53205a35df853c8270700171424fa347a Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 18:25:20 +0800 Subject: [PATCH 063/103] [test] fix ci 2 --- .github/workflows/{test-thread.yml => thread.yml} | 7 +++---- scripts/route.sh | 8 -------- 2 files changed, 3 insertions(+), 12 deletions(-) rename .github/workflows/{test-thread.yml => thread.yml} (86%) diff --git a/.github/workflows/test-thread.yml b/.github/workflows/thread.yml similarity index 86% rename from .github/workflows/test-thread.yml rename to .github/workflows/thread.yml index 7782e1a3b0d..57668744cb3 100644 --- a/.github/workflows/test-thread.yml +++ b/.github/workflows/thread.yml @@ -1,4 +1,4 @@ -name: test-thread +name: Thread Support Tests on: [push, pull_request] @@ -9,6 +9,7 @@ jobs: fail-fast: false matrix: php: ['8.1-zts', '8.2-zts', '8.3-zts'] + name: ${{ matrix.php-version }} steps: - uses: actions/checkout@v4 - name: Setup PHP @@ -30,11 +31,9 @@ jobs: pwd echo "`git log -20 --pretty --oneline`" echo "`git log -10 --stat --pretty --oneline`" - - name: Run pecl-install.sh - run: | - sudo ${{runner.workspace}}/swoole-src/scripts/pecl-install.sh - name: Run Swoole test run: | export SWOOLE_BRANCH=${GITHUB_REF##*/} + export PHP_VERSION=${{ matrix.php-version }} export SWOOLE_THREAD=1 ${{runner.workspace}}/swoole-src/scripts/route.sh diff --git a/scripts/route.sh b/scripts/route.sh index aadbff9d8b9..9f38fd1ded2 100755 --- a/scripts/route.sh +++ b/scripts/route.sh @@ -3,14 +3,6 @@ __CURRENT__=`pwd` __DIR__=$(cd "$(dirname "$0")";pwd) export DOCKER_COMPOSE_VERSION="1.21.0" -[ -z "${SWOOLE_BRANCH}" ] && export SWOOLE_BRANCH="master" -[ -z "${SWOOLE_BUILD_DIR}" ] && export SWOOLE_BUILD_DIR=$(cd "$(dirname "$0")";cd ../;pwd) -[ -z "${PHP_VERSION_ID}" ] && export PHP_VERSION_ID=`php -r "echo PHP_VERSION_ID;"` -if [ ${PHP_VERSION_ID} -lt 80300 ]; then - export PHP_VERSION="`php -r "echo PHP_MAJOR_VERSION;"`.`php -r "echo PHP_MINOR_VERSION;"`" -else - export PHP_VERSION="rc" -fi if [ "${SWOOLE_BRANCH}" = "alpine" ]; then export PHP_VERSION="${PHP_VERSION}-alpine" fi From 1c017a4cc4b1e5e84f79da5a802ec316e466afc8 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 18:32:48 +0800 Subject: [PATCH 064/103] [test] fix ci 3 --- .github/workflows/conflict-exts.yml | 1 + .github/workflows/thread.yml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/conflict-exts.yml b/.github/workflows/conflict-exts.yml index 0ecb394afa6..d158869b793 100644 --- a/.github/workflows/conflict-exts.yml +++ b/.github/workflows/conflict-exts.yml @@ -4,6 +4,7 @@ on: [push, pull_request] jobs: tests: + if: "github.repository_owner == 'swoole' && !contains(github.event.head_commit.message, '[test]')" runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml index 57668744cb3..8851052f6ed 100644 --- a/.github/workflows/thread.yml +++ b/.github/workflows/thread.yml @@ -9,7 +9,7 @@ jobs: fail-fast: false matrix: php: ['8.1-zts', '8.2-zts', '8.3-zts'] - name: ${{ matrix.php-version }} + name: ${{ matrix.php }} steps: - uses: actions/checkout@v4 - name: Setup PHP @@ -34,6 +34,6 @@ jobs: - name: Run Swoole test run: | export SWOOLE_BRANCH=${GITHUB_REF##*/} - export PHP_VERSION=${{ matrix.php-version }} + export PHP_VERSION=${{ matrix.php }} export SWOOLE_THREAD=1 ${{runner.workspace}}/swoole-src/scripts/route.sh From 35bd09fcca41c60f6e7226d0523cbd3fb2c47ebf Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 18:38:45 +0800 Subject: [PATCH 065/103] [test] fix ci 4 --- .github/workflows/thread.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml index 8851052f6ed..6ebc4307cb3 100644 --- a/.github/workflows/thread.yml +++ b/.github/workflows/thread.yml @@ -34,6 +34,7 @@ jobs: - name: Run Swoole test run: | export SWOOLE_BRANCH=${GITHUB_REF##*/} - export PHP_VERSION=${{ matrix.php }} export SWOOLE_THREAD=1 + export SWOOLE_BUILD_DIR=${{runner.workspace}} + export PHP_VERSION=${{ matrix.php }} ${{runner.workspace}}/swoole-src/scripts/route.sh From d0fbc1516ec0a8c1f05db50aa51b4abea15d9e8f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 18:46:20 +0800 Subject: [PATCH 066/103] [test] fix ci 5 --- .github/workflows/thread.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml index 6ebc4307cb3..b003a2d9eec 100644 --- a/.github/workflows/thread.yml +++ b/.github/workflows/thread.yml @@ -35,6 +35,8 @@ jobs: run: | export SWOOLE_BRANCH=${GITHUB_REF##*/} export SWOOLE_THREAD=1 - export SWOOLE_BUILD_DIR=${{runner.workspace}} + export SWOOLE_BUILD_DIR=$(realpath(.)) export PHP_VERSION=${{ matrix.php }} - ${{runner.workspace}}/swoole-src/scripts/route.sh + echo ${SWOOLE_BUILD_DIR} + echo ${SWOOLE_BRANCH} + ${SWOOLE_BUILD_DIR}/swoole-src/scripts/route.sh From 23c5d7bef7f2f0888ae8e1855b0c19e7c47d6b06 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 18:48:20 +0800 Subject: [PATCH 067/103] [test] fix ci 6 --- .github/workflows/thread.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml index b003a2d9eec..11c3b566e0f 100644 --- a/.github/workflows/thread.yml +++ b/.github/workflows/thread.yml @@ -35,7 +35,7 @@ jobs: run: | export SWOOLE_BRANCH=${GITHUB_REF##*/} export SWOOLE_THREAD=1 - export SWOOLE_BUILD_DIR=$(realpath(.)) + export SWOOLE_BUILD_DIR=$(realpath .) export PHP_VERSION=${{ matrix.php }} echo ${SWOOLE_BUILD_DIR} echo ${SWOOLE_BRANCH} From 1f9378112d5c63b90035e875f2f412e9536d85c5 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 18:50:43 +0800 Subject: [PATCH 068/103] [test] fix ci 7 --- .github/workflows/thread.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml index 11c3b566e0f..ce5113f35a2 100644 --- a/.github/workflows/thread.yml +++ b/.github/workflows/thread.yml @@ -37,6 +37,4 @@ jobs: export SWOOLE_THREAD=1 export SWOOLE_BUILD_DIR=$(realpath .) export PHP_VERSION=${{ matrix.php }} - echo ${SWOOLE_BUILD_DIR} - echo ${SWOOLE_BRANCH} - ${SWOOLE_BUILD_DIR}/swoole-src/scripts/route.sh + ${SWOOLE_BUILD_DIR}/scripts/route.sh From a20ccdac5975e79435e28f1d6b983cb1c1201899 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 19:09:13 +0800 Subject: [PATCH 069/103] [test] fix ci 8 --- scripts/docker-thread-route.sh | 6 +++--- scripts/run-tests.sh | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/docker-thread-route.sh b/scripts/docker-thread-route.sh index 7a33f58ce12..cfbdf6016c6 100755 --- a/scripts/docker-thread-route.sh +++ b/scripts/docker-thread-route.sh @@ -13,9 +13,9 @@ uname -a && echo "" php -v && echo "" # compile in docker -echo "" && echo "📦 Compile thread test in docker..." && echo "" +echo "" && echo "📦 Compile ext-swoole[thread] in docker..." && echo "" ./docker-compile-with-thread.sh # run unit tests -echo "" && echo "📋 PHP unit tests in docker..." && echo "" -./run-tests.sh swoole-thread +echo "" && echo "📋 Run phpt tests[thread] in docker..." && echo "" +./run-tests.sh diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index da9497b644b..8c022a5e277 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -59,6 +59,8 @@ echo "" && echo "🌵️️ Current branch is ${SWOOLE_BRANCH}" && echo "" if [ "${SWOOLE_BRANCH}" = "valgrind" ]; then dir="base" options="${options} -m" +elif [ $SWOOLE_THREAD = 1 ]; then + dir="swoole_thread" else dir="swoole_*" fi From 70efa87bd78bdb8e0ae252adb8afc498a54e78ca Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 19:16:56 +0800 Subject: [PATCH 070/103] [test] fix ci 9 --- scripts/route.sh | 2 +- scripts/run-tests.sh | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/route.sh b/scripts/route.sh index 9f38fd1ded2..0b7d9af0ea7 100755 --- a/scripts/route.sh +++ b/scripts/route.sh @@ -107,7 +107,7 @@ start_docker_containers # && trap "remove_tests_resources" echo "\n⏳ Run tests in docker...\n" -if [ $SWOOLE_THREAD = 1 ]; then +if [ "$SWOOLE_THREAD" = 1 ]; then run_thread_tests_in_docker else run_tests_in_docker diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index 8c022a5e277..aecaf09b544 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -29,7 +29,7 @@ done # run tests @params($1=list_file, $2=options) run_tests(){ - ./start.sh \ + echo \ "`tr '\n' ' ' < ${1} | xargs`" \ -w ${1} \ ${2} @@ -59,11 +59,12 @@ echo "" && echo "🌵️️ Current branch is ${SWOOLE_BRANCH}" && echo "" if [ "${SWOOLE_BRANCH}" = "valgrind" ]; then dir="base" options="${options} -m" -elif [ $SWOOLE_THREAD = 1 ]; then +elif [ "$SWOOLE_THREAD" = 1 ]; then dir="swoole_thread" else dir="swoole_*" fi +echo "${dir}" echo "${dir}" > tests.list for i in 1 2 3 4 5 do From c5e55f6082e99b71557d10d0eb70ff73a5378274 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 19:25:31 +0800 Subject: [PATCH 071/103] [test] fix ci 10 --- scripts/docker-thread-route.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/docker-thread-route.sh b/scripts/docker-thread-route.sh index cfbdf6016c6..d6af300e654 100755 --- a/scripts/docker-thread-route.sh +++ b/scripts/docker-thread-route.sh @@ -1,6 +1,7 @@ #!/bin/sh -e __CURRENT__=$(pwd) __DIR__=$(cd "$(dirname "$0")";pwd) +export SWOOLE_THREAD=1 # enter the dir cd "${__DIR__}" From 1fbbd4990cdfe1c11d16c1bf6a35119caa4ffc31 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 19:58:04 +0800 Subject: [PATCH 072/103] [test] fix ci 11 --- ext-src/swoole_thread.cc | 2 +- scripts/run-tests.sh | 2 +- tests/swoole_thread/async-io.phpt | 45 ++++++++++++++++++++++ tests/swoole_thread/queue.phpt | 62 +++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 tests/swoole_thread/async-io.phpt create mode 100644 tests/swoole_thread/queue.phpt diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index a5e7f18b930..3dcb24a5dbe 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -242,7 +242,7 @@ struct ZendArray : ThreadResource { void intkey_offsetGet(zend_long index, zval *return_value) { lock_.lock_rd(); - ArrayItem *item = (ArrayItem *) zend_hash_index_find(&ht, index); + ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); if (item) { item->fetch(return_value); } diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index aecaf09b544..774bbf14059 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -29,7 +29,7 @@ done # run tests @params($1=list_file, $2=options) run_tests(){ - echo \ + ./start.sh \ "`tr '\n' ' ' < ${1} | xargs`" \ -w ${1} \ ${2} diff --git a/tests/swoole_thread/async-io.phpt b/tests/swoole_thread/async-io.phpt new file mode 100644 index 00000000000..58b531e3d56 --- /dev/null +++ b/tests/swoole_thread/async-io.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_thread: async-io +--SKIPIF-- + +--FILE-- +join(); + } + Assert::eq($atomic->get(), C * N); +} else { + $atomic = $args[2]; + Co\run(function () use ($atomic, $md5) { + $n = N; + while ($n--) { + $atomic->add(); + $rs = \Swoole\Coroutine\System::readFile(__FILE__); + Assert::eq(md5($rs), $md5); + } + }); + exit(0); +} +echo "DONE\n"; +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_thread/queue.phpt b/tests/swoole_thread/queue.phpt new file mode 100644 index 00000000000..192a67422a3 --- /dev/null +++ b/tests/swoole_thread/queue.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_thread: queue +--SKIPIF-- + +--FILE-- +push($rdata, Queue::NOTIFY_ONE); + usleep(random_int(100, 1000)); + } + $n = 4; + while ($n--) { + $queue->push('', Queue::NOTIFY_ONE); + } + for ($i = 0; $i < C; $i++) { + $threads[$i]->join(); + $total_child += $map[$i]; + } + Assert::eq($queue->count(), 0); + Assert::eq($total_parent, $total_child); +} else { + $i = $args[1]; + $queue = $args[2]; + $map = $args[3]; + $map[$i] = 0; + while (1) { + $job = $queue->pop(-1); + if (!$job) { + break; + } + $map[$i] += strlen($job); + Assert::assert(strlen($job), 16); + } + exit(0); +} +?> +--EXPECTF-- + From b20cb3c0a6f8499c6657188c729b5ee7cbe29b54 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Mon, 15 Apr 2024 20:44:04 +0800 Subject: [PATCH 073/103] [test] remove swoole_timer_set, async settings can only be set in the main thread --- ext-src/php_swoole.cc | 4 +-- ext-src/stubs/php_swoole_timer.stub.php | 2 -- ext-src/stubs/php_swoole_timer_arginfo.h | 6 +--- ext-src/swoole_async_coro.cc | 7 ++++- ext-src/swoole_atomic.cc | 4 +++ ext-src/swoole_lock.cc | 2 ++ ext-src/swoole_server.cc | 2 +- ext-src/swoole_timer.cc | 19 ------------- include/swoole.h | 1 + php_swoole.h | 1 - src/core/base.cc | 1 + tests/swoole_thread/pipe.phpt | 36 ++++++++++++++++++++++++ 12 files changed, 53 insertions(+), 32 deletions(-) create mode 100644 tests/swoole_thread/pipe.phpt diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 1248481f1ff..f290005b060 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -188,7 +188,6 @@ PHP_INI_BEGIN() /** * enable swoole coroutine */ -STD_ZEND_INI_BOOLEAN("swoole.enable_coroutine", "On", PHP_INI_ALL, OnUpdateBool, enable_coroutine, zend_swoole_globals, swoole_globals) STD_ZEND_INI_BOOLEAN("swoole.enable_library", "On", PHP_INI_ALL, OnUpdateBool, enable_library, zend_swoole_globals, swoole_globals) STD_ZEND_INI_BOOLEAN("swoole.enable_fiber_mock", "Off", PHP_INI_ALL, OnUpdateBool, enable_fiber_mock, zend_swoole_globals, swoole_globals) /** @@ -211,7 +210,6 @@ PHP_INI_END() // clang-format on static void php_swoole_init_globals(zend_swoole_globals *swoole_globals) { - swoole_globals->enable_coroutine = 1; swoole_globals->enable_library = 1; swoole_globals->enable_fiber_mock = 0; swoole_globals->enable_preemptive_scheduler = 0; @@ -339,7 +337,7 @@ SW_API bool php_swoole_is_enable_coroutine() { if (sw_server()) { return sw_server()->is_enable_coroutine(); } else { - return SWOOLE_G(enable_coroutine); + return SwooleG.enable_coroutine; } } diff --git a/ext-src/stubs/php_swoole_timer.stub.php b/ext-src/stubs/php_swoole_timer.stub.php index 1265a6be977..dde22eb3567 100644 --- a/ext-src/stubs/php_swoole_timer.stub.php +++ b/ext-src/stubs/php_swoole_timer.stub.php @@ -1,7 +1,5 @@ enable_coroutine = zval_is_true(ztmp); } else { - serv->enable_coroutine = SWOOLE_G(enable_coroutine); + serv->enable_coroutine = SwooleG.enable_coroutine; } if (php_swoole_array_get_value(vht, "send_timeout", ztmp)) { serv->send_timeout = zval_get_double(ztmp); diff --git a/ext-src/swoole_timer.cc b/ext-src/swoole_timer.cc index 4f27ef1a258..750898df568 100644 --- a/ext-src/swoole_timer.cc +++ b/ext-src/swoole_timer.cc @@ -36,7 +36,6 @@ static zend_object_handlers swoole_timer_handlers; static zend_class_entry *swoole_timer_iterator_ce; SW_EXTERN_C_BEGIN -static PHP_FUNCTION(swoole_timer_set); static PHP_FUNCTION(swoole_timer_after); static PHP_FUNCTION(swoole_timer_tick); static PHP_FUNCTION(swoole_timer_exists); @@ -50,7 +49,6 @@ SW_EXTERN_C_END // clang-format off static const zend_function_entry swoole_timer_methods[] = { - ZEND_FENTRY(set, ZEND_FN(swoole_timer_set), arginfo_swoole_timer_set, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED) ZEND_FENTRY(tick, ZEND_FN(swoole_timer_tick), arginfo_swoole_timer_tick, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) ZEND_FENTRY(after, ZEND_FN(swoole_timer_after), arginfo_swoole_timer_after, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) ZEND_FENTRY(exists, ZEND_FN(swoole_timer_exists), arginfo_swoole_timer_exists, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) @@ -69,8 +67,6 @@ void php_swoole_timer_minit(int module_number) { SW_INIT_CLASS_ENTRY_BASE(swoole_timer_iterator, "Swoole\\Timer\\Iterator", nullptr, nullptr, spl_ce_ArrayIterator); - SW_FUNCTION_ALIAS( - &swoole_timer_ce->function_table, "set", CG(function_table), "swoole_timer_set", arginfo_swoole_timer_set); SW_FUNCTION_ALIAS( &swoole_timer_ce->function_table, "after", CG(function_table), "swoole_timer_after", arginfo_swoole_timer_tick); SW_FUNCTION_ALIAS( @@ -203,21 +199,6 @@ static void timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) { RETURN_LONG(tnode->id); } -static PHP_FUNCTION(swoole_timer_set) { - zval *zset = nullptr; - zval *ztmp; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - HashTable *vht = Z_ARRVAL_P(zset); - - if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { - SWOOLE_G(enable_coroutine) = zval_is_true(ztmp); - } -} - static PHP_FUNCTION(swoole_timer_after) { timer_add(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } diff --git a/include/swoole.h b/include/swoole.h index 20381909044..3f423821aec 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -739,6 +739,7 @@ struct Global { uchar dns_lookup_random : 1; uchar use_async_resolver : 1; uchar use_name_resolver : 1; + uchar enable_coroutine : 1; uint8_t process_type; uint32_t process_id; diff --git a/php_swoole.h b/php_swoole.h index aa6cb096f06..539ea21fefd 100644 --- a/php_swoole.h +++ b/php_swoole.h @@ -51,7 +51,6 @@ ZEND_BEGIN_MODULE_GLOBALS(swoole) zend_bool display_errors; zend_bool cli; zend_bool use_shortname; - zend_bool enable_coroutine; zend_bool enable_preemptive_scheduler; zend_bool enable_library; zend_bool enable_fiber_mock; diff --git a/src/core/base.cc b/src/core/base.cc index a3ea0de9a9e..f966974f6ba 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -162,6 +162,7 @@ void swoole_init(void) { SwooleG.running = 1; SwooleG.init = 1; + SwooleG.enable_coroutine = 1; SwooleG.std_allocator = {malloc, calloc, realloc, free}; SwooleG.fatal_error = swoole_fatal_error_impl; SwooleG.cpu_num = SW_MAX(1, sysconf(_SC_NPROCESSORS_ONLN)); diff --git a/tests/swoole_thread/pipe.phpt b/tests/swoole_thread/pipe.phpt new file mode 100644 index 00000000000..5182806919a --- /dev/null +++ b/tests/swoole_thread/pipe.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_thread: lock +--SKIPIF-- + +--FILE-- +recv(8192), $rdata); + $thread->join(); + echo "DONE\n"; + }); +} else { + $sockets = $args[1]; + $rdata = $args[2]; + Co\run(function () use ($sockets, $rdata){ + usleep(100); + $sockets[1]->send($rdata); + }); + exit(0); +} +?> +--EXPECTF-- +DONE From 1594cf52c9c5067bbb15b0bc19ba133d50a43398 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 16 Apr 2024 19:46:55 +0800 Subject: [PATCH 074/103] optimize create socket (#5293) * optimize create socket * fix error * optimize code --- ext-src/php_swoole_http_server.h | 12 +++---- ext-src/swoole_server.cc | 4 +++ include/swoole_server.h | 2 ++ src/server/master.cc | 25 +------------- src/server/port.cc | 52 +++++++++++++++++++++++++++++ src/server/reactor_process.cc | 56 ++++++++++---------------------- 6 files changed, 82 insertions(+), 69 deletions(-) diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h index 4d025788af4..22855ccfcbc 100644 --- a/ext-src/php_swoole_http_server.h +++ b/ext-src/php_swoole_http_server.h @@ -48,35 +48,35 @@ int swoole_http2_server_goaway(swoole::http::Context *ctx, static inline void http_server_add_server_array(HashTable *ht, zend_string *key, const char *value) { zval tmp; ZVAL_STRING(&tmp, value); - zend_hash_add(ht, key, &tmp); + zend_hash_add_new(ht, key, &tmp); } static inline void http_server_add_server_array(HashTable *ht, zend_string *key, const char *value, size_t length) { zval tmp; ZVAL_STRINGL(&tmp, value, length); - zend_hash_add(ht, key, &tmp); + zend_hash_add_new(ht, key, &tmp); } static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zend_long value) { zval tmp; ZVAL_LONG(&tmp, value); - zend_hash_add(ht, key, &tmp); + zend_hash_add_new(ht, key, &tmp); } static inline void http_server_add_server_array(HashTable *ht, zend_string *key, double value) { zval tmp; ZVAL_DOUBLE(&tmp, value); - zend_hash_add(ht, key, &tmp); + zend_hash_add_new(ht, key, &tmp); } static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zend_string *value) { zval tmp; ZVAL_STR(&tmp, value); - zend_hash_add(ht, key, &tmp); + zend_hash_add_new(ht, key, &tmp); } static inline void http_server_add_server_array(HashTable *ht, zend_string *key, zval *value) { - zend_hash_add(ht, key, value); + zend_hash_add_new(ht, key, value); } static inline void http_server_set_object_fd_property(zend_object *object, zend_class_entry *ce, long fd) { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 538dfd12142..9b5b1f24d73 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -139,8 +139,10 @@ static zend_class_entry *swoole_server_task_result_ce; static zend_object_handlers swoole_server_task_result_handlers; static SW_THREAD_LOCAL zval swoole_server_instance; +#ifdef SW_THREAD static SW_THREAD_LOCAL WorkerFn worker_thread_fn; static SW_THREAD_LOCAL std::vector swoole_server_port_properties; +#endif static sw_inline ServerObject *server_fetch_object(zend_object *obj) { return (ServerObject *) ((char *) obj - swoole_server_handlers.offset); @@ -2374,6 +2376,7 @@ static PHP_METHOD(swoole_server, set) { zend_long v = zval_get_long(ztmp); serv->message_queue_key = SW_MAX(0, SW_MIN(v, INT64_MAX)); } +#ifdef SW_THREAD // bootstrap if (php_swoole_array_get_value(vht, "bootstrap", ztmp)) { zend::object_set(ZEND_THIS, ZEND_STRL("bootstrap"), ztmp); @@ -2386,6 +2389,7 @@ static PHP_METHOD(swoole_server, set) { } else { ZVAL_NULL(&server_object->init_arguments); } +#endif if (serv->task_enable_coroutine && (serv->task_ipc_mode == Server::TASK_IPC_MSGQUEUE || serv->task_ipc_mode == Server::TASK_IPC_PREEMPTIVE)) { diff --git a/include/swoole_server.h b/include/swoole_server.h index 36f2373b7fd..d9ccf586a3c 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -325,6 +325,8 @@ struct ListenPort { void close(); bool import(int sock); const char *get_protocols(); + int create_socket(Server *server); + void close_socket(); #ifdef SW_USE_OPENSSL bool ssl_create_context(SSLContext *context); diff --git a/src/server/master.cc b/src/server/master.cc index b77fb64639e..21a2029f2da 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1706,29 +1706,6 @@ int Server::add_systemd_socket() { return count; } -static bool Server_create_socket(ListenPort *ls) { - ls->socket = make_socket( - ls->type, ls->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); - if (!ls->socket) { - return false; - } -#if defined(SW_SUPPORT_DTLS) && defined(HAVE_KQUEUE) - if (ls->is_dtls()) { - ls->socket->set_reuse_port(); - } -#endif - - if (ls->socket->bind(ls->host, &ls->port) < 0) { - swoole_set_last_error(errno); - ls->socket->free(); - return false; - } - - ls->socket->info.assign(ls->type, ls->host, ls->port); - - return true; -} - ListenPort *Server::add_port(SocketType type, const char *host, int port) { if (session_list) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_WRONG_OPERATION, "must add port before server is created"); @@ -1786,7 +1763,7 @@ ListenPort *Server::add_port(SocketType type, const char *host, int port) { } #endif - if (!Server_create_socket(ls)) { + if (ls->create_socket(this) < 0) { swoole_set_last_error(errno); return nullptr; } diff --git a/src/server/port.cc b/src/server/port.cc index 89902ae709e..af037bef3b7 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -792,4 +792,56 @@ size_t ListenPort::get_connection_num() const { } } +int ListenPort::create_socket(Server *server) { + if (socket) { +#if defined(__linux__) && defined(HAVE_REUSEPORT) + if (server->enable_reuse_port) { + close_socket(); + } else +#endif + { + return SW_OK; + } + } + + socket = make_socket( + type, is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); + if (socket == nullptr) { + swoole_set_last_error(errno); + return SW_ERR; + } + +#if defined(SW_SUPPORT_DTLS) && defined(HAVE_KQUEUE) + if (ls->is_dtls()) { + socket->set_reuse_port(); + } +#endif + +#if defined(__linux__) && defined(HAVE_REUSEPORT) + if (server->enable_reuse_port) { + if (socket->set_reuse_port() < 0) { + socket->free(); + return SW_ERR; + } + } +#endif + + if (socket->bind(host, &port) < 0) { + swoole_set_last_error(errno); + socket->free(); + return SW_ERR; + } + + socket->info.assign(type, host, port); + return SW_OK; +} + +void ListenPort::close_socket() { + if (::close(socket->fd) < 0) { + swoole_sys_warning("close(%d) failed", socket->fd); + } + delete socket; + socket = nullptr; +} + } // namespace swoole diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 6ef8d84e678..d4c9370d33a 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -24,10 +24,6 @@ static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event); static int ReactorProcess_onClose(Reactor *reactor, Event *event); static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode); -#ifdef HAVE_REUSEPORT -static int ReactorProcess_reuse_port(ListenPort *ls); -#endif - static bool Server_is_single(Server *serv) { return (serv->worker_num == 1 && serv->task_worker_num == 0 && serv->max_request == 0 && serv->user_worker_list.empty()); @@ -39,24 +35,19 @@ int Server::start_reactor_processes() { // listen TCP if (have_stream_sock == 1) { for (auto ls : ports) { - if (ls->is_dgram()) { - continue; - } -#ifdef HAVE_REUSEPORT - if (enable_reuse_port) { - if (::close(ls->socket->fd) < 0) { - swoole_sys_warning("close(%d) failed", ls->socket->fd); - } - delete ls->socket; - ls->socket = nullptr; - continue; - } else + if (ls->is_stream()) { +#if defined(__linux__) && defined(HAVE_REUSEPORT) + if (!enable_reuse_port) { #endif - { - // listen server socket - if (ls->listen() < 0) { - return SW_ERR; + // listen server socket + if (ls->listen() < 0) { + return SW_ERR; + } +#if defined(__linux__) && defined(HAVE_REUSEPORT) + } else { + ls->close_socket(); } +#endif } } } @@ -194,12 +185,16 @@ int Server::worker_main_loop(ProcessPool *pool, Worker *worker) { serv->worker_signal_init(); for (auto ls : serv->ports) { -#ifdef HAVE_REUSEPORT +#if defined(__linux__) and defined(HAVE_REUSEPORT) if (ls->is_stream() && serv->enable_reuse_port) { - if (ReactorProcess_reuse_port(ls) < 0) { + if (ls->create_socket(serv) < 0) { swoole_event_free(); return SW_ERR; } + + if (ls->listen() < 0) { + return SW_ERR; + } } #endif if (reactor->add(ls->socket, SW_EVENT_READ) < 0) { @@ -350,21 +345,4 @@ static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode) { ReactorProcess_onClose(reactor, ¬ify_ev); }); } - -#ifdef HAVE_REUSEPORT -static int ReactorProcess_reuse_port(ListenPort *ls) { - ls->socket = swoole::make_socket( - ls->type, ls->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); - if (ls->socket->set_reuse_port() < 0) { - ls->socket->free(); - return SW_ERR; - } - if (ls->socket->bind(ls->host, &ls->port) < 0) { - ls->socket->free(); - return SW_ERR; - } - return ls->listen(); -} -#endif - } // namespace swoole From 20eec88beafc63707f45a5ee6eaa0b6c4b818527 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 16 Apr 2024 20:31:38 +0800 Subject: [PATCH 075/103] [test] fix tests --- ext-src/stubs/php_swoole.stub.php | 2 +- ext-src/stubs/php_swoole_arginfo.h | 4 ++-- ext-src/swoole_async_coro.cc | 1 + ext-src/swoole_runtime.cc | 38 ++++++++++++++++++++---------- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/ext-src/stubs/php_swoole.stub.php b/ext-src/stubs/php_swoole.stub.php index ba952951a10..bc52e6f31b5 100644 --- a/ext-src/stubs/php_swoole.stub.php +++ b/ext-src/stubs/php_swoole.stub.php @@ -16,7 +16,7 @@ function swoole_async_dns_lookup_coro(string $domain_name, float $timeout = 60, { } -function swoole_async_set(array $settings): void +function swoole_async_set(array $settings): bool { } diff --git a/ext-src/stubs/php_swoole_arginfo.h b/ext-src/stubs/php_swoole_arginfo.h index 64cdf7b9dfe..34aec6bcf1d 100644 --- a/ext-src/stubs/php_swoole_arginfo.h +++ b/ext-src/stubs/php_swoole_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 20f9cbe81acd5771dbf8e18dd8af8952540ead91 */ + * Stub hash: 1ab45a47bad71a13ad16c3d92dcb8612920ae84c */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_version, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -15,7 +15,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_async_dns_lookup_coro, 0, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_LONG, 0, "AF_INET") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_async_set, 0, 1, IS_VOID, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_swoole_async_set, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, settings, IS_ARRAY, 0) ZEND_END_ARG_INFO() diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index 4c5c444892c..02bc801c72c 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -124,6 +124,7 @@ PHP_FUNCTION(swoole_async_set) { if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { SwooleG.enable_coroutine = zval_is_true(ztmp); } + RETURN_TRUE; } PHP_FUNCTION(swoole_async_dns_lookup_coro) { diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index c41912d304c..2248274f7c9 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -110,9 +110,6 @@ struct NetStream { bool blocking; }; -static bool runtime_hook_init = false; -static int runtime_hook_flags = 0; - static struct { php_stream_transport_factory tcp; php_stream_transport_factory udp; @@ -172,9 +169,15 @@ static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { #define SW_HOOK_LIBRARY_FE(name, arg_info) \ ZEND_RAW_FENTRY("swoole_hook_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) +static SW_THREAD_LOCAL bool runtime_hook_init = false; +static SW_THREAD_LOCAL int runtime_hook_flags = 0; static SW_THREAD_LOCAL zend_array *tmp_function_table = nullptr; static SW_THREAD_LOCAL std::unordered_map child_class_entries; +#ifdef SW_THREAD +static std::unordered_map ori_func_handlers; +#endif + SW_EXTERN_C_BEGIN #include "ext/standard/file.h" #include "thirdparty/php/streams/plain_wrapper.c" @@ -1956,6 +1959,10 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend zf->internal_function.arg_info = arg_info; } +#ifdef SW_THREAD + ori_func_handlers[std::string(zf->common.function_name->val, zf->common.function_name->len)] = rf->ori_handler; +#endif + if (use_php_func) { char func[128]; memcpy(func, ZEND_STRL("swoole_")); @@ -2051,17 +2058,22 @@ static PHP_FUNCTION(swoole_stream_socket_pair) { } static PHP_FUNCTION(swoole_user_func_handler) { - zend_fcall_info fci; - fci.size = sizeof(fci); - fci.object = nullptr; - ZVAL_UNDEF(&fci.function_name); - fci.retval = return_value; - fci.param_count = ZEND_NUM_ARGS(); - fci.params = ZEND_CALL_ARG(execute_data, 1); - fci.named_params = NULL; - real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, execute_data->func->common.function_name); - zend_call_function(&fci, rf->fci_cache); + if (rf) { + zend_fcall_info fci; + fci.size = sizeof(fci); + fci.object = nullptr; + fci.retval = return_value; + fci.param_count = ZEND_NUM_ARGS(); + fci.params = ZEND_CALL_ARG(execute_data, 1); + fci.named_params = NULL; + ZVAL_UNDEF(&fci.function_name); + zend_call_function(&fci, rf->fci_cache); + } else { + zend_string *fn_str = execute_data->func->common.function_name; + auto ori_handler = ori_func_handlers[std::string(fn_str->val, fn_str->len)]; + ori_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } } zend_class_entry *find_class_entry(const char *name, size_t length) { From d66eabfc18043e7719628b0c3eee61649e035bf9 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 16 Apr 2024 21:11:56 +0800 Subject: [PATCH 076/103] [test] fix tests [3] --- ext-src/swoole_runtime.cc | 5 +++-- src/server/port.cc | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 2248274f7c9..a41fc6e7da8 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -169,8 +169,8 @@ static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { #define SW_HOOK_LIBRARY_FE(name, arg_info) \ ZEND_RAW_FENTRY("swoole_hook_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) -static SW_THREAD_LOCAL bool runtime_hook_init = false; -static SW_THREAD_LOCAL int runtime_hook_flags = 0; +static bool runtime_hook_init = false; +static int runtime_hook_flags = 0; static SW_THREAD_LOCAL zend_array *tmp_function_table = nullptr; static SW_THREAD_LOCAL std::unordered_map child_class_entries; @@ -265,6 +265,7 @@ void php_swoole_runtime_mshutdown() { #ifdef SW_USE_CURL swoole_native_curl_mshutdown(); #endif + PHPCoroutine::disable_hook(); } static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err) { diff --git a/src/server/port.cc b/src/server/port.cc index af037bef3b7..393b06d6073 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -812,7 +812,7 @@ int ListenPort::create_socket(Server *server) { } #if defined(SW_SUPPORT_DTLS) && defined(HAVE_KQUEUE) - if (ls->is_dtls()) { + if (is_dtls()) { socket->set_reuse_port(); } #endif From 93d6cec3743e64dde19d76f26485162913d4687f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 17 Apr 2024 10:52:50 +0800 Subject: [PATCH 077/103] [test] fix tests [4] --- ext-src/swoole_runtime.cc | 76 +++++++++++++++++++++++++---------- include/swoole_error.h | 1 + tests/swoole_thread/pipe.phpt | 8 +++- 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index a41fc6e7da8..0dd9b64f77c 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -173,10 +173,7 @@ static bool runtime_hook_init = false; static int runtime_hook_flags = 0; static SW_THREAD_LOCAL zend_array *tmp_function_table = nullptr; static SW_THREAD_LOCAL std::unordered_map child_class_entries; - -#ifdef SW_THREAD static std::unordered_map ori_func_handlers; -#endif SW_EXTERN_C_BEGIN #include "ext/standard/file.h" @@ -265,7 +262,10 @@ void php_swoole_runtime_mshutdown() { #ifdef SW_USE_CURL swoole_native_curl_mshutdown(); #endif +#ifdef SW_THREAD PHPCoroutine::disable_hook(); +#endif + ori_func_handlers.clear(); } static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err) { @@ -1173,6 +1173,12 @@ void PHPCoroutine::enable_unsafe_function() { } bool PHPCoroutine::enable_hook(uint32_t flags) { +#ifdef SW_THREAD + if (!tsrm_is_main_thread()) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + return false; + } +#endif if (swoole_isset_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK)) { swoole_call_hook((enum swGlobalHookType) PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK, &flags); } @@ -1592,6 +1598,13 @@ static PHP_METHOD(swoole_runtime, enableCoroutine) { } } +#ifdef SW_THREAD + if (runtime_hook_init && flags == 0) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + RETURN_FALSE; + } +#endif + PHPCoroutine::set_hook_flags(flags); RETURN_BOOL(PHPCoroutine::enable_hook(flags)); } @@ -1615,6 +1628,15 @@ static PHP_METHOD(swoole_runtime, setHookFlags) { Z_PARAM_LONG(flags) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); +#ifdef SW_THREAD + // In a multi-threaded environment, disabling the hook is prohibited. + // It can only be enabled once in the main thread. + if (runtime_hook_init && flags == 0) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + RETURN_FALSE; + } +#endif + PHPCoroutine::set_hook_flags(flags); RETURN_BOOL(PHPCoroutine::enable_hook(flags)); } @@ -1950,6 +1972,7 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend return; } + auto fn_str = zf->common.function_name; rf = (real_func *) emalloc(sizeof(real_func)); sw_memset_zero(rf, sizeof(*rf)); rf->function = zf; @@ -1960,16 +1983,14 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend zf->internal_function.arg_info = arg_info; } -#ifdef SW_THREAD - ori_func_handlers[std::string(zf->common.function_name->val, zf->common.function_name->len)] = rf->ori_handler; -#endif + ori_func_handlers[std::string(fn_str->val, fn_str->len)] = rf->ori_handler; if (use_php_func) { char func[128]; memcpy(func, ZEND_STRL("swoole_")); - memcpy(func + 7, zf->common.function_name->val, zf->common.function_name->len); + memcpy(func + 7, fn_str->val, fn_str->len); - ZVAL_STRINGL(&rf->name, func, zf->common.function_name->len + 7); + ZVAL_STRINGL(&rf->name, func, fn_str->len + 7); char *func_name; zend_fcall_info_cache *func_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); @@ -1981,7 +2002,7 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend rf->fci_cache = func_cache; } - zend_hash_add_ptr(tmp_function_table, zf->common.function_name, rf); + zend_hash_add_ptr(tmp_function_table, fn_str, rf); } static void unhook_func(const char *name, size_t l_name) { @@ -2059,22 +2080,33 @@ static PHP_FUNCTION(swoole_stream_socket_pair) { } static PHP_FUNCTION(swoole_user_func_handler) { - real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, execute_data->func->common.function_name); - if (rf) { - zend_fcall_info fci; - fci.size = sizeof(fci); - fci.object = nullptr; - fci.retval = return_value; - fci.param_count = ZEND_NUM_ARGS(); - fci.params = ZEND_CALL_ARG(execute_data, 1); - fci.named_params = NULL; - ZVAL_UNDEF(&fci.function_name); - zend_call_function(&fci, rf->fci_cache); - } else { - zend_string *fn_str = execute_data->func->common.function_name; + auto fn_str = execute_data->func->common.function_name; + if (!swoole_coroutine_is_in()) { auto ori_handler = ori_func_handlers[std::string(fn_str->val, fn_str->len)]; ori_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; } + + real_func *rf = (real_func *) zend_hash_find_ptr(tmp_function_table, fn_str); + if (!rf) { +#ifdef SW_THREAD + hook_func(fn_str->val, fn_str->len); + rf = (real_func *) zend_hash_find_ptr(tmp_function_table, fn_str); +#else + zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_UNDEFINED_BEHAVIOR, "%s func not exists", fn_str->val); + return; +#endif + } + + zend_fcall_info fci; + fci.size = sizeof(fci); + fci.object = nullptr; + fci.retval = return_value; + fci.param_count = ZEND_NUM_ARGS(); + fci.params = ZEND_CALL_ARG(execute_data, 1); + fci.named_params = NULL; + ZVAL_UNDEF(&fci.function_name); + zend_call_function(&fci, rf->fci_cache); } zend_class_entry *find_class_entry(const char *name, size_t length) { diff --git a/include/swoole_error.h b/include/swoole_error.h index 603d0cd38bc..3d6097e3318 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -35,6 +35,7 @@ enum swErrorCode { SW_ERROR_PROTOCOL_ERROR, SW_ERROR_WRONG_OPERATION, SW_ERROR_PHP_RUNTIME_NOTICE, // Non-fatal errors, just runtime warnings + SW_ERROR_UNDEFINED_BEHAVIOR = 600, SW_ERROR_FILE_NOT_EXIST = 700, SW_ERROR_FILE_TOO_LARGE, diff --git a/tests/swoole_thread/pipe.phpt b/tests/swoole_thread/pipe.phpt index 5182806919a..ebc722b0065 100644 --- a/tests/swoole_thread/pipe.phpt +++ b/tests/swoole_thread/pipe.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_thread: lock +swoole_thread: pipe --SKIPIF-- send($rdata); }); exit(0); From 14b040677b3ad37e01431e9298f3cc2f72c48a42 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:27:30 +0800 Subject: [PATCH 078/103] sync plain_wrapper (#5296) --- thirdparty/php/streams/plain_wrapper.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index 5776deb3039..c4281f10687 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -246,9 +246,15 @@ static void _sw_detect_is_seekable(php_stdio_stream_data *self) { } } #elif defined(PHP_WIN32) +#if PHP_VERSION_ID >= 80300 + uintptr_t handle = _get_osfhandle(self->fd); + + if (handle != (uintptr_t)INVALID_HANDLE_VALUE) { +#else zend_uintptr_t handle = _get_osfhandle(self->fd); if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) { +#endif DWORD file_type = GetFileType((HANDLE)handle); self->is_seekable = !(file_type == FILE_TYPE_PIPE || file_type == FILE_TYPE_CHAR); @@ -687,10 +693,15 @@ static int sw_php_stdiop_set_option(php_stream *stream, int option, int value, v if (fd == -1) { return -1; } - +#if PHP_VERSION_ID >= 80300 + if ((uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) { + return 0; + } +#else if ((zend_uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) { return 0; } +#endif if (!swoole_coroutine_flock_ex(stream->orig_path, fd, value)) { data->lock_flag = value; @@ -972,6 +983,13 @@ static php_stream_size_t php_plain_files_dirstream_read(php_stream *stream, char result = readdir(dir); if (result) { PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name)); +#if PHP_VERSION_ID >= 80300 +#ifdef _DIRENT_HAVE_D_TYPE + ent->d_type = result->d_type; +#else + ent->d_type = DT_UNKNOWN; +#endif +#endif return sizeof(php_stream_dirent); } return 0; From 0f85227a2e6eaedecdb5c9f5f46817ba50d53950 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 19 Apr 2024 10:32:21 +0800 Subject: [PATCH 079/103] [test] fix tests [5] --- ext-src/swoole_async_coro.cc | 2 ++ tests/swoole_thread/signal.phpt | 57 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 tests/swoole_thread/signal.phpt diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index 02bc801c72c..fee43f06fe6 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -71,11 +71,13 @@ void php_swoole_set_aio_option(HashTable *vht) { PHP_FUNCTION(swoole_async_set) { #ifdef SW_THREAD if (!tsrm_is_main_thread()) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); RETURN_FALSE; } #endif if (sw_reactor()) { php_swoole_fatal_error(E_ERROR, "eventLoop has already been created. unable to change settings"); + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); RETURN_FALSE; } diff --git a/tests/swoole_thread/signal.phpt b/tests/swoole_thread/signal.phpt new file mode 100644 index 00000000000..ce04966e10e --- /dev/null +++ b/tests/swoole_thread/signal.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_thread: signal +--SKIPIF-- + +--FILE-- +send('exit'); + } + Co\go(function () use ($parent_pipe, $thread) { + // 从管道中读取子线程退出的信息 + echo $parent_pipe->recv(8192), PHP_EOL; + // 回收子线程 + $thread->join(); + }); + }); +} else { + $argv = $args[0]; + $sockets = $args[1]; + $child_pipe = $sockets[0]; + Co\run(function () use ($child_pipe, $argv) { + // 收到父线程的指令,开始退出 + echo $child_pipe->recv(8192), PHP_EOL; + // 通知父线程已退出 + $child_pipe->send('child exit'); + }); + exit(0); +} +?> +--EXPECTF-- +timer +signal term +exit +child exit From d0e8154b8f869658f9e3309ee8b791a94a064f51 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 23 Apr 2024 11:59:22 +0800 Subject: [PATCH 080/103] No limit on the maximum buffer length of the read pipeline --- ext-src/swoole_process.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ext-src/swoole_process.cc b/ext-src/swoole_process.cc index 84883f00f90..667ffcc1cf8 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -702,10 +702,6 @@ static PHP_METHOD(swoole_process, read) { RETURN_FALSE; } - if (buf_size > 65536) { - buf_size = 65536; - } - Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); if (process->pipe_current == nullptr) { From 2589cfc7c2beb586b057fe8aa13102af83fea907 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 23 Apr 2024 16:12:32 +0800 Subject: [PATCH 081/103] Refactor --- ext-src/php_swoole.cc | 5 + ext-src/php_swoole_private.h | 5 + ext-src/php_swoole_thread.h | 271 ++++++ ext-src/stubs/php_swoole_atomic.stub.php | 6 - ext-src/stubs/php_swoole_atomic_arginfo.h | 11 +- ext-src/stubs/php_swoole_lock.stub.php | 5 +- ext-src/stubs/php_swoole_lock_arginfo.h | 8 +- ext-src/stubs/php_swoole_thread.stub.php | 32 - ext-src/stubs/php_swoole_thread_arginfo.h | 63 +- .../php_swoole_thread_arraylist.stub.php | 13 + .../php_swoole_thread_arraylist_arginfo.h | 30 + .../stubs/php_swoole_thread_atomic.stub.php | 26 + .../stubs/php_swoole_thread_atomic_arginfo.h | 51 ++ ext-src/stubs/php_swoole_thread_lock.stub.php | 14 + .../stubs/php_swoole_thread_lock_arginfo.h | 27 + ext-src/stubs/php_swoole_thread_map.stub.php | 14 + ext-src/stubs/php_swoole_thread_map_arginfo.h | 33 + .../stubs/php_swoole_thread_queue.stub.php | 11 + .../stubs/php_swoole_thread_queue_arginfo.h | 22 + ext-src/swoole_atomic.cc | 210 +---- ext-src/swoole_lock.cc | 90 +- ext-src/swoole_thread.cc | 816 ------------------ ext-src/swoole_thread_arraylist.cc | 182 ++++ ext-src/swoole_thread_atomic.cc | 362 ++++++++ ext-src/swoole_thread_lock.cc | 230 +++++ ext-src/swoole_thread_map.cc | 196 +++++ ext-src/swoole_thread_queue.cc | 259 ++++++ include/swoole_atomic.h | 62 ++ include/swoole_error.h | 1 + tests/swoole_thread/async-io.phpt | 11 +- tests/swoole_thread/lock.phpt | 2 +- 31 files changed, 1850 insertions(+), 1218 deletions(-) create mode 100644 ext-src/stubs/php_swoole_thread_arraylist.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_arraylist_arginfo.h create mode 100644 ext-src/stubs/php_swoole_thread_atomic.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_atomic_arginfo.h create mode 100644 ext-src/stubs/php_swoole_thread_lock.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_lock_arginfo.h create mode 100644 ext-src/stubs/php_swoole_thread_map.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_map_arginfo.h create mode 100644 ext-src/stubs/php_swoole_thread_queue.stub.php create mode 100644 ext-src/stubs/php_swoole_thread_queue_arginfo.h create mode 100644 ext-src/swoole_thread_arraylist.cc create mode 100644 ext-src/swoole_thread_atomic.cc create mode 100644 ext-src/swoole_thread_lock.cc create mode 100644 ext-src/swoole_thread_map.cc create mode 100644 ext-src/swoole_thread_queue.cc diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index f290005b060..196c8238cd3 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -749,6 +749,11 @@ PHP_MINIT_FUNCTION(swoole) { #endif #ifdef SW_THREAD php_swoole_thread_minit(module_number); + php_swoole_thread_atomic_minit(module_number); + php_swoole_thread_lock_minit(module_number); + php_swoole_thread_queue_minit(module_number); + php_swoole_thread_map_minit(module_number); + php_swoole_thread_arraylist_minit(module_number); #endif SwooleG.fatal_error = fatal_error; diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 38d10cad617..46272a5e344 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -269,6 +269,11 @@ void php_swoole_redis_server_minit(int module_number); void php_swoole_name_resolver_minit(int module_number); #ifdef SW_THREAD void php_swoole_thread_minit(int module_number); +void php_swoole_thread_atomic_minit(int module_number); +void php_swoole_thread_lock_minit(int module_number); +void php_swoole_thread_queue_minit(int module_number); +void php_swoole_thread_map_minit(int module_number); +void php_swoole_thread_arraylist_minit(int module_number); #endif /** diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index 78fa5176920..096a834ba68 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -21,6 +21,8 @@ #ifdef SW_THREAD +#include "swoole_lock.h" + typedef uint32_t ThreadResourceId; struct ThreadResource; @@ -55,4 +57,273 @@ struct ThreadResource { } }; + +struct ArrayItem { + uint32_t type; + zend_string *key; + union { + zend_string *str; + zend_long lval; + double dval; + zend_string *serialized_object; + } value; + + ArrayItem(zval *zvalue) { + key = nullptr; + value = {}; + store(zvalue); + } + + void store(zval *zvalue) { + type = Z_TYPE_P(zvalue); + switch (type) { + case IS_LONG: + value.lval = zval_get_long(zvalue); + break; + case IS_DOUBLE: + value.dval = zval_get_double(zvalue); + break; + case IS_STRING: { + value.str = zend_string_init(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), 1); + break; + case IS_TRUE: + case IS_FALSE: + case IS_NULL: + break; + } + default: { + auto _serialized_object = php_swoole_thread_serialize(zvalue); + if (!_serialized_object) { + type = IS_UNDEF; + break; + } else { + type = IS_SERIALIZED_OBJECT; + value.serialized_object = _serialized_object; + } + break; + } + } + } + + void fetch(zval *return_value) { + switch (type) { + case IS_LONG: + RETVAL_LONG(value.lval); + break; + case IS_DOUBLE: + RETVAL_LONG(value.dval); + break; + case IS_TRUE: + RETVAL_TRUE; + break; + case IS_FALSE: + RETVAL_FALSE; + break; + case IS_STRING: + RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); + break; + case IS_SERIALIZED_OBJECT: + php_swoole_thread_unserialize(value.serialized_object, return_value); + break; + default: + break; + } + } + + void release() { + if (type == IS_STRING) { + zend_string_release(value.str); + value.str = nullptr; + } else if (type == IS_SERIALIZED_OBJECT) { + zend_string_release(value.serialized_object); + value.serialized_object = nullptr; + } + } + + ~ArrayItem() { + if (value.str) { + release(); + } + if (key) { + zend_string_release(key); + } + } +}; + +struct ZendArray : ThreadResource { + swoole::RWLock lock_; + zend_array ht; + + static void item_dtor(zval *pDest) { + ArrayItem *item = (ArrayItem *) Z_PTR_P(pDest); + delete item; + } + + ZendArray() : ThreadResource(), lock_(0) { + zend_hash_init(&ht, 0, NULL, item_dtor, 1); + } + + ~ZendArray() { + zend_hash_destroy(&ht); + } + + void clean() { + lock_.lock(); + zend_hash_clean(&ht); + lock_.unlock(); + } + + bool index_exists(zend_long index) { + return index < (zend_long) zend_hash_num_elements(&ht); + } + + void strkey_offsetGet(zval *zkey, zval *return_value) { + zend::String skey(zkey); + lock_.lock_rd(); + ArrayItem *item = (ArrayItem *) zend_hash_find_ptr(&ht, skey.get()); + if (item) { + item->fetch(return_value); + } + lock_.unlock(); + } + + void strkey_offsetExists(zval *zkey, zval *return_value) { + zend::String skey(zkey); + lock_.lock_rd(); + RETVAL_BOOL(zend_hash_find_ptr(&ht, skey.get()) != NULL); + lock_.unlock(); + } + + void strkey_offsetUnset(zval *zkey) { + zend::String skey(zkey); + lock_.lock(); + zend_hash_del(&ht, skey.get()); + lock_.unlock(); + } + + void strkey_offsetSet(zval *zkey, zval *zvalue) { + zend::String skey(zkey); + auto item = new ArrayItem(zvalue); + item->key = zend_string_init(skey.val(), skey.len(), 1); + lock_.lock(); + zend_hash_update_ptr(&ht, item->key, item); + lock_.unlock(); + } + + void count(zval *return_value) { + lock_.lock_rd(); + RETVAL_LONG(zend_hash_num_elements(&ht)); + lock_.unlock(); + } + + void keys(zval *return_value) { + lock_.lock_rd(); + zend_ulong elem_count = zend_hash_num_elements(&ht); + array_init_size(return_value, elem_count); + zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); + zend_ulong num_idx; + zend_string *str_idx; + zval *entry; + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + if (HT_IS_PACKED(&ht) && HT_IS_WITHOUT_HOLES(&ht)) { + /* Optimistic case: range(0..n-1) for vector-like packed array */ + zend_ulong lval = 0; + + for (; lval < elem_count; ++lval) { + ZEND_HASH_FILL_SET_LONG(lval); + ZEND_HASH_FILL_NEXT(); + } + } else { + /* Go through input array and add keys to the return array */ + ZEND_HASH_FOREACH_KEY_VAL(&ht, num_idx, str_idx, entry) { + if (str_idx) { + ZEND_HASH_FILL_SET_STR(zend_string_init(str_idx->val, str_idx->len, 0)); + } else { + ZEND_HASH_FILL_SET_LONG(num_idx); + } + ZEND_HASH_FILL_NEXT(); + } + ZEND_HASH_FOREACH_END(); + } + (void) entry; + } + ZEND_HASH_FILL_END(); + lock_.unlock(); + } + + void intkey_offsetGet(zend_long index, zval *return_value) { + lock_.lock_rd(); + ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + if (item) { + item->fetch(return_value); + } + lock_.unlock(); + } + + void intkey_offsetGet(zval *zkey, zval *return_value) { + intkey_offsetGet(zval_get_long(zkey), return_value); + } + + void intkey_offsetExists(zval *zkey, zval *return_value) { + zend_long index = zval_get_long(zkey); + lock_.lock_rd(); + RETVAL_BOOL(zend_hash_index_find_ptr(&ht, index) != NULL); + lock_.unlock(); + } + + void intkey_offsetUnset(zval *zkey) { + zend_long index = zval_get_long(zkey); + lock_.lock(); + zend_hash_index_del(&ht, index); + lock_.unlock(); + } + + void intkey_offsetSet(zval *zkey, zval *zvalue) { + zend_long index = zval_get_long(zkey); + auto item = new ArrayItem(zvalue); + lock_.lock(); + zend_hash_index_update_ptr(&ht, index, item); + lock_.unlock(); + } + + bool index_offsetGet(zval *zkey, zval *return_value) { + zend_long index = zval_get_long(zkey); + bool out_of_range = true; + lock_.lock_rd(); + if (index_exists(index)) { + out_of_range = false; + ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); + if (item) { + item->fetch(return_value); + } + } + lock_.unlock(); + return !out_of_range; + } + + bool index_offsetSet(zval *zkey, zval *zvalue) { + zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); + auto item = new ArrayItem(zvalue); + bool success = true; + lock_.lock(); + if (index > zend_hash_num_elements(&ht)) { + success = false; + delete item; + } else if (index == -1 || index == zend_hash_num_elements(&ht)) { + zend_hash_next_index_insert_ptr(&ht, item); + } else { + zend_hash_index_update_ptr(&ht, index, item); + } + lock_.unlock(); + return success; + } + + void index_offsetExists(zval *zkey, zval *return_value) { + zend_long index = zval_get_long(zkey); + lock_.lock_rd(); + RETVAL_BOOL(index_exists(index)); + lock_.unlock(); + } +}; + #endif diff --git a/ext-src/stubs/php_swoole_atomic.stub.php b/ext-src/stubs/php_swoole_atomic.stub.php index 71bb5eec9d4..adfd74b0f5f 100644 --- a/ext-src/stubs/php_swoole_atomic.stub.php +++ b/ext-src/stubs/php_swoole_atomic.stub.php @@ -9,9 +9,6 @@ public function set(int $value): void {} public function cmpset(int $cmp_value, int $new_value): bool {} public function wait(float $timeout = 1.0): bool {} public function wakeup(int $count = 1): bool {} - #ifdef SW_THREAD - public function __wakeup(): void {} - #endif } } @@ -23,8 +20,5 @@ public function sub(int $sub_value = 1): int {} public function get(): int {} public function set(int $value): void {} public function cmpset(int $cmp_value, int $new_value): bool {} - #ifdef SW_THREAD - public function __wakeup(): void {} - #endif } } diff --git a/ext-src/stubs/php_swoole_atomic_arginfo.h b/ext-src/stubs/php_swoole_atomic_arginfo.h index 62fb6bf7dd1..fbf4d01aa4d 100644 --- a/ext-src/stubs/php_swoole_atomic_arginfo.h +++ b/ext-src/stubs/php_swoole_atomic_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 081724c8ea467fa6daf08906f0d0fcb7603795ed */ + * Stub hash: 7c83f8fbe7fd48ac2c7a2756a4e33a9edd666e42 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Atomic___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "0") @@ -33,11 +33,6 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Atomic_wakeup, 0, 0 ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "1") ZEND_END_ARG_INFO() -#if defined(SW_THREAD) -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Atomic___wakeup, 0, 0, IS_VOID, 0) -ZEND_END_ARG_INFO() -#endif - #define arginfo_class_Swoole_Atomic_Long___construct arginfo_class_Swoole_Atomic___construct #define arginfo_class_Swoole_Atomic_Long_add arginfo_class_Swoole_Atomic_add @@ -49,7 +44,3 @@ ZEND_END_ARG_INFO() #define arginfo_class_Swoole_Atomic_Long_set arginfo_class_Swoole_Atomic_set #define arginfo_class_Swoole_Atomic_Long_cmpset arginfo_class_Swoole_Atomic_cmpset - -#if defined(SW_THREAD) -#define arginfo_class_Swoole_Atomic_Long___wakeup arginfo_class_Swoole_Atomic___wakeup -#endif diff --git a/ext-src/stubs/php_swoole_lock.stub.php b/ext-src/stubs/php_swoole_lock.stub.php index 7c23e7a8532..9ad3c621b20 100644 --- a/ext-src/stubs/php_swoole_lock.stub.php +++ b/ext-src/stubs/php_swoole_lock.stub.php @@ -1,7 +1,7 @@ -#include - -static sw_inline int swoole_futex_wait(sw_atomic_t *atomic, double timeout) { - if (sw_atomic_cmp_set(atomic, 1, 0)) { - return SW_OK; - } - - int ret; - struct timespec _timeout; - - if (timeout > 0) { - _timeout.tv_sec = (long) timeout; - _timeout.tv_nsec = (timeout - _timeout.tv_sec) * 1000 * 1000 * 1000; - ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, &_timeout, nullptr, 0); - } else { - ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, nullptr, nullptr, 0); - } - if (ret == SW_OK && sw_atomic_cmp_set(atomic, 1, 0)) { - return SW_OK; - } else { - return SW_ERR; - } -} - -static sw_inline int swoole_futex_wakeup(sw_atomic_t *atomic, int n) { - if (sw_atomic_cmp_set(atomic, 0, 1)) { - return syscall(SYS_futex, atomic, FUTEX_WAKE, n, nullptr, nullptr, 0); - } else { - return SW_OK; - } -} - -#else -static sw_inline int swoole_atomic_wait(sw_atomic_t *atomic, double timeout) { - if (sw_atomic_cmp_set(atomic, (sw_atomic_t) 1, (sw_atomic_t) 0)) { - return SW_OK; - } - timeout = timeout <= 0 ? INT_MAX : timeout; - int32_t i = (int32_t) sw_atomic_sub_fetch(atomic, 1); - while (timeout > 0) { - if ((int32_t) *atomic > i) { - return SW_OK; - } else { - usleep(1000); - timeout -= 0.001; - } - } - sw_atomic_fetch_add(atomic, 1); - return SW_ERR; -} - -static sw_inline int swoole_atomic_wakeup(sw_atomic_t *atomic, int n) { - if (1 == (int32_t) *atomic) { - return SW_OK; - } - sw_atomic_fetch_add(atomic, n); - return SW_OK; -} -#endif - zend_class_entry *swoole_atomic_ce; static zend_object_handlers swoole_atomic_handlers; zend_class_entry *swoole_atomic_long_ce; static zend_object_handlers swoole_atomic_long_handlers; -#ifdef SW_THREAD -struct AtomicResource: public ThreadResource { - sw_atomic_t *ptr_; - AtomicResource(): ThreadResource() { - ptr_ = new sw_atomic_t; - } - ~AtomicResource() { - delete ptr_; - } -}; -#endif - struct AtomicObject { sw_atomic_t *ptr; -#ifdef SW_THREAD - AtomicResource *res; -#endif zend_object std; }; @@ -123,17 +45,7 @@ void php_swoole_atomic_set_ptr(zval *zobject, sw_atomic_t *ptr) { } static void php_swoole_atomic_free_object(zend_object *object) { -#ifdef SW_THREAD - AtomicObject *o = php_swoole_atomic_fetch_object(object); - zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); - if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { - delete o->res; - o->res = nullptr; - o->ptr = nullptr; - } -#else sw_mem_pool()->free((void *) php_swoole_atomic_fetch_object(object)->ptr); -#endif zend_object_std_dtor(object); } @@ -146,34 +58,16 @@ static zend_object *php_swoole_atomic_create_object(zend_class_entry *ce) { zend_object_std_init(&atomic->std, ce); object_properties_init(&atomic->std, ce); atomic->std.handlers = &swoole_atomic_handlers; - -#ifndef SW_THREAD atomic->ptr = (sw_atomic_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_t)); if (atomic->ptr == nullptr) { zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); } -#endif return &atomic->std; } -#ifdef SW_THREAD -struct AtomicLongResource: public ThreadResource { - sw_atomic_long_t *ptr_; - AtomicLongResource(): ThreadResource() { - ptr_ = new sw_atomic_long_t; - } - ~AtomicLongResource() { - delete ptr_; - } -}; -#endif - struct AtomicLongObject { sw_atomic_long_t *ptr; -#ifdef SW_THREAD - AtomicLongResource *res; -#endif zend_object std; }; @@ -190,18 +84,7 @@ void php_swoole_atomic_long_set_ptr(zval *zobject, sw_atomic_long_t *ptr) { } static void php_swoole_atomic_long_free_object(zend_object *object) { -#ifdef SW_THREAD - AtomicLongObject *o = php_swoole_atomic_long_fetch_object(object); - zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); - if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { - delete o->res; - o->res = nullptr; - o->ptr = nullptr; - } -#else sw_mem_pool()->free((void *) php_swoole_atomic_long_fetch_object(object)->ptr); -#endif - zend_object_std_dtor(object); } @@ -215,12 +98,10 @@ static zend_object *php_swoole_atomic_long_create_object(zend_class_entry *ce) { object_properties_init(&atomic_long->std, ce); atomic_long->std.handlers = &swoole_atomic_long_handlers; -#ifndef SW_THREAD atomic_long->ptr = (sw_atomic_long_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_long_t)); if (atomic_long->ptr == nullptr) { zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); } -#endif return &atomic_long->std; } @@ -234,9 +115,6 @@ static PHP_METHOD(swoole_atomic, set); static PHP_METHOD(swoole_atomic, cmpset); static PHP_METHOD(swoole_atomic, wait); static PHP_METHOD(swoole_atomic, wakeup); -#ifdef SW_THREAD -static PHP_METHOD(swoole_atomic, __wakeup); -#endif static PHP_METHOD(swoole_atomic_long, __construct); static PHP_METHOD(swoole_atomic_long, add); @@ -244,9 +122,6 @@ static PHP_METHOD(swoole_atomic_long, sub); static PHP_METHOD(swoole_atomic_long, get); static PHP_METHOD(swoole_atomic_long, set); static PHP_METHOD(swoole_atomic_long, cmpset); -#ifdef SW_THREAD -static PHP_METHOD(swoole_atomic_long, __wakeup); -#endif SW_EXTERN_C_END // clang-format off @@ -261,9 +136,6 @@ static const zend_function_entry swoole_atomic_methods[] = PHP_ME(swoole_atomic, wait, arginfo_class_Swoole_Atomic_wait, ZEND_ACC_PUBLIC) PHP_ME(swoole_atomic, wakeup, arginfo_class_Swoole_Atomic_wakeup, ZEND_ACC_PUBLIC) PHP_ME(swoole_atomic, cmpset, arginfo_class_Swoole_Atomic_cmpset, ZEND_ACC_PUBLIC) -#ifdef SW_THREAD - PHP_ME(swoole_atomic, __wakeup, arginfo_class_Swoole_Atomic___wakeup, ZEND_ACC_PUBLIC) -#endif PHP_FE_END }; @@ -275,9 +147,6 @@ static const zend_function_entry swoole_atomic_long_methods[] = PHP_ME(swoole_atomic_long, get, arginfo_class_Swoole_Atomic_Long_get, ZEND_ACC_PUBLIC) PHP_ME(swoole_atomic_long, set, arginfo_class_Swoole_Atomic_Long_set, ZEND_ACC_PUBLIC) PHP_ME(swoole_atomic_long, cmpset, arginfo_class_Swoole_Atomic_Long_cmpset, ZEND_ACC_PUBLIC) -#ifdef SW_THREAD - PHP_ME(swoole_atomic_long, __wakeup, arginfo_class_Swoole_Atomic_Long___wakeup, ZEND_ACC_PUBLIC) -#endif PHP_FE_END }; @@ -285,22 +154,14 @@ static const zend_function_entry swoole_atomic_long_methods[] = void php_swoole_atomic_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_atomic, "Swoole\\Atomic", nullptr, swoole_atomic_methods); -#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic); -#else - zend_declare_property_long(swoole_atomic_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); -#endif SW_SET_CLASS_CLONEABLE(swoole_atomic, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( swoole_atomic, php_swoole_atomic_create_object, php_swoole_atomic_free_object, AtomicObject, std); SW_INIT_CLASS_ENTRY(swoole_atomic_long, "Swoole\\Atomic\\Long", nullptr, swoole_atomic_long_methods); -#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_atomic_long); -#else - zend_declare_property_long(swoole_atomic_long_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); -#endif SW_SET_CLASS_CLONEABLE(swoole_atomic_long, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_atomic_long, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT(swoole_atomic_long, @@ -311,7 +172,7 @@ void php_swoole_atomic_minit(int module_number) { } PHP_METHOD(swoole_atomic, __construct) { - auto o = php_swoole_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); + sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS); zend_long value = 0; ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) @@ -319,18 +180,7 @@ PHP_METHOD(swoole_atomic, __construct) { Z_PARAM_LONG(value) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef SW_THREAD - if (o->ptr) { - zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } - o->res = new AtomicResource(); - auto resource_id = php_swoole_thread_resource_insert(o->res); - zend_update_property_long(swoole_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); - o->ptr = o->res->ptr_; -#endif - - *o->ptr = (sw_atomic_t) value; + *atomic = (sw_atomic_t) value; } PHP_METHOD(swoole_atomic, add) { @@ -394,11 +244,7 @@ PHP_METHOD(swoole_atomic, wait) { Z_PARAM_DOUBLE(timeout) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef HAVE_FUTEX - SW_CHECK_RETURN(swoole_futex_wait(atomic, timeout)); -#else - SW_CHECK_RETURN(swoole_atomic_wait(atomic, timeout)); -#endif + SW_CHECK_RETURN(sw_atomic_futex_wait(atomic, timeout)); } PHP_METHOD(swoole_atomic, wakeup) { @@ -410,28 +256,11 @@ PHP_METHOD(swoole_atomic, wakeup) { Z_PARAM_LONG(n) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef HAVE_FUTEX - SW_CHECK_RETURN(swoole_futex_wakeup(atomic, (int) n)); -#else - SW_CHECK_RETURN(swoole_atomic_wakeup(atomic, n)); -#endif + SW_CHECK_RETURN(sw_atomic_futex_wakeup(atomic, (int) n)); } -#ifdef SW_THREAD -static PHP_METHOD(swoole_atomic, __wakeup) { - auto o = php_swoole_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); - o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!o->res) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - return; - } - o->ptr = o->res->ptr_; -} -#endif - PHP_METHOD(swoole_atomic_long, __construct) { - auto o = php_swoole_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); + sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(ZEND_THIS); zend_long value = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -439,18 +268,8 @@ PHP_METHOD(swoole_atomic_long, __construct) { Z_PARAM_LONG(value) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef SW_THREAD - if (o->ptr) { - zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } - o->res = new AtomicLongResource(); - auto resource_id = php_swoole_thread_resource_insert(o->res); - zend_update_property_long(swoole_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); - o->ptr = o->res->ptr_; -#endif - - *o->ptr = (sw_atomic_long_t) value; + *atomic_long = (sw_atomic_long_t) value; + RETURN_TRUE; } PHP_METHOD(swoole_atomic_long, add) { @@ -504,16 +323,3 @@ PHP_METHOD(swoole_atomic_long, cmpset) { RETURN_BOOL(sw_atomic_cmp_set(atomic_long, (sw_atomic_long_t) cmp_value, (sw_atomic_long_t) set_value)); } - -#ifdef SW_THREAD -static PHP_METHOD(swoole_atomic_long, __wakeup) { - auto o = php_swoole_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); - o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!o->res) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - return; - } - o->ptr = o->res->ptr_; -} -#endif diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 4ac88051866..9effbb595e6 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -15,7 +15,6 @@ */ #include "php_swoole_private.h" -#include "php_swoole_thread.h" #include "swoole_memory.h" #include "swoole_lock.h" @@ -35,23 +34,8 @@ using swoole::RWLock; static zend_class_entry *swoole_lock_ce; static zend_object_handlers swoole_lock_handlers; -#ifdef SW_THREAD -struct LockResource: public ThreadResource { - Lock *lock_; - LockResource(Lock *lock): ThreadResource() { - lock_ = lock; - } - ~LockResource() { - delete lock_; - } -}; -#endif - struct LockObject { Lock *lock; -#ifdef SW_THREAD - LockResource *lock_res; -#endif zend_object std; }; @@ -77,18 +61,9 @@ void php_swoole_lock_set_ptr(zval *zobject, Lock *ptr) { static void php_swoole_lock_free_object(zend_object *object) { LockObject *o = php_swoole_lock_fetch_object(object); -#ifdef SW_THREAD - zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); - if (o->lock_res && php_swoole_thread_resource_free(resource_id, o->lock_res)) { - delete o->lock_res; - o->lock_res = nullptr; - o->lock = nullptr; - } -#else if (o->lock) { delete o->lock; } -#endif zend_object_std_dtor(object); } @@ -110,9 +85,6 @@ static PHP_METHOD(swoole_lock, lock_read); static PHP_METHOD(swoole_lock, trylock_read); static PHP_METHOD(swoole_lock, unlock); static PHP_METHOD(swoole_lock, destroy); -#ifdef SW_THREAD -static PHP_METHOD(swoole_lock, __wakeup); -#endif SW_EXTERN_C_END // clang-format off @@ -127,20 +99,13 @@ static const zend_function_entry swoole_lock_methods[] = PHP_ME(swoole_lock, trylock_read, arginfo_class_Swoole_Lock_trylock_read, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, unlock, arginfo_class_Swoole_Lock_unlock, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, destroy, arginfo_class_Swoole_Lock_destroy, ZEND_ACC_PUBLIC) -#ifdef SW_THREAD - PHP_ME(swoole_lock, __wakeup, arginfo_class_Swoole_Lock___wakeup, ZEND_ACC_PUBLIC) -#endif PHP_FE_END }; // clang-format on void php_swoole_lock_minit(int module_number) { SW_INIT_CLASS_ENTRY(swoole_lock, "Swoole\\Lock", nullptr, swoole_lock_methods); -#ifndef SW_THREAD SW_SET_CLASS_NOT_SERIALIZABLE(swoole_lock); -#else - zend_declare_property_long(swoole_lock_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); -#endif SW_SET_CLASS_CLONEABLE(swoole_lock, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_lock, sw_zend_class_unset_property_deny); SW_SET_CLASS_CUSTOM_OBJECT( @@ -154,9 +119,6 @@ void php_swoole_lock_minit(int module_number) { zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("SPINLOCK"), Lock::SPIN_LOCK); #endif zend_declare_property_long(swoole_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); -#ifdef SW_THREAD - zend_declare_property_long(swoole_lock_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); -#endif SW_REGISTER_LONG_CONSTANT("SWOOLE_MUTEX", Lock::MUTEX); #ifdef HAVE_RWLOCK @@ -168,47 +130,40 @@ void php_swoole_lock_minit(int module_number) { } static PHP_METHOD(swoole_lock, __construct) { - auto o = php_swoole_lock_fetch_object(Z_OBJ_P(ZEND_THIS)); - if (o->lock != nullptr) { + Lock *lock = php_swoole_lock_get_ptr(ZEND_THIS); + if (lock != nullptr) { zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } - zend_long type = swoole::Lock::MUTEX; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(type) - ZEND_PARSE_PARAMETERS_END(); + zend_long type = Lock::MUTEX; + char *filelock; + size_t filelock_len = 0; -#ifdef SW_THREAD - bool process_shared = false; -#else - bool process_shared = true; -#endif + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ls", &type, &filelock, &filelock_len) == FAILURE) { + RETURN_FALSE; + } - Lock *lock; switch (type) { #ifdef HAVE_SPINLOCK case Lock::SPIN_LOCK: - lock = new SpinLock(process_shared ? 1 : 0); + lock = new SpinLock(1); break; #endif #ifdef HAVE_RWLOCK case Lock::RW_LOCK: - lock = new RWLock(process_shared ? 1 : 0); + lock = new RWLock(1); break; #endif case Lock::MUTEX: + lock = new Mutex(Mutex::PROCESS_SHARED); + break; default: - lock = new Mutex(process_shared ? Mutex::PROCESS_SHARED: 0); + zend_throw_exception( + swoole_exception_ce, "lock type[%d] is not support", type); + RETURN_FALSE; break; } -#ifdef SW_THREAD - o->lock_res = new LockResource(lock); - auto resource_id = php_swoole_thread_resource_insert(o->lock_res); - zend_update_property_long(swoole_lock_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); -#endif php_swoole_lock_set_ptr(ZEND_THIS, lock); RETURN_TRUE; } @@ -260,22 +215,7 @@ static PHP_METHOD(swoole_lock, lock_read) { } static PHP_METHOD(swoole_lock, destroy) { -#ifndef SW_THREAD Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); delete lock; php_swoole_lock_set_ptr(ZEND_THIS, nullptr); -#endif -} - -#ifdef SW_THREAD -static PHP_METHOD(swoole_lock, __wakeup) { - auto o = php_swoole_lock_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); - o->lock_res = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!o->lock_res) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - return; - } - php_swoole_lock_set_ptr(ZEND_THIS, o->lock_res->lock_); } -#endif diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 3dcb24a5dbe..244ef90efb8 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -23,7 +23,6 @@ #include #include -#include #include "swoole_lock.h" @@ -31,290 +30,11 @@ BEGIN_EXTERN_C() #include "stubs/php_swoole_thread_arginfo.h" END_EXTERN_C() -using swoole::RWLock; - zend_class_entry *swoole_thread_ce; static zend_object_handlers swoole_thread_handlers; -zend_class_entry *swoole_thread_map_ce; -static zend_object_handlers swoole_thread_map_handlers; - -zend_class_entry *swoole_thread_arraylist_ce; -static zend_object_handlers swoole_thread_arraylist_handlers; - -zend_class_entry *swoole_thread_queue_ce; -static zend_object_handlers swoole_thread_queue_handlers; - TSRMLS_CACHE_DEFINE(); -struct ArrayItem { - uint32_t type; - zend_string *key; - union { - zend_string *str; - zend_long lval; - double dval; - zend_string *serialized_object; - } value; - - ArrayItem(zval *zvalue) { - key = nullptr; - value = {}; - store(zvalue); - } - - void store(zval *zvalue) { - type = Z_TYPE_P(zvalue); - switch (type) { - case IS_LONG: - value.lval = zval_get_long(zvalue); - break; - case IS_DOUBLE: - value.dval = zval_get_double(zvalue); - break; - case IS_STRING: { - value.str = zend_string_init(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), 1); - break; - case IS_TRUE: - case IS_FALSE: - case IS_NULL: - break; - } - default: { - auto _serialized_object = php_swoole_thread_serialize(zvalue); - if (!_serialized_object) { - type = IS_UNDEF; - break; - } else { - type = IS_SERIALIZED_OBJECT; - value.serialized_object = _serialized_object; - } - break; - } - } - } - - void fetch(zval *return_value) { - switch (type) { - case IS_LONG: - RETVAL_LONG(value.lval); - break; - case IS_DOUBLE: - RETVAL_LONG(value.dval); - break; - case IS_TRUE: - RETVAL_TRUE; - break; - case IS_FALSE: - RETVAL_FALSE; - break; - case IS_STRING: - RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); - break; - case IS_SERIALIZED_OBJECT: - php_swoole_thread_unserialize(value.serialized_object, return_value); - break; - default: - break; - } - } - - void release() { - if (type == IS_STRING) { - zend_string_release(value.str); - value.str = nullptr; - } else if (type == IS_SERIALIZED_OBJECT) { - zend_string_release(value.serialized_object); - value.serialized_object = nullptr; - } - } - - ~ArrayItem() { - if (value.str) { - release(); - } - if (key) { - zend_string_release(key); - } - } -}; - -struct ZendArray : ThreadResource { - RWLock lock_; - zend_array ht; - - static void item_dtor(zval *pDest) { - ArrayItem *item = (ArrayItem *) Z_PTR_P(pDest); - delete item; - } - - ZendArray() : ThreadResource(), lock_(0) { - zend_hash_init(&ht, 0, NULL, item_dtor, 1); - } - - ~ZendArray() { - zend_hash_destroy(&ht); - } - - void clean() { - lock_.lock(); - zend_hash_clean(&ht); - lock_.unlock(); - } - - bool index_exists(zend_long index) { - return index < (zend_long) zend_hash_num_elements(&ht); - } - - void strkey_offsetGet(zval *zkey, zval *return_value) { - zend::String skey(zkey); - lock_.lock_rd(); - ArrayItem *item = (ArrayItem *) zend_hash_find_ptr(&ht, skey.get()); - if (item) { - item->fetch(return_value); - } - lock_.unlock(); - } - - void strkey_offsetExists(zval *zkey, zval *return_value) { - zend::String skey(zkey); - lock_.lock_rd(); - RETVAL_BOOL(zend_hash_find_ptr(&ht, skey.get()) != NULL); - lock_.unlock(); - } - - void strkey_offsetUnset(zval *zkey) { - zend::String skey(zkey); - lock_.lock(); - zend_hash_del(&ht, skey.get()); - lock_.unlock(); - } - - void strkey_offsetSet(zval *zkey, zval *zvalue) { - zend::String skey(zkey); - auto item = new ArrayItem(zvalue); - item->key = zend_string_init(skey.val(), skey.len(), 1); - lock_.lock(); - zend_hash_update_ptr(&ht, item->key, item); - lock_.unlock(); - } - - void count(zval *return_value) { - lock_.lock_rd(); - RETVAL_LONG(zend_hash_num_elements(&ht)); - lock_.unlock(); - } - - void keys(zval *return_value) { - lock_.lock_rd(); - zend_ulong elem_count = zend_hash_num_elements(&ht); - array_init_size(return_value, elem_count); - zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); - zend_ulong num_idx; - zend_string *str_idx; - zval *entry; - ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - if (HT_IS_PACKED(&ht) && HT_IS_WITHOUT_HOLES(&ht)) { - /* Optimistic case: range(0..n-1) for vector-like packed array */ - zend_ulong lval = 0; - - for (; lval < elem_count; ++lval) { - ZEND_HASH_FILL_SET_LONG(lval); - ZEND_HASH_FILL_NEXT(); - } - } else { - /* Go through input array and add keys to the return array */ - ZEND_HASH_FOREACH_KEY_VAL(&ht, num_idx, str_idx, entry) { - if (str_idx) { - ZEND_HASH_FILL_SET_STR(zend_string_init(str_idx->val, str_idx->len, 0)); - } else { - ZEND_HASH_FILL_SET_LONG(num_idx); - } - ZEND_HASH_FILL_NEXT(); - } - ZEND_HASH_FOREACH_END(); - } - (void) entry; - } - ZEND_HASH_FILL_END(); - lock_.unlock(); - } - - void intkey_offsetGet(zend_long index, zval *return_value) { - lock_.lock_rd(); - ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); - if (item) { - item->fetch(return_value); - } - lock_.unlock(); - } - - void intkey_offsetGet(zval *zkey, zval *return_value) { - intkey_offsetGet(zval_get_long(zkey), return_value); - } - - void intkey_offsetExists(zval *zkey, zval *return_value) { - zend_long index = zval_get_long(zkey); - lock_.lock_rd(); - RETVAL_BOOL(zend_hash_index_find_ptr(&ht, index) != NULL); - lock_.unlock(); - } - - void intkey_offsetUnset(zval *zkey) { - zend_long index = zval_get_long(zkey); - lock_.lock(); - zend_hash_index_del(&ht, index); - lock_.unlock(); - } - - void intkey_offsetSet(zval *zkey, zval *zvalue) { - zend_long index = zval_get_long(zkey); - auto item = new ArrayItem(zvalue); - lock_.lock(); - zend_hash_index_update_ptr(&ht, index, item); - lock_.unlock(); - } - - bool index_offsetGet(zval *zkey, zval *return_value) { - zend_long index = zval_get_long(zkey); - bool out_of_range = true; - lock_.lock_rd(); - if (index_exists(index)) { - out_of_range = false; - ArrayItem *item = (ArrayItem *) zend_hash_index_find_ptr(&ht, index); - if (item) { - item->fetch(return_value); - } - } - lock_.unlock(); - return !out_of_range; - } - - bool index_offsetSet(zval *zkey, zval *zvalue) { - zend_long index = ZVAL_IS_NULL(zkey) ? -1 : zval_get_long(zkey); - auto item = new ArrayItem(zvalue); - bool success = true; - lock_.lock(); - if (index > zend_hash_num_elements(&ht)) { - success = false; - delete item; - } else if (index == -1 || index == zend_hash_num_elements(&ht)) { - zend_hash_next_index_insert_ptr(&ht, item); - } else { - zend_hash_index_update_ptr(&ht, index, item); - } - lock_.unlock(); - return success; - } - - void index_offsetExists(zval *zkey, zval *return_value) { - zend_long index = zval_get_long(zkey); - lock_.lock_rd(); - RETVAL_BOOL(index_exists(index)); - lock_.unlock(); - } -}; - typedef std::thread Thread; struct ThreadObject { @@ -322,112 +42,6 @@ struct ThreadObject { zend_object std; }; -struct ThreadMapObject { - ZendArray *map; - zend_object std; -}; - -struct ThreadArrayListObject { - ZendArray *list; - zend_object std; -}; - -struct Queue : ThreadResource { - std::queue queue; - std::mutex lock_; - std::condition_variable cv_; - - enum { - NOTIFY_NONE = 0, - NOTIFY_ONE = 1, - NOTIFY_ALL = 2, - }; - - Queue() : ThreadResource(), queue() {} - - ~Queue() { - clean(); - } - - void push(zval *zvalue) { - auto item = new ArrayItem(zvalue); - lock_.lock(); - queue.push(item); - lock_.unlock(); - } - - void pop(zval *return_value) { - ArrayItem *item = nullptr; - lock_.lock(); - if (!queue.empty()) { - item = queue.front(); - queue.pop(); - } - lock_.unlock(); - if (item) { - item->fetch(return_value); - delete item; - } - } - - void push_notify(zval *zvalue, bool notify_all) { - auto item = new ArrayItem(zvalue); - std::unique_lock _lock(lock_); - queue.push(item); - if (notify_all) { - cv_.notify_all(); - } else { - cv_.notify_one(); - } - } - - void pop_wait(zval *return_value, double timeout) { - ArrayItem *item = nullptr; - std::unique_lock _lock(lock_); - SW_LOOP { - if (!queue.empty()) { - item = queue.front(); - queue.pop(); - break; - } else { - if (timeout > 0) { - if (cv_.wait_for(_lock, std::chrono::duration(timeout)) == std::cv_status::timeout) { - break; - } - } else { - cv_.wait(_lock); - } - } - } - _lock.unlock(); - if (item) { - item->fetch(return_value); - delete item; - } - } - - void count(zval *return_value) { - lock_.lock(); - RETVAL_LONG(queue.size()); - lock_.unlock(); - } - - void clean() { - lock_.lock(); - while (!queue.empty()) { - ArrayItem *item = queue.front(); - delete item; - queue.pop(); - } - lock_.unlock(); - } -}; - -struct ThreadQueueObject { - Queue *queue; - zend_object std; -}; - static void php_swoole_thread_join(zend_object *object); static thread_local zval thread_argv; @@ -488,126 +102,6 @@ static void php_swoole_thread_join(zend_object *object) { } } -// =======================================Map============================================== -static sw_inline ThreadMapObject *thread_map_fetch_object(zend_object *obj) { - return (ThreadMapObject *) ((char *) obj - swoole_thread_map_handlers.offset); -} - -static sw_inline zend_long thread_map_get_resource_id(zend_object *obj) { - zval rv, *property = zend_read_property(swoole_thread_map_ce, obj, ZEND_STRL("id"), 1, &rv); - return property ? zval_get_long(property) : 0; -} - -static sw_inline zend_long thread_map_get_resource_id(zval *zobject) { - return thread_map_get_resource_id(Z_OBJ_P(zobject)); -} - -static void thread_map_free_object(zend_object *object) { - zend_long resource_id = thread_map_get_resource_id(object); - ThreadMapObject *mo = thread_map_fetch_object(object); - if (mo->map && php_swoole_thread_resource_free(resource_id, mo->map)) { - delete mo->map; - mo->map = nullptr; - } - zend_object_std_dtor(object); -} - -static zend_object *thread_map_create_object(zend_class_entry *ce) { - ThreadMapObject *mo = (ThreadMapObject *) zend_object_alloc(sizeof(ThreadMapObject), ce); - zend_object_std_init(&mo->std, ce); - object_properties_init(&mo->std, ce); - mo->std.handlers = &swoole_thread_map_handlers; - return &mo->std; -} - -ThreadMapObject *thread_map_fetch_object_check(zval *zobject) { - ThreadMapObject *map = thread_map_fetch_object(Z_OBJ_P(zobject)); - if (!map->map) { - php_swoole_fatal_error(E_ERROR, "must call constructor first"); - } - return map; -} - -// =======================================ArrayList============================================== -static sw_inline ThreadArrayListObject *thread_arraylist_fetch_object(zend_object *obj) { - return (ThreadArrayListObject *) ((char *) obj - swoole_thread_arraylist_handlers.offset); -} - -static sw_inline zend_long thread_arraylist_get_resource_id(zend_object *obj) { - zval rv, *property = zend_read_property(swoole_thread_arraylist_ce, obj, ZEND_STRL("id"), 1, &rv); - return property ? zval_get_long(property) : 0; -} - -static sw_inline zend_long thread_arraylist_get_resource_id(zval *zobject) { - return thread_arraylist_get_resource_id(Z_OBJ_P(zobject)); -} - -static void thread_arraylist_free_object(zend_object *object) { - zend_long resource_id = thread_arraylist_get_resource_id(object); - ThreadArrayListObject *ao = thread_arraylist_fetch_object(object); - if (ao->list && php_swoole_thread_resource_free(resource_id, ao->list)) { - delete ao->list; - ao->list = nullptr; - } - zend_object_std_dtor(object); -} - -static zend_object *thread_arraylist_create_object(zend_class_entry *ce) { - ThreadArrayListObject *ao = (ThreadArrayListObject *) zend_object_alloc(sizeof(ThreadArrayListObject), ce); - zend_object_std_init(&ao->std, ce); - object_properties_init(&ao->std, ce); - ao->std.handlers = &swoole_thread_arraylist_handlers; - return &ao->std; -} - -ThreadArrayListObject *thread_arraylist_fetch_object_check(zval *zobject) { - ThreadArrayListObject *ao = thread_arraylist_fetch_object(Z_OBJ_P(zobject)); - if (!ao->list) { - php_swoole_fatal_error(E_ERROR, "must call constructor first"); - } - return ao; -} - -// =======================================Queue============================================== -static sw_inline ThreadQueueObject *thread_queue_fetch_object(zend_object *obj) { - return (ThreadQueueObject *) ((char *) obj - swoole_thread_map_handlers.offset); -} - -static sw_inline zend_long thread_queue_get_resource_id(zend_object *obj) { - zval rv, *property = zend_read_property(swoole_thread_queue_ce, obj, ZEND_STRL("id"), 1, &rv); - return property ? zval_get_long(property) : 0; -} - -static sw_inline zend_long thread_queue_get_resource_id(zval *zobject) { - return thread_queue_get_resource_id(Z_OBJ_P(zobject)); -} - -static void thread_queue_free_object(zend_object *object) { - zend_long resource_id = thread_queue_get_resource_id(object); - ThreadQueueObject *qo = thread_queue_fetch_object(object); - if (qo->queue && php_swoole_thread_resource_free(resource_id, qo->queue)) { - delete qo->queue; - qo->queue = nullptr; - } - zend_object_std_dtor(object); -} - -static zend_object *thread_queue_create_object(zend_class_entry *ce) { - ThreadQueueObject *qo = (ThreadQueueObject *) zend_object_alloc(sizeof(ThreadQueueObject), ce); - zend_object_std_init(&qo->std, ce); - object_properties_init(&qo->std, ce); - qo->std.handlers = &swoole_thread_queue_handlers; - return &qo->std; -} - -ThreadQueueObject *thread_queue_fetch_object_check(zval *zobject) { - ThreadQueueObject *qo = thread_queue_fetch_object(Z_OBJ_P(zobject)); - if (!qo->queue) { - php_swoole_fatal_error(E_ERROR, "must call constructor first"); - } - return qo; -} - SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_thread, __construct); static PHP_METHOD(swoole_thread, join); @@ -616,33 +110,6 @@ static PHP_METHOD(swoole_thread, detach); static PHP_METHOD(swoole_thread, exec); static PHP_METHOD(swoole_thread, getArguments); static PHP_METHOD(swoole_thread, getId); - -static PHP_METHOD(swoole_thread_map, __construct); -static PHP_METHOD(swoole_thread_map, offsetGet); -static PHP_METHOD(swoole_thread_map, offsetExists); -static PHP_METHOD(swoole_thread_map, offsetSet); -static PHP_METHOD(swoole_thread_map, offsetUnset); -static PHP_METHOD(swoole_thread_map, count); -static PHP_METHOD(swoole_thread_map, keys); -static PHP_METHOD(swoole_thread_map, clean); -static PHP_METHOD(swoole_thread_map, __wakeup); - -static PHP_METHOD(swoole_thread_arraylist, __construct); -static PHP_METHOD(swoole_thread_arraylist, offsetGet); -static PHP_METHOD(swoole_thread_arraylist, offsetExists); -static PHP_METHOD(swoole_thread_arraylist, offsetSet); -static PHP_METHOD(swoole_thread_arraylist, offsetUnset); -static PHP_METHOD(swoole_thread_arraylist, count); -static PHP_METHOD(swoole_thread_arraylist, clean); -static PHP_METHOD(swoole_thread_arraylist, __wakeup); - -static PHP_METHOD(swoole_thread_queue, __construct); -static PHP_METHOD(swoole_thread_queue, push); -static PHP_METHOD(swoole_thread_queue, pop); -static PHP_METHOD(swoole_thread_queue, count); -static PHP_METHOD(swoole_thread_queue, clean); -static PHP_METHOD(swoole_thread_queue, __wakeup); - SW_EXTERN_C_END // clang-format off @@ -656,41 +123,6 @@ static const zend_function_entry swoole_thread_methods[] = { PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; - -static const zend_function_entry swoole_thread_map_methods[] = { - PHP_ME(swoole_thread_map, __construct, arginfo_class_Swoole_Thread_Map___construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_map, offsetGet, arginfo_class_Swoole_Thread_Map_offsetGet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_map, offsetExists, arginfo_class_Swoole_Thread_Map_offsetExists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_map, offsetSet, arginfo_class_Swoole_Thread_Map_offsetSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_map, offsetUnset, arginfo_class_Swoole_Thread_Map_offsetUnset, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_map, count, arginfo_class_Swoole_Thread_Map_count, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_map, clean, arginfo_class_Swoole_Thread_Map_clean, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_map, keys, arginfo_class_Swoole_Thread_Map_keys, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_map, __wakeup, arginfo_class_Swoole_Thread_Map___wakeup, ZEND_ACC_PUBLIC) - PHP_FE_END -}; - -static const zend_function_entry swoole_thread_arraylist_methods[] = { - PHP_ME(swoole_thread_arraylist, __construct, arginfo_class_Swoole_Thread_ArrayList___construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_arraylist, offsetGet, arginfo_class_Swoole_Thread_ArrayList_offsetGet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_arraylist, offsetExists, arginfo_class_Swoole_Thread_ArrayList_offsetExists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_arraylist, offsetSet, arginfo_class_Swoole_Thread_ArrayList_offsetSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_arraylist, offsetUnset, arginfo_class_Swoole_Thread_ArrayList_offsetUnset, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_arraylist, clean, arginfo_class_Swoole_Thread_ArrayList_clean, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_arraylist, count, arginfo_class_Swoole_Thread_ArrayList_count, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_arraylist, __wakeup, arginfo_class_Swoole_Thread_ArrayList___wakeup, ZEND_ACC_PUBLIC) - PHP_FE_END -}; - -static const zend_function_entry swoole_thread_queue_methods[] = { - PHP_ME(swoole_thread_queue, __construct, arginfo_class_Swoole_Thread_Queue___construct, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_queue, push, arginfo_class_Swoole_Thread_Queue_push, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_queue, pop, arginfo_class_Swoole_Thread_Queue_pop, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_queue, clean, arginfo_class_Swoole_Thread_Queue_clean, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_queue, count, arginfo_class_Swoole_Thread_Queue_count, ZEND_ACC_PUBLIC) - PHP_ME(swoole_thread_queue, __wakeup, arginfo_class_Swoole_Thread_Queue___wakeup, ZEND_ACC_PUBLIC) - PHP_FE_END -}; // clang-format on void php_swoole_thread_minit(int module_number) { @@ -704,39 +136,6 @@ void php_swoole_thread_minit(int module_number) { zend_declare_property_long(swoole_thread_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); zend_declare_class_constant_long( swoole_thread_ce, ZEND_STRL("HARDWARE_CONCURRENCY"), std::thread::hardware_concurrency()); - - SW_INIT_CLASS_ENTRY(swoole_thread_map, "Swoole\\Thread\\Map", nullptr, swoole_thread_map_methods); - SW_SET_CLASS_CLONEABLE(swoole_thread_map, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_map, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_thread_map, thread_map_create_object, thread_map_free_object, ThreadMapObject, std); - - zend_class_implements(swoole_thread_map_ce, 2, zend_ce_arrayaccess, zend_ce_countable); - zend_declare_property_long(swoole_thread_map_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); - - SW_INIT_CLASS_ENTRY(swoole_thread_arraylist, "Swoole\\Thread\\ArrayList", nullptr, swoole_thread_arraylist_methods); - SW_SET_CLASS_CLONEABLE(swoole_thread_arraylist, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_arraylist, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_arraylist, - thread_arraylist_create_object, - thread_arraylist_free_object, - ThreadArrayListObject, - std); - - zend_class_implements(swoole_thread_arraylist_ce, 2, zend_ce_arrayaccess, zend_ce_countable); - zend_declare_property_long(swoole_thread_arraylist_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); - - SW_INIT_CLASS_ENTRY(swoole_thread_queue, "Swoole\\Thread\\Queue", nullptr, swoole_thread_queue_methods); - SW_SET_CLASS_CLONEABLE(swoole_thread_queue, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_queue, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_thread_queue, thread_queue_create_object, thread_queue_free_object, ThreadQueueObject, std); - - zend_class_implements(swoole_thread_queue_ce, 1, zend_ce_countable); - zend_declare_property_long(swoole_thread_queue_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); - - zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ONE"), Queue::NOTIFY_ONE); - zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ALL"), Queue::NOTIFY_ALL); } static PHP_METHOD(swoole_thread, __construct) {} @@ -907,219 +306,4 @@ static PHP_METHOD(swoole_thread, exec) { swoole_thread_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("id"), to->thread->native_handle()); } -static PHP_METHOD(swoole_thread_map, __construct) { - auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); - mo->map = new ZendArray(); - auto resource_id = php_swoole_thread_resource_insert(mo->map); - zend_update_property_long(swoole_thread_map_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); -} - -#define ZEND_ARRAY_CALL_METHOD(array, method, zkey, ...) \ - if (ZVAL_IS_LONG(zkey)) { \ - array->intkey_##method(zkey, ##__VA_ARGS__); \ - } else { \ - array->strkey_##method(zkey, ##__VA_ARGS__); \ - } - -static PHP_METHOD(swoole_thread_map, offsetGet) { - zval *zkey; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zkey) - ZEND_PARSE_PARAMETERS_END(); - - auto mo = thread_map_fetch_object_check(ZEND_THIS); - ZEND_ARRAY_CALL_METHOD(mo->map, offsetGet, zkey, return_value); -} - -static PHP_METHOD(swoole_thread_map, offsetExists) { - zval *zkey; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zkey) - ZEND_PARSE_PARAMETERS_END(); - - auto mo = thread_map_fetch_object_check(ZEND_THIS); - ZEND_ARRAY_CALL_METHOD(mo->map, offsetExists, zkey, return_value); -} - -static PHP_METHOD(swoole_thread_map, offsetSet) { - zval *zkey; - zval *zvalue; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ZVAL(zkey) - Z_PARAM_ZVAL(zvalue) - ZEND_PARSE_PARAMETERS_END(); - - auto mo = thread_map_fetch_object_check(ZEND_THIS); - ZEND_ARRAY_CALL_METHOD(mo->map, offsetSet, zkey, zvalue); -} - -static PHP_METHOD(swoole_thread_map, offsetUnset) { - zval *zkey; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zkey) - ZEND_PARSE_PARAMETERS_END(); - - auto mo = thread_map_fetch_object_check(ZEND_THIS); - ZEND_ARRAY_CALL_METHOD(mo->map, offsetUnset, zkey); -} - -static PHP_METHOD(swoole_thread_map, count) { - auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->map->count(return_value); -} - -static PHP_METHOD(swoole_thread_map, keys) { - auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->map->keys(return_value); -} - -static PHP_METHOD(swoole_thread_map, clean) { - auto mo = thread_map_fetch_object_check(ZEND_THIS); - mo->map->clean(); -} - -static PHP_METHOD(swoole_thread_map, __wakeup) { - auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = thread_map_get_resource_id(ZEND_THIS); - mo->map = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!mo->map) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - } -} - -static PHP_METHOD(swoole_thread_arraylist, __construct) { - ZEND_PARSE_PARAMETERS_NONE(); - - auto ao = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); - ao->list = new ZendArray(); - auto resource_id = php_swoole_thread_resource_insert(ao->list); - zend_update_property_long(swoole_thread_arraylist_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); -} - -static PHP_METHOD(swoole_thread_arraylist, offsetGet) { - zval *zkey; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zkey) - ZEND_PARSE_PARAMETERS_END(); - - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); - if (!ao->list->index_offsetGet(zkey, return_value)) { - zend_throw_exception(swoole_exception_ce, "out of range", -1); - } -} - -static PHP_METHOD(swoole_thread_arraylist, offsetExists) { - zval *zkey; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zkey) - ZEND_PARSE_PARAMETERS_END(); - - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); - ao->list->index_offsetExists(zkey, return_value); -} - -static PHP_METHOD(swoole_thread_arraylist, offsetSet) { - zval *zkey; - zval *zvalue; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ZVAL(zkey) - Z_PARAM_ZVAL(zvalue) - ZEND_PARSE_PARAMETERS_END(); - - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); - if (!ao->list->index_offsetSet(zkey, zvalue)) { - zend_throw_exception(swoole_exception_ce, "out of range", -1); - } -} - -static PHP_METHOD(swoole_thread_arraylist, offsetUnset) { - zend_throw_exception(swoole_exception_ce, "unsupported", -3); -} - -static PHP_METHOD(swoole_thread_arraylist, count) { - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); - ao->list->count(return_value); -} - -static PHP_METHOD(swoole_thread_arraylist, clean) { - auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); - ao->list->clean(); -} - -static PHP_METHOD(swoole_thread_arraylist, __wakeup) { - auto mo = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = thread_arraylist_get_resource_id(ZEND_THIS); - mo->list = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!mo->list) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - } -} - -static PHP_METHOD(swoole_thread_queue, __construct) { - auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); - qo->queue = new Queue(); - auto resource_id = php_swoole_thread_resource_insert(qo->queue); - zend_update_property_long(swoole_thread_queue_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); -} - -static PHP_METHOD(swoole_thread_queue, push) { - zval *zvalue; - zend_long notify_which = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ZVAL(zvalue) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(notify_which) - ZEND_PARSE_PARAMETERS_END(); - - auto qo = thread_queue_fetch_object_check(ZEND_THIS); - if (notify_which > 0) { - qo->queue->push_notify(zvalue, notify_which == Queue::NOTIFY_ALL); - } else { - qo->queue->push(zvalue); - } -} - -static PHP_METHOD(swoole_thread_queue, pop) { - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END(); - - auto qo = thread_queue_fetch_object_check(ZEND_THIS); - if (timeout == 0) { - qo->queue->pop(return_value); - } else { - qo->queue->pop_wait(return_value, timeout); - } -} - -static PHP_METHOD(swoole_thread_queue, count) { - auto qo = thread_queue_fetch_object_check(ZEND_THIS); - qo->queue->count(return_value); -} - -static PHP_METHOD(swoole_thread_queue, clean) { - auto qo = thread_queue_fetch_object_check(ZEND_THIS); - qo->queue->clean(); -} - -static PHP_METHOD(swoole_thread_queue, __wakeup) { - auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); - zend_long resource_id = thread_queue_get_resource_id(ZEND_THIS); - qo->queue = static_cast(php_swoole_thread_resource_fetch(resource_id)); - if (!qo->queue) { - zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); - } -} - #endif diff --git a/ext-src/swoole_thread_arraylist.cc b/ext-src/swoole_thread_arraylist.cc new file mode 100644 index 00000000000..7ec202bc9d3 --- /dev/null +++ b/ext-src/swoole_thread_arraylist.cc @@ -0,0 +1,182 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" + +#ifdef SW_THREAD +#include "php_swoole_thread.h" + +SW_EXTERN_C_BEGIN +#include "stubs/php_swoole_thread_arraylist_arginfo.h" +SW_EXTERN_C_END + +zend_class_entry *swoole_thread_arraylist_ce; +static zend_object_handlers swoole_thread_arraylist_handlers; + +struct ThreadArrayListObject { + ZendArray *list; + zend_object std; +}; + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_arraylist, __construct); +static PHP_METHOD(swoole_thread_arraylist, offsetGet); +static PHP_METHOD(swoole_thread_arraylist, offsetExists); +static PHP_METHOD(swoole_thread_arraylist, offsetSet); +static PHP_METHOD(swoole_thread_arraylist, offsetUnset); +static PHP_METHOD(swoole_thread_arraylist, count); +static PHP_METHOD(swoole_thread_arraylist, clean); +static PHP_METHOD(swoole_thread_arraylist, __wakeup); +SW_EXTERN_C_END + +static sw_inline ThreadArrayListObject *thread_arraylist_fetch_object(zend_object *obj) { + return (ThreadArrayListObject *) ((char *) obj - swoole_thread_arraylist_handlers.offset); +} + +static sw_inline zend_long thread_arraylist_get_resource_id(zend_object *obj) { + zval rv, *property = zend_read_property(swoole_thread_arraylist_ce, obj, ZEND_STRL("id"), 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static sw_inline zend_long thread_arraylist_get_resource_id(zval *zobject) { + return thread_arraylist_get_resource_id(Z_OBJ_P(zobject)); +} + +static void thread_arraylist_free_object(zend_object *object) { + zend_long resource_id = thread_arraylist_get_resource_id(object); + ThreadArrayListObject *ao = thread_arraylist_fetch_object(object); + if (ao->list && php_swoole_thread_resource_free(resource_id, ao->list)) { + delete ao->list; + ao->list = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *thread_arraylist_create_object(zend_class_entry *ce) { + ThreadArrayListObject *ao = (ThreadArrayListObject *) zend_object_alloc(sizeof(ThreadArrayListObject), ce); + zend_object_std_init(&ao->std, ce); + object_properties_init(&ao->std, ce); + ao->std.handlers = &swoole_thread_arraylist_handlers; + return &ao->std; +} + +ThreadArrayListObject *thread_arraylist_fetch_object_check(zval *zobject) { + ThreadArrayListObject *ao = thread_arraylist_fetch_object(Z_OBJ_P(zobject)); + if (!ao->list) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return ao; +} + +// clang-format off +static const zend_function_entry swoole_thread_arraylist_methods[] = { + PHP_ME(swoole_thread_arraylist, __construct, arginfo_class_Swoole_Thread_ArrayList___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetGet, arginfo_class_Swoole_Thread_ArrayList_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetExists, arginfo_class_Swoole_Thread_ArrayList_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetSet, arginfo_class_Swoole_Thread_ArrayList_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, offsetUnset, arginfo_class_Swoole_Thread_ArrayList_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, clean, arginfo_class_Swoole_Thread_ArrayList_clean, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, count, arginfo_class_Swoole_Thread_ArrayList_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_arraylist, __wakeup, arginfo_class_Swoole_Thread_ArrayList___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_arraylist_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_arraylist, "Swoole\\Thread\\ArrayList", nullptr, swoole_thread_arraylist_methods); + SW_SET_CLASS_CLONEABLE(swoole_thread_arraylist, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_arraylist, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_arraylist, + thread_arraylist_create_object, + thread_arraylist_free_object, + ThreadArrayListObject, + std); + + zend_class_implements(swoole_thread_arraylist_ce, 2, zend_ce_arrayaccess, zend_ce_countable); + zend_declare_property_long(swoole_thread_arraylist_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); +} + +static PHP_METHOD(swoole_thread_arraylist, __construct) { + ZEND_PARSE_PARAMETERS_NONE(); + + auto ao = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); + ao->list = new ZendArray(); + auto resource_id = php_swoole_thread_resource_insert(ao->list); + zend_update_property_long(swoole_thread_arraylist_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +} + +static PHP_METHOD(swoole_thread_arraylist, offsetGet) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + if (!ao->list->index_offsetGet(zkey, return_value)) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); + } +} + +static PHP_METHOD(swoole_thread_arraylist, offsetExists) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + ao->list->index_offsetExists(zkey, return_value); +} + +static PHP_METHOD(swoole_thread_arraylist, offsetSet) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + if (!ao->list->index_offsetSet(zkey, zvalue)) { + zend_throw_exception(swoole_exception_ce, "out of range", -1); + } +} + +static PHP_METHOD(swoole_thread_arraylist, offsetUnset) { + zend_throw_exception(swoole_exception_ce, "unsupported", -3); +} + +static PHP_METHOD(swoole_thread_arraylist, count) { + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + ao->list->count(return_value); +} + +static PHP_METHOD(swoole_thread_arraylist, clean) { + auto ao = thread_arraylist_fetch_object_check(ZEND_THIS); + ao->list->clean(); +} + +static PHP_METHOD(swoole_thread_arraylist, __wakeup) { + auto mo = thread_arraylist_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = thread_arraylist_get_resource_id(ZEND_THIS); + mo->list = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!mo->list) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + } +} +#endif diff --git a/ext-src/swoole_thread_atomic.cc b/ext-src/swoole_thread_atomic.cc new file mode 100644 index 00000000000..f4bfd71c14a --- /dev/null +++ b/ext-src/swoole_thread_atomic.cc @@ -0,0 +1,362 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" +#include "php_swoole_thread.h" +#include "swoole_memory.h" + +#ifdef SW_THREAD + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_thread_atomic_arginfo.h" +END_EXTERN_C() + +zend_class_entry *swoole_thread_atomic_ce; +static zend_object_handlers swoole_thread_atomic_handlers; + +zend_class_entry *swoole_thread_atomic_long_ce; +static zend_object_handlers swoole_thread_atomic_long_handlers; + +struct AtomicResource: public ThreadResource { + sw_atomic_t value; +}; + +struct AtomicObject { + AtomicResource *res; + zend_object std; +}; + +static sw_inline AtomicObject *php_swoole_thread_atomic_fetch_object(zend_object *obj) { + return (AtomicObject *) ((char *) obj - swoole_thread_atomic_handlers.offset); +} + +static sw_atomic_t *php_swoole_thread_atomic_get_ptr(zval *zobject) { + return &php_swoole_thread_atomic_fetch_object(Z_OBJ_P(zobject))->res->value; +} + +static void php_swoole_thread_atomic_free_object(zend_object *object) { + AtomicObject *o = php_swoole_thread_atomic_fetch_object(object); + zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); + if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { + delete o->res; + o->res = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_thread_atomic_create_object(zend_class_entry *ce) { + AtomicObject *atomic = (AtomicObject *) zend_object_alloc(sizeof(AtomicObject), ce); + if (atomic == nullptr) { + zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); + } + + zend_object_std_init(&atomic->std, ce); + object_properties_init(&atomic->std, ce); + atomic->std.handlers = &swoole_thread_atomic_handlers; + + return &atomic->std; +} + +struct AtomicLongResource: public ThreadResource { + sw_atomic_long_t value; +}; + +struct AtomicLongObject { + AtomicLongResource *res; + zend_object std; +}; + +static sw_inline AtomicLongObject *php_swoole_thread_atomic_long_fetch_object(zend_object *obj) { + return (AtomicLongObject *) ((char *) obj - swoole_thread_atomic_long_handlers.offset); +} + +static sw_atomic_long_t *php_swoole_thread_atomic_long_get_ptr(zval *zobject) { + return &php_swoole_thread_atomic_long_fetch_object(Z_OBJ_P(zobject))->res->value; +} + +static void php_swoole_thread_atomic_long_free_object(zend_object *object) { + AtomicLongObject *o = php_swoole_thread_atomic_long_fetch_object(object); + zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); + if (o->res && php_swoole_thread_resource_free(resource_id, o->res)) { + delete o->res; + o->res = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_thread_atomic_long_create_object(zend_class_entry *ce) { + AtomicLongObject *atomic_long = (AtomicLongObject *) zend_object_alloc(sizeof(AtomicLongObject), ce); + zend_object_std_init(&atomic_long->std, ce); + object_properties_init(&atomic_long->std, ce); + atomic_long->std.handlers = &swoole_thread_atomic_long_handlers; + return &atomic_long->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_atomic, __construct); +static PHP_METHOD(swoole_thread_atomic, add); +static PHP_METHOD(swoole_thread_atomic, sub); +static PHP_METHOD(swoole_thread_atomic, get); +static PHP_METHOD(swoole_thread_atomic, set); +static PHP_METHOD(swoole_thread_atomic, cmpset); +static PHP_METHOD(swoole_thread_atomic, wait); +static PHP_METHOD(swoole_thread_atomic, wakeup); +#ifdef SW_THREAD +static PHP_METHOD(swoole_thread_atomic, __wakeup); +#endif + +static PHP_METHOD(swoole_thread_atomic_long, __construct); +static PHP_METHOD(swoole_thread_atomic_long, add); +static PHP_METHOD(swoole_thread_atomic_long, sub); +static PHP_METHOD(swoole_thread_atomic_long, get); +static PHP_METHOD(swoole_thread_atomic_long, set); +static PHP_METHOD(swoole_thread_atomic_long, cmpset); +#ifdef SW_THREAD +static PHP_METHOD(swoole_thread_atomic_long, __wakeup); +#endif +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_atomic_methods[] = +{ + PHP_ME(swoole_thread_atomic, __construct, arginfo_class_Swoole_Thread_Atomic___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, add, arginfo_class_Swoole_Thread_Atomic_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, sub, arginfo_class_Swoole_Thread_Atomic_sub, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, get, arginfo_class_Swoole_Thread_Atomic_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, set, arginfo_class_Swoole_Thread_Atomic_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, wait, arginfo_class_Swoole_Thread_Atomic_wait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, wakeup, arginfo_class_Swoole_Thread_Atomic_wakeup, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, cmpset, arginfo_class_Swoole_Thread_Atomic_cmpset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic, __wakeup, arginfo_class_Swoole_Thread_Atomic___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +static const zend_function_entry swoole_thread_atomic_long_methods[] = +{ + PHP_ME(swoole_thread_atomic_long, __construct, arginfo_class_Swoole_Thread_Atomic_Long___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, add, arginfo_class_Swoole_Thread_Atomic_Long_add, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, sub, arginfo_class_Swoole_Thread_Atomic_Long_sub, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, get, arginfo_class_Swoole_Thread_Atomic_Long_get, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, set, arginfo_class_Swoole_Thread_Atomic_Long_set, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, cmpset, arginfo_class_Swoole_Thread_Atomic_Long_cmpset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_atomic_long, __wakeup, arginfo_class_Swoole_Thread_Atomic_Long___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_atomic_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_atomic, "Swoole\\Thread\\Atomic", nullptr, swoole_thread_atomic_methods); + zend_declare_property_long(swoole_thread_atomic_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + SW_SET_CLASS_CLONEABLE(swoole_thread_atomic, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_atomic, php_swoole_thread_atomic_create_object, php_swoole_thread_atomic_free_object, AtomicObject, std); + + SW_INIT_CLASS_ENTRY(swoole_thread_atomic_long, "Swoole\\Thread\\Atomic\\Long", nullptr, swoole_thread_atomic_long_methods); + zend_declare_property_long(swoole_thread_atomic_long_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + SW_SET_CLASS_CLONEABLE(swoole_thread_atomic_long, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic_long, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_atomic_long, + php_swoole_thread_atomic_long_create_object, + php_swoole_thread_atomic_long_free_object, + AtomicLongObject, + std); +} + +PHP_METHOD(swoole_thread_atomic, __construct) { + auto o = php_swoole_thread_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long value = 0; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (o->res) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + o->res = new AtomicResource(); + auto resource_id = php_swoole_thread_resource_insert(o->res); + zend_update_property_long(swoole_thread_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +} + +PHP_METHOD(swoole_thread_atomic, add) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + zend_long add_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(add_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_add_fetch(atomic, (uint32_t) add_value)); +} + +PHP_METHOD(swoole_thread_atomic, sub) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + zend_long sub_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(sub_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_sub_fetch(atomic, (uint32_t) sub_value)); +} + +PHP_METHOD(swoole_thread_atomic, get) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + RETURN_LONG(*atomic); +} + +PHP_METHOD(swoole_thread_atomic, set) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + zend_long set_value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + *atomic = (uint32_t) set_value; +} + +PHP_METHOD(swoole_thread_atomic, cmpset) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + zend_long cmp_value, set_value; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(cmp_value) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(sw_atomic_cmp_set(atomic, (sw_atomic_t) cmp_value, (sw_atomic_t) set_value)); +} + +PHP_METHOD(swoole_thread_atomic, wait) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + double timeout = 1.0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + SW_CHECK_RETURN(sw_atomic_futex_wait(atomic, timeout)); +} + +PHP_METHOD(swoole_thread_atomic, wakeup) { + sw_atomic_t *atomic = php_swoole_thread_atomic_get_ptr(ZEND_THIS); + zend_long n = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(n) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + SW_CHECK_RETURN(sw_atomic_futex_wakeup(atomic, (int) n)); +} + +static PHP_METHOD(swoole_thread_atomic, __wakeup) { + auto o = php_swoole_thread_atomic_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); + o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!o->res) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } +} + +PHP_METHOD(swoole_thread_atomic_long, __construct) { + auto o = php_swoole_thread_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long value = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (o->res) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + o->res = new AtomicLongResource(); + auto resource_id = php_swoole_thread_resource_insert(o->res); + zend_update_property_long(swoole_thread_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +} + +PHP_METHOD(swoole_thread_atomic_long, add) { + sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + zend_long add_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(add_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_add_fetch(atomic_long, (sw_atomic_long_t) add_value)); +} + +PHP_METHOD(swoole_thread_atomic_long, sub) { + sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + zend_long sub_value = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(sub_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_LONG(sw_atomic_sub_fetch(atomic_long, (sw_atomic_long_t) sub_value)); +} + +PHP_METHOD(swoole_thread_atomic_long, get) { + sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + RETURN_LONG(*atomic_long); +} + +PHP_METHOD(swoole_thread_atomic_long, set) { + sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + zend_long set_value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + *atomic_long = (sw_atomic_long_t) set_value; +} + +PHP_METHOD(swoole_thread_atomic_long, cmpset) { + sw_atomic_long_t *atomic_long = php_swoole_thread_atomic_long_get_ptr(ZEND_THIS); + zend_long cmp_value, set_value; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(cmp_value) + Z_PARAM_LONG(set_value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(sw_atomic_cmp_set(atomic_long, (sw_atomic_long_t) cmp_value, (sw_atomic_long_t) set_value)); +} + +static PHP_METHOD(swoole_thread_atomic_long, __wakeup) { + auto o = php_swoole_thread_atomic_long_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); + o->res = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!o->res) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } +} +#endif diff --git a/ext-src/swoole_thread_lock.cc b/ext-src/swoole_thread_lock.cc new file mode 100644 index 00000000000..c3087c7ca2b --- /dev/null +++ b/ext-src/swoole_thread_lock.cc @@ -0,0 +1,230 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_private.h" +#include "php_swoole_thread.h" +#include "swoole_memory.h" +#include "swoole_lock.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_thread_lock_arginfo.h" +END_EXTERN_C() + +using swoole::Lock; +using swoole::Mutex; +#ifdef HAVE_SPINLOCK +using swoole::SpinLock; +#endif +#ifdef HAVE_RWLOCK +using swoole::RWLock; +#endif + +static zend_class_entry *swoole_thread_lock_ce; +static zend_object_handlers swoole_thread_lock_handlers; + +#ifdef SW_THREAD +struct LockResource : public ThreadResource { + Lock *lock_; + LockResource(int type) : ThreadResource() { + switch (type) { +#ifdef HAVE_SPINLOCK + case Lock::SPIN_LOCK: + lock_ = new SpinLock(0); + break; +#endif +#ifdef HAVE_RWLOCK + case Lock::RW_LOCK: + lock_ = new RWLock(0); + break; +#endif + case Lock::MUTEX: + default: + lock_ = new Mutex(0); + break; + } + } + ~LockResource() { + delete lock_; + } +}; +#endif + +struct LockObject { + LockResource *lock; + zend_object std; +}; + +static sw_inline LockObject *php_swoole_thread_lock_fetch_object(zend_object *obj) { + return (LockObject *) ((char *) obj - swoole_thread_lock_handlers.offset); +} + +static Lock *php_swoole_thread_lock_get_ptr(zval *zobject) { + return php_swoole_thread_lock_fetch_object(Z_OBJ_P(zobject))->lock->lock_; +} + +static Lock *php_swoole_thread_lock_get_and_check_ptr(zval *zobject) { + Lock *lock = php_swoole_thread_lock_get_ptr(zobject); + if (!lock) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return lock; +} + +static void php_swoole_thread_lock_free_object(zend_object *object) { + LockObject *o = php_swoole_thread_lock_fetch_object(object); + zend_long resource_id = zend::object_get_long(object, ZEND_STRL("id")); + if (o->lock && php_swoole_thread_resource_free(resource_id, o->lock)) { + delete o->lock; + o->lock = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_thread_lock_create_object(zend_class_entry *ce) { + LockObject *lock = (LockObject *) zend_object_alloc(sizeof(LockObject), ce); + zend_object_std_init(&lock->std, ce); + object_properties_init(&lock->std, ce); + lock->std.handlers = &swoole_thread_lock_handlers; + return &lock->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_lock, __construct); +static PHP_METHOD(swoole_thread_lock, __destruct); +static PHP_METHOD(swoole_thread_lock, lock); +static PHP_METHOD(swoole_thread_lock, lockwait); +static PHP_METHOD(swoole_thread_lock, trylock); +static PHP_METHOD(swoole_thread_lock, lock_read); +static PHP_METHOD(swoole_thread_lock, trylock_read); +static PHP_METHOD(swoole_thread_lock, unlock); +static PHP_METHOD(swoole_thread_lock, destroy); +#ifdef SW_THREAD +static PHP_METHOD(swoole_thread_lock, __wakeup); +#endif +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_lock_methods[] = +{ + PHP_ME(swoole_thread_lock, __construct, arginfo_class_Swoole_Thread_Lock___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, __destruct, arginfo_class_Swoole_Thread_Lock___destruct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, lock, arginfo_class_Swoole_Thread_Lock_lock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, lockwait, arginfo_class_Swoole_Thread_Lock_locakwait, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, trylock, arginfo_class_Swoole_Thread_Lock_trylock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, lock_read, arginfo_class_Swoole_Thread_Lock_lock_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, trylock_read, arginfo_class_Swoole_Thread_Lock_trylock_read, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, unlock, arginfo_class_Swoole_Thread_Lock_unlock, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_lock, __wakeup, arginfo_class_Swoole_Thread_Lock___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_lock_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_lock, "Swoole\\Thread\\Lock", nullptr, swoole_thread_lock_methods); + zend_declare_property_long(swoole_thread_lock_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); + SW_SET_CLASS_CLONEABLE(swoole_thread_lock, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_lock, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_lock, php_swoole_thread_lock_create_object, php_swoole_thread_lock_free_object, LockObject, std); + + zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("MUTEX"), Lock::MUTEX); +#ifdef HAVE_RWLOCK + zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("RWLOCK"), Lock::RW_LOCK); +#endif +#ifdef HAVE_SPINLOCK + zend_declare_class_constant_long(swoole_thread_lock_ce, ZEND_STRL("SPINLOCK"), Lock::SPIN_LOCK); +#endif + zend_declare_property_long(swoole_thread_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); +#ifdef SW_THREAD + zend_declare_property_long(swoole_thread_lock_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); +#endif +} + +static PHP_METHOD(swoole_thread_lock, __construct) { + auto o = php_swoole_thread_lock_fetch_object(Z_OBJ_P(ZEND_THIS)); + if (o->lock != nullptr) { + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + zend_long type = swoole::Lock::MUTEX; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(type) + ZEND_PARSE_PARAMETERS_END(); + + o->lock = new LockResource(type); + auto resource_id = php_swoole_thread_resource_insert(o->lock); + zend_update_property_long(swoole_thread_lock_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_thread_lock, __destruct) {} + +static PHP_METHOD(swoole_thread_lock, lock) { + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->lock()); +} + +static PHP_METHOD(swoole_thread_lock, lockwait) { + double timeout = 1.0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &timeout) == FAILURE) { + RETURN_FALSE; + } + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + if (lock->get_type() != Lock::MUTEX) { + zend_throw_exception(swoole_exception_ce, "only mutex supports lockwait", -2); + RETURN_FALSE; + } + Mutex *mutex = dynamic_cast(lock); + if (mutex == nullptr) { + zend_throw_exception(swoole_exception_ce, "wrong lock type", -3); + RETURN_FALSE; + } + SW_LOCK_CHECK_RETURN(mutex->lock_wait((int) timeout * 1000)); +} + +static PHP_METHOD(swoole_thread_lock, unlock) { + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->unlock()); +} + +static PHP_METHOD(swoole_thread_lock, trylock) { + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->trylock()); +} + +static PHP_METHOD(swoole_thread_lock, trylock_read) { + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->trylock_rd()); +} + +static PHP_METHOD(swoole_thread_lock, lock_read) { + Lock *lock = php_swoole_thread_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->lock_rd()); +} + +static PHP_METHOD(swoole_thread_lock, __wakeup) { + auto o = php_swoole_thread_lock_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = zend::object_get_long(ZEND_THIS, ZEND_STRL("id")); + o->lock = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!o->lock) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + return; + } +} diff --git a/ext-src/swoole_thread_map.cc b/ext-src/swoole_thread_map.cc new file mode 100644 index 00000000000..705d38597ab --- /dev/null +++ b/ext-src/swoole_thread_map.cc @@ -0,0 +1,196 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" + +#ifdef SW_THREAD +#include "php_swoole_thread.h" + +SW_EXTERN_C_BEGIN +#include "stubs/php_swoole_thread_map_arginfo.h" +SW_EXTERN_C_END + +zend_class_entry *swoole_thread_map_ce; +static zend_object_handlers swoole_thread_map_handlers; + +struct ThreadMapObject { + ZendArray *map; + zend_object std; +}; + +static sw_inline ThreadMapObject *thread_map_fetch_object(zend_object *obj) { + return (ThreadMapObject *) ((char *) obj - swoole_thread_map_handlers.offset); +} + +static sw_inline zend_long thread_map_get_resource_id(zend_object *obj) { + zval rv, *property = zend_read_property(swoole_thread_map_ce, obj, ZEND_STRL("id"), 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static sw_inline zend_long thread_map_get_resource_id(zval *zobject) { + return thread_map_get_resource_id(Z_OBJ_P(zobject)); +} + +static void thread_map_free_object(zend_object *object) { + zend_long resource_id = thread_map_get_resource_id(object); + ThreadMapObject *mo = thread_map_fetch_object(object); + if (mo->map && php_swoole_thread_resource_free(resource_id, mo->map)) { + delete mo->map; + mo->map = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *thread_map_create_object(zend_class_entry *ce) { + ThreadMapObject *mo = (ThreadMapObject *) zend_object_alloc(sizeof(ThreadMapObject), ce); + zend_object_std_init(&mo->std, ce); + object_properties_init(&mo->std, ce); + mo->std.handlers = &swoole_thread_map_handlers; + return &mo->std; +} + +ThreadMapObject *thread_map_fetch_object_check(zval *zobject) { + ThreadMapObject *map = thread_map_fetch_object(Z_OBJ_P(zobject)); + if (!map->map) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return map; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_map, __construct); +static PHP_METHOD(swoole_thread_map, offsetGet); +static PHP_METHOD(swoole_thread_map, offsetExists); +static PHP_METHOD(swoole_thread_map, offsetSet); +static PHP_METHOD(swoole_thread_map, offsetUnset); +static PHP_METHOD(swoole_thread_map, count); +static PHP_METHOD(swoole_thread_map, keys); +static PHP_METHOD(swoole_thread_map, clean); +static PHP_METHOD(swoole_thread_map, __wakeup); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_map_methods[] = { + PHP_ME(swoole_thread_map, __construct, arginfo_class_Swoole_Thread_Map___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetGet, arginfo_class_Swoole_Thread_Map_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetExists, arginfo_class_Swoole_Thread_Map_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetSet, arginfo_class_Swoole_Thread_Map_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, offsetUnset, arginfo_class_Swoole_Thread_Map_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, count, arginfo_class_Swoole_Thread_Map_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, clean, arginfo_class_Swoole_Thread_Map_clean, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, keys, arginfo_class_Swoole_Thread_Map_keys, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_map, __wakeup, arginfo_class_Swoole_Thread_Map___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_map_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_map, "Swoole\\Thread\\Map", nullptr, swoole_thread_map_methods); + SW_SET_CLASS_CLONEABLE(swoole_thread_map, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_map, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_map, thread_map_create_object, thread_map_free_object, ThreadMapObject, std); + + zend_class_implements(swoole_thread_map_ce, 2, zend_ce_arrayaccess, zend_ce_countable); + zend_declare_property_long(swoole_thread_map_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); +} + + +static PHP_METHOD(swoole_thread_map, __construct) { + auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); + mo->map = new ZendArray(); + auto resource_id = php_swoole_thread_resource_insert(mo->map); + zend_update_property_long(swoole_thread_map_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +} + +#define ZEND_ARRAY_CALL_METHOD(array, method, zkey, ...) \ + if (ZVAL_IS_LONG(zkey)) { \ + array->intkey_##method(zkey, ##__VA_ARGS__); \ + } else { \ + array->strkey_##method(zkey, ##__VA_ARGS__); \ + } + +static PHP_METHOD(swoole_thread_map, offsetGet) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetGet, zkey, return_value); +} + +static PHP_METHOD(swoole_thread_map, offsetExists) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetExists, zkey, return_value); +} + +static PHP_METHOD(swoole_thread_map, offsetSet) { + zval *zkey; + zval *zvalue; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zkey) + Z_PARAM_ZVAL(zvalue) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetSet, zkey, zvalue); +} + +static PHP_METHOD(swoole_thread_map, offsetUnset) { + zval *zkey; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zkey) + ZEND_PARSE_PARAMETERS_END(); + + auto mo = thread_map_fetch_object_check(ZEND_THIS); + ZEND_ARRAY_CALL_METHOD(mo->map, offsetUnset, zkey); +} + +static PHP_METHOD(swoole_thread_map, count) { + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->map->count(return_value); +} + +static PHP_METHOD(swoole_thread_map, keys) { + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->map->keys(return_value); +} + +static PHP_METHOD(swoole_thread_map, clean) { + auto mo = thread_map_fetch_object_check(ZEND_THIS); + mo->map->clean(); +} + +static PHP_METHOD(swoole_thread_map, __wakeup) { + auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = thread_map_get_resource_id(ZEND_THIS); + mo->map = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!mo->map) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + } +} + +#endif diff --git a/ext-src/swoole_thread_queue.cc b/ext-src/swoole_thread_queue.cc new file mode 100644 index 00000000000..daa5c68a97c --- /dev/null +++ b/ext-src/swoole_thread_queue.cc @@ -0,0 +1,259 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole_cxx.h" + +#ifdef SW_THREAD +#include "php_swoole_thread.h" +#include "stubs/php_swoole_thread_queue_arginfo.h" + +#include +#include + +zend_class_entry *swoole_thread_queue_ce; +static zend_object_handlers swoole_thread_queue_handlers; + +struct Queue : ThreadResource { + std::queue queue; + std::mutex lock_; + std::condition_variable cv_; + + enum { + NOTIFY_NONE = 0, + NOTIFY_ONE = 1, + NOTIFY_ALL = 2, + }; + + Queue() : ThreadResource(), queue() {} + + ~Queue() { + clean(); + } + + void push(zval *zvalue) { + auto item = new ArrayItem(zvalue); + lock_.lock(); + queue.push(item); + lock_.unlock(); + } + + void pop(zval *return_value) { + ArrayItem *item = nullptr; + lock_.lock(); + if (!queue.empty()) { + item = queue.front(); + queue.pop(); + } + lock_.unlock(); + if (item) { + item->fetch(return_value); + delete item; + } + } + + void push_notify(zval *zvalue, bool notify_all) { + auto item = new ArrayItem(zvalue); + std::unique_lock _lock(lock_); + queue.push(item); + if (notify_all) { + cv_.notify_all(); + } else { + cv_.notify_one(); + } + } + + void pop_wait(zval *return_value, double timeout) { + ArrayItem *item = nullptr; + std::unique_lock _lock(lock_); + SW_LOOP { + if (!queue.empty()) { + item = queue.front(); + queue.pop(); + break; + } else { + if (timeout > 0) { + if (cv_.wait_for(_lock, std::chrono::duration(timeout)) == std::cv_status::timeout) { + break; + } + } else { + cv_.wait(_lock); + } + } + } + _lock.unlock(); + if (item) { + item->fetch(return_value); + delete item; + } + } + + void count(zval *return_value) { + lock_.lock(); + RETVAL_LONG(queue.size()); + lock_.unlock(); + } + + void clean() { + lock_.lock(); + while (!queue.empty()) { + ArrayItem *item = queue.front(); + delete item; + queue.pop(); + } + lock_.unlock(); + } +}; + +struct ThreadQueueObject { + Queue *queue; + zend_object std; +}; + +static sw_inline ThreadQueueObject *thread_queue_fetch_object(zend_object *obj) { + return (ThreadQueueObject *) ((char *) obj - swoole_thread_queue_handlers.offset); +} + +static sw_inline zend_long thread_queue_get_resource_id(zend_object *obj) { + zval rv, *property = zend_read_property(swoole_thread_queue_ce, obj, ZEND_STRL("id"), 1, &rv); + return property ? zval_get_long(property) : 0; +} + +static sw_inline zend_long thread_queue_get_resource_id(zval *zobject) { + return thread_queue_get_resource_id(Z_OBJ_P(zobject)); +} + +static void thread_queue_free_object(zend_object *object) { + zend_long resource_id = thread_queue_get_resource_id(object); + ThreadQueueObject *qo = thread_queue_fetch_object(object); + if (qo->queue && php_swoole_thread_resource_free(resource_id, qo->queue)) { + delete qo->queue; + qo->queue = nullptr; + } + zend_object_std_dtor(object); +} + +static zend_object *thread_queue_create_object(zend_class_entry *ce) { + ThreadQueueObject *qo = (ThreadQueueObject *) zend_object_alloc(sizeof(ThreadQueueObject), ce); + zend_object_std_init(&qo->std, ce); + object_properties_init(&qo->std, ce); + qo->std.handlers = &swoole_thread_queue_handlers; + return &qo->std; +} + +ThreadQueueObject *thread_queue_fetch_object_check(zval *zobject) { + ThreadQueueObject *qo = thread_queue_fetch_object(Z_OBJ_P(zobject)); + if (!qo->queue) { + php_swoole_fatal_error(E_ERROR, "must call constructor first"); + } + return qo; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_thread_queue, __construct); +static PHP_METHOD(swoole_thread_queue, push); +static PHP_METHOD(swoole_thread_queue, pop); +static PHP_METHOD(swoole_thread_queue, count); +static PHP_METHOD(swoole_thread_queue, clean); +static PHP_METHOD(swoole_thread_queue, __wakeup); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_thread_queue_methods[] = { + PHP_ME(swoole_thread_queue, __construct, arginfo_class_Swoole_Thread_Queue___construct, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, push, arginfo_class_Swoole_Thread_Queue_push, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, pop, arginfo_class_Swoole_Thread_Queue_pop, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, clean, arginfo_class_Swoole_Thread_Queue_clean, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, count, arginfo_class_Swoole_Thread_Queue_count, ZEND_ACC_PUBLIC) + PHP_ME(swoole_thread_queue, __wakeup, arginfo_class_Swoole_Thread_Queue___wakeup, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +// clang-format on + +void php_swoole_thread_queue_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_thread_queue, "Swoole\\Thread\\Queue", nullptr, swoole_thread_queue_methods); + SW_SET_CLASS_CLONEABLE(swoole_thread_queue, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_queue, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_thread_queue, thread_queue_create_object, thread_queue_free_object, ThreadQueueObject, std); + + zend_class_implements(swoole_thread_queue_ce, 1, zend_ce_countable); + zend_declare_property_long(swoole_thread_queue_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); + + zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ONE"), Queue::NOTIFY_ONE); + zend_declare_class_constant_long(swoole_thread_queue_ce, ZEND_STRL("NOTIFY_ALL"), Queue::NOTIFY_ALL); +} + +static PHP_METHOD(swoole_thread_queue, __construct) { + auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); + qo->queue = new Queue(); + auto resource_id = php_swoole_thread_resource_insert(qo->queue); + zend_update_property_long(swoole_thread_queue_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); +} + +static PHP_METHOD(swoole_thread_queue, push) { + zval *zvalue; + zend_long notify_which = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(zvalue) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(notify_which) + ZEND_PARSE_PARAMETERS_END(); + + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + if (notify_which > 0) { + qo->queue->push_notify(zvalue, notify_which == Queue::NOTIFY_ALL); + } else { + qo->queue->push(zvalue); + } +} + +static PHP_METHOD(swoole_thread_queue, pop) { + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END(); + + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + if (timeout == 0) { + qo->queue->pop(return_value); + } else { + qo->queue->pop_wait(return_value, timeout); + } +} + +static PHP_METHOD(swoole_thread_queue, count) { + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + qo->queue->count(return_value); +} + +static PHP_METHOD(swoole_thread_queue, clean) { + auto qo = thread_queue_fetch_object_check(ZEND_THIS); + qo->queue->clean(); +} + +static PHP_METHOD(swoole_thread_queue, __wakeup) { + auto qo = thread_queue_fetch_object(Z_OBJ_P(ZEND_THIS)); + zend_long resource_id = thread_queue_get_resource_id(ZEND_THIS); + qo->queue = static_cast(php_swoole_thread_resource_fetch(resource_id)); + if (!qo->queue) { + zend_throw_exception(swoole_exception_ce, EMSG_NO_RESOURCE, ECODE_NO_RESOURCE); + } +} + +#endif diff --git a/include/swoole_atomic.h b/include/swoole_atomic.h index adead3cd1f2..6fe231b4eb3 100644 --- a/include/swoole_atomic.h +++ b/include/swoole_atomic.h @@ -42,3 +42,65 @@ typedef sw_atomic_uint32_t sw_atomic_t; #endif #define sw_spinlock_release(lock) __sync_lock_release(lock) + +#ifdef HAVE_FUTEX +#include +#include + +static inline int sw_atomic_futex_wait(sw_atomic_t *atomic, double timeout) { + if (sw_atomic_cmp_set(atomic, 1, 0)) { + return 0; + } + + int ret; + struct timespec _timeout; + + if (timeout > 0) { + _timeout.tv_sec = (long) timeout; + _timeout.tv_nsec = (timeout - _timeout.tv_sec) * 1000 * 1000 * 1000; + ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, &_timeout, NULL, 0); + } else { + ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, NULL, NULL, 0); + } + if (ret == 0 && sw_atomic_cmp_set(atomic, 1, 0)) { + return 0; + } else { + return -1; + } +} + +static inline int sw_atomic_futex_wakeup(sw_atomic_t *atomic, int n) { + if (sw_atomic_cmp_set(atomic, 0, 1)) { + return syscall(SYS_futex, atomic, FUTEX_WAKE, n, NULL, NULL, 0); + } else { + return 0; + } +} + +#else +static inline int sw_atomic_futex_wait(sw_atomic_t *atomic, double timeout) { + if (sw_atomic_cmp_set(atomic, (sw_atomic_t) 1, (sw_atomic_t) 0)) { + return 0; + } + timeout = timeout <= 0 ? INT_MAX : timeout; + int32_t i = (int32_t) sw_atomic_sub_fetch(atomic, 1); + while (timeout > 0) { + if ((int32_t) *atomic > i) { + return 0; + } else { + usleep(1000); + timeout -= 0.001; + } + } + sw_atomic_fetch_add(atomic, 1); + return -1; +} + +static inline int sw_atomic_futex_wakeup(sw_atomic_t *atomic, int n) { + if (1 == (int32_t) *atomic) { + return 0; + } + sw_atomic_fetch_add(atomic, n); + return 0; +} +#endif diff --git a/include/swoole_error.h b/include/swoole_error.h index 3d6097e3318..c608de54eec 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -36,6 +36,7 @@ enum swErrorCode { SW_ERROR_WRONG_OPERATION, SW_ERROR_PHP_RUNTIME_NOTICE, // Non-fatal errors, just runtime warnings SW_ERROR_UNDEFINED_BEHAVIOR = 600, + SW_ERROR_NOT_THREAD_SAFETY, SW_ERROR_FILE_NOT_EXIST = 700, SW_ERROR_FILE_TOO_LARGE, diff --git a/tests/swoole_thread/async-io.phpt b/tests/swoole_thread/async-io.phpt index 58b531e3d56..e9aa5de4fc5 100644 --- a/tests/swoole_thread/async-io.phpt +++ b/tests/swoole_thread/async-io.phpt @@ -12,6 +12,7 @@ use Swoole\Thread; const C = 4; const N = 256; +const M = 9999; $args = Thread::getArguments(); $running = true; @@ -19,20 +20,24 @@ $md5 = md5_file(__FILE__); if (empty($args)) { $threads = []; - $atomic = new Swoole\Atomic(); + $atomic = new Swoole\Thread\Atomic(); + $atomicLong = new Swoole\Thread\Atomic\Long(); for ($i = 0; $i < C; $i++) { - $threads[] = Thread::exec(__FILE__, $argv, $i, $atomic); + $threads[] = Thread::exec(__FILE__, $argv, $i, $atomic, $atomicLong); } for ($i = 0; $i < C; $i++) { $threads[$i]->join(); } Assert::eq($atomic->get(), C * N); + Assert::eq($atomicLong->get(), C * N * M); } else { $atomic = $args[2]; - Co\run(function () use ($atomic, $md5) { + $atomicLong = $args[3]; + Co\run(function () use ($atomic, $atomicLong, $md5) { $n = N; while ($n--) { $atomic->add(); + $atomicLong->add(M); $rs = \Swoole\Coroutine\System::readFile(__FILE__); Assert::eq(md5($rs), $md5); } diff --git a/tests/swoole_thread/lock.phpt b/tests/swoole_thread/lock.phpt index 1f25e946ae2..9554db680b7 100644 --- a/tests/swoole_thread/lock.phpt +++ b/tests/swoole_thread/lock.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/skipif.inc'; require __DIR__ . '/../include/bootstrap.php'; use Swoole\Thread; -use Swoole\Lock; +use Swoole\Thread\Lock; $args = Thread::getArguments(); From 5cc516b48c0aa9600900455949ed3c6aac23b87f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 23 Apr 2024 16:18:37 +0800 Subject: [PATCH 082/103] fix --- ext-src/swoole_thread_lock.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext-src/swoole_thread_lock.cc b/ext-src/swoole_thread_lock.cc index c3087c7ca2b..bccd45cd6ac 100644 --- a/ext-src/swoole_thread_lock.cc +++ b/ext-src/swoole_thread_lock.cc @@ -19,6 +19,8 @@ #include "swoole_memory.h" #include "swoole_lock.h" +#ifdef SW_THREAD + BEGIN_EXTERN_C() #include "stubs/php_swoole_thread_lock_arginfo.h" END_EXTERN_C() @@ -228,3 +230,5 @@ static PHP_METHOD(swoole_thread_lock, __wakeup) { return; } } + +#endif From 8b95d8b89c15652e3d7743c988ba1eb2b64df3fb Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 23 Apr 2024 16:35:06 +0800 Subject: [PATCH 083/103] optimize tests --- .github/workflows/framework.yml | 3 ++- .github/workflows/test-linux.yml | 2 +- .github/workflows/thread.yml | 1 + ext-src/swoole_server.cc | 4 ++++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index c78daf14448..ccb4ceebb5a 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -7,7 +7,8 @@ on: jobs: linux: runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, '[test]')" + if: 0 +# if: "!contains(github.event.head_commit.message, '[test]')" strategy: fail-fast: false matrix: diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 1e5fef9c955..98656baae84 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -5,7 +5,7 @@ on: [push, pull_request] jobs: test-linux: runs-on: ubuntu-latest - if: 0 + if: "!contains(github.event.head_commit.message, '[zts]')" strategy: fail-fast: false matrix: diff --git a/.github/workflows/thread.yml b/.github/workflows/thread.yml index ce5113f35a2..eb81aa23927 100644 --- a/.github/workflows/thread.yml +++ b/.github/workflows/thread.yml @@ -5,6 +5,7 @@ on: [push, pull_request] jobs: test-linux: runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[nts]')" strategy: fail-fast: false matrix: diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 9b5b1f24d73..7e300f5efe1 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1927,7 +1927,11 @@ static PHP_METHOD(swoole_server, __construct) { Z_PARAM_LONG(sock_type) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); +#ifdef SW_THREAD if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS && serv_mode != Server::MODE_THREAD) { +#else + if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS) { +#endif zend_throw_error(NULL, "invalid $mode parameters %d", (int) serv_mode); RETURN_FALSE; } From 0168ebe269f47f789a430a90b600ea3744683cab Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 23 Apr 2024 16:42:56 +0800 Subject: [PATCH 084/103] fix tests --- .github/workflows/test-linux.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 98656baae84..bcbdc938d93 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -35,4 +35,6 @@ jobs: - name: Run Swoole test run: | export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_BUILD_DIR=$(realpath .) + export PHP_VERSION=${{ matrix.php }} ${{runner.workspace}}/swoole-src/scripts/route.sh From f6fcafb89363f13a79cd5b74f8fd29970a64ebb8 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 23 Apr 2024 16:43:17 +0800 Subject: [PATCH 085/103] code format --- ext-src/swoole_admin_server.cc | 2 +- ext-src/swoole_async_coro.cc | 42 ++++++++++++++++----------------- ext-src/swoole_client_coro.cc | 2 +- ext-src/swoole_coroutine.cc | 29 ++++++++++------------- ext-src/swoole_http2_server.cc | 9 ++----- ext-src/swoole_http_response.cc | 18 +++++++------- ext-src/swoole_http_server.cc | 3 +-- ext-src/swoole_lock.cc | 3 +-- ext-src/swoole_oracle.cc | 9 ++++--- ext-src/swoole_pgsql.cc | 3 ++- ext-src/swoole_server.cc | 6 ++--- ext-src/swoole_server_port.cc | 4 +--- ext-src/swoole_thread_atomic.cc | 14 +++++++---- ext-src/swoole_thread_map.cc | 1 - 14 files changed, 70 insertions(+), 75 deletions(-) diff --git a/ext-src/swoole_admin_server.cc b/ext-src/swoole_admin_server.cc index 7c322443532..3c35e3d81e2 100644 --- a/ext-src/swoole_admin_server.cc +++ b/ext-src/swoole_admin_server.cc @@ -291,7 +291,7 @@ static json get_socket_info(int fd) { {"rcv_space", info.tcpi_rcv_space}, {"total_retrans", info.tcpi_total_retrans}, }; -#endif // defined(__FreeBSD__) || defined(__NetBSD__) +#endif // defined(__FreeBSD__) || defined(__NetBSD__) return jinfo; } #endif diff --git a/ext-src/swoole_async_coro.cc b/ext-src/swoole_async_coro.cc index fee43f06fe6..b50f77d75b1 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -44,27 +44,27 @@ void php_swoole_async_coro_rshutdown() { void php_swoole_set_aio_option(HashTable *vht) { zval *ztmp; /* AIO */ - if (php_swoole_array_get_value(vht, "aio_core_worker_num", ztmp)) { - zend_long v = zval_get_long(ztmp); - v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); - SwooleG.aio_core_worker_num = v; - } - if (php_swoole_array_get_value(vht, "aio_worker_num", ztmp)) { - zend_long v = zval_get_long(ztmp); - v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); - SwooleG.aio_worker_num = v; - } - if (php_swoole_array_get_value(vht, "aio_max_wait_time", ztmp)) { - SwooleG.aio_max_wait_time = zval_get_double(ztmp); - } - if (php_swoole_array_get_value(vht, "aio_max_idle_time", ztmp)) { - SwooleG.aio_max_idle_time = zval_get_double(ztmp); - } + if (php_swoole_array_get_value(vht, "aio_core_worker_num", ztmp)) { + zend_long v = zval_get_long(ztmp); + v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); + SwooleG.aio_core_worker_num = v; + } + if (php_swoole_array_get_value(vht, "aio_worker_num", ztmp)) { + zend_long v = zval_get_long(ztmp); + v = SW_MAX(1, SW_MIN(v, UINT32_MAX)); + SwooleG.aio_worker_num = v; + } + if (php_swoole_array_get_value(vht, "aio_max_wait_time", ztmp)) { + SwooleG.aio_max_wait_time = zval_get_double(ztmp); + } + if (php_swoole_array_get_value(vht, "aio_max_idle_time", ztmp)) { + SwooleG.aio_max_idle_time = zval_get_double(ztmp); + } #if defined(__linux__) && defined(SW_USE_IOURING) - if (php_swoole_array_get_value(vht, "iouring_entries", ztmp)) { - zend_long v = zval_get_long(ztmp); - SwooleG.iouring_entries = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } + if (php_swoole_array_get_value(vht, "iouring_entries", ztmp)) { + zend_long v = zval_get_long(ztmp); + SwooleG.iouring_entries = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } #endif } @@ -183,5 +183,5 @@ PHP_FUNCTION(swoole_async_dns_lookup_coro) { } memcpy(cache->address, Z_STRVAL_P(return_value), Z_STRLEN_P(return_value)); cache->address[Z_STRLEN_P(return_value)] = '\0'; - cache->update_time = Timer::get_absolute_msec() + (int64_t)(SwooleG.dns_cache_refresh_time * 1000); + cache->update_time = Timer::get_absolute_msec() + (int64_t) (SwooleG.dns_cache_refresh_time * 1000); } diff --git a/ext-src/swoole_client_coro.cc b/ext-src/swoole_client_coro.cc index acd38ce917a..9fe1f8c7c46 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -113,7 +113,7 @@ static void client_coro_free_object(zend_object *object) { } #define CLIENT_CORO_GET_SOCKET_SAFE(__sock) \ - SW_CLIENT_GET_SOCKET_SAFE(__sock, &client_coro_get_client(ZEND_THIS)->zsocket); \ + SW_CLIENT_GET_SOCKET_SAFE(__sock, &client_coro_get_client(ZEND_THIS)->zsocket); \ if (!__sock) { \ php_swoole_socket_set_error_properties( \ ZEND_THIS, SW_ERROR_CLIENT_NO_CONNECTION, swoole_strerror(SW_ERROR_CLIENT_NO_CONNECTION)); \ diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index a8a1dfbfdf2..8d94c2cd9e3 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -859,32 +859,30 @@ void PHPCoroutine::fiber_context_switch_try_notify(PHPContext *from, PHPContext #endif /* SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT */ #ifdef ZEND_CHECK_STACK_LIMIT -void* PHPCoroutine::stack_limit(PHPContext *ctx) -{ +void *PHPCoroutine::stack_limit(PHPContext *ctx) { #ifdef SW_USE_THREAD_CONTEXT return nullptr; #else - zend_ulong reserve = EG(reserved_stack_size); + zend_ulong reserve = EG(reserved_stack_size); #ifdef __APPLE__ - /* On Apple Clang, the stack probing function ___chkstk_darwin incorrectly - * probes a location that is twice the entered function's stack usage away - * from the stack pointer, when using an alternative stack. - * https://openradar.appspot.com/radar?id=5497722702397440 - */ - reserve = reserve * 2; + /* On Apple Clang, the stack probing function ___chkstk_darwin incorrectly + * probes a location that is twice the entered function's stack usage away + * from the stack pointer, when using an alternative stack. + * https://openradar.appspot.com/radar?id=5497722702397440 + */ + reserve = reserve * 2; #endif if (!ctx->co) { return nullptr; } - /* stack->pointer is the end of the stack */ - return (int8_t*)ctx->co->get_ctx().get_stack() + reserve; + /* stack->pointer is the end of the stack */ + return (int8_t *) ctx->co->get_ctx().get_stack() + reserve; #endif } -void* PHPCoroutine::stack_base(PHPContext *ctx) -{ +void *PHPCoroutine::stack_base(PHPContext *ctx) { #ifdef SW_USE_THREAD_CONTEXT return nullptr; #else @@ -892,7 +890,7 @@ void* PHPCoroutine::stack_base(PHPContext *ctx) return nullptr; } - return (void*)((uintptr_t)ctx->co->get_ctx().get_stack() + ctx->co->get_ctx().get_stack_size()); + return (void *) ((uintptr_t) ctx->co->get_ctx().get_stack() + ctx->co->get_ctx().get_stack_size()); #endif } #endif /* ZEND_CHECK_STACK_LIMIT */ @@ -911,8 +909,7 @@ struct AutoloadQueue { std::queue *queue; }; -static zend_class_entry *swoole_coroutine_autoload(zend_string *name, zend_string *lc_name) -{ +static zend_class_entry *swoole_coroutine_autoload(zend_string *name, zend_string *lc_name) { auto current = Coroutine::get_current(); if (!current) { return original_zend_autoload(name, lc_name); diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc index 785aaba9630..b7e97c179bd 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -213,13 +213,8 @@ static bool http2_server_is_static_file(Server *serv, HttpContext *ctx) { if (1 == tasks.size()) { if (SW_HTTP_PARTIAL_CONTENT == handler.status_code) { std::stringstream content_range; - content_range << "bytes " - << tasks[0].offset - << "-" - << (tasks[0].length + tasks[0].offset - 1) - << "/" - << handler.get_filesize() - << "\r\n"; + content_range << "bytes " << tasks[0].offset << "-" << (tasks[0].length + tasks[0].offset - 1) << "/" + << handler.get_filesize() << "\r\n"; auto content_range_str = content_range.str(); ctx->set_header(ZEND_STRL("Content-Range"), content_range_str.c_str(), content_range_str.length(), 0); } else { diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 9eb633d5e22..fdfe0c2b613 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -997,10 +997,10 @@ static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const } char *cookie = nullptr, *date = nullptr; - size_t cookie_size = name_len + 1; // add 1 for null char - cookie_size += 50; // strlen("; expires=Fri, 31-Dec-9999 23:59:59 GMT; Max-Age=0") + size_t cookie_size = name_len + 1; // add 1 for null char + cookie_size += 50; // strlen("; expires=Fri, 31-Dec-9999 23:59:59 GMT; Max-Age=0") if (value_len == 0) { - cookie_size += 8; // strlen("=deleted") + cookie_size += 8; // strlen("=deleted") } if (expires > 0) { // Max-Age will be no longer than 12 digits since the @@ -1008,22 +1008,22 @@ static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const cookie_size += 11; } if (path_len > 0) { - cookie_size += path_len + 7; // strlen("; path=") + cookie_size += path_len + 7; // strlen("; path=") } if (domain_len > 0) { - cookie_size += domain_len + 9; // strlen("; domain=") + cookie_size += domain_len + 9; // strlen("; domain=") } if (secure) { - cookie_size += 8; // strlen("; secure") + cookie_size += 8; // strlen("; secure") } if (httponly) { - cookie_size += 10; // strlen("; httponly") + cookie_size += 10; // strlen("; httponly") } if (samesite_len > 0) { - cookie_size += samesite_len + 11; // strlen("; samesite=") + cookie_size += samesite_len + 11; // strlen("; samesite=") } if (priority_len > 0) { - cookie_size += priority_len + 11; // strlen("; priority=") + cookie_size += priority_len + 11; // strlen("; priority=") } if (value_len == 0) { diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index c4a4a85e2ea..b94b7a095ee 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -417,8 +417,7 @@ void swoole_http_server_onAfterResponse(HttpContext *ctx) { swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); if (!queued_http_contexts.empty()) { HttpContext *ctx = queued_http_contexts.front(); - swoole_trace( - "[POP 1] concurrency=%u, ctx=%p, request=%p", sw_worker()->concurrency, ctx, ctx->request.zobject); + swoole_trace("[POP 1] concurrency=%u, ctx=%p, request=%p", sw_worker()->concurrency, ctx, ctx->request.zobject); queued_http_contexts.pop(); swoole_event_defer( [](void *private_data) { diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc index 9effbb595e6..902dcf79535 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.cc @@ -159,8 +159,7 @@ static PHP_METHOD(swoole_lock, __construct) { lock = new Mutex(Mutex::PROCESS_SHARED); break; default: - zend_throw_exception( - swoole_exception_ce, "lock type[%d] is not support", type); + zend_throw_exception(swoole_exception_ce, "lock type[%d] is not support", type); RETURN_FALSE; break; } diff --git a/ext-src/swoole_oracle.cc b/ext-src/swoole_oracle.cc index af7d90e4e92..d426d956977 100644 --- a/ext-src/swoole_oracle.cc +++ b/ext-src/swoole_oracle.cc @@ -49,7 +49,8 @@ sword swoole_oci_stmt_prepare( OCIStmt *stmtp, OCIError *errhp, const OraText *stmt, ub4 stmt_len, ub4 language, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_prepare"); sword result = 0; - php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtPrepare(stmtp, errhp, stmt, stmt_len, language, mode); }); + php_swoole_async(swoole_oracle_blocking, + [&]() { result = OCIStmtPrepare(stmtp, errhp, stmt, stmt_len, language, mode); }); return result; } @@ -64,7 +65,8 @@ sword swoole_oci_stmt_execute(OCISvcCtx *svchp, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_execute"); sword result = 0; - php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtExecute(svchp, stmtp, errhp, iters, rowoff, snap_in, snap_out, mode); }); + php_swoole_async(swoole_oracle_blocking, + [&]() { result = OCIStmtExecute(svchp, stmtp, errhp, iters, rowoff, snap_in, snap_out, mode); }); return result; } @@ -80,7 +82,8 @@ sword swoole_oci_stmt_fetch(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orie sword swoole_oci_stmt_fetch2(OCIStmt *stmtp, OCIError *errhp, ub4 nrows, ub2 orientation, sb4 scrollOffset, ub4 mode) { swoole_trace_log(SW_TRACE_CO_ORACLE, "oci_stmt_fetch2"); sword result = 0; - php_swoole_async(swoole_oracle_blocking, [&]() { result = OCIStmtFetch2(stmtp, errhp, nrows, orientation, scrollOffset, mode); }); + php_swoole_async(swoole_oracle_blocking, + [&]() { result = OCIStmtFetch2(stmtp, errhp, nrows, orientation, scrollOffset, mode); }); return result; } diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index 08aa243713b..12f5d10400b 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -188,7 +188,8 @@ void swoole_pgsql_set_blocking(bool blocking) { } void php_swoole_pgsql_minit(int module_id) { - if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("PGSQL_ATTR_DISABLE_PREPARES")) == nullptr) { + if (zend_hash_str_find(&php_pdo_get_dbh_ce()->constants_table, ZEND_STRL("PGSQL_ATTR_DISABLE_PREPARES")) == + nullptr) { REGISTER_PDO_CLASS_CONST_LONG("PGSQL_ATTR_DISABLE_PREPARES", PDO_PGSQL_ATTR_DISABLE_PREPARES); REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_IDLE", (zend_long) PGSQL_TRANSACTION_IDLE); REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_ACTIVE", (zend_long) PGSQL_TRANSACTION_ACTIVE); diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 7e300f5efe1..2a93773b54f 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1927,11 +1927,11 @@ static PHP_METHOD(swoole_server, __construct) { Z_PARAM_LONG(sock_type) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS #ifdef SW_THREAD - if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS && serv_mode != Server::MODE_THREAD) { -#else - if (serv_mode != Server::MODE_BASE && serv_mode != Server::MODE_PROCESS) { + && serv_mode != Server::MODE_THREAD #endif + ) { zend_throw_error(NULL, "invalid $mode parameters %d", (int) serv_mode); RETURN_FALSE; } diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc index e76a07b5c48..28b0f51197e 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -201,9 +201,7 @@ static ssize_t php_swoole_server_length_func(const Protocol *protocol, network:: ssize_t ret = -1; ZVAL_STRINGL(&zdata, pl->buf, pl->buf_size); - HOOK_PHP_CALL_STACK( - auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, 1, &zdata, &retval); - ); + HOOK_PHP_CALL_STACK(auto call_result = sw_zend_call_function_ex(nullptr, fci_cache, 1, &zdata, &retval);); if (UNEXPECTED(call_result) != SUCCESS) { php_swoole_fatal_error(E_WARNING, "length function handler error"); } else { diff --git a/ext-src/swoole_thread_atomic.cc b/ext-src/swoole_thread_atomic.cc index f4bfd71c14a..3a0605d352f 100644 --- a/ext-src/swoole_thread_atomic.cc +++ b/ext-src/swoole_thread_atomic.cc @@ -30,7 +30,7 @@ static zend_object_handlers swoole_thread_atomic_handlers; zend_class_entry *swoole_thread_atomic_long_ce; static zend_object_handlers swoole_thread_atomic_long_handlers; -struct AtomicResource: public ThreadResource { +struct AtomicResource : public ThreadResource { sw_atomic_t value; }; @@ -70,7 +70,7 @@ static zend_object *php_swoole_thread_atomic_create_object(zend_class_entry *ce) return &atomic->std; } -struct AtomicLongResource: public ThreadResource { +struct AtomicLongResource : public ThreadResource { sw_atomic_long_t value; }; @@ -162,10 +162,14 @@ void php_swoole_thread_atomic_minit(int module_number) { zend_declare_property_long(swoole_thread_atomic_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); SW_SET_CLASS_CLONEABLE(swoole_thread_atomic, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_thread_atomic, php_swoole_thread_atomic_create_object, php_swoole_thread_atomic_free_object, AtomicObject, std); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_thread_atomic, + php_swoole_thread_atomic_create_object, + php_swoole_thread_atomic_free_object, + AtomicObject, + std); - SW_INIT_CLASS_ENTRY(swoole_thread_atomic_long, "Swoole\\Thread\\Atomic\\Long", nullptr, swoole_thread_atomic_long_methods); + SW_INIT_CLASS_ENTRY( + swoole_thread_atomic_long, "Swoole\\Thread\\Atomic\\Long", nullptr, swoole_thread_atomic_long_methods); zend_declare_property_long(swoole_thread_atomic_long_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); SW_SET_CLASS_CLONEABLE(swoole_thread_atomic_long, sw_zend_class_clone_deny); SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_thread_atomic_long, sw_zend_class_unset_property_deny); diff --git a/ext-src/swoole_thread_map.cc b/ext-src/swoole_thread_map.cc index 705d38597ab..9da7a653a04 100644 --- a/ext-src/swoole_thread_map.cc +++ b/ext-src/swoole_thread_map.cc @@ -108,7 +108,6 @@ void php_swoole_thread_map_minit(int module_number) { zend_declare_property_long(swoole_thread_map_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC); } - static PHP_METHOD(swoole_thread_map, __construct) { auto mo = thread_map_fetch_object(Z_OBJ_P(ZEND_THIS)); mo->map = new ZendArray(); From 69effb709559a2b137359190c9004293f4ec06ea Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 23 Apr 2024 16:59:26 +0800 Subject: [PATCH 086/103] fix tests --- tests/include/skipif.inc | 5 +++++ tests/swoole_thread/async-io.phpt | 1 + tests/swoole_thread/lock.phpt | 1 + tests/swoole_thread/pipe.phpt | 1 + tests/swoole_thread/queue.phpt | 1 + tests/swoole_thread/signal.phpt | 1 + 6 files changed, 10 insertions(+) diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index d33f93f8710..e2ea43b210f 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -153,6 +153,11 @@ function skip_if_darwin() skip('not support on darwin', stripos(PHP_OS, 'Darwin') !== false); } +function skip_if_nts() +{ + skip('not support in nts', !defined('SWOOLE_THREAD')); +} + function skip_if_musl_libc() { skip('not support when use musl libc', !empty(`ldd 2>&1 | grep -i musl`)); diff --git a/tests/swoole_thread/async-io.phpt b/tests/swoole_thread/async-io.phpt index e9aa5de4fc5..5af3ac329db 100644 --- a/tests/swoole_thread/async-io.phpt +++ b/tests/swoole_thread/async-io.phpt @@ -3,6 +3,7 @@ swoole_thread: async-io --SKIPIF-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- Date: Tue, 23 Apr 2024 18:21:28 +0800 Subject: [PATCH 087/103] fix tests --- ext-src/swoole_runtime.cc | 9 ++-- ext-src/swoole_server.cc | 50 +++++++++---------- tests/swoole_feature/cross_close/redis.phpt | 15 +++--- .../cross_close/redis_by_server.phpt | 9 ++-- tests/swoole_timer/enable_coroutine2.phpt | 19 ++++--- tests/swoole_timer/function_alias.phpt | 1 - 6 files changed, 53 insertions(+), 50 deletions(-) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 0dd9b64f77c..76f4a855b70 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -236,6 +236,10 @@ void php_swoole_runtime_rinit() { } void php_swoole_runtime_rshutdown() { +#ifdef SW_THREAD + PHPCoroutine::disable_hook(); +#endif + void *ptr; ZEND_HASH_FOREACH_PTR(tmp_function_table, ptr) { real_func *rf = reinterpret_cast(ptr); @@ -255,6 +259,7 @@ void php_swoole_runtime_rshutdown() { efree(tmp_function_table); tmp_function_table = nullptr; + ori_func_handlers.clear(); clear_class_entries(); } @@ -262,10 +267,6 @@ void php_swoole_runtime_mshutdown() { #ifdef SW_USE_CURL swoole_native_curl_mshutdown(); #endif -#ifdef SW_THREAD - PHPCoroutine::disable_hook(); -#endif - ori_func_handlers.clear(); } static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err) { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 2a93773b54f..174ae5ca0b4 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -206,7 +206,6 @@ static void server_free_object(zend_object *object) { sw_zend_fci_cache_discard((zend_fcall_info_cache *) serv->private_data_3); efree(serv->private_data_3); } - ZVAL_NULL(php_swoole_server_zval_ptr(serv)); for (int i = 0; i < PHP_SWOOLE_SERVER_CALLBACK_NUM; i++) { zend_fcall_info_cache *fci_cache = property->callbacks[i]; if (fci_cache) { @@ -2646,22 +2645,32 @@ static PHP_METHOD(swoole_server, start) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); #ifdef SW_THREAD - zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); - zend_string *bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); - zend_string *argv = nullptr; - zval thread_argv; - - if (!ZVAL_IS_NULL(&server_object->init_arguments)) { - call_user_function(NULL, NULL, &server_object->init_arguments, &thread_argv, 0, NULL); - argv = php_swoole_thread_serialize(&thread_argv); + if (serv->is_thread_mode()) { + zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); + zend_string *bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); + zend_string *argv = nullptr; + zval thread_argv = {}; + + if (!ZVAL_IS_NULL(&server_object->init_arguments)) { + call_user_function(NULL, NULL, &server_object->init_arguments, &thread_argv, 0, NULL); + argv = php_swoole_thread_serialize(&thread_argv); + } + + serv->worker_thread_start = [bootstrap, argv](const WorkerFn &fn) { + worker_thread_fn = fn; + zend_string *bootstrap_copy = zend_string_dup(bootstrap, 1); + zend_string *argv_copy = argv ? zend_string_dup(argv, 1) : nullptr; + php_swoole_thread_start(bootstrap_copy, argv_copy); + }; + + ON_SCOPE_EXIT { + zend_string_release(bootstrap); + if (argv) { + zend_string_release(argv); + } + zval_ptr_dtor(&thread_argv); + }; } - - serv->worker_thread_start = [bootstrap, argv](const WorkerFn &fn) { - worker_thread_fn = fn; - zend_string *bootstrap_copy = zend_string_dup(bootstrap, 1); - zend_string *argv_copy = argv ? zend_string_dup(argv, 1) : nullptr; - php_swoole_thread_start(bootstrap_copy, argv_copy); - }; #endif server_object->register_callback(); @@ -2670,15 +2679,6 @@ static PHP_METHOD(swoole_server, start) { if (serv->start() < 0) { php_swoole_fatal_error(E_ERROR, "failed to start server. Error: %s", sw_error); } - -#ifdef SW_THREAD - zend_string_release(bootstrap); - if (argv) { - zend_string_release(argv); - } - zval_ptr_dtor(&thread_argv); -#endif - RETURN_TRUE; } diff --git a/tests/swoole_feature/cross_close/redis.phpt b/tests/swoole_feature/cross_close/redis.phpt index 640f2a259fc..b48d371cbb4 100644 --- a/tests/swoole_feature/cross_close/redis.phpt +++ b/tests/swoole_feature/cross_close/redis.phpt @@ -21,14 +21,13 @@ $pm->parentFunc = function () use ($pm) { echo "DONE\n"; $pm->kill(); }); - $ret = $redis->get($pm->getRandomData()); - echo "CLOSED\n"; - Assert::assert(!$ret); - Assert::assert(!$redis->connected); - Assert::assert(in_array($redis->errType, [SWOOLE_REDIS_ERR_IO, SWOOLE_REDIS_ERR_EOF], true)); - if ($redis->errType === SWOOLE_REDIS_ERR_IO) { - Assert::same($redis->errCode, SOCKET_ECANCELED); + try { + $ret = $redis->get($pm->getRandomData()); + } catch (\RedisException $e) { + $ret = false; + echo "CLOSED\n"; } + Assert::assert(!$ret); }); }); }; @@ -38,6 +37,7 @@ $pm->childFunc = function () use ($pm) { Assert::assert($server->bind('127.0.0.1', $pm->getFreePort())); Assert::assert($server->listen()); go(function () use ($pm, $server) { + $pm->wakeup(); if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\Socket)) { switch_process(); $data = $conn->recv(); @@ -50,7 +50,6 @@ $pm->childFunc = function () use ($pm) { } $server->close(); }); - $pm->wakeup(); }); }; $pm->childFirst(); diff --git a/tests/swoole_feature/cross_close/redis_by_server.phpt b/tests/swoole_feature/cross_close/redis_by_server.phpt index 64f889fc803..a4436cfe99e 100644 --- a/tests/swoole_feature/cross_close/redis_by_server.phpt +++ b/tests/swoole_feature/cross_close/redis_by_server.phpt @@ -13,10 +13,11 @@ $pm->parentFunc = function () use ($pm) { $redis = new \redis; Assert::assert($redis->connect('127.0.0.1', $pm->getFreePort())); echo "GET\n"; - Assert::assert(!$redis->get($pm->getRandomData())); - echo "CLOSED\n"; - Assert::same($redis->errType, SWOOLE_REDIS_ERR_EOF); - Assert::same($redis->errCode, SOCKET_ECONNRESET); + try { + $redis->get($pm->getRandomData()); + } catch (\RedisException $e) { + echo "CLOSED\n"; + } $pm->kill(); echo "DONE\n"; }); diff --git a/tests/swoole_timer/enable_coroutine2.phpt b/tests/swoole_timer/enable_coroutine2.phpt index 44ad8d319e2..18e0d107342 100644 --- a/tests/swoole_timer/enable_coroutine2.phpt +++ b/tests/swoole_timer/enable_coroutine2.phpt @@ -5,20 +5,23 @@ swoole_timer: enable_coroutine setting --FILE-- false ]); Swoole\Timer::after(1, function () { $uid = Co::getuid(); echo "#{$uid}\n"; - Swoole\Timer::set([ - 'enable_coroutine' => true - ]); - Swoole\Timer::after(1, function () { - $uid = Co::getuid(); - echo "#{$uid}\n"; - }); }); +Swoole\Event::wait(); + +swoole_async_set([ + 'enable_coroutine' => true +]); +Swoole\Timer::after(1, function () { + $uid = Co::getuid(); + echo "#{$uid}\n"; +}); +Swoole\Event::wait(); ?> --EXPECT-- #-1 diff --git a/tests/swoole_timer/function_alias.phpt b/tests/swoole_timer/function_alias.phpt index 61cf74fab80..870f6eca27d 100644 --- a/tests/swoole_timer/function_alias.phpt +++ b/tests/swoole_timer/function_alias.phpt @@ -7,7 +7,6 @@ swoole_timer: function alias require __DIR__ . '/../include/bootstrap.php'; var_dump( - function_exists('swoole_timer_set') && function_exists('swoole_timer_after') && function_exists('swoole_timer_tick') && function_exists('swoole_timer_exists') && From 0890024e28fec0a300c85aebecd3f2a683716bb6 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 23 Apr 2024 18:58:57 +0800 Subject: [PATCH 088/103] fix tests [3] --- tests/swoole_coroutine/signal_listener.phpt | 2 +- tests/swoole_runtime/stream_select/base.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/swoole_coroutine/signal_listener.phpt b/tests/swoole_coroutine/signal_listener.phpt index 3e124e20734..567b17664c8 100644 --- a/tests/swoole_coroutine/signal_listener.phpt +++ b/tests/swoole_coroutine/signal_listener.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Coroutine; use Swoole\Process; -ini_set('swoole.enable_coroutine', 'off'); +swoole_async_set(['enable_coroutine' => false]); $pm = new ProcessManager; $pm->parentFunc = function () use ($pm) { diff --git a/tests/swoole_runtime/stream_select/base.phpt b/tests/swoole_runtime/stream_select/base.phpt index 7870b3b26a1..8e6c07d5ebe 100644 --- a/tests/swoole_runtime/stream_select/base.phpt +++ b/tests/swoole_runtime/stream_select/base.phpt @@ -7,7 +7,6 @@ swoole_runtime/stream_select: base require __DIR__ . '/../../include/bootstrap.php'; Swoole\Runtime::enableCoroutine(); go(function () { - Swoole\Runtime::enableCoroutine(); $fp1 = stream_socket_client("tcp://www.baidu.com:80", $errno, $errstr, 30); $fp2 = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); if (!$fp1) { @@ -28,5 +27,6 @@ go(function () { fclose($fp1); } }); +Swoole\Runtime::enableCoroutine(0); ?> --EXPECT-- From afecffce51d618e2c2b8ffb3ebae449fe3460631 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 23 Apr 2024 19:15:40 +0800 Subject: [PATCH 089/103] Fix transfer_t struct missing (#5303) * Fix missing transfer_t * Fix missing transfer_t --- include/swoole_coroutine_context.h | 4 ++-- src/coroutine/context.cc | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/swoole_coroutine_context.h b/include/swoole_coroutine_context.h index ce9d43833e8..57ba161e844 100644 --- a/include/swoole_coroutine_context.h +++ b/include/swoole_coroutine_context.h @@ -90,10 +90,10 @@ class Context { void *private_data_; bool end_; -#if USE_UCONTEXT +#if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT) static void context_func(void *arg); #else - static void context_func(transfer_t arg); + static void context_func(coroutine_transfer_t arg); #endif }; diff --git a/src/coroutine/context.cc b/src/coroutine/context.cc index 31045cddd3d..face44ededb 100644 --- a/src/coroutine/context.cc +++ b/src/coroutine/context.cc @@ -57,7 +57,7 @@ Context::Context(size_t stack_size, CoroutineFunc fn, void *private_data) valgrind_stack_id = VALGRIND_STACK_REGISTER(sp, stack_); #endif -#if USE_UCONTEXT +#ifdef USE_UCONTEXT if (-1 == getcontext(&ctx_)) { swoole_throw_error(SW_ERROR_CO_GETCONTEXT_FAILED); sw_free(stack_); @@ -120,7 +120,7 @@ ssize_t Context::get_stack_usage() { #endif bool Context::swap_in() { -#if USE_UCONTEXT +#ifdef USE_UCONTEXT return 0 == swapcontext(&swap_ctx_, &ctx_); #else coroutine_transfer_t transfer_data = swoole_jump_fcontext(ctx_, (void *) this); @@ -130,7 +130,7 @@ bool Context::swap_in() { } bool Context::swap_out() { -#if USE_UCONTEXT +#ifdef USE_UCONTEXT return 0 == swapcontext(&ctx_, &swap_ctx_); #else coroutine_transfer_t transfer_data = swoole_jump_fcontext(swap_ctx_, (void *) this); @@ -140,11 +140,11 @@ bool Context::swap_out() { } void Context::context_func( -#if USE_UCONTEXT +#if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT) void *arg) { auto *_this = (Context *) arg; #else - transfer_t arg) { + coroutine_transfer_t arg) { auto *_this = (Context *) arg.data; _this->swap_ctx_ = arg.fctx; #endif From 0a2671f2c1d0c9627349d9ec718ed6b1fac262c9 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 23 Apr 2024 19:41:44 +0800 Subject: [PATCH 090/103] Optimize thread context --- ext-src/php_swoole_coroutine.h | 3 +-- include/swoole_coroutine.h | 6 ------ include/swoole_coroutine_context.h | 8 ++++---- src/coroutine/context.cc | 4 +--- src/coroutine/thread_context.cc | 2 +- 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/ext-src/php_swoole_coroutine.h b/ext-src/php_swoole_coroutine.h index a0aa09bcec3..383aefef197 100644 --- a/ext-src/php_swoole_coroutine.h +++ b/ext-src/php_swoole_coroutine.h @@ -252,7 +252,7 @@ class PHPCoroutine { } static inline void init_main_context() { - main_context.co = Coroutine::init_main_coroutine(); + main_context.co = nullptr; #ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT main_context.fiber_context = EG(main_fiber_context); main_context.fiber_init_notified = true; @@ -261,7 +261,6 @@ class PHPCoroutine { } static inline void free_main_context() { - delete main_context.co; main_context = {}; } diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 05464ae75a6..ec23f0e0e69 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -163,12 +163,6 @@ class Coroutine { #endif } - static inline Coroutine *init_main_coroutine() { - Coroutine *co = new Coroutine(0, nullptr, nullptr); - co->state = STATE_RUNNING; - return co; - } - static void activate(); static void deactivate(); diff --git a/include/swoole_coroutine_context.h b/include/swoole_coroutine_context.h index 57ba161e844..0dffd6f326c 100644 --- a/include/swoole_coroutine_context.h +++ b/include/swoole_coroutine_context.h @@ -45,6 +45,10 @@ typedef fcontext_t coroutine_context_t; typedef transfer_t coroutine_transfer_t; #endif +#if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT) +typedef void * coroutine_transfer_t; +#endif + typedef std::function CoroutineFunc; namespace swoole { @@ -90,11 +94,7 @@ class Context { void *private_data_; bool end_; -#if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT) - static void context_func(void *arg); -#else static void context_func(coroutine_transfer_t arg); -#endif }; } // namespace coroutine diff --git a/src/coroutine/context.cc b/src/coroutine/context.cc index face44ededb..8a6c10a9143 100644 --- a/src/coroutine/context.cc +++ b/src/coroutine/context.cc @@ -139,12 +139,10 @@ bool Context::swap_out() { #endif } -void Context::context_func( +void Context::context_func(coroutine_transfer_t arg) { #if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT) - void *arg) { auto *_this = (Context *) arg; #else - coroutine_transfer_t arg) { auto *_this = (Context *) arg.data; _this->swap_ctx_ = arg.fctx; #endif diff --git a/src/coroutine/thread_context.cc b/src/coroutine/thread_context.cc index 8dc232249bf..b670d495719 100644 --- a/src/coroutine/thread_context.cc +++ b/src/coroutine/thread_context.cc @@ -88,7 +88,7 @@ bool Context::swap_out() { return true; } -void Context::context_func(void *arg) { +void Context::context_func(coroutine_transfer_t arg) { swoole_signal_block_all(); Context *_this = (Context *) arg; SwooleTG.reactor = g_reactor; From c2b4472866c2bd9c1c2aef42b2e8f88003c84704 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Tue, 23 Apr 2024 19:42:08 +0800 Subject: [PATCH 091/103] Fix tests[4] --- tests/include/config.php | 8 ++++++++ tests/swoole_runtime/stream_select/base.phpt | 4 ++-- tests/swoole_runtime/stream_select/blocked.phpt | 6 +++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/include/config.php b/tests/include/config.php index 5c7cb0c6149..d54a78d3848 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -147,6 +147,14 @@ ]); } +if (IS_IN_CI) { + define('TEST_DOMAIN_1', 'www.google.com'); + define('TEST_DOMAIN_2', 'www.yahoo.com'); +} else { + define('TEST_DOMAIN_1', 'www.baidu.com'); + define('TEST_DOMAIN_2', 'www.qq.com'); +} + /** =============== IP ================ */ define('IP_REGEX', '/^(?:[\d]{1,3}\.){3}[\d]{1,3}$/'); diff --git a/tests/swoole_runtime/stream_select/base.phpt b/tests/swoole_runtime/stream_select/base.phpt index 8e6c07d5ebe..731f7813d86 100644 --- a/tests/swoole_runtime/stream_select/base.phpt +++ b/tests/swoole_runtime/stream_select/base.phpt @@ -7,8 +7,8 @@ swoole_runtime/stream_select: base require __DIR__ . '/../../include/bootstrap.php'; Swoole\Runtime::enableCoroutine(); go(function () { - $fp1 = stream_socket_client("tcp://www.baidu.com:80", $errno, $errstr, 30); - $fp2 = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); + $fp1 = stream_socket_client("tcp://" . TEST_DOMAIN_1 . ":80", $errno, $errstr, 30); + $fp2 = stream_socket_client("tcp://" . TEST_DOMAIN_2 . ":80", $errno, $errstr, 30); if (!$fp1) { echo "$errstr ($errno)
\n"; } else { diff --git a/tests/swoole_runtime/stream_select/blocked.phpt b/tests/swoole_runtime/stream_select/blocked.phpt index a684299be8d..61bd607763b 100644 --- a/tests/swoole_runtime/stream_select/blocked.phpt +++ b/tests/swoole_runtime/stream_select/blocked.phpt @@ -6,12 +6,12 @@ swoole_runtime/stream_select: blocked \n"; } else { - fwrite($fp1, "GET / HTTP/1.0\r\nHost: www.baidu.com\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n"); + fwrite($fp1, "GET / HTTP/1.0\r\nHost: " . TEST_DOMAIN_1 . "\r\nUser-Agent: curl/7.58.0\r\nAccept: */*\r\n\r\n"); $r_array = [$fp1, $fp2]; $w_array = $e_array = null; $n = stream_select($r_array, $w_array, $e_array, 10); From cf0f1db3fa8b91060d8318527328ecb7d67e328d Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 24 Apr 2024 16:07:05 +0800 Subject: [PATCH 092/103] Refactor --- examples/thread/aio.php | 2 +- examples/thread/atomic.php | 4 +- examples/thread/lock.php | 2 +- ext-src/php_swoole.cc | 3 ++ ext-src/php_swoole_private.h | 1 + ext-src/stubs/php_swoole_thread.stub.php | 1 + ext-src/stubs/php_swoole_thread_arginfo.h | 4 +- ext-src/swoole_server.cc | 8 +--- ext-src/swoole_thread.cc | 55 +++++++++++++++++++++-- tests/include/lib/src/ThreadManager.php | 21 +++++++++ tests/include/skipif.inc | 6 --- tests/swoole_thread/async-io.phpt | 7 +-- tests/swoole_thread/info.phpt | 33 ++++++++++++++ tests/swoole_thread/lock.phpt | 16 ++++--- tests/swoole_thread/pipe.phpt | 8 ++-- tests/swoole_thread/queue.phpt | 8 ++-- tests/swoole_thread/signal.phpt | 8 ++-- 17 files changed, 142 insertions(+), 45 deletions(-) create mode 100644 tests/include/lib/src/ThreadManager.php create mode 100644 tests/swoole_thread/info.phpt diff --git a/examples/thread/aio.php b/examples/thread/aio.php index 38ad30fe5af..bdb8492ad8c 100644 --- a/examples/thread/aio.php +++ b/examples/thread/aio.php @@ -10,7 +10,7 @@ if (empty($args)) { $threads = []; - $atomic = new Swoole\Atomic(); + $atomic = new Swoole\Thread\Atomic(); for ($i = 0; $i < $c; $i++) { $threads[] = Thread::exec(__FILE__, $i, $atomic); } diff --git a/examples/thread/atomic.php b/examples/thread/atomic.php index f4a60e8a1a2..94d92ec0c21 100644 --- a/examples/thread/atomic.php +++ b/examples/thread/atomic.php @@ -1,8 +1,8 @@ is_worker_thread()) { - return; + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + RETURN_FALSE; } if (serv->is_started()) { php_swoole_fatal_error( diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 244ef90efb8..8a78a4a9541 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -23,6 +23,7 @@ #include #include +#include #include "swoole_lock.h" @@ -33,6 +34,12 @@ END_EXTERN_C() zend_class_entry *swoole_thread_ce; static zend_object_handlers swoole_thread_handlers; +static struct { + char *path_translated; + zend_string *argv_serialized; + int argc; +} request_info; + TSRMLS_CACHE_DEFINE(); typedef std::thread Thread; @@ -110,6 +117,7 @@ static PHP_METHOD(swoole_thread, detach); static PHP_METHOD(swoole_thread, exec); static PHP_METHOD(swoole_thread, getArguments); static PHP_METHOD(swoole_thread, getId); +static PHP_METHOD(swoole_thread, getTsrmInfo); SW_EXTERN_C_END // clang-format off @@ -121,6 +129,7 @@ static const zend_function_entry swoole_thread_methods[] = { PHP_ME(swoole_thread, exec, arginfo_class_Swoole_Thread_exec, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getArguments, arginfo_class_Swoole_Thread_getArguments, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(swoole_thread, getId, arginfo_class_Swoole_Thread_getId, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(swoole_thread, getTsrmInfo, arginfo_class_Swoole_Thread_getTsrmInfo, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END }; // clang-format on @@ -215,14 +224,37 @@ bool php_swoole_thread_unserialize(zend_string *data, zval *zv) { return unserialized; } +void php_swoole_thread_rinit() { + if (tsrm_is_main_thread()) { + if (SG(request_info).path_translated) { + request_info.path_translated = strdup(SG(request_info).path_translated); + } + // Return reference + zval *global_argv = zend_hash_find_ind(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV)); + request_info.argv_serialized = php_swoole_thread_serialize(global_argv); + request_info.argc = SG(request_info).argc; + } +} + void php_swoole_thread_rshutdown() { zval_dtor(&thread_argv); + if (tsrm_is_main_thread()) { + if (request_info.path_translated) { + free((void *) request_info.path_translated); + request_info.path_translated = nullptr; + } + if (request_info.argv_serialized) { + zend_string_release(request_info.argv_serialized); + request_info.argv_serialized = nullptr; + } + } } -void php_swoole_thread_start(zend_string *file, zend_string *argv) { +void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { ts_resource(0); TSRMLS_CACHE_UPDATE(); zend_file_handle file_handle{}; + zval global_argc, global_argv; PG(expose_php) = 0; PG(auto_globals_jit) = 1; @@ -243,15 +275,23 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv) { SG(sapi_started) = 0; SG(headers_sent) = 1; SG(request_info).no_headers = 1; + SG(request_info).path_translated = request_info.path_translated; + SG(request_info).argc = request_info.argc; zend_stream_init_filename(&file_handle, ZSTR_VAL(file)); file_handle.primary_script = 1; zend_first_try { - if (argv == nullptr || ZSTR_LEN(argv) == 0) { + if (argv_serialized == nullptr || ZSTR_LEN(argv_serialized) == 0) { array_init(&thread_argv); } else { - php_swoole_thread_unserialize(argv, &thread_argv); + php_swoole_thread_unserialize(argv_serialized, &thread_argv); + } + if (request_info.argv_serialized) { + php_swoole_thread_unserialize(request_info.argv_serialized, &global_argv); + ZVAL_LONG(&global_argc, request_info.argc); + zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV), &global_argv); + zend_hash_update(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGC), &global_argc); } php_execute_script(&file_handle); } @@ -263,7 +303,7 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv) { _startup_error: zend_string_release(file); - zend_string_release(argv); + zend_string_release(argv_serialized); ts_free_thread(); swoole_thread_clean(); } @@ -306,4 +346,11 @@ static PHP_METHOD(swoole_thread, exec) { swoole_thread_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("id"), to->thread->native_handle()); } +static PHP_METHOD(swoole_thread, getTsrmInfo) { + array_init(return_value); + add_assoc_bool(return_value, "is_main_thread", tsrm_is_main_thread()); + add_assoc_bool(return_value, "is_shutdown", tsrm_is_shutdown()); + add_assoc_string(return_value, "api_name", tsrm_api_name()); +} + #endif diff --git a/tests/include/lib/src/ThreadManager.php b/tests/include/lib/src/ThreadManager.php new file mode 100644 index 00000000000..ab1934db356 --- /dev/null +++ b/tests/include/lib/src/ThreadManager.php @@ -0,0 +1,21 @@ +parentFunc)(); + } else { + ($this->childFunc)(...$args); + exit(0); + } + } +} diff --git a/tests/include/skipif.inc b/tests/include/skipif.inc index e2ea43b210f..90eb0a62242 100644 --- a/tests/include/skipif.inc +++ b/tests/include/skipif.inc @@ -26,12 +26,6 @@ skip( (function () { global $argv; skip_if_php_version_lower_than('7.0'); - if (defined('SWOOLE_THREAD')) { - $targs = Swoole\Thread::getArguments(); - if ($targs) { - $argv = $targs[0]; - } - } if (!getenv('PHPT') && substr($argv[0], -4) === '.php') { skip('please read ' . dirname(dirname(__FILE__)) . '/README.md and try again'); } diff --git a/tests/swoole_thread/async-io.phpt b/tests/swoole_thread/async-io.phpt index 5af3ac329db..045b9376243 100644 --- a/tests/swoole_thread/async-io.phpt +++ b/tests/swoole_thread/async-io.phpt @@ -24,7 +24,7 @@ if (empty($args)) { $atomic = new Swoole\Thread\Atomic(); $atomicLong = new Swoole\Thread\Atomic\Long(); for ($i = 0; $i < C; $i++) { - $threads[] = Thread::exec(__FILE__, $argv, $i, $atomic, $atomicLong); + $threads[] = Thread::exec(__FILE__, $i, $atomic, $atomicLong); } for ($i = 0; $i < C; $i++) { $threads[$i]->join(); @@ -32,8 +32,9 @@ if (empty($args)) { Assert::eq($atomic->get(), C * N); Assert::eq($atomicLong->get(), C * N * M); } else { - $atomic = $args[2]; - $atomicLong = $args[3]; + $id = $args[0]; + $atomic = $args[1]; + $atomicLong = $args[2]; Co\run(function () use ($atomic, $atomicLong, $md5) { $n = N; while ($n--) { diff --git a/tests/swoole_thread/info.phpt b/tests/swoole_thread/info.phpt new file mode 100644 index 00000000000..c84d8a8c69e --- /dev/null +++ b/tests/swoole_thread/info.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_thread: info +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $thread = Thread::exec(__FILE__, 'child'); + $info = Thread::getTsrmInfo(); + Assert::true($info['is_main_thread']); + Assert::eq($info['api_name'], 'POSIX Threads'); + $thread->join(); +}; + +$tm->childFunc = function () { + $info = Thread::getTsrmInfo(); + Assert::false($info['is_main_thread']); + Assert::eq($info['api_name'], 'POSIX Threads'); +}; + +$tm->run(); +?> +--EXPECTF-- + diff --git a/tests/swoole_thread/lock.phpt b/tests/swoole_thread/lock.phpt index 7548de0c4db..179aa5d9283 100644 --- a/tests/swoole_thread/lock.phpt +++ b/tests/swoole_thread/lock.phpt @@ -12,23 +12,25 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Thread; use Swoole\Thread\Lock; -$args = Thread::getArguments(); +$tm = new \SwooleTest\ThreadManager(); -if (empty($args)) { - global $argv; +$tm->parentFunc = function () { $lock = new Lock; $lock->lock(); - $thread = Thread::exec(__FILE__, $argv, $lock); + $thread = Thread::exec(__FILE__, $lock); $lock->lock(); echo "main thread\n"; $thread->join(); -} else { +}; + +$tm->childFunc = function ($lock) { echo "child thread\n"; - $lock = $args[1]; usleep(200_000); $lock->unlock(); exit(0); -} +}; + +$tm->run(); ?> --EXPECTF-- child thread diff --git a/tests/swoole_thread/pipe.phpt b/tests/swoole_thread/pipe.phpt index 7b5dba931cd..8ba07a121fb 100644 --- a/tests/swoole_thread/pipe.phpt +++ b/tests/swoole_thread/pipe.phpt @@ -16,17 +16,15 @@ $args = Thread::getArguments(); if (empty($args)) { $rdata = random_bytes(random_int(1024, 2048)); Co\run(function () use ($rdata) { - global $argv; $sockets = swoole_coroutine_socketpair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); - $thread = Thread::exec(__FILE__, $argv, $sockets, $rdata); + $thread = Thread::exec(__FILE__, $sockets, $rdata); Assert::eq($sockets[0]->recv(8192), $rdata); $thread->join(); echo "DONE\n"; }); } else { - $argv = $args[0]; - $sockets = $args[1]; - $rdata = $args[2]; + $sockets = $args[0]; + $rdata = $args[1]; // Child threads are not allowed to modify hook flags Assert::false(Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL)); Co\run(function () use ($sockets, $rdata, $argv) { diff --git a/tests/swoole_thread/queue.phpt b/tests/swoole_thread/queue.phpt index d2fe2f7e002..a33c1da8eec 100644 --- a/tests/swoole_thread/queue.phpt +++ b/tests/swoole_thread/queue.phpt @@ -24,7 +24,7 @@ if (empty($args)) { $queue = new Queue; $map = new Thread\Map(); for ($i = 0; $i < C; $i++) { - $threads[] = Thread::exec(__FILE__, $argv, $i, $queue, $map); + $threads[] = Thread::exec(__FILE__, $i, $queue, $map); } $n = N; while ($n--) { @@ -44,9 +44,9 @@ if (empty($args)) { Assert::eq($queue->count(), 0); Assert::eq($total_parent, $total_child); } else { - $i = $args[1]; - $queue = $args[2]; - $map = $args[3]; + $i = $args[0]; + $queue = $args[1]; + $map = $args[2]; $map[$i] = 0; while (1) { $job = $queue->pop(-1); diff --git a/tests/swoole_thread/signal.phpt b/tests/swoole_thread/signal.phpt index 876609b80f4..8a68b60080c 100644 --- a/tests/swoole_thread/signal.phpt +++ b/tests/swoole_thread/signal.phpt @@ -18,9 +18,8 @@ $args = Thread::getArguments(); if (empty($args)) { Co\run(function () { - global $argv; $sockets = swoole_coroutine_socketpair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); - $thread = Thread::exec(__FILE__, $argv, $sockets); + $thread = Thread::exec(__FILE__, $sockets); $parent_pipe = $sockets[1]; Timer::after(500, function () { echo "timer\n"; @@ -39,10 +38,9 @@ if (empty($args)) { }); }); } else { - $argv = $args[0]; - $sockets = $args[1]; + $sockets = $args[0]; $child_pipe = $sockets[0]; - Co\run(function () use ($child_pipe, $argv) { + Co\run(function () use ($child_pipe) { // 收到父线程的指令,开始退出 echo $child_pipe->recv(8192), PHP_EOL; // 通知父线程已退出 From 92afad3bba5d5784aca94d065f80a9f057c3d23d Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 25 Apr 2024 12:59:50 +0800 Subject: [PATCH 093/103] Refactor --- ext-src/swoole_runtime.cc | 6 +- ext-src/swoole_server.cc | 25 +--- ext-src/swoole_thread.cc | 6 +- src/server/master.cc | 174 ++++++++++++++---------- src/server/reactor_thread.cc | 13 +- src/server/thread.cc | 8 ++ tests/include/lib/src/ThreadManager.php | 1 - 7 files changed, 133 insertions(+), 100 deletions(-) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 76f4a855b70..e0bb4bda177 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -237,7 +237,10 @@ void php_swoole_runtime_rinit() { void php_swoole_runtime_rshutdown() { #ifdef SW_THREAD - PHPCoroutine::disable_hook(); + if (tsrm_is_main_thread()) { + PHPCoroutine::disable_hook(); + ori_func_handlers.clear(); + } #endif void *ptr; @@ -259,7 +262,6 @@ void php_swoole_runtime_rshutdown() { efree(tmp_function_table); tmp_function_table = nullptr; - ori_func_handlers.clear(); clear_class_entries(); } diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index ce2334a49bb..1c3f7ff9f38 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -99,7 +99,7 @@ void php_swoole_server_rshutdown() { Server *serv = sw_server(); serv->drain_worker_pipe(); - if (serv->is_started() && !serv->is_user_worker() && !serv->is_worker_thread()) { + if (serv->is_started() && serv->is_running() && !serv->is_user_worker()) { if (php_swoole_is_fatal_error()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, @@ -2617,7 +2617,7 @@ static PHP_METHOD(swoole_server, start) { #ifdef SW_THREAD if (serv->is_worker_thread()) { worker_thread_fn(); - return; + RETURN_TRUE; } #endif @@ -3865,29 +3865,12 @@ static PHP_METHOD(swoole_server, getManagerPid) { static PHP_METHOD(swoole_server, getMasterPid) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - RETURN_LONG(serv->gs->master_pid); + RETURN_LONG(serv->get_master_pid()); } static PHP_METHOD(swoole_server, shutdown) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->is_started())) { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - pid_t pid; - if (serv->is_base_mode()) { - pid = serv->get_manager_pid() == 0 ? serv->get_master_pid() : serv->get_manager_pid(); - } else { - pid = serv->get_master_pid(); - } - - if (swoole_kill(pid, SIGTERM) < 0) { - php_swoole_sys_error(E_WARNING, "failed to shutdown, kill(%d, SIGTERM) failed", pid); - RETURN_FALSE; - } else { - RETURN_TRUE; - } + RETURN_BOOL(serv->shutdown()); } static PHP_METHOD(swoole_server, stop) { diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 8a78a4a9541..7d96dd2689e 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -231,8 +231,10 @@ void php_swoole_thread_rinit() { } // Return reference zval *global_argv = zend_hash_find_ind(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV)); - request_info.argv_serialized = php_swoole_thread_serialize(global_argv); - request_info.argc = SG(request_info).argc; + if (global_argv) { + request_info.argv_serialized = php_swoole_thread_serialize(global_argv); + request_info.argc = SG(request_info).argc; + } } } diff --git a/src/server/master.cc b/src/server/master.cc index 21a2029f2da..4996c6a5f86 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -868,24 +868,94 @@ void Server::clear_timer() { } bool Server::shutdown() { + if (sw_unlikely(!is_started())) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + return false; + } + + pid_t pid; + if (is_base_mode()) { + pid = get_manager_pid() == 0 ? get_master_pid() : get_manager_pid(); + } else if (is_thread_mode()) { + return factory->shutdown(); + } else { + pid = get_master_pid(); + } + + if (swoole_kill(pid, SIGTERM) < 0) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "failed to shutdown, kill(%d, SIGTERM) failed", pid); + return false; + } + + return true; +} + +bool Server::signal_handler_reload(bool reload_all_workers) { + reload(reload_all_workers); + sw_logger()->reopen(); + return true; +} + +bool Server::signal_handler_read_message() { + gs->event_workers.read_message = true; + return true; +} + +bool Server::signal_handler_reopen_logger() { + uint32_t i; + Worker *worker; + for (i = 0; i < worker_num + task_worker_num + get_user_worker_num(); i++) { + worker = get_worker(i); + swoole_kill(worker->pid, SIGRTMIN); + } + if (is_process_mode()) { + swoole_kill(gs->manager_pid, SIGRTMIN); + } + sw_logger()->reopen(); + return true; +} + +void Server::stop_master_thread() { + Reactor *reactor = SwooleTG.reactor; + reactor->set_wait_exit(true); + for (auto port : ports) { + if (port->is_dgram() and is_process_mode()) { + continue; + } + reactor->del(port->socket); + } + if (pipe_command) { + reactor->del(pipe_command->get_socket(true)); + } + clear_timer(); + if (max_wait_time > 0) { + time_t shutdown_time = std::time(nullptr); + auto fn = [shutdown_time, this](Reactor *reactor, size_t &) { + time_t now = std::time(nullptr); + if (now - shutdown_time > max_wait_time) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, + "graceful shutdown failed, forced termination"); + reactor->running = false; + } + return true; + }; + reactor->set_exit_condition(Reactor::EXIT_CONDITION_FORCED_TERMINATION, fn); + } +} + +bool Server::signal_handler_shutdown() { swoole_trace_log(SW_TRACE_SERVER, "shutdown begin"); if (is_base_mode()) { if (gs->manager_pid > 0) { - if (getpid() == gs->manager_pid) { - running = false; - return true; - } else { - return swoole_kill(gs->manager_pid, SIGTERM) == 0; - } + running = false; } else { + // single process worker, exit directly gs->event_workers.running = 0; stop_async_worker(sw_worker()); - return true; } - } - - if (getpid() != gs->master_pid) { - return swoole_kill(gs->master_pid, SIGTERM) == 0; + return true; } if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN)) { swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, this); @@ -894,40 +964,28 @@ bool Server::shutdown() { onBeforeShutdown(this); } running = false; - // stop all thread - if (SwooleTG.reactor) { - Reactor *reactor = SwooleTG.reactor; - reactor->set_wait_exit(true); - for (auto port : ports) { - if (port->is_dgram() and is_process_mode()) { - continue; - } - reactor->del(port->socket); - } - if (pipe_command) { - reactor->del(pipe_command->get_socket(true)); - } - clear_timer(); - if (max_wait_time > 0) { - time_t shutdown_time = std::time(nullptr); - auto fn = [shutdown_time, this](Reactor *reactor, size_t &) { - time_t now = std::time(nullptr); - if (now - shutdown_time > max_wait_time) { - swoole_error_log(SW_LOG_WARNING, - SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, - "graceful shutdown failed, forced termination"); - reactor->running = false; - } - return true; - }; - reactor->set_exit_condition(Reactor::EXIT_CONDITION_FORCED_TERMINATION, fn); - } - } - + stop_master_thread(); swoole_trace_log(SW_TRACE_SERVER, "shutdown end"); return true; } +bool Server::signal_handler_child_exit() { + if (!running) { + return false; + } + if (is_base_mode()) { + return false; + } + int status; + pid_t pid = waitpid(-1, &status, WNOHANG); + if (pid > 0 && pid == gs->manager_pid) { + swoole_warning("Fatal Error: manager process exit. status=%d, signal=[%s]", + WEXITSTATUS(status), + swoole_signal_to_str(WTERMSIG(status))); + } + return true; +} + void Server::destroy() { swoole_trace_log(SW_TRACE_SERVER, "release service"); if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_SERVER_SHUTDOWN)) { @@ -1782,51 +1840,27 @@ static void Server_signal_handler(int sig) { return; } - int status; - pid_t pid; switch (sig) { case SIGTERM: - serv->shutdown(); + serv->signal_handler_shutdown(); break; case SIGCHLD: - if (!serv->running) { - break; - } - if (sw_server()->is_base_mode()) { - break; - } - pid = waitpid(-1, &status, WNOHANG); - if (pid > 0 && pid == serv->gs->manager_pid) { - swoole_warning("Fatal Error: manager process exit. status=%d, signal=[%s]", - WEXITSTATUS(status), - swoole_signal_to_str(WTERMSIG(status))); - } + serv->signal_handler_child_exit(); break; case SIGVTALRM: swoole_warning("SIGVTALRM coming"); break; case SIGUSR1: case SIGUSR2: - serv->reload(sig == SIGUSR1); - sw_logger()->reopen(); + serv->signal_handler_reload(sig == SIGUSR1); break; case SIGIO: - serv->gs->event_workers.read_message = true; + serv->signal_handler_read_message(); break; default: - #ifdef SIGRTMIN if (sig == SIGRTMIN) { - uint32_t i; - Worker *worker; - for (i = 0; i < serv->worker_num + serv->task_worker_num + serv->get_user_worker_num(); i++) { - worker = serv->get_worker(i); - swoole_kill(worker->pid, SIGRTMIN); - } - if (serv->is_process_mode()) { - swoole_kill(serv->gs->manager_pid, SIGRTMIN); - } - sw_logger()->reopen(); + serv->signal_handler_reopen_logger(); } #endif break; diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 42fba7a6843..0bc0c33d5af 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -32,7 +32,6 @@ static int ReactorThread_onRead(Reactor *reactor, Event *ev); static int ReactorThread_onWrite(Reactor *reactor, Event *ev); static int ReactorThread_onPacketReceived(Reactor *reactor, Event *event); static int ReactorThread_onClose(Reactor *reactor, Event *event); -static void ReactorThread_shutdown(Reactor *reactor); static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode); #ifdef SW_USE_OPENSSL @@ -302,7 +301,7 @@ static int ReactorThread_onClose(Reactor *reactor, Event *event) { } } -static void ReactorThread_shutdown(Reactor *reactor) { +void ReactorThread::shutdown(Reactor *reactor) { Server *serv = (Server *) reactor->ptr; // stop listen UDP Port if (serv->have_dgram_sock == 1) { @@ -316,6 +315,10 @@ static void ReactorThread_shutdown(Reactor *reactor) { } } + if (serv->is_thread_mode()) { + reactor->del(serv->get_worker(reactor->id)->pipe_worker); + } + serv->foreach_connection([serv, reactor](Connection *conn) { if (conn->fd % serv->reactor_num != reactor->id) { return; @@ -326,6 +329,8 @@ static void ReactorThread_shutdown(Reactor *reactor) { }); reactor->set_wait_exit(true); + + message_bus.free_buffer(); } /** @@ -352,9 +357,9 @@ static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) { } else if (resp->info.type == SW_SERVER_EVENT_COMMAND_RESPONSE) { auto packet = thread->message_bus.get_packet(); serv->call_command_callback(resp->info.fd, std::string(packet.data, packet.length)); - return SW_OK; } else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) { - ReactorThread_shutdown(reactor); + thread->shutdown(reactor); + return SW_OK; } else if (resp->info.type == SW_SERVER_EVENT_FINISH) { serv->onFinish(serv, (EventData *) resp); } else if (resp->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { diff --git a/src/server/thread.cc b/src/server/thread.cc index 9566cfa60a5..342c865b962 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -56,6 +56,14 @@ bool ThreadFactory::start() { } bool ThreadFactory::shutdown() { + server_->running = false; + SW_LOOP_N(server_->reactor_num) { + auto thread = server_->get_thread(i); + DataHead ev = {}; + ev.type = SW_SERVER_EVENT_SHUTDOWN; + thread->notify_pipe->send_blocking((void *) &ev, sizeof(ev)); + } + server_->stop_master_thread(); return true; } diff --git a/tests/include/lib/src/ThreadManager.php b/tests/include/lib/src/ThreadManager.php index ab1934db356..9fbf19771b6 100644 --- a/tests/include/lib/src/ThreadManager.php +++ b/tests/include/lib/src/ThreadManager.php @@ -15,7 +15,6 @@ function run($redirectStdout = false): void ($this->parentFunc)(); } else { ($this->childFunc)(...$args); - exit(0); } } } From 44b3d0f1c5ebe02df1857eb55b2c881b0bb954db Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 25 Apr 2024 13:00:10 +0800 Subject: [PATCH 094/103] Refactor --- include/swoole_server.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/swoole_server.h b/include/swoole_server.h index d9ccf586a3c..d62e8111b62 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -159,6 +159,7 @@ struct ReactorThread { MessageBus message_bus; int init(Server *serv, Reactor *reactor, uint16_t reactor_id); + void shutdown(Reactor *reactor); }; struct ServerPortGS { @@ -1125,6 +1126,7 @@ class Server { } void stop_async_worker(Worker *worker); + void stop_master_thread(); Pipe *get_pipe_object(int pipe_fd) { return (Pipe *) connection_list[pipe_fd].object; @@ -1150,6 +1152,10 @@ class Server { return factory != nullptr; } + bool is_running() { + return running; + } + bool is_master() { return swoole_get_process_type() == SW_PROCESS_MASTER; } @@ -1174,6 +1180,10 @@ class Server { return is_thread_mode() && swoole_get_thread_type() == Server::THREAD_WORKER; } + bool is_worker_process() { + return !is_thread_mode() && (is_worker() || is_task_worker()); + } + bool is_reactor_thread() { return swoole_get_thread_type() == Server::THREAD_REACTOR; } @@ -1421,6 +1431,15 @@ class Server { void worker_signal_init(void); std::function worker_thread_start; + /** + * [Master] + */ + bool signal_handler_shutdown(); + bool signal_handler_child_exit(); + bool signal_handler_reload(bool reload_all_workers); + bool signal_handler_read_message(); + bool signal_handler_reopen_logger(); + static int worker_main_loop(ProcessPool *pool, Worker *worker); static void worker_signal_handler(int signo); static void reactor_thread_main_loop(Server *serv, int reactor_id); From 9fd4829239a7def02a9bd6c4f8111e2436b91d4b Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 25 Apr 2024 17:43:50 +0800 Subject: [PATCH 095/103] fix server shutdown --- ext-src/swoole_server.cc | 33 ++++++----- include/swoole_server.h | 8 ++- src/server/master.cc | 30 ++++++++-- src/server/reactor_thread.cc | 82 +++++++++++++++------------- src/server/thread.cc | 15 ++--- tests/swoole_thread/server/base.phpt | 79 +++++++++++++++++++++++++++ 6 files changed, 179 insertions(+), 68 deletions(-) create mode 100644 tests/swoole_thread/server/base.phpt diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 1c3f7ff9f38..62b75c7c38a 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2641,31 +2641,25 @@ static PHP_METHOD(swoole_server, start) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); #ifdef SW_THREAD + zend_string *bootstrap = nullptr; + zend_string *thread_argv_serialized = nullptr; + zval thread_argv = {}; + if (serv->is_thread_mode()) { zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); - zend_string *bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); - zend_string *argv = nullptr; - zval thread_argv = {}; + bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); if (!ZVAL_IS_NULL(&server_object->init_arguments)) { call_user_function(NULL, NULL, &server_object->init_arguments, &thread_argv, 0, NULL); - argv = php_swoole_thread_serialize(&thread_argv); + thread_argv_serialized = php_swoole_thread_serialize(&thread_argv); } - serv->worker_thread_start = [bootstrap, argv](const WorkerFn &fn) { + serv->worker_thread_start = [bootstrap, thread_argv_serialized](const WorkerFn &fn) { worker_thread_fn = fn; zend_string *bootstrap_copy = zend_string_dup(bootstrap, 1); - zend_string *argv_copy = argv ? zend_string_dup(argv, 1) : nullptr; + zend_string *argv_copy = thread_argv_serialized ? zend_string_dup(thread_argv_serialized, 1) : nullptr; php_swoole_thread_start(bootstrap_copy, argv_copy); }; - - ON_SCOPE_EXIT { - zend_string_release(bootstrap); - if (argv) { - zend_string_release(argv); - } - zval_ptr_dtor(&thread_argv); - }; } #endif @@ -2675,6 +2669,17 @@ static PHP_METHOD(swoole_server, start) { if (serv->start() < 0) { php_swoole_fatal_error(E_ERROR, "failed to start server. Error: %s", sw_error); } + +#ifdef SW_THREAD + if (bootstrap) { + zend_string_release(bootstrap); + } + if (thread_argv_serialized) { + zend_string_release(thread_argv_serialized); + } + zval_ptr_dtor(&thread_argv); +#endif + RETURN_TRUE; } diff --git a/include/swoole_server.h b/include/swoole_server.h index d62e8111b62..bb00a664f09 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -160,6 +160,8 @@ struct ReactorThread { int init(Server *serv, Reactor *reactor, uint16_t reactor_id); void shutdown(Reactor *reactor); + int close_connection(Reactor *reactor, SessionId session_id); + void clean(); }; struct ServerPortGS { @@ -1075,6 +1077,10 @@ class Server { } } + bool is_master_thread() { + return swoole_get_thread_type() == Server::THREAD_MASTER; + } + bool is_hash_dispatch_mode() { return dispatch_mode == DISPATCH_FDMOD || dispatch_mode == DISPATCH_IPMOD || dispatch_mode == DISPATCH_CO_CONN_LB; @@ -1311,7 +1317,7 @@ class Server { void call_hook(enum HookType type, void *arg); void call_worker_start_callback(Worker *worker); - ResultCode call_command_handler(MessageBus &mb, uint16_t worker_id, network::Socket *sock); + void call_command_handler(MessageBus &mb, uint16_t worker_id, network::Socket *sock); std::string call_command_handler_in_master(int command_id, const std::string &msg); void call_command_callback(int64_t request_id, const std::string &result); void foreach_connection(const std::function &callback); diff --git a/src/server/master.cc b/src/server/master.cc index 4996c6a5f86..e41d8fa20d1 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -93,13 +93,13 @@ void Server::call_command_callback(int64_t request_id, const std::string &result iter->second(this, result); } -ResultCode Server::call_command_handler(MessageBus &mb, uint16_t worker_id, Socket *sock) { +void Server::call_command_handler(MessageBus &mb, uint16_t worker_id, Socket *sock) { PipeBuffer *buffer = mb.get_buffer(); int command_id = buffer->info.server_fd; auto iter = command_handlers.find(command_id); if (iter == command_handlers.end()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_COMMAND, "Unknown command[command_id=%d]", command_id); - return SW_OK; + return; } Server::Command::Handler handler = iter->second; @@ -114,7 +114,7 @@ ResultCode Server::call_command_handler(MessageBus &mb, uint16_t worker_id, Sock task.info.len = result.length(); task.data = result.c_str(); - return mb.write(sock, &task) ? SW_OK : SW_ERR; + mb.write(sock, &task); } std::string Server::call_command_handler_in_master(int command_id, const std::string &msg) { @@ -877,7 +877,12 @@ bool Server::shutdown() { if (is_base_mode()) { pid = get_manager_pid() == 0 ? get_master_pid() : get_manager_pid(); } else if (is_thread_mode()) { - return factory->shutdown(); + if (is_master_thread()) { + stop_master_thread(); + } else { + running = false; + } + return true; } else { pid = get_master_pid(); } @@ -943,6 +948,14 @@ void Server::stop_master_thread() { }; reactor->set_exit_condition(Reactor::EXIT_CONDITION_FORCED_TERMINATION, fn); } + if (is_thread_mode()) { + SW_LOOP_N(reactor_num) { + auto thread = get_thread(i); + DataHead ev = {}; + ev.type = SW_SERVER_EVENT_SHUTDOWN; + thread->notify_pipe->send_blocking((void *) &ev, sizeof(ev)); + } + } } bool Server::signal_handler_shutdown() { @@ -1004,7 +1017,7 @@ void Server::destroy() { if (task_worker_num > 0) { gs->task_workers.destroy(); } - } else { + } else if (is_process_mode()) { swoole_trace_log(SW_TRACE_SERVER, "terminate reactor threads"); /** * Wait until all the end of the thread @@ -1646,6 +1659,11 @@ void Server::timer_callback(Timer *timer, TimerNode *tnode) { if (serv->hooks[Server::HOOK_MASTER_TIMER]) { serv->call_hook(Server::HOOK_MASTER_TIMER, serv); } + + if (!serv->is_running()) { + sw_reactor()->running = false; + serv->stop_master_thread(); + } } int Server::add_worker(Worker *worker) { @@ -1664,7 +1682,7 @@ bool Server::add_command(const std::string &name, int accepted_process_types, co if (commands.find(name) != commands.end()) { return false; } - if (is_process_mode() && pipe_command == nullptr) { + if (!is_base_mode() && pipe_command == nullptr) { auto _pipe = new UnixSocket(false, SOCK_DGRAM); if (!_pipe->ready()) { delete _pipe; diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 0bc0c33d5af..881f4ee2bdc 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -329,8 +329,39 @@ void ReactorThread::shutdown(Reactor *reactor) { }); reactor->set_wait_exit(true); +} - message_bus.free_buffer(); +int ReactorThread::close_connection(Reactor *reactor, SessionId session_id) { + Server *serv = (Server *) reactor->ptr; + Connection *conn = serv->get_connection_verify_no_ssl(session_id); + if (!conn) { + swoole_error_log(SW_LOG_TRACE, + SW_ERROR_SESSION_NOT_EXIST, + "force close connection failed, session#%ld does not exist", + session_id); + return SW_OK; + } + + if (serv->disable_notify || conn->close_force) { + return Server::close_connection(reactor, conn->socket); + } + +#ifdef SW_USE_OPENSSL + /** + * SSL connections that have not completed the handshake, + * do not need to notify the workers, just close + */ + if (conn->ssl && !conn->ssl_ready) { + return Server::close_connection(reactor, conn->socket); + } +#endif + conn->close_force = 1; + Event _ev = {}; + _ev.fd = conn->fd; + _ev.socket = conn->socket; + reactor->trigger_close_event(&_ev); + + return SW_OK; } /** @@ -350,49 +381,21 @@ static int ReactorThread_onPipeRead(Reactor *reactor, Event *ev) { if (resp->info.type == SW_SERVER_EVENT_INCOMING) { Connection *conn = serv->get_connection_verify_no_ssl(resp->info.fd); if (conn && serv->connection_incoming(reactor, conn) < 0) { - return reactor->close(reactor, conn->socket); + reactor->close(reactor, conn->socket); } } else if (resp->info.type == SW_SERVER_EVENT_COMMAND_REQUEST) { - return serv->call_command_handler(thread->message_bus, thread->id, thread->pipe_command); + serv->call_command_handler(thread->message_bus, thread->id, thread->pipe_command); } else if (resp->info.type == SW_SERVER_EVENT_COMMAND_RESPONSE) { auto packet = thread->message_bus.get_packet(); serv->call_command_callback(resp->info.fd, std::string(packet.data, packet.length)); } else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) { thread->shutdown(reactor); - return SW_OK; } else if (resp->info.type == SW_SERVER_EVENT_FINISH) { serv->onFinish(serv, (EventData *) resp); } else if (resp->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { serv->onPipeMessage(serv, (EventData *) resp); } else if (resp->info.type == SW_SERVER_EVENT_CLOSE_FORCE) { - SessionId session_id = resp->info.fd; - Connection *conn = serv->get_connection_verify_no_ssl(session_id); - if (!conn) { - swoole_error_log(SW_LOG_TRACE, - SW_ERROR_SESSION_NOT_EXIST, - "force close connection failed, session#%ld does not exist", - session_id); - return SW_OK; - } - - if (serv->disable_notify || conn->close_force) { - return Server::close_connection(reactor, conn->socket); - } - -#ifdef SW_USE_OPENSSL - /** - * SSL connections that have not completed the handshake, - * do not need to notify the workers, just close - */ - if (conn->ssl && !conn->ssl_ready) { - return Server::close_connection(reactor, conn->socket); - } -#endif - conn->close_force = 1; - Event _ev = {}; - _ev.fd = conn->fd; - _ev.socket = conn->socket; - reactor->trigger_close_event(&_ev); + thread->close_connection(reactor, resp->info.fd); } else { PacketPtr packet = thread->message_bus.get_packet(); _send.info = resp->info; @@ -793,6 +796,15 @@ int ReactorThread::init(Server *serv, Reactor *reactor, uint16_t reactor_id) { return SW_OK; } +void ReactorThread::clean() { + sw_free(pipe_sockets); + if (pipe_command) { + pipe_command->fd = -1; + delete pipe_command; + } + message_bus.free_buffer(); +} + void Server::reactor_thread_main_loop(Server *serv, int reactor_id) { SwooleTG.id = reactor_id; SwooleTG.type = Server::THREAD_REACTOR; @@ -824,11 +836,7 @@ void Server::reactor_thread_main_loop(Server *serv, int reactor_id) { if (serv->is_thread_mode()) { serv->worker_stop_callback(serv->get_worker(reactor_id)); } - sw_free(thread->pipe_sockets); - if (thread->pipe_command) { - thread->pipe_command->fd = -1; - delete thread->pipe_command; - } + thread->clean(); } static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode) { diff --git a/src/server/thread.cc b/src/server/thread.cc index 342c865b962..46fa1d57001 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -56,21 +56,16 @@ bool ThreadFactory::start() { } bool ThreadFactory::shutdown() { - server_->running = false; - SW_LOOP_N(server_->reactor_num) { - auto thread = server_->get_thread(i); - DataHead ev = {}; - ev.type = SW_SERVER_EVENT_SHUTDOWN; - thread->notify_pipe->send_blocking((void *) &ev, sizeof(ev)); + for (auto &thread : threads_) { + if (thread.joinable()) { + thread.join(); + } } - server_->stop_master_thread(); return true; } ThreadFactory::~ThreadFactory() { - for (auto &thread : threads_) { - thread.join(); - } + } void ThreadFactory::at_thread_exit(Worker *worker) { diff --git a/tests/swoole_thread/server/base.phpt b/tests/swoole_thread/server/base.phpt new file mode 100644 index 00000000000..9de0b22c4fc --- /dev/null +++ b/tests/swoole_thread/server/base.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_thread/server: base +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$tm->parentFunc = function () use ($tm) { + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + $thread = Thread::exec(__FILE__, $queue, $atomic); + echo $queue->pop(-1); + Co\run(function () use ($tm) { + $cli = new Co\Client(SWOOLE_SOCK_TCP); + $cli->set([ + 'open_eof_check' => true, + 'package_eof' => "\r\n", + ]); + Assert::assert($cli->connect('127.0.0.1', $tm->getFreePort(), 2)); + $cli->send(json_encode(['type' => 'eof']) . "\r\n"); + Assert::eq($cli->recv(), "EOF\r\n"); + }); + $atomic->set(0); + echo "done\n"; + echo $queue->pop(-1); +}; + +$tm->childFunc = function ($queue, $atomic) use ($tm) { + $serv = new Swoole\Server('127.0.0.1', $tm->getFreePort(), SWOOLE_THREAD); + $serv->set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'open_eof_check' => true, + 'package_eof' => "\r\n", + 'init_arguments' => function () use ($queue, $atomic) { + return [$queue, $atomic]; + } + )); + $serv->on("WorkerStart", function (Swoole\Server $serv, $workerId) use ($queue, $atomic) { + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + \Swoole\Timer::tick(200, function ($timerId) use ($atomic, $serv) { + if ($atomic->get() == 0) { + $serv->shutdown(); + \Swoole\Timer::clear($timerId); + } + }); + } + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $json = json_decode(rtrim($data)); + if ($json->type == 'eof') { + $serv->send($fd, "EOF\r\n"); + } + }); + $serv->on('shutdown', function () use ($queue, $atomic) { + $queue->push("shutdown\n", Thread\Queue::NOTIFY_ALL); + }); + $serv->start(); +}; + +$tm->run(); +?> +--EXPECT-- +begin +done +shutdown From 04c421edf8444c9157f95c7e50da5535ca978612 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 25 Apr 2024 18:47:54 +0800 Subject: [PATCH 096/103] fix thread atomic --- ext-src/swoole_thread_atomic.cc | 2 + tests/swoole_thread/atomic_ctor.phpt | 45 ++++++++ .../server/send_large_packet.phpt | 101 ++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 tests/swoole_thread/atomic_ctor.phpt create mode 100644 tests/swoole_thread/server/send_large_packet.phpt diff --git a/ext-src/swoole_thread_atomic.cc b/ext-src/swoole_thread_atomic.cc index 3a0605d352f..b9dc88088c4 100644 --- a/ext-src/swoole_thread_atomic.cc +++ b/ext-src/swoole_thread_atomic.cc @@ -196,6 +196,7 @@ PHP_METHOD(swoole_thread_atomic, __construct) { o->res = new AtomicResource(); auto resource_id = php_swoole_thread_resource_insert(o->res); zend_update_property_long(swoole_thread_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); + o->res->value = value; } PHP_METHOD(swoole_thread_atomic, add) { @@ -300,6 +301,7 @@ PHP_METHOD(swoole_thread_atomic_long, __construct) { o->res = new AtomicLongResource(); auto resource_id = php_swoole_thread_resource_insert(o->res); zend_update_property_long(swoole_thread_atomic_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), resource_id); + o->res->value = value; } PHP_METHOD(swoole_thread_atomic_long, add) { diff --git a/tests/swoole_thread/atomic_ctor.phpt b/tests/swoole_thread/atomic_ctor.phpt new file mode 100644 index 00000000000..99063beaf87 --- /dev/null +++ b/tests/swoole_thread/atomic_ctor.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_thread: atomic ctor +--SKIPIF-- + +--FILE-- +parentFunc = function () { + $lock = new Lock; + $lock->lock(); + $num1 = random_int(1, 1 << 31); + $num2 = random_int(1 << 31, PHP_INT_MAX); + $atomic1 = new Swoole\Thread\Atomic($num1); + $atomic2 = new Swoole\Thread\Atomic\Long($num2); + $thread = Thread::exec(__FILE__, $lock, $atomic1, $atomic2, $num1, $num2); + $lock->lock(); + echo "main thread\n"; + $thread->join(); +}; + +$tm->childFunc = function ($lock, $atomic1, $atomic2, $num1, $num2) { + echo "child thread\n"; + usleep(200_000); + $lock->unlock(); + Assert::eq($atomic1->get(), $num1); + Assert::eq($atomic2->get(), $num2); + exit(0); +}; + +$tm->run(); +?> +--EXPECTF-- +child thread +main thread + diff --git a/tests/swoole_thread/server/send_large_packet.phpt b/tests/swoole_thread/server/send_large_packet.phpt new file mode 100644 index 00000000000..889ade98496 --- /dev/null +++ b/tests/swoole_thread/server/send_large_packet.phpt @@ -0,0 +1,101 @@ +--TEST-- +swoole_thread/server: send large packet +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$tm->parentFunc = function () use ($tm) { + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + $thread = Thread::exec(__FILE__, $queue, $atomic); + echo $queue->pop(-1); + + $c = MAX_CONCURRENCY_LOW; + $n = MAX_REQUESTS_LOW; + + for ($i = 0; $i < $c; $i++) { + go(function () use ($tm, $i, $n, $atomic) { + $cli = new Co\Client(SWOOLE_SOCK_TCP); + $cli->set([ + 'open_length_check' => true, + 'package_max_length' => 4 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + ]); + if ($cli->connect('127.0.0.1', $tm->getFreePort(), 2) == false) { + echo "ERROR\n"; + return; + } + for ($i = 0; $i < $n; $i++) { + $sid = strval(rand(10000000, 99999999)); + $send_data = str_repeat('A', 1000) . $sid; + $cli->send(pack('N', strlen($send_data)) . $send_data); + $data = $cli->recv(); + Assert::same(strlen($data), SIZE); + Assert::same($sid, substr($data, -8, 8)); + } + }); + } + Swoole\Event::wait(); + $atomic->set(0); + echo "done\n"; + echo $queue->pop(-1); +}; + +$tm->childFunc = function ($queue, $atomic) use ($tm) { + $serv = new Swoole\Server('127.0.0.1', $tm->getFreePort(), SWOOLE_THREAD); + $serv->set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'open_length_check' => true, + 'package_max_length' => 4 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + 'init_arguments' => function () use ($queue, $atomic) { + return [$queue, $atomic]; + } + )); + $serv->on("WorkerStart", function (Swoole\Server $serv, $workerId) use ($queue, $atomic) { + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + \Swoole\Timer::tick(200, function ($timerId) use ($atomic, $serv) { + if ($atomic->get() == 0) { + $serv->shutdown(); + \Swoole\Timer::clear($timerId); + } + }); + } + }); + $serv->on("WorkerStop", function (Swoole\Server $serv, $workerId) use ($queue, $atomic) { + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) use ($queue, $atomic) { + $send_data = str_repeat('A', SIZE - 12) . substr($data, -8, 8); + $serv->send($fd, pack('N', strlen($send_data)) . $send_data); + }); + $serv->on('shutdown', function () use ($queue, $atomic) { + $queue->push("shutdown\n", Thread\Queue::NOTIFY_ALL); + }); + $serv->start(); +}; + +$tm->run(); +?> +--EXPECT-- +begin +done +shutdown From bd288293330b5ef749b38776b9fccc3fe4776830 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Thu, 25 Apr 2024 19:50:04 +0800 Subject: [PATCH 097/103] fix core tests --- CMakeLists.txt | 2 +- core-tests/CMakeLists.txt | 8 ++++---- core-tests/src/os/async.cpp | 6 ++++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 08009e56302..31243767f25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ PROJECT(libswoole) +cmake_minimum_required(VERSION 2.8.12) ENABLE_LANGUAGE(ASM) set(SWOOLE_VERSION 6.0.0-dev) @@ -6,7 +7,6 @@ set(SWOOLE_VERSION 6.0.0-dev) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -cmake_minimum_required(VERSION 2.8) file(READ ./config.h SWOOLE_CONFIG_FILE) diff --git a/core-tests/CMakeLists.txt b/core-tests/CMakeLists.txt index a9d66ebf37c..8f28da5bb47 100755 --- a/core-tests/CMakeLists.txt +++ b/core-tests/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.1) +cmake_minimum_required(VERSION 2.8.12) project(core_tests) @@ -10,14 +10,14 @@ file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cpp deps/llhttp/src/*.c) add_definitions(-DHAVE_CONFIG_H) -set(core_tests_includes ./include/ ../thirdparty ../thirdparty/hiredis ./deps/llhttp/include) +set(core_tests_includes ./include/ ../thirdparty ../thirdparty/hiredis ./deps/llhttp/include /usr/local/include) set(core_tests_libraries) set(core_tests_link_directories /usr/local/lib) -list(APPEND core_tests_libraries pthread) +list(APPEND core_tests_libraries pthread gtest gtest_main) # find GTest -find_package(GTest) +find_package(GTest REQUIRED) if (!${GTEST_FOUND}) message(FATAL_ERROR "Not found GTest") endif() diff --git a/core-tests/src/os/async.cpp b/core-tests/src/os/async.cpp index b34f5ef0052..f1163e7df1e 100644 --- a/core-tests/src/os/async.cpp +++ b/core-tests/src/os/async.cpp @@ -82,6 +82,12 @@ TEST(async, schedule) { ASSERT_GT(SwooleTG.async_threads->get_queue_size(), 100); ASSERT_GT(SwooleTG.async_threads->get_task_num(), 100); break; + } else if (count == N - 1) { + ASSERT_EQ(SwooleTG.async_threads->get_worker_num(), 4); + ASSERT_EQ(SwooleTG.async_threads->get_queue_size(), 1); + ASSERT_EQ(SwooleTG.async_threads->get_task_num(), 1); + } else if (count < N / 2) { + ASSERT_GT(SwooleTG.async_threads->get_worker_num(), 4); } } }); From 0932447fdfed1c9e941f3010ea0c2824aac91be5 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 6 May 2024 13:31:46 +0800 Subject: [PATCH 098/103] Fix Sqlite pdo segmentfault (#5311) * fix pdo * fix sqlite error * fix sqlite error * fix sqlite error --- config.m4 | 4 +- .../pdo_sqlite_createaggregate.phpt | 1 - thirdparty/php81/pdo_sqlite/sqlite_driver.c | 1362 ++++++++--------- .../php81/pdo_sqlite/sqlite_statement.c | 656 ++++---- .../php83/pdo_sqlite/php_pdo_sqlite_int.h | 80 + thirdparty/php83/pdo_sqlite/sqlite_driver.c | 802 ++++++++++ .../php83/pdo_sqlite/sqlite_driver.stub.php | 18 + .../php83/pdo_sqlite/sqlite_driver_arginfo.h | 34 + .../php83/pdo_sqlite/sqlite_statement.c | 381 +++++ 9 files changed, 2296 insertions(+), 1042 deletions(-) create mode 100644 thirdparty/php83/pdo_sqlite/php_pdo_sqlite_int.h create mode 100644 thirdparty/php83/pdo_sqlite/sqlite_driver.c create mode 100644 thirdparty/php83/pdo_sqlite/sqlite_driver.stub.php create mode 100644 thirdparty/php83/pdo_sqlite/sqlite_driver_arginfo.h create mode 100644 thirdparty/php83/pdo_sqlite/sqlite_statement.c diff --git a/config.m4 b/config.m4 index 25172389285..69e9511c9c0 100644 --- a/config.m4 +++ b/config.m4 @@ -1075,7 +1075,9 @@ EOF thirdparty/php80/pdo_sqlite/sqlite_driver.c \ thirdparty/php80/pdo_sqlite/sqlite_statement.c \ thirdparty/php81/pdo_sqlite/sqlite_driver.c \ - thirdparty/php81/pdo_sqlite/sqlite_statement.c" + thirdparty/php81/pdo_sqlite/sqlite_statement.c \ + thirdparty/php83/pdo_sqlite/sqlite_driver.c \ + thirdparty/php83/pdo_sqlite/sqlite_statement.c" fi SW_ASM_DIR="thirdparty/boost/asm/" diff --git a/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt index c1220e88bbd..cc0b145e6f8 100644 --- a/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt +++ b/tests/swoole_pdo_sqlite/pdo_sqlite_createaggregate.phpt @@ -22,7 +22,6 @@ run(function() { $db->sqliteCreateAggregate('testing', function(&$a, $b) { $a .= $b; return $a; }, function(&$v) { return $v; }); - foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { var_dump($row); } diff --git a/thirdparty/php81/pdo_sqlite/sqlite_driver.c b/thirdparty/php81/pdo_sqlite/sqlite_driver.c index 61a74de28bf..61f6948a202 100644 --- a/thirdparty/php81/pdo_sqlite/sqlite_driver.c +++ b/thirdparty/php81/pdo_sqlite/sqlite_driver.c @@ -16,7 +16,7 @@ #define SW_USE_SQLITE_HOOK #include "php_swoole_sqlite.h" -#if PHP_VERSION_ID >= 80100 +#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" @@ -26,544 +26,510 @@ int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */ { - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; - pdo_sqlite_error_info *einfo = &H->einfo; - - einfo->errcode = sqlite3_errcode(H->db); - einfo->file = file; - einfo->line = line; - - if (einfo->errcode != SQLITE_OK) { - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - } - einfo->errmsg = pestrdup((char*)sqlite3_errmsg(H->db), dbh->is_persistent); - } else { /* no error */ - strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); - return 0; - } - switch (einfo->errcode) { - case SQLITE_NOTFOUND: - strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); - break; - - case SQLITE_INTERRUPT: - strncpy(*pdo_err, "01002", sizeof(*pdo_err)); - break; - - case SQLITE_NOLFS: - strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); - break; - - case SQLITE_TOOBIG: - strncpy(*pdo_err, "22001", sizeof(*pdo_err)); - break; - - case SQLITE_CONSTRAINT: - strncpy(*pdo_err, "23000", sizeof(*pdo_err)); - break; - - case SQLITE_ERROR: - default: - strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); - break; - } - - if (!dbh->methods) { - pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); - } - - return einfo->errcode; + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_sqlite_error_info *einfo = &H->einfo; + + einfo->errcode = sqlite3_errcode(H->db); + einfo->file = file; + einfo->line = line; + + if (einfo->errcode != SQLITE_OK) { + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + einfo->errmsg = pestrdup((char *) sqlite3_errmsg(H->db), dbh->is_persistent); + } else { /* no error */ + strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); + return 0; + } + switch (einfo->errcode) { + case SQLITE_NOTFOUND: + strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); + break; + + case SQLITE_INTERRUPT: + strncpy(*pdo_err, "01002", sizeof(*pdo_err)); + break; + + case SQLITE_NOLFS: + strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); + break; + + case SQLITE_TOOBIG: + strncpy(*pdo_err, "22001", sizeof(*pdo_err)); + break; + + case SQLITE_CONSTRAINT: + strncpy(*pdo_err, "23000", sizeof(*pdo_err)); + break; + + case SQLITE_ERROR: + default: + strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); + break; + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return einfo->errcode; } /* }}} */ -static void pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_sqlite_error_info *einfo = &H->einfo; +static void pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_sqlite_error_info *einfo = &H->einfo; - if (einfo->errcode) { - add_next_index_long(info, einfo->errcode); - add_next_index_string(info, einfo->errmsg); - } + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } } -static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) -{ - struct pdo_sqlite_func *func; - - while (H->funcs) { - func = H->funcs; - H->funcs = func->next; - - if (H->db) { - /* delete the function from the handle */ - sqlite3_create_function(H->db, - func->funcname, - func->argc, - SQLITE_UTF8, - func, - NULL, NULL, NULL); - } - - efree((char*)func->funcname); - if (!Z_ISUNDEF(func->func)) { - zval_ptr_dtor(&func->func); - } - if (!Z_ISUNDEF(func->step)) { - zval_ptr_dtor(&func->step); - } - if (!Z_ISUNDEF(func->fini)) { - zval_ptr_dtor(&func->fini); - } - efree(func); - } - - while (H->collations) { - struct pdo_sqlite_collation *collation; - collation = H->collations; - H->collations = collation->next; - - if (H->db) { - /* delete the collation from the handle */ - sqlite3_create_collation(H->db, - collation->name, - SQLITE_UTF8, - collation, - NULL); - } - - efree((char*)collation->name); - if (!Z_ISUNDEF(collation->callback)) { - zval_ptr_dtor(&collation->callback); - } - efree(collation); - } +static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) { + struct pdo_sqlite_func *func; + + while (H->funcs) { + func = H->funcs; + H->funcs = func->next; + + if (H->db) { + /* delete the function from the handle */ + sqlite3_create_function(H->db, func->funcname, func->argc, SQLITE_UTF8, func, NULL, NULL, NULL); + } + + efree((char *) func->funcname); + if (!Z_ISUNDEF(func->func)) { + zval_ptr_dtor(&func->func); + } + if (!Z_ISUNDEF(func->step)) { + zval_ptr_dtor(&func->step); + } + if (!Z_ISUNDEF(func->fini)) { + zval_ptr_dtor(&func->fini); + } + efree(func); + } + + while (H->collations) { + struct pdo_sqlite_collation *collation; + collation = H->collations; + H->collations = collation->next; + + if (H->db) { + /* delete the collation from the handle */ + sqlite3_create_collation(H->db, collation->name, SQLITE_UTF8, collation, NULL); + } + + efree((char *) collation->name); + if (!Z_ISUNDEF(collation->callback)) { + zval_ptr_dtor(&collation->callback); + } + efree(collation); + } } static void sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */ { - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; - if (H) { - pdo_sqlite_error_info *einfo = &H->einfo; + if (H) { + pdo_sqlite_error_info *einfo = &H->einfo; - pdo_sqlite_cleanup_callbacks(H); - if (H->db) { + pdo_sqlite_cleanup_callbacks(H); + if (H->db) { #ifdef HAVE_SW_SQLITE3_CLOSE_V2 - sqlite3_close_v2(H->db); + sqlite3_close_v2(H->db); #else - sqlite3_close(H->db); + sqlite3_close(H->db); #endif - H->db = NULL; - } - if (einfo->errmsg) { - pefree(einfo->errmsg, dbh->is_persistent); - einfo->errmsg = NULL; - } - pefree(H, dbh->is_persistent); - dbh->driver_data = NULL; - } + H->db = NULL; + } + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } } /* }}} */ -static bool sqlite_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); - int i; - const char *tail; - - S->H = H; - stmt->driver_data = S; - stmt->methods = &swoole_sqlite_stmt_methods; - stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL|PDO_PLACEHOLDER_NAMED; - - if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { - H->einfo.errcode = SQLITE_ERROR; - pdo_sqlite_error(dbh); - return false; - } - - i = sqlite3_prepare_v2(H->db, ZSTR_VAL(sql), ZSTR_LEN(sql), &S->stmt, &tail); - if (i == SQLITE_OK) { - return true; - } - - pdo_sqlite_error(dbh); - - return false; +static bool sqlite_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); + int i; + const char *tail; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_sqlite_stmt_methods; + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL | PDO_PLACEHOLDER_NAMED; + + if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { + H->einfo.errcode = SQLITE_ERROR; + pdo_sqlite_error(dbh); + return false; + } + + i = sqlite3_prepare_v2(H->db, ZSTR_VAL(sql), ZSTR_LEN(sql), &S->stmt, &tail); + if (i == SQLITE_OK) { + return true; + } + + pdo_sqlite_error(dbh); + + return false; } -static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, ZSTR_VAL(sql), NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - - return -1; - } else { - return sqlite3_changes(H->db); - } +static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, ZSTR_VAL(sql), NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) sqlite3_free(errmsg); + + return -1; + } else { + return sqlite3_changes(H->db); + } } -static zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; +static zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; - return zend_i64_to_str(sqlite3_last_insert_rowid(H->db)); + return zend_i64_to_str(sqlite3_last_insert_rowid(H->db)); } /* NB: doesn't handle binary strings... use prepared stmts for that */ -static zend_string* sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) -{ - char *quoted; - if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) { - return NULL; - } - quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); - /* TODO use %Q format? */ - sqlite3_snprintf(2*ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted)); - zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0); - efree(quoted); - return quoted_str; +static zend_string *sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) { + char *quoted; + if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) { + return NULL; + } + quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); + /* TODO use %Q format? */ + sqlite3_snprintf(2 * ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted)); + zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0); + efree(quoted); + return quoted_str; } -static bool sqlite_handle_begin(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return false; - } - return true; +static bool sqlite_handle_begin(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) sqlite3_free(errmsg); + return false; + } + return true; } -static bool sqlite_handle_commit(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return false; - } - return true; +static bool sqlite_handle_commit(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) sqlite3_free(errmsg); + return false; + } + return true; } -static bool sqlite_handle_rollback(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - char *errmsg = NULL; - - if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, &errmsg) != SQLITE_OK) { - pdo_sqlite_error(dbh); - if (errmsg) - sqlite3_free(errmsg); - return false; - } - return true; +static bool sqlite_handle_rollback(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + char *errmsg = NULL; + + if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, &errmsg) != SQLITE_OK) { + pdo_sqlite_error(dbh); + if (errmsg) sqlite3_free(errmsg); + return false; + } + return true; } -static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) -{ - switch (attr) { - case PDO_ATTR_CLIENT_VERSION: - case PDO_ATTR_SERVER_VERSION: - ZVAL_STRING(return_value, (char *)sqlite3_libversion()); - break; +static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) { + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + case PDO_ATTR_SERVER_VERSION: + ZVAL_STRING(return_value, (char *) sqlite3_libversion()); + break; - default: - return 0; - } + default: + return 0; + } - return 1; + return 1; } -static bool pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - zend_long lval; - - switch (attr) { - case PDO_ATTR_TIMEOUT: - if (!pdo_get_long_param(&lval, val)) { - return false; - } - sqlite3_busy_timeout(H->db, lval * 1000); - return true; - case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: - if (!pdo_get_long_param(&lval, val)) { - return false; - } - sqlite3_extended_result_codes(H->db, lval); - return true; - } - return false; +static bool pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + zend_long lval; + + switch (attr) { + case PDO_ATTR_TIMEOUT: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_busy_timeout(H->db, lval * 1000); + return true; + case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_extended_result_codes(H->db, lval); + return true; + } + return false; } typedef struct { - zval val; - zend_long row; + zval val; + zend_long row; } aggregate_context; -static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, - int argc, sqlite3_value **argv, sqlite3_context *context, - int is_agg) -{ - zval *zargs = NULL; - zval retval; - int i; - int ret; - int fake_argc; - aggregate_context *agg_context = NULL; - - if (is_agg) { - is_agg = 2; - } - - fake_argc = argc + is_agg; - - fc->fci.size = sizeof(fc->fci); - ZVAL_COPY_VALUE(&fc->fci.function_name, cb); - fc->fci.object = NULL; - fc->fci.retval = &retval; - fc->fci.param_count = fake_argc; - - /* build up the params */ - - if (fake_argc) { - zargs = safe_emalloc(fake_argc, sizeof(zval), 0); - } - - if (is_agg) { - agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); - if (!agg_context) { - efree(zargs); - return FAILURE; - } - if (Z_ISUNDEF(agg_context->val)) { - ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); - } - ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); - ZVAL_LONG(&zargs[1], ++agg_context->row); - } - - for (i = 0; i < argc; i++) { - /* get the value */ - switch (sqlite3_value_type(argv[i])) { - case SQLITE_INTEGER: - ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); - break; - - case SQLITE_FLOAT: - ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); - break; - - case SQLITE_NULL: - ZVAL_NULL(&zargs[i + is_agg]); - break; - - case SQLITE_BLOB: - case SQLITE3_TEXT: - default: - ZVAL_STRINGL(&zargs[i + is_agg], (char*)sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); - break; - } - } - - fc->fci.params = zargs; - - if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } - - /* clean up the params */ - if (zargs) { - for (i = is_agg; i < fake_argc; i++) { - zval_ptr_dtor(&zargs[i]); - } - if (is_agg) { - zval_ptr_dtor(&zargs[1]); - } - efree(zargs); - } - - if (!is_agg || !argv) { - /* only set the sqlite return value if we are a scalar function, - * or if we are finalizing an aggregate */ - if (!Z_ISUNDEF(retval)) { - switch (Z_TYPE(retval)) { - case IS_LONG: - sqlite3_result_int(context, Z_LVAL(retval)); - break; - - case IS_NULL: - sqlite3_result_null(context); - break; - - case IS_DOUBLE: - sqlite3_result_double(context, Z_DVAL(retval)); - break; - - default: - if (!try_convert_to_string(&retval)) { - ret = FAILURE; - break; - } - sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); - break; - } - } else { - sqlite3_result_error(context, "failed to invoke callback", 0); - } - - if (agg_context) { - zval_ptr_dtor(&agg_context->val); - } - } else { - /* we're stepping in an aggregate; the return value goes into - * the context */ - if (agg_context) { - if (Z_ISUNDEF(retval)) { - zval_ptr_dtor(&agg_context->val); - return FAILURE; - } - zval_ptr_dtor(Z_REFVAL(agg_context->val)); - ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); - ZVAL_UNDEF(&retval); - } - } - - if (!Z_ISUNDEF(retval)) { - zval_ptr_dtor(&retval); - } - - return ret; +static int do_callback( + struct pdo_sqlite_fci *fc, zval *cb, int argc, sqlite3_value **argv, sqlite3_context *context, int is_agg) { + zval *zargs = NULL; + zval retval; + int i; + int ret; + int fake_argc; + aggregate_context *agg_context = NULL; + + if (is_agg) { + is_agg = 2; + } + + fake_argc = argc + is_agg; + + fc->fci.size = sizeof(fc->fci); + ZVAL_COPY_VALUE(&fc->fci.function_name, cb); + fc->fci.object = NULL; + fc->fci.retval = &retval; + fc->fci.param_count = fake_argc; + + /* build up the params */ + + if (fake_argc) { + zargs = safe_emalloc(fake_argc, sizeof(zval), 0); + } + + if (is_agg) { + agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); + if (!agg_context) { + efree(zargs); + return FAILURE; + } + if (Z_ISUNDEF(agg_context->val)) { + ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); + } + ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); + ZVAL_LONG(&zargs[1], ++agg_context->row); + } + + for (i = 0; i < argc; i++) { + /* get the value */ + switch (sqlite3_value_type(argv[i])) { + case SQLITE_INTEGER: + ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); + break; + + case SQLITE_FLOAT: + ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); + break; + + case SQLITE_NULL: + ZVAL_NULL(&zargs[i + is_agg]); + break; + + case SQLITE_BLOB: + case SQLITE3_TEXT: + default: + ZVAL_STRINGL(&zargs[i + is_agg], (char *) sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); + break; + } + } + + fc->fci.params = zargs; + + if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } + + /* clean up the params */ + if (zargs) { + for (i = is_agg; i < fake_argc; i++) { + zval_ptr_dtor(&zargs[i]); + } + if (is_agg) { + zval_ptr_dtor(&zargs[1]); + } + efree(zargs); + } + + if (!is_agg || !argv) { + /* only set the sqlite return value if we are a scalar function, + * or if we are finalizing an aggregate */ + if (!Z_ISUNDEF(retval)) { + switch (Z_TYPE(retval)) { + case IS_LONG: + sqlite3_result_int(context, Z_LVAL(retval)); + break; + + case IS_NULL: + sqlite3_result_null(context); + break; + + case IS_DOUBLE: + sqlite3_result_double(context, Z_DVAL(retval)); + break; + + default: + if (!try_convert_to_string(&retval)) { + ret = FAILURE; + break; + } + sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); + break; + } + } else { + sqlite3_result_error(context, "failed to invoke callback", 0); + } + + if (agg_context) { + zval_ptr_dtor(&agg_context->val); + } + } else { + /* we're stepping in an aggregate; the return value goes into + * the context */ + if (agg_context) { + if (Z_ISUNDEF(retval)) { + zval_ptr_dtor(&agg_context->val); + return FAILURE; + } + zval_ptr_dtor(Z_REFVAL(agg_context->val)); + ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); + ZVAL_UNDEF(&retval); + } + } + + if (!Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + + return ret; } -static void php_sqlite3_func_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); +static void php_sqlite3_func_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); - do_callback(&func->afunc, &func->func, argc, argv, context, 0); + do_callback(&func->afunc, &func->func, argc, argv, context, 0); } -static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, - sqlite3_value **argv) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); +static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); - do_callback(&func->astep, &func->step, argc, argv, context, 1); + do_callback(&func->astep, &func->step, argc, argv, context, 1); } -static void php_sqlite3_func_final_callback(sqlite3_context *context) -{ - struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); +static void php_sqlite3_func_final_callback(sqlite3_context *context) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); - do_callback(&func->afini, &func->fini, 0, NULL, context, 1); + do_callback(&func->afini, &func->fini, 0, NULL, context, 1); } -static int php_sqlite3_collation_callback(void *context, - int string1_len, const void *string1, - int string2_len, const void *string2) -{ - int ret; - zval zargs[2]; - zval retval; - struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; - - collation->fc.fci.size = sizeof(collation->fc.fci); - ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); - collation->fc.fci.object = NULL; - collation->fc.fci.retval = &retval; - - // Prepare the arguments. - ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); - ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); - collation->fc.fci.param_count = 2; - collation->fc.fci.params = zargs; - - if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { - php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); - } else if (!Z_ISUNDEF(retval)) { - if (Z_TYPE(retval) != IS_LONG) { - convert_to_long(&retval); - } - ret = 0; - if (Z_LVAL(retval) > 0) { - ret = 1; - } else if (Z_LVAL(retval) < 0) { - ret = -1; - } - zval_ptr_dtor(&retval); - } - - zval_ptr_dtor(&zargs[0]); - zval_ptr_dtor(&zargs[1]); - - return ret; +static int php_sqlite3_collation_callback( + void *context, int string1_len, const void *string1, int string2_len, const void *string2) { + int ret; + zval zargs[2]; + zval retval; + struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation *) context; + + collation->fc.fci.size = sizeof(collation->fc.fci); + ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); + collation->fc.fci.object = NULL; + collation->fc.fci.retval = &retval; + + // Prepare the arguments. + ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); + ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); + collation->fc.fci.param_count = 2; + collation->fc.fci.params = zargs; + + if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } else if (!Z_ISUNDEF(retval)) { + if (Z_TYPE(retval) != IS_LONG) { + convert_to_long(&retval); + } + ret = 0; + if (Z_LVAL(retval) > 0) { + ret = 1; + } else if (Z_LVAL(retval) < 0) { + ret = -1; + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + + return ret; } /* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags]) Registers a UDF with the sqlite db handle */ -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) -{ - struct pdo_sqlite_func *func; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - char *func_name; - size_t func_name_len; - zend_long argc = -1; - zend_long flags = 0; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - ZEND_PARSE_PARAMETERS_START(2, 4) - Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(fci, fcc) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(argc) - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - - ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, - func, php_sqlite3_func_callback, NULL, NULL); - if (ret == SQLITE_OK) { - func->funcname = estrdup(func_name); - - ZVAL_COPY(&func->func, &fci.function_name); - - func->argc = argc; - - func->next = H->funcs; - H->funcs = func; - - RETURN_TRUE; - } - - efree(func); - RETURN_FALSE; +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) { + struct pdo_sqlite_func *func; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + zend_long flags = 0; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(fci, fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + func = (struct pdo_sqlite_func *) ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function( + H->db, func_name, argc, flags | SQLITE_UTF8, func, php_sqlite3_func_callback, NULL, NULL); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->func, &fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; } /* }}} */ @@ -586,274 +552,266 @@ PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) aggregate UDF. */ -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) -{ - struct pdo_sqlite_func *func; - zend_fcall_info step_fci, fini_fci; - zend_fcall_info_cache step_fcc, fini_fcc; - char *func_name; - size_t func_name_len; - zend_long argc = -1; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; - - ZEND_PARSE_PARAMETERS_START(3, 4) - Z_PARAM_STRING(func_name, func_name_len) - Z_PARAM_FUNC(step_fci, step_fcc) - Z_PARAM_FUNC(fini_fci, fini_fcc) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(argc) - ZEND_PARSE_PARAMETERS_END(); - - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; - - H = (pdo_sqlite_db_handle *)dbh->driver_data; - - func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); - - ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, - func, NULL, php_sqlite3_func_step_callback, php_sqlite3_func_final_callback); - if (ret == SQLITE_OK) { - func->funcname = estrdup(func_name); - - ZVAL_COPY(&func->step, &step_fci.function_name); - - ZVAL_COPY(&func->fini, &fini_fci.function_name); - - func->argc = argc; - - func->next = H->funcs; - H->funcs = func; - - RETURN_TRUE; - } - - efree(func); - RETURN_FALSE; +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) { + struct pdo_sqlite_func *func; + zend_fcall_info step_fci, fini_fci; + zend_fcall_info_cache step_fcc, fini_fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(step_fci, step_fcc) + Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + func = (struct pdo_sqlite_func *) ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, + func_name, + argc, + SQLITE_UTF8, + func, + NULL, + php_sqlite3_func_step_callback, + php_sqlite3_func_final_callback); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->step, &step_fci.function_name); + + ZVAL_COPY(&func->fini, &fini_fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; } /* }}} */ /* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback) Registers a collation with the sqlite db handle */ -PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) -{ - struct pdo_sqlite_collation *collation; - zend_fcall_info fci; - zend_fcall_info_cache fcc; - char *collation_name; - size_t collation_name_len; - pdo_dbh_t *dbh; - pdo_sqlite_db_handle *H; - int ret; +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) { + struct pdo_sqlite_collation *collation; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *collation_name; + size_t collation_name_len; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(collation_name, collation_name_len) - Z_PARAM_FUNC(fci, fcc) - ZEND_PARSE_PARAMETERS_END(); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); - dbh = Z_PDO_DBH_P(ZEND_THIS); - PDO_CONSTRUCT_CHECK; + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; - H = (pdo_sqlite_db_handle *)dbh->driver_data; + H = (pdo_sqlite_db_handle *) dbh->driver_data; - collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); + collation = (struct pdo_sqlite_collation *) ecalloc(1, sizeof(*collation)); - ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); - if (ret == SQLITE_OK) { - collation->name = estrdup(collation_name); + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); + if (ret == SQLITE_OK) { + collation->name = estrdup(collation_name); - ZVAL_COPY(&collation->callback, &fci.function_name); + ZVAL_COPY(&collation->callback, &fci.function_name); - collation->next = H->collations; - H->collations = collation; + collation->next = H->collations; + H->collations = collation; - RETURN_TRUE; - } + RETURN_TRUE; + } - efree(collation); - RETURN_FALSE; + efree(collation); + RETURN_FALSE; } /* }}} */ -static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) -{ - switch (kind) { - case PDO_DBH_DRIVER_METHOD_KIND_DBH: - return class_PDO_SQLite_Ext_methods; +static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) { + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_SQLite_Ext_methods; - default: - return NULL; - } + default: + return NULL; + } } -static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) -{ - pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *)dbh->driver_data; - /* unregister functions, so that they don't linger for the next - * request */ - if (H) { - pdo_sqlite_cleanup_callbacks(H); - } +static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + /* unregister functions, so that they don't linger for the next + * request */ + if (H) { + pdo_sqlite_cleanup_callbacks(H); + } } -static void pdo_sqlite_get_gc(pdo_dbh_t *dbh, zend_get_gc_buffer *gc_buffer) -{ - pdo_sqlite_db_handle *H = dbh->driver_data; - - struct pdo_sqlite_func *func = H->funcs; - while (func) { - zend_get_gc_buffer_add_zval(gc_buffer, &func->func); - zend_get_gc_buffer_add_zval(gc_buffer, &func->step); - zend_get_gc_buffer_add_zval(gc_buffer, &func->fini); - func = func->next; - } - - struct pdo_sqlite_collation *collation = H->collations; - while (collation) { - zend_get_gc_buffer_add_zval(gc_buffer, &collation->callback); - collation = collation->next; - } +static void pdo_sqlite_get_gc(pdo_dbh_t *dbh, zend_get_gc_buffer *gc_buffer) { + pdo_sqlite_db_handle *H = dbh->driver_data; + + struct pdo_sqlite_func *func = H->funcs; + while (func) { + zend_get_gc_buffer_add_zval(gc_buffer, &func->func); + zend_get_gc_buffer_add_zval(gc_buffer, &func->step); + zend_get_gc_buffer_add_zval(gc_buffer, &func->fini); + func = func->next; + } + + struct pdo_sqlite_collation *collation = H->collations; + while (collation) { + zend_get_gc_buffer_add_zval(gc_buffer, &collation->callback); + collation = collation->next; + } } -static const struct pdo_dbh_methods sqlite_methods = { - sqlite_handle_closer, - sqlite_handle_preparer, - sqlite_handle_doer, - sqlite_handle_quoter, - sqlite_handle_begin, - sqlite_handle_commit, - sqlite_handle_rollback, - pdo_sqlite_set_attr, - pdo_sqlite_last_insert_id, - pdo_sqlite_fetch_error_func, - pdo_sqlite_get_attribute, - NULL, /* check_liveness: not needed */ - get_driver_methods, - pdo_sqlite_request_shutdown, - NULL, /* in transaction, use PDO's internal tracking mechanism */ - pdo_sqlite_get_gc -}; - -static char *make_filename_safe(const char *filename) -{ - if (!filename) { - return NULL; - } - if (*filename && strncasecmp(filename, "file:", 5) == 0) { - if (PG(open_basedir) && *PG(open_basedir)) { - return NULL; - } - return estrdup(filename); - } - if (*filename && memcmp(filename, ":memory:", sizeof(":memory:"))) { - char *fullpath = expand_filepath(filename, NULL); - - if (!fullpath) { - return NULL; - } - - if (php_check_open_basedir(fullpath)) { - efree(fullpath); - return NULL; - } - return fullpath; - } - return estrdup(filename); +static const struct pdo_dbh_methods sqlite_methods = {sqlite_handle_closer, + sqlite_handle_preparer, + sqlite_handle_doer, + sqlite_handle_quoter, + sqlite_handle_begin, + sqlite_handle_commit, + sqlite_handle_rollback, + pdo_sqlite_set_attr, + pdo_sqlite_last_insert_id, + pdo_sqlite_fetch_error_func, + pdo_sqlite_get_attribute, + NULL, /* check_liveness: not needed */ + get_driver_methods, + pdo_sqlite_request_shutdown, + NULL, /* in transaction, use PDO's internal tracking mechanism */ + pdo_sqlite_get_gc}; + +static char *make_filename_safe(const char *filename) { + if (!filename) { + return NULL; + } + if (*filename && strncasecmp(filename, "file:", 5) == 0) { + if (PG(open_basedir) && *PG(open_basedir)) { + return NULL; + } + return estrdup(filename); + } + if (*filename && memcmp(filename, ":memory:", sizeof(":memory:"))) { + char *fullpath = expand_filepath(filename, NULL); + + if (!fullpath) { + return NULL; + } + + if (php_check_open_basedir(fullpath)) { + efree(fullpath); + return NULL; + } + return fullpath; + } + return estrdup(filename); } -static int authorizer(void *autharg, int access_type, const char *arg3, const char *arg4, - const char *arg5, const char *arg6) -{ - char *filename; - switch (access_type) { - case SQLITE_COPY: { - filename = make_filename_safe(arg4); - if (!filename) { - return SQLITE_DENY; - } - efree(filename); - return SQLITE_OK; - } - - case SQLITE_ATTACH: { - filename = make_filename_safe(arg3); - if (!filename) { - return SQLITE_DENY; - } - efree(filename); - return SQLITE_OK; - } - - default: - /* access allowed */ - return SQLITE_OK; - } +static int authorizer( + void *autharg, int access_type, const char *arg3, const char *arg4, const char *arg5, const char *arg6) { + char *filename; + switch (access_type) { + case SQLITE_COPY: { + filename = make_filename_safe(arg4); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + case SQLITE_ATTACH: { + filename = make_filename_safe(arg3); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + default: + /* access allowed */ + return SQLITE_OK; + } } static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ { - pdo_sqlite_db_handle *H; - int i, ret = 0; - zend_long timeout = 60, flags; - char *filename; + pdo_sqlite_db_handle *H; + int i, ret = 0; + zend_long timeout = 60, flags; + char *filename; - H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); + H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); - H->einfo.errcode = 0; - H->einfo.errmsg = NULL; - dbh->driver_data = H; + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + dbh->driver_data = H; - /* skip all but this one param event */ - dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); + /* skip all but this one param event */ + dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); - filename = make_filename_safe(dbh->data_source); + filename = make_filename_safe(dbh->data_source); - if (!filename) { - zend_throw_exception_ex(php_pdo_get_exception(), 0, - "open_basedir prohibits opening %s", - dbh->data_source); - goto cleanup; - } + if (!filename) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "open_basedir prohibits opening %s", dbh->data_source); + goto cleanup; + } - flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); - if (!(PG(open_basedir) && *PG(open_basedir))) { - flags |= SQLITE_OPEN_URI; - } - i = sqlite3_open_v2(filename, &H->db, flags, NULL); + if (!(PG(open_basedir) && *PG(open_basedir))) { + flags |= SQLITE_OPEN_URI; + } + i = sqlite3_open_v2(filename, &H->db, flags, NULL); - efree(filename); + efree(filename); - if (i != SQLITE_OK) { - pdo_sqlite_error(dbh); - goto cleanup; - } + if (i != SQLITE_OK) { + pdo_sqlite_error(dbh); + goto cleanup; + } - if (PG(open_basedir) && *PG(open_basedir)) { - sqlite3_set_authorizer(H->db, authorizer, NULL); - } + if (PG(open_basedir) && *PG(open_basedir)) { + sqlite3_set_authorizer(H->db, authorizer, NULL); + } - if (driver_options) { - timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); - } - sqlite3_busy_timeout(H->db, timeout * 1000); + if (driver_options) { + timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); + } + sqlite3_busy_timeout(H->db, timeout * 1000); - dbh->alloc_own_columns = 1; - dbh->max_escaped_char_length = 2; + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; - ret = 1; + ret = 1; cleanup: - dbh->methods = &sqlite_methods; + dbh->methods = &sqlite_methods; - return ret; + return ret; } /* }}} */ -const pdo_driver_t swoole_pdo_sqlite_driver = { - PDO_DRIVER_HEADER(sqlite), - pdo_sqlite_handle_factory -}; +const pdo_driver_t swoole_pdo_sqlite_driver = {PDO_DRIVER_HEADER(sqlite), pdo_sqlite_handle_factory}; #endif diff --git a/thirdparty/php81/pdo_sqlite/sqlite_statement.c b/thirdparty/php81/pdo_sqlite/sqlite_statement.c index 3647e58b311..9081cbb7788 100644 --- a/thirdparty/php81/pdo_sqlite/sqlite_statement.c +++ b/thirdparty/php81/pdo_sqlite/sqlite_statement.c @@ -17,385 +17,365 @@ #define SW_USE_SQLITE_HOOK #include "php_swoole_sqlite.h" -#if PHP_VERSION_ID >= 80100 +#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "pdo/php_pdo.h" -static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; +static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; - if (S->stmt) { - sqlite3_finalize(S->stmt); - S->stmt = NULL; - } - efree(S); - return 1; + if (S->stmt) { + sqlite3_finalize(S->stmt); + S->stmt = NULL; + } + efree(S); + return 1; } -static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - - if (stmt->executed && !S->done) { - sqlite3_reset(S->stmt); - } - - S->done = 0; - switch (sqlite3_step(S->stmt)) { - case SQLITE_ROW: - S->pre_fetched = 1; - php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); - return 1; - - case SQLITE_DONE: - php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); - stmt->row_count = sqlite3_changes(S->H->db); - sqlite3_reset(S->stmt); - S->done = 1; - return 1; - - case SQLITE_ERROR: - sqlite3_reset(S->stmt); - ZEND_FALLTHROUGH; - case SQLITE_MISUSE: - case SQLITE_BUSY: - default: - pdo_sqlite_error_stmt(stmt); - return 0; - } +static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + } + + S->done = 0; + switch (sqlite3_step(S->stmt)) { + case SQLITE_ROW: + S->pre_fetched = 1; + php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); + return 1; + + case SQLITE_DONE: + php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); + stmt->row_count = sqlite3_changes(S->H->db); + sqlite3_reset(S->stmt); + S->done = 1; + return 1; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + case SQLITE_MISUSE: + case SQLITE_BUSY: + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } } -static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, - enum pdo_param_event event_type) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - zval *parameter; - - switch (event_type) { - case PDO_PARAM_EVT_EXEC_PRE: - if (stmt->executed && !S->done) { - sqlite3_reset(S->stmt); - S->done = 1; - } - - if (param->is_param) { - - if (param->paramno == -1) { - param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; - } - - switch (PDO_PARAM_TYPE(param->param_type)) { - case PDO_PARAM_STMT: - return 0; - - case PDO_PARAM_NULL: - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - pdo_sqlite_error_stmt(stmt); - return 0; - - case PDO_PARAM_INT: - case PDO_PARAM_BOOL: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - } else { - convert_to_long(parameter); +static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, + struct pdo_bound_param_data *param, + enum pdo_param_event event_type) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + zval *parameter; + + switch (event_type) { + case PDO_PARAM_EVT_EXEC_PRE: + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + S->done = 1; + } + + if (param->is_param) { + if (param->paramno == -1) { + param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; + } + + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_NULL: + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_INT: + case PDO_PARAM_BOOL: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + convert_to_long(parameter); #if ZEND_LONG_MAX > 2147483647 - if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { - return 1; - } + if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } #else - if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { - return 1; - } + if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } #endif - } - pdo_sqlite_error_stmt(stmt); - return 0; - - case PDO_PARAM_LOB: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_RESOURCE) { - php_stream *stm = NULL; - php_stream_from_zval_no_verify(stm, parameter); - if (stm) { - zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); - zval_ptr_dtor(parameter); - ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); - } else { - pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); - return 0; - } - } else if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - pdo_sqlite_error_stmt(stmt); - return 0; - } else { - if (!try_convert_to_string(parameter)) { - return 0; - } - } - - if (SQLITE_OK == sqlite3_bind_blob(S->stmt, param->paramno + 1, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - SQLITE_STATIC)) { - return 1; - } - return 0; - - case PDO_PARAM_STR: - default: - if (Z_ISREF(param->parameter)) { - parameter = Z_REFVAL(param->parameter); - } else { - parameter = ¶m->parameter; - } - if (Z_TYPE_P(parameter) == IS_NULL) { - if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { - return 1; - } - } else { - if (!try_convert_to_string(parameter)) { - return 0; - } - if (SQLITE_OK == sqlite3_bind_text(S->stmt, param->paramno + 1, - Z_STRVAL_P(parameter), - Z_STRLEN_P(parameter), - SQLITE_STATIC)) { - return 1; - } - } - pdo_sqlite_error_stmt(stmt); - return 0; - } - } - break; - - default: - ; - } - return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_LOB: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm = NULL; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + zval_ptr_dtor(parameter); + ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); + } else { + pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); + return 0; + } + } else if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + } + + if (SQLITE_OK == + sqlite3_bind_blob( + S->stmt, param->paramno + 1, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), SQLITE_STATIC)) { + return 1; + } + return 0; + + case PDO_PARAM_STR: + default: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + if (SQLITE_OK == + sqlite3_bind_text( + S->stmt, param->paramno + 1, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), SQLITE_STATIC)) { + return 1; + } + } + pdo_sqlite_error_stmt(stmt); + return 0; + } + } + break; + + default:; + } + return 1; } -static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, - enum pdo_fetch_orientation ori, zend_long offset) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - int i; - if (!S->stmt) { - return 0; - } - if (S->pre_fetched) { - S->pre_fetched = 0; - return 1; - } - if (S->done) { - return 0; - } - i = sqlite3_step(S->stmt); - switch (i) { - case SQLITE_ROW: - return 1; - - case SQLITE_DONE: - S->done = 1; - sqlite3_reset(S->stmt); - return 0; - - case SQLITE_ERROR: - sqlite3_reset(S->stmt); - ZEND_FALLTHROUGH; - default: - pdo_sqlite_error_stmt(stmt); - return 0; - } +static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + int i; + if (!S->stmt) { + return 0; + } + if (S->pre_fetched) { + S->pre_fetched = 0; + return 1; + } + if (S->done) { + return 0; + } + i = sqlite3_step(S->stmt); + switch (i) { + case SQLITE_ROW: + return 1; + + case SQLITE_DONE: + S->done = 1; + sqlite3_reset(S->stmt); + return 0; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } } -static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - const char *str; +static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + const char *str; - if(colno >= sqlite3_column_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return 0; - } + if (colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } - str = sqlite3_column_name(S->stmt, colno); - stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); - stmt->columns[colno].maxlen = SIZE_MAX; - stmt->columns[colno].precision = 0; + str = sqlite3_column_name(S->stmt, colno); + stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); + stmt->columns[colno].maxlen = SIZE_MAX; + stmt->columns[colno].precision = 0; - return 1; + return 1; } -static int pdo_sqlite_stmt_get_col( - pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - if (!S->stmt) { - return 0; - } - if(colno >= sqlite3_data_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return 0; - } - switch (sqlite3_column_type(S->stmt, colno)) { - case SQLITE_NULL: - ZVAL_NULL(result); - return 1; - - case SQLITE_INTEGER: { - int64_t i = sqlite3_column_int64(S->stmt, colno); +static int pdo_sqlite_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + if (!S->stmt) { + return 0; + } + if (colno >= sqlite3_data_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + ZVAL_NULL(result); + return 1; + + case SQLITE_INTEGER: { + int64_t i = sqlite3_column_int64(S->stmt, colno); #if SIZEOF_ZEND_LONG < 8 - if (i > ZEND_LONG_MAX || i < ZEND_LONG_MIN) { - ZVAL_STRINGL(result, - (char *) sqlite3_column_text(S->stmt, colno), - sqlite3_column_bytes(S->stmt, colno)); - return 1; - } + if (i > ZEND_LONG_MAX || i < ZEND_LONG_MIN) { + ZVAL_STRINGL(result, (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + } #endif - ZVAL_LONG(result, i); - return 1; - } - - case SQLITE_FLOAT: - ZVAL_DOUBLE(result, sqlite3_column_double(S->stmt, colno)); - return 1; - - case SQLITE_BLOB: - ZVAL_STRINGL_FAST(result, - sqlite3_column_blob(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); - return 1; - - default: - ZVAL_STRINGL_FAST(result, - (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); - return 1; - } + ZVAL_LONG(result, i); + return 1; + } + + case SQLITE_FLOAT: + ZVAL_DOUBLE(result, sqlite3_column_double(S->stmt, colno)); + return 1; + + case SQLITE_BLOB: + ZVAL_STRINGL_FAST(result, sqlite3_column_blob(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + + default: + ZVAL_STRINGL_FAST(result, (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + } } -static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - const char *str; - zval flags; - - if (!S->stmt) { - return FAILURE; - } - if(colno >= sqlite3_column_count(S->stmt)) { - /* error invalid column */ - pdo_sqlite_error_stmt(stmt); - return FAILURE; - } - - array_init(return_value); - array_init(&flags); - - switch (sqlite3_column_type(S->stmt, colno)) { - case SQLITE_NULL: - add_assoc_string(return_value, "native_type", "null"); - add_assoc_long(return_value, "pdo_type", PDO_PARAM_NULL); - break; - - case SQLITE_FLOAT: - add_assoc_string(return_value, "native_type", "double"); - add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); - break; - - case SQLITE_BLOB: - add_next_index_string(&flags, "blob"); - /* TODO Check this is correct */ - ZEND_FALLTHROUGH; - case SQLITE_TEXT: - add_assoc_string(return_value, "native_type", "string"); - add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); - break; - - case SQLITE_INTEGER: - add_assoc_string(return_value, "native_type", "integer"); - add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT); - break; - } - - str = sqlite3_column_decltype(S->stmt, colno); - if (str) { - add_assoc_string(return_value, "sqlite:decl_type", (char *)str); - } +static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + const char *str; + zval flags; + + if (!S->stmt) { + return FAILURE; + } + if (colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + add_assoc_string(return_value, "native_type", "null"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_NULL); + break; + + case SQLITE_FLOAT: + add_assoc_string(return_value, "native_type", "double"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_BLOB: + add_next_index_string(&flags, "blob"); + /* TODO Check this is correct */ + ZEND_FALLTHROUGH; + case SQLITE_TEXT: + add_assoc_string(return_value, "native_type", "string"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_INTEGER: + add_assoc_string(return_value, "native_type", "integer"); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT); + break; + } + + str = sqlite3_column_decltype(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "sqlite:decl_type", (char *) str); + } #ifdef HAVE_SW_SQLITE3_COLUMN_TABLE_NAME - str = sqlite3_column_table_name(S->stmt, colno); - if (str) { - add_assoc_string(return_value, "table", (char *)str); - } + str = sqlite3_column_table_name(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "table", (char *) str); + } #endif - add_assoc_zval(return_value, "flags", &flags); + add_assoc_zval(return_value, "flags", &flags); - return SUCCESS; + return SUCCESS; } -static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; - sqlite3_reset(S->stmt); - return 1; +static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + sqlite3_reset(S->stmt); + return 1; } -static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) -{ - pdo_sqlite_stmt *S = (pdo_sqlite_stmt*)stmt->driver_data; +static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; - switch (attr) { - case PDO_SQLITE_ATTR_READONLY_STATEMENT: - ZVAL_FALSE(val); + switch (attr) { + case PDO_SQLITE_ATTR_READONLY_STATEMENT: + ZVAL_FALSE(val); #if SQLITE_VERSION_NUMBER >= 3007004 - if (sqlite3_stmt_readonly(S->stmt)) { - ZVAL_TRUE(val); - } + if (sqlite3_stmt_readonly(S->stmt)) { + ZVAL_TRUE(val); + } #endif - break; + break; - default: - return 0; - } + default: + return 0; + } - return 1; + return 1; } -const struct pdo_stmt_methods swoole_sqlite_stmt_methods = { - pdo_sqlite_stmt_dtor, - pdo_sqlite_stmt_execute, - pdo_sqlite_stmt_fetch, - pdo_sqlite_stmt_describe, - pdo_sqlite_stmt_get_col, - pdo_sqlite_stmt_param_hook, - NULL, /* set_attr */ - pdo_sqlite_stmt_get_attribute, /* get_attr */ - pdo_sqlite_stmt_col_meta, - NULL, /* next_rowset */ - pdo_sqlite_stmt_cursor_closer -}; +const struct pdo_stmt_methods swoole_sqlite_stmt_methods = {pdo_sqlite_stmt_dtor, + pdo_sqlite_stmt_execute, + pdo_sqlite_stmt_fetch, + pdo_sqlite_stmt_describe, + pdo_sqlite_stmt_get_col, + pdo_sqlite_stmt_param_hook, + NULL, /* set_attr */ + pdo_sqlite_stmt_get_attribute, /* get_attr */ + pdo_sqlite_stmt_col_meta, + NULL, /* next_rowset */ + pdo_sqlite_stmt_cursor_closer}; #endif diff --git a/thirdparty/php83/pdo_sqlite/php_pdo_sqlite_int.h b/thirdparty/php83/pdo_sqlite/php_pdo_sqlite_int.h new file mode 100644 index 00000000000..0e78c4be19d --- /dev/null +++ b/thirdparty/php83/pdo_sqlite/php_pdo_sqlite_int.h @@ -0,0 +1,80 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_PDO_SQLITE_INT_H +#define PHP_PDO_SQLITE_INT_H + +#include + +typedef struct { + const char *file; + int line; + unsigned int errcode; + char *errmsg; +} pdo_sqlite_error_info; + +struct pdo_sqlite_fci { + zend_fcall_info fci; + zend_fcall_info_cache fcc; +}; + +struct pdo_sqlite_func { + struct pdo_sqlite_func *next; + + zval func, step, fini; + int argc; + const char *funcname; + + /* accelerated callback references */ + struct pdo_sqlite_fci afunc, astep, afini; +}; + +struct pdo_sqlite_collation { + struct pdo_sqlite_collation *next; + + const char *name; + zval callback; + struct pdo_sqlite_fci fc; +}; + +typedef struct { + sqlite3 *db; + pdo_sqlite_error_info einfo; + struct pdo_sqlite_func *funcs; + struct pdo_sqlite_collation *collations; +} pdo_sqlite_db_handle; + +typedef struct { + pdo_sqlite_db_handle *H; + sqlite3_stmt *stmt; + unsigned pre_fetched:1; + unsigned done:1; +} pdo_sqlite_stmt; + + +extern int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line); +#define pdo_sqlite_error(s) _pdo_sqlite_error(s, NULL, __FILE__, __LINE__) +#define pdo_sqlite_error_stmt(s) _pdo_sqlite_error(stmt->dbh, stmt, __FILE__, __LINE__) + +extern const struct pdo_stmt_methods swoole_sqlite_stmt_methods; + +enum { + PDO_SQLITE_ATTR_OPEN_FLAGS = PDO_ATTR_DRIVER_SPECIFIC, + PDO_SQLITE_ATTR_READONLY_STATEMENT, + PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES +}; + +#endif diff --git a/thirdparty/php83/pdo_sqlite/sqlite_driver.c b/thirdparty/php83/pdo_sqlite/sqlite_driver.c new file mode 100644 index 00000000000..e130d779f9c --- /dev/null +++ b/thirdparty/php83/pdo_sqlite/sqlite_driver.c @@ -0,0 +1,802 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong | + +----------------------------------------------------------------------+ +*/ +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" +#include "php_swoole_call_stack.h" + +#if PHP_VERSION_ID >= 80300 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" +#include "zend_exceptions.h" +#include "sqlite_driver_arginfo.h" + +int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_error_type *pdo_err = stmt ? &stmt->error_code : &dbh->error_code; + pdo_sqlite_error_info *einfo = &H->einfo; + + einfo->errcode = sqlite3_errcode(H->db); + einfo->file = file; + einfo->line = line; + + if (einfo->errcode != SQLITE_OK) { + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + } + einfo->errmsg = pestrdup((char *) sqlite3_errmsg(H->db), dbh->is_persistent); + } else { /* no error */ + strncpy(*pdo_err, PDO_ERR_NONE, sizeof(*pdo_err)); + return 0; + } + switch (einfo->errcode) { + case SQLITE_NOTFOUND: + strncpy(*pdo_err, "42S02", sizeof(*pdo_err)); + break; + + case SQLITE_INTERRUPT: + strncpy(*pdo_err, "01002", sizeof(*pdo_err)); + break; + + case SQLITE_NOLFS: + strncpy(*pdo_err, "HYC00", sizeof(*pdo_err)); + break; + + case SQLITE_TOOBIG: + strncpy(*pdo_err, "22001", sizeof(*pdo_err)); + break; + + case SQLITE_CONSTRAINT: + strncpy(*pdo_err, "23000", sizeof(*pdo_err)); + break; + + case SQLITE_ERROR: + default: + strncpy(*pdo_err, "HY000", sizeof(*pdo_err)); + break; + } + + if (!dbh->methods) { + pdo_throw_exception(einfo->errcode, einfo->errmsg, pdo_err); + } + + return einfo->errcode; +} +/* }}} */ + +static void pdo_sqlite_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_sqlite_error_info *einfo = &H->einfo; + + if (einfo->errcode) { + add_next_index_long(info, einfo->errcode); + add_next_index_string(info, einfo->errmsg); + } +} + +static void pdo_sqlite_cleanup_callbacks(pdo_sqlite_db_handle *H) { + struct pdo_sqlite_func *func; + + while (H->funcs) { + func = H->funcs; + H->funcs = func->next; + + if (H->db) { + /* delete the function from the handle */ + sqlite3_create_function(H->db, func->funcname, func->argc, SQLITE_UTF8, func, NULL, NULL, NULL); + } + + efree((char *) func->funcname); + if (!Z_ISUNDEF(func->func)) { + zval_ptr_dtor(&func->func); + } + if (!Z_ISUNDEF(func->step)) { + zval_ptr_dtor(&func->step); + } + if (!Z_ISUNDEF(func->fini)) { + zval_ptr_dtor(&func->fini); + } + efree(func); + } + + while (H->collations) { + struct pdo_sqlite_collation *collation; + collation = H->collations; + H->collations = collation->next; + + if (H->db) { + /* delete the collation from the handle */ + sqlite3_create_collation(H->db, collation->name, SQLITE_UTF8, collation, NULL); + } + + efree((char *) collation->name); + if (!Z_ISUNDEF(collation->callback)) { + zval_ptr_dtor(&collation->callback); + } + efree(collation); + } +} + +static void sqlite_handle_closer(pdo_dbh_t *dbh) /* {{{ */ +{ + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (H) { + pdo_sqlite_error_info *einfo = &H->einfo; + + pdo_sqlite_cleanup_callbacks(H); + if (H->db) { +#ifdef HAVE_SW_SQLITE3_CLOSE_V2 + sqlite3_close_v2(H->db); +#else + sqlite3_close(H->db); +#endif + H->db = NULL; + } + if (einfo->errmsg) { + pefree(einfo->errmsg, dbh->is_persistent); + einfo->errmsg = NULL; + } + pefree(H, dbh->is_persistent); + dbh->driver_data = NULL; + } +} +/* }}} */ + +static bool sqlite_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + pdo_sqlite_stmt *S = ecalloc(1, sizeof(pdo_sqlite_stmt)); + int i; + const char *tail; + + S->H = H; + stmt->driver_data = S; + stmt->methods = &swoole_sqlite_stmt_methods; + stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL | PDO_PLACEHOLDER_NAMED; + + if (PDO_CURSOR_FWDONLY != pdo_attr_lval(driver_options, PDO_ATTR_CURSOR, PDO_CURSOR_FWDONLY)) { + H->einfo.errcode = SQLITE_ERROR; + pdo_sqlite_error(dbh); + return false; + } + + i = sqlite3_prepare_v2(H->db, ZSTR_VAL(sql), ZSTR_LEN(sql), &S->stmt, &tail); + if (i == SQLITE_OK) { + return true; + } + + pdo_sqlite_error(dbh); + + return false; +} + +static zend_long sqlite_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, ZSTR_VAL(sql), NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return -1; + } else { + return sqlite3_changes(H->db); + } +} + +static zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string *name) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + return zend_i64_to_str(sqlite3_last_insert_rowid(H->db)); +} + +/* NB: doesn't handle binary strings... use prepared stmts for that */ +static zend_string *sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) { + char *quoted; + if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) { + return NULL; + } + quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); + /* TODO use %Q format? */ + sqlite3_snprintf(2 * ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted)); + zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0); + efree(quoted); + return quoted_str; +} + +static bool sqlite_handle_begin(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, "BEGIN", NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return false; + } + return true; +} + +static bool sqlite_handle_commit(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, "COMMIT", NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return false; + } + return true; +} + +static bool sqlite_handle_rollback(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + + if (sqlite3_exec(H->db, "ROLLBACK", NULL, NULL, NULL) != SQLITE_OK) { + pdo_sqlite_error(dbh); + return false; + } + return true; +} + +static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_value) { + switch (attr) { + case PDO_ATTR_CLIENT_VERSION: + case PDO_ATTR_SERVER_VERSION: + ZVAL_STRING(return_value, (char *) sqlite3_libversion()); + break; + + default: + return 0; + } + + return 1; +} + +static bool pdo_sqlite_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + zend_long lval; + + switch (attr) { + case PDO_ATTR_TIMEOUT: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_busy_timeout(H->db, lval * 1000); + return true; + case PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES: + if (!pdo_get_long_param(&lval, val)) { + return false; + } + sqlite3_extended_result_codes(H->db, lval); + return true; + } + return false; +} + +typedef struct { + zval val; + zend_long row; +} aggregate_context; + +static int do_callback( + struct pdo_sqlite_fci *fc, zval *cb, int argc, sqlite3_value **argv, sqlite3_context *context, int is_agg) { + zval *zargs = NULL; + zval retval; + int i; + int ret; + int fake_argc; + aggregate_context *agg_context = NULL; + + if (is_agg) { + is_agg = 2; + } + + fake_argc = argc + is_agg; + + fc->fci.size = sizeof(fc->fci); + ZVAL_COPY_VALUE(&fc->fci.function_name, cb); + fc->fci.object = NULL; + fc->fci.retval = &retval; + fc->fci.param_count = fake_argc; + + /* build up the params */ + + if (fake_argc) { + zargs = safe_emalloc(fake_argc, sizeof(zval), 0); + } + + if (is_agg) { + agg_context = sqlite3_aggregate_context(context, sizeof(aggregate_context)); + if (!agg_context) { + efree(zargs); + return FAILURE; + } + if (Z_ISUNDEF(agg_context->val)) { + ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); + } + ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); + ZVAL_LONG(&zargs[1], ++agg_context->row); + } + + for (i = 0; i < argc; i++) { + /* get the value */ + switch (sqlite3_value_type(argv[i])) { + case SQLITE_INTEGER: + ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); + break; + + case SQLITE_FLOAT: + ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); + break; + + case SQLITE_NULL: + ZVAL_NULL(&zargs[i + is_agg]); + break; + + case SQLITE_BLOB: + case SQLITE3_TEXT: + default: + ZVAL_STRINGL(&zargs[i + is_agg], (char *) sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); + break; + } + } + + fc->fci.params = zargs; + + HOOK_PHP_CALL_STACK(ret = zend_call_function(&fc->fci, &fc->fcc);); + if (ret == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } + + /* clean up the params */ + if (zargs) { + for (i = is_agg; i < fake_argc; i++) { + zval_ptr_dtor(&zargs[i]); + } + if (is_agg) { + zval_ptr_dtor(&zargs[1]); + } + efree(zargs); + } + + if (!is_agg || !argv) { + /* only set the sqlite return value if we are a scalar function, + * or if we are finalizing an aggregate */ + if (!Z_ISUNDEF(retval)) { + switch (Z_TYPE(retval)) { + case IS_LONG: + sqlite3_result_int(context, Z_LVAL(retval)); + break; + + case IS_NULL: + sqlite3_result_null(context); + break; + + case IS_DOUBLE: + sqlite3_result_double(context, Z_DVAL(retval)); + break; + + default: + if (!try_convert_to_string(&retval)) { + ret = FAILURE; + break; + } + sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); + break; + } + } else { + sqlite3_result_error(context, "failed to invoke callback", 0); + } + + if (agg_context) { + zval_ptr_dtor(&agg_context->val); + } + } else { + /* we're stepping in an aggregate; the return value goes into + * the context */ + if (agg_context) { + if (Z_ISUNDEF(retval)) { + zval_ptr_dtor(&agg_context->val); + return FAILURE; + } + zval_ptr_dtor(Z_REFVAL(agg_context->val)); + ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); + ZVAL_UNDEF(&retval); + } + } + + if (!Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + + return ret; +} + +static void php_sqlite3_func_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); + + do_callback(&func->afunc, &func->func, argc, argv, context, 0); +} + +static void php_sqlite3_func_step_callback(sqlite3_context *context, int argc, sqlite3_value **argv) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); + + do_callback(&func->astep, &func->step, argc, argv, context, 1); +} + +static void php_sqlite3_func_final_callback(sqlite3_context *context) { + struct pdo_sqlite_func *func = (struct pdo_sqlite_func *) sqlite3_user_data(context); + + do_callback(&func->afini, &func->fini, 0, NULL, context, 1); +} + +static int php_sqlite3_collation_callback( + void *context, int string1_len, const void *string1, int string2_len, const void *string2) { + int ret; + zval zargs[2]; + zval retval; + struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation *) context; + + collation->fc.fci.size = sizeof(collation->fc.fci); + ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); + collation->fc.fci.object = NULL; + collation->fc.fci.retval = &retval; + + // Prepare the arguments. + ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); + ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); + collation->fc.fci.param_count = 2; + collation->fc.fci.params = zargs; + + HOOK_PHP_CALL_STACK(ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc);); + if (ret == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } else if (!Z_ISUNDEF(retval)) { + if (Z_TYPE(retval) != IS_LONG) { + convert_to_long(&retval); + } + ret = 0; + if (Z_LVAL(retval) > 0) { + ret = 1; + } else if (Z_LVAL(retval) < 0) { + ret = -1; + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + + return ret; +} + +/* {{{ bool SQLite::sqliteCreateFunction(string name, callable callback [, int argcount, int flags]) + Registers a UDF with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateFunction) { + struct pdo_sqlite_func *func; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + zend_long flags = 0; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(fci, fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + func = (struct pdo_sqlite_func *) ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function( + H->db, func_name, argc, flags | SQLITE_UTF8, func, php_sqlite3_func_callback, NULL, NULL); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->func, &fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::sqliteCreateAggregate(string name, callable step, callable fini [, int argcount]) + Registers a UDF with the sqlite db handle */ + +/* The step function should have the prototype: + mixed step(mixed $context, int $rownumber, $value [, $value2 [, ...]]) + + $context will be null for the first row; on subsequent rows it will have + the value that was previously returned from the step function; you should + use this to maintain state for the aggregate. + + The fini function should have the prototype: + mixed fini(mixed $context, int $rownumber) + + $context will hold the return value from the very last call to the step function. + rownumber will hold the number of rows over which the aggregate was performed. + The return value of this function will be used as the return value for this + aggregate UDF. +*/ + +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateAggregate) { + struct pdo_sqlite_func *func; + zend_fcall_info step_fci, fini_fci; + zend_fcall_info_cache step_fcc, fini_fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(step_fci, step_fcc) + Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + func = (struct pdo_sqlite_func *) ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, + func_name, + argc, + SQLITE_UTF8, + func, + NULL, + php_sqlite3_func_step_callback, + php_sqlite3_func_final_callback); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->step, &step_fci.function_name); + + ZVAL_COPY(&func->fini, &fini_fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::sqliteCreateCollation(string name, callable callback) + Registers a collation with the sqlite db handle */ +PHP_METHOD(PDO_SQLite_Ext, sqliteCreateCollation) { + struct pdo_sqlite_collation *collation; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *collation_name; + size_t collation_name_len; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *) dbh->driver_data; + + collation = (struct pdo_sqlite_collation *) ecalloc(1, sizeof(*collation)); + + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_sqlite3_collation_callback); + if (ret == SQLITE_OK) { + collation->name = estrdup(collation_name); + + ZVAL_COPY(&collation->callback, &fci.function_name); + + collation->next = H->collations; + H->collations = collation; + + RETURN_TRUE; + } + + efree(collation); + RETURN_FALSE; +} +/* }}} */ + +static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind) { + switch (kind) { + case PDO_DBH_DRIVER_METHOD_KIND_DBH: + return class_PDO_SQLite_Ext_methods; + + default: + return NULL; + } +} + +static void pdo_sqlite_request_shutdown(pdo_dbh_t *dbh) { + pdo_sqlite_db_handle *H = (pdo_sqlite_db_handle *) dbh->driver_data; + /* unregister functions, so that they don't linger for the next + * request */ + if (H) { + pdo_sqlite_cleanup_callbacks(H); + } +} + +static void pdo_sqlite_get_gc(pdo_dbh_t *dbh, zend_get_gc_buffer *gc_buffer) { + pdo_sqlite_db_handle *H = dbh->driver_data; + + struct pdo_sqlite_func *func = H->funcs; + while (func) { + zend_get_gc_buffer_add_zval(gc_buffer, &func->func); + zend_get_gc_buffer_add_zval(gc_buffer, &func->step); + zend_get_gc_buffer_add_zval(gc_buffer, &func->fini); + func = func->next; + } + + struct pdo_sqlite_collation *collation = H->collations; + while (collation) { + zend_get_gc_buffer_add_zval(gc_buffer, &collation->callback); + collation = collation->next; + } +} + +static const struct pdo_dbh_methods sqlite_methods = {sqlite_handle_closer, + sqlite_handle_preparer, + sqlite_handle_doer, + sqlite_handle_quoter, + sqlite_handle_begin, + sqlite_handle_commit, + sqlite_handle_rollback, + pdo_sqlite_set_attr, + pdo_sqlite_last_insert_id, + pdo_sqlite_fetch_error_func, + pdo_sqlite_get_attribute, + NULL, /* check_liveness: not needed */ + get_driver_methods, + pdo_sqlite_request_shutdown, + NULL, /* in transaction, use PDO's internal tracking mechanism */ + pdo_sqlite_get_gc}; + +static char *make_filename_safe(const char *filename) { + if (!filename) { + return NULL; + } + if (*filename && strncasecmp(filename, "file:", 5) == 0) { + if (PG(open_basedir) && *PG(open_basedir)) { + return NULL; + } + return estrdup(filename); + } + if (*filename && memcmp(filename, ":memory:", sizeof(":memory:"))) { + char *fullpath = expand_filepath(filename, NULL); + + if (!fullpath) { + return NULL; + } + + if (php_check_open_basedir(fullpath)) { + efree(fullpath); + return NULL; + } + return fullpath; + } + return estrdup(filename); +} + +static int authorizer( + void *autharg, int access_type, const char *arg3, const char *arg4, const char *arg5, const char *arg6) { + char *filename; + switch (access_type) { + case SQLITE_ATTACH: { + filename = make_filename_safe(arg3); + if (!filename) { + return SQLITE_DENY; + } + efree(filename); + return SQLITE_OK; + } + + default: + /* access allowed */ + return SQLITE_OK; + } +} + +static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ */ +{ + pdo_sqlite_db_handle *H; + int i, ret = 0; + zend_long timeout = 60, flags; + char *filename; + + H = pecalloc(1, sizeof(pdo_sqlite_db_handle), dbh->is_persistent); + + H->einfo.errcode = 0; + H->einfo.errmsg = NULL; + dbh->driver_data = H; + + /* skip all but this one param event */ + dbh->skip_param_evt = 0x7F ^ (1 << PDO_PARAM_EVT_EXEC_PRE); + + filename = make_filename_safe(dbh->data_source); + + if (!filename) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "open_basedir prohibits opening %s", dbh->data_source); + goto cleanup; + } + + flags = pdo_attr_lval(driver_options, PDO_SQLITE_ATTR_OPEN_FLAGS, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + + if (!(PG(open_basedir) && *PG(open_basedir))) { + flags |= SQLITE_OPEN_URI; + } + i = sqlite3_open_v2(filename, &H->db, flags, NULL); + + efree(filename); + + if (i != SQLITE_OK) { + pdo_sqlite_error(dbh); + goto cleanup; + } + + if (PG(open_basedir) && *PG(open_basedir)) { + sqlite3_set_authorizer(H->db, authorizer, NULL); + } + + if (driver_options) { + timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, timeout); + } + sqlite3_busy_timeout(H->db, timeout * 1000); + + dbh->alloc_own_columns = 1; + dbh->max_escaped_char_length = 2; + + ret = 1; + +cleanup: + dbh->methods = &sqlite_methods; + + return ret; +} +/* }}} */ + +const pdo_driver_t swoole_pdo_sqlite_driver = {PDO_DRIVER_HEADER(sqlite), pdo_sqlite_handle_factory}; +#endif diff --git a/thirdparty/php83/pdo_sqlite/sqlite_driver.stub.php b/thirdparty/php83/pdo_sqlite/sqlite_driver.stub.php new file mode 100644 index 00000000000..add395f2b99 --- /dev/null +++ b/thirdparty/php83/pdo_sqlite/sqlite_driver.stub.php @@ -0,0 +1,18 @@ + | + +----------------------------------------------------------------------+ +*/ + +#define SW_USE_SQLITE_HOOK +#include "php_swoole_sqlite.h" + +#if PHP_VERSION_ID >= 80300 +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "pdo/php_pdo.h" + +static int pdo_sqlite_stmt_dtor(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + + if (S->stmt) { + sqlite3_finalize(S->stmt); + S->stmt = NULL; + } + efree(S); + return 1; +} + +static int pdo_sqlite_stmt_execute(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + } + + S->done = 0; + switch (sqlite3_step(S->stmt)) { + case SQLITE_ROW: + S->pre_fetched = 1; + php_pdo_stmt_set_column_count(stmt, sqlite3_data_count(S->stmt)); + return 1; + + case SQLITE_DONE: + php_pdo_stmt_set_column_count(stmt, sqlite3_column_count(S->stmt)); + stmt->row_count = sqlite3_changes(S->H->db); + sqlite3_reset(S->stmt); + S->done = 1; + return 1; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + case SQLITE_MISUSE: + case SQLITE_BUSY: + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, + struct pdo_bound_param_data *param, + enum pdo_param_event event_type) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + zval *parameter; + + switch (event_type) { + case PDO_PARAM_EVT_EXEC_PRE: + if (stmt->executed && !S->done) { + sqlite3_reset(S->stmt); + S->done = 1; + } + + if (param->is_param) { + if (param->paramno == -1) { + param->paramno = sqlite3_bind_parameter_index(S->stmt, ZSTR_VAL(param->name)) - 1; + } + + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_NULL: + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_INT: + case PDO_PARAM_BOOL: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + convert_to_long(parameter); +#if ZEND_LONG_MAX > 2147483647 + if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#else + if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(parameter))) { + return 1; + } +#endif + } + pdo_sqlite_error_stmt(stmt); + return 0; + + case PDO_PARAM_LOB: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm = NULL; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + zend_string *mem = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + zval_ptr_dtor(parameter); + ZVAL_STR(parameter, mem ? mem : ZSTR_EMPTY_ALLOC()); + } else { + pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource"); + return 0; + } + } else if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + pdo_sqlite_error_stmt(stmt); + return 0; + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + } + + if (SQLITE_OK == + sqlite3_bind_blob( + S->stmt, param->paramno + 1, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), SQLITE_STATIC)) { + return 1; + } + return 0; + + case PDO_PARAM_STR: + default: + if (Z_ISREF(param->parameter)) { + parameter = Z_REFVAL(param->parameter); + } else { + parameter = ¶m->parameter; + } + if (Z_TYPE_P(parameter) == IS_NULL) { + if (sqlite3_bind_null(S->stmt, param->paramno + 1) == SQLITE_OK) { + return 1; + } + } else { + if (!try_convert_to_string(parameter)) { + return 0; + } + if (SQLITE_OK == + sqlite3_bind_text( + S->stmt, param->paramno + 1, Z_STRVAL_P(parameter), Z_STRLEN_P(parameter), SQLITE_STATIC)) { + return 1; + } + } + pdo_sqlite_error_stmt(stmt); + return 0; + } + } + break; + + default:; + } + return 1; +} + +static int pdo_sqlite_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + int i; + if (!S->stmt) { + return 0; + } + if (S->pre_fetched) { + S->pre_fetched = 0; + return 1; + } + if (S->done) { + return 0; + } + i = sqlite3_step(S->stmt); + switch (i) { + case SQLITE_ROW: + return 1; + + case SQLITE_DONE: + S->done = 1; + sqlite3_reset(S->stmt); + return 0; + + case SQLITE_ERROR: + sqlite3_reset(S->stmt); + ZEND_FALLTHROUGH; + default: + pdo_sqlite_error_stmt(stmt); + return 0; + } +} + +static int pdo_sqlite_stmt_describe(pdo_stmt_t *stmt, int colno) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + const char *str; + + if (colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + + str = sqlite3_column_name(S->stmt, colno); + stmt->columns[colno].name = zend_string_init(str, strlen(str), 0); + stmt->columns[colno].maxlen = SIZE_MAX; + stmt->columns[colno].precision = 0; + + return 1; +} + +static int pdo_sqlite_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *result, enum pdo_param_type *type) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + if (!S->stmt) { + return 0; + } + if (colno >= sqlite3_data_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return 0; + } + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + ZVAL_NULL(result); + return 1; + + case SQLITE_INTEGER: { + int64_t i = sqlite3_column_int64(S->stmt, colno); +#if SIZEOF_ZEND_LONG < 8 + if (i > ZEND_LONG_MAX || i < ZEND_LONG_MIN) { + ZVAL_STRINGL(result, (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + } +#endif + ZVAL_LONG(result, i); + return 1; + } + + case SQLITE_FLOAT: + ZVAL_DOUBLE(result, sqlite3_column_double(S->stmt, colno)); + return 1; + + case SQLITE_BLOB: + ZVAL_STRINGL_FAST(result, sqlite3_column_blob(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + + default: + ZVAL_STRINGL_FAST(result, (char *) sqlite3_column_text(S->stmt, colno), sqlite3_column_bytes(S->stmt, colno)); + return 1; + } +} + +static int pdo_sqlite_stmt_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + const char *str; + zval flags; + + if (!S->stmt) { + return FAILURE; + } + if (colno >= sqlite3_column_count(S->stmt)) { + /* error invalid column */ + pdo_sqlite_error_stmt(stmt); + return FAILURE; + } + + array_init(return_value); + array_init(&flags); + + switch (sqlite3_column_type(S->stmt, colno)) { + case SQLITE_NULL: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_NULL); + break; + + case SQLITE_FLOAT: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_DOUBLE)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_BLOB: + add_next_index_string(&flags, "blob"); + /* TODO Check this is correct */ + ZEND_FALLTHROUGH; + case SQLITE_TEXT: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_STRING)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_STR); + break; + + case SQLITE_INTEGER: + add_assoc_str(return_value, "native_type", ZSTR_KNOWN(ZEND_STR_INTEGER)); + add_assoc_long(return_value, "pdo_type", PDO_PARAM_INT); + break; + } + + str = sqlite3_column_decltype(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "sqlite:decl_type", (char *) str); + } + +#ifdef HAVE_SW_SQLITE3_COLUMN_TABLE_NAME + str = sqlite3_column_table_name(S->stmt, colno); + if (str) { + add_assoc_string(return_value, "table", (char *) str); + } +#endif + + add_assoc_zval(return_value, "flags", &flags); + + return SUCCESS; +} + +static int pdo_sqlite_stmt_cursor_closer(pdo_stmt_t *stmt) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + sqlite3_reset(S->stmt); + return 1; +} + +static int pdo_sqlite_stmt_get_attribute(pdo_stmt_t *stmt, zend_long attr, zval *val) { + pdo_sqlite_stmt *S = (pdo_sqlite_stmt *) stmt->driver_data; + + switch (attr) { + case PDO_SQLITE_ATTR_READONLY_STATEMENT: + ZVAL_FALSE(val); + +#if SQLITE_VERSION_NUMBER >= 3007004 + if (sqlite3_stmt_readonly(S->stmt)) { + ZVAL_TRUE(val); + } +#endif + break; + + default: + return 0; + } + + return 1; +} + +const struct pdo_stmt_methods swoole_sqlite_stmt_methods = {pdo_sqlite_stmt_dtor, + pdo_sqlite_stmt_execute, + pdo_sqlite_stmt_fetch, + pdo_sqlite_stmt_describe, + pdo_sqlite_stmt_get_col, + pdo_sqlite_stmt_param_hook, + NULL, /* set_attr */ + pdo_sqlite_stmt_get_attribute, /* get_attr */ + pdo_sqlite_stmt_col_meta, + NULL, /* next_rowset */ + pdo_sqlite_stmt_cursor_closer}; +#endif From e30f4058ef707ef075e6888a4f747a8bb2682293 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Mon, 6 May 2024 13:39:53 +0800 Subject: [PATCH 099/103] fix core tests --- core-tests/src/os/async.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-tests/src/os/async.cpp b/core-tests/src/os/async.cpp index f1163e7df1e..8dad920991e 100644 --- a/core-tests/src/os/async.cpp +++ b/core-tests/src/os/async.cpp @@ -78,7 +78,7 @@ TEST(async, schedule) { count--; if (count == 0) { swoole_timer_del(timer); - ASSERT_EQ(SwooleTG.async_threads->get_worker_num(), 128); + ASSERT_GT(SwooleTG.async_threads->get_worker_num(), 16); ASSERT_GT(SwooleTG.async_threads->get_queue_size(), 100); ASSERT_GT(SwooleTG.async_threads->get_task_num(), 100); break; From f857737b5e7440ce922955b2c76543dcdb653296 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Mon, 6 May 2024 18:40:25 +0800 Subject: [PATCH 100/103] Support passing streams between threads --- ext-src/php_swoole_thread.h | 69 +-------- ext-src/stubs/php_swoole_thread.stub.php | 2 +- ext-src/stubs/php_swoole_thread_arginfo.h | 6 +- ext-src/swoole_thread.cc | 168 +++++++++++++++++----- tests/swoole_thread/co-stream.phpt | 57 ++++++++ tests/swoole_thread/stream.phpt | 52 +++++++ 6 files changed, 251 insertions(+), 103 deletions(-) create mode 100644 tests/swoole_thread/co-stream.phpt create mode 100644 tests/swoole_thread/stream.phpt diff --git a/ext-src/php_swoole_thread.h b/ext-src/php_swoole_thread.h index 096a834ba68..405a16573d4 100644 --- a/ext-src/php_swoole_thread.h +++ b/ext-src/php_swoole_thread.h @@ -39,6 +39,7 @@ zval *php_swoole_thread_get_arguments(); #define EMSG_NO_RESOURCE "resource not found" #define ECODE_NO_RESOURCE -2 +#define IS_STREAM_SOCKET 98 #define IS_SERIALIZED_OBJECT 99 struct ThreadResource { @@ -74,71 +75,9 @@ struct ArrayItem { store(zvalue); } - void store(zval *zvalue) { - type = Z_TYPE_P(zvalue); - switch (type) { - case IS_LONG: - value.lval = zval_get_long(zvalue); - break; - case IS_DOUBLE: - value.dval = zval_get_double(zvalue); - break; - case IS_STRING: { - value.str = zend_string_init(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), 1); - break; - case IS_TRUE: - case IS_FALSE: - case IS_NULL: - break; - } - default: { - auto _serialized_object = php_swoole_thread_serialize(zvalue); - if (!_serialized_object) { - type = IS_UNDEF; - break; - } else { - type = IS_SERIALIZED_OBJECT; - value.serialized_object = _serialized_object; - } - break; - } - } - } - - void fetch(zval *return_value) { - switch (type) { - case IS_LONG: - RETVAL_LONG(value.lval); - break; - case IS_DOUBLE: - RETVAL_LONG(value.dval); - break; - case IS_TRUE: - RETVAL_TRUE; - break; - case IS_FALSE: - RETVAL_FALSE; - break; - case IS_STRING: - RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); - break; - case IS_SERIALIZED_OBJECT: - php_swoole_thread_unserialize(value.serialized_object, return_value); - break; - default: - break; - } - } - - void release() { - if (type == IS_STRING) { - zend_string_release(value.str); - value.str = nullptr; - } else if (type == IS_SERIALIZED_OBJECT) { - zend_string_release(value.serialized_object); - value.serialized_object = nullptr; - } - } + void store(zval *zvalue); + void fetch(zval *return_value); + void release(); ~ArrayItem() { if (value.str) { diff --git a/ext-src/stubs/php_swoole_thread.stub.php b/ext-src/stubs/php_swoole_thread.stub.php index 569b3709858..ec486a5010e 100644 --- a/ext-src/stubs/php_swoole_thread.stub.php +++ b/ext-src/stubs/php_swoole_thread.stub.php @@ -2,7 +2,7 @@ namespace Swoole { class Thread { public int $id; - private function __construct() {} + public function __construct(string $script_file, mixed ...$args) {} public function join(): bool {} public function joinable(): bool {} diff --git a/ext-src/stubs/php_swoole_thread_arginfo.h b/ext-src/stubs/php_swoole_thread_arginfo.h index 6aff3d945eb..33eab8f9652 100644 --- a/ext-src/stubs/php_swoole_thread_arginfo.h +++ b/ext-src/stubs/php_swoole_thread_arginfo.h @@ -1,7 +1,9 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 57a2a703c0e0a37729ab2e2df280fbb24e78404f */ + * Stub hash: 54c9d0c2a88bb65cab67d14129aa56806c31bb00 */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Swoole_Thread___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, script_file, IS_STRING, 0) + ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Swoole_Thread_join, 0, 0, _IS_BOOL, 0) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 7d96dd2689e..efae04f59ac 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -50,6 +50,7 @@ struct ThreadObject { }; static void php_swoole_thread_join(zend_object *object); +static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject); static thread_local zval thread_argv; static zend_long thread_resource_id = 0; @@ -147,7 +148,9 @@ void php_swoole_thread_minit(int module_number) { swoole_thread_ce, ZEND_STRL("HARDWARE_CONCURRENCY"), std::thread::hardware_concurrency()); } -static PHP_METHOD(swoole_thread, __construct) {} +static PHP_METHOD(swoole_thread, __construct) { + php_swoole_thread_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_THIS); +} static PHP_METHOD(swoole_thread, join) { ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(ZEND_THIS)); @@ -252,6 +255,42 @@ void php_swoole_thread_rshutdown() { } } +static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject) { + char *script_file; + size_t l_script_file; + zval *args; + int argc; + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_STRING(script_file, l_script_file) + Z_PARAM_VARIADIC('+', args, argc) + ZEND_PARSE_PARAMETERS_END(); + + if (l_script_file < 1) { + zend_throw_exception(swoole_exception_ce, "exec file name is empty", SW_ERROR_INVALID_PARAMS); + return; + } + + ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(zobject)); + zend_string *file = zend_string_init(script_file, l_script_file, 1); + + zval zargv; + array_init(&zargv); + for (int i = 0; i < argc; i++) { + zend::array_add(&zargv, &args[i]); + } + zend_string *argv = php_swoole_thread_serialize(&zargv); + zval_dtor(&zargv); + + if (!argv) { + zend_string_release(file); + return; + } + + to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); + zend_update_property_long(swoole_thread_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("id"), to->thread->native_handle()); +} + void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { ts_resource(0); TSRMLS_CACHE_UPDATE(); @@ -311,41 +350,8 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { } static PHP_METHOD(swoole_thread, exec) { - char *script_file; - size_t l_script_file; - zval *args; - int argc; - - ZEND_PARSE_PARAMETERS_START(1, -1) - Z_PARAM_STRING(script_file, l_script_file) - Z_PARAM_VARIADIC('+', args, argc) - ZEND_PARSE_PARAMETERS_END(); - - if (l_script_file < 1) { - php_swoole_fatal_error(E_WARNING, "exec file name is empty"); - RETURN_FALSE; - } - object_init_ex(return_value, swoole_thread_ce); - ThreadObject *to = php_swoole_thread_fetch_object(Z_OBJ_P(return_value)); - zend_string *file = zend_string_init(script_file, l_script_file, 1); - - zval zargv; - array_init(&zargv); - for (int i = 0; i < argc; i++) { - zend::array_add(&zargv, &args[i]); - } - zend_string *argv = php_swoole_thread_serialize(&zargv); - zval_dtor(&zargv); - - if (!argv) { - zend_string_release(file); - return; - } - - to->thread = new std::thread([file, argv]() { php_swoole_thread_start(file, argv); }); - zend_update_property_long( - swoole_thread_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("id"), to->thread->native_handle()); + php_swoole_thread_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, return_value); } static PHP_METHOD(swoole_thread, getTsrmInfo) { @@ -355,4 +361,96 @@ static PHP_METHOD(swoole_thread, getTsrmInfo) { add_assoc_string(return_value, "api_name", tsrm_api_name()); } +void ArrayItem::store(zval *zvalue) { + type = Z_TYPE_P(zvalue); + switch (type) { + case IS_LONG: + value.lval = zval_get_long(zvalue); + break; + case IS_DOUBLE: + value.dval = zval_get_double(zvalue); + break; + case IS_STRING: { + value.str = zend_string_init(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), 1); + break; + } + case IS_TRUE: + case IS_FALSE: + case IS_NULL: + break; + case IS_RESOURCE: { + php_stream *stream; + int sock_fd; + int cast_flags = PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL; + if ((php_stream_from_zval_no_verify(stream, zvalue))) { + if (php_stream_cast(stream, cast_flags, (void **) &sock_fd, 1) == SUCCESS && sock_fd >= 0) { + value.lval = dup(sock_fd); + if (value.lval != -1) { + type = IS_STREAM_SOCKET; + break; + } + } + } + } + /* no break */ + default: { + auto _serialized_object = php_swoole_thread_serialize(zvalue); + if (!_serialized_object) { + type = IS_UNDEF; + break; + } else { + type = IS_SERIALIZED_OBJECT; + value.serialized_object = _serialized_object; + } + break; + } + } +} + +void ArrayItem::fetch(zval *return_value) { + switch (type) { + case IS_LONG: + RETVAL_LONG(value.lval); + break; + case IS_DOUBLE: + RETVAL_LONG(value.dval); + break; + case IS_TRUE: + RETVAL_TRUE; + break; + case IS_FALSE: + RETVAL_FALSE; + break; + case IS_STRING: + RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); + break; + case IS_STREAM_SOCKET: { + std::string path = "php://fd/" + std::to_string(value.lval); + php_stream *stream = php_stream_open_wrapper_ex(path.c_str(), "", 0, NULL, NULL); + if (stream) { + php_stream_to_zval(stream, return_value); + } + break; + } + case IS_SERIALIZED_OBJECT: + php_swoole_thread_unserialize(value.serialized_object, return_value); + break; + default: + break; + } +} + +void ArrayItem::release() { + if (type == IS_STRING) { + zend_string_release(value.str); + value.str = nullptr; + } else if (type == IS_STREAM_SOCKET) { + ::close(value.lval); + value.lval = -1; + } else if (type == IS_SERIALIZED_OBJECT) { + zend_string_release(value.serialized_object); + value.serialized_object = nullptr; + } +} + #endif diff --git a/tests/swoole_thread/co-stream.phpt b/tests/swoole_thread/co-stream.phpt new file mode 100644 index 00000000000..59f6bbe68c9 --- /dev/null +++ b/tests/swoole_thread/co-stream.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_thread: co stream +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$tm->parentFunc = function () use ($tm) { + Runtime::enableCoroutine(SWOOLE_HOOK_ALL); + Co\run(function () use ($tm) { + $queue = new Queue(); + $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + $queue->push($fp); + $thread = new Thread(__FILE__, $queue); + var_dump('main thread'); + $thread->join(); + }); +}; + +$tm->childFunc = function ($queue) use ($tm) { + var_dump('child thread'); + $fp = $queue->pop(); + Co\run(function () use ($fp, $tm) { + var_dump('child thread, co 0'); + Co\go(function () use ($tm) { + var_dump('child thread, co 1'); + $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + Assert::notEmpty($client); + $data = fread($client, 8192); + Assert::eq($data, "hello world\n"); + fclose($client); + }); + $conn = stream_socket_accept($fp, -1); + fwrite($conn, "hello world\n"); + fclose($conn); + fclose($fp); + }); +}; + +$tm->run(); +?> +--EXPECT-- +string(11) "main thread" +string(12) "child thread" +string(18) "child thread, co 0" +string(18) "child thread, co 1" diff --git a/tests/swoole_thread/stream.phpt b/tests/swoole_thread/stream.phpt new file mode 100644 index 00000000000..3052ce1ba00 --- /dev/null +++ b/tests/swoole_thread/stream.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_thread: stream +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$tm->parentFunc = function () use ($tm) { + $queue = new Queue(); + $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + $queue->push($fp); + $thread = new Thread(__FILE__, $queue, 0); + var_dump('main thread'); + $thread->join(); +}; + +$tm->childFunc = function ($queue, $id) use ($tm) { + if ($id === 0) { + var_dump('child thread 0'); + $fp = $queue->pop(); + $thread = new Thread(__FILE__, $queue, 1); + $conn = stream_socket_accept($fp, -1); + fwrite($conn, "hello world\n"); + fclose($conn); + fclose($fp); + $thread->join(); + } else { + var_dump('child thread 1'); + $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + Assert::notEmpty($client); + $data = fread($client, 8192); + Assert::eq($data, "hello world\n"); + fclose($client); + } +}; + +$tm->run(); +?> +--EXPECT-- +string(11) "main thread" +string(14) "child thread 0" +string(14) "child thread 1" From 108ee2a0e333c11404043f368a40b6d10b13c95f Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Mon, 6 May 2024 20:17:56 +0800 Subject: [PATCH 101/103] Support using stream as a thread argument --- ext-src/swoole_thread.cc | 90 +++++++++++++++++++++++------ tests/swoole_thread/stream_arg.phpt | 49 ++++++++++++++++ 2 files changed, 121 insertions(+), 18 deletions(-) create mode 100644 tests/swoole_thread/stream_arg.phpt diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index efae04f59ac..783347668c7 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -34,6 +34,8 @@ END_EXTERN_C() zend_class_entry *swoole_thread_ce; static zend_object_handlers swoole_thread_handlers; +zend_class_entry *swoole_thread_stream_ce; + static struct { char *path_translated; zend_string *argv_serialized; @@ -51,6 +53,8 @@ struct ThreadObject { static void php_swoole_thread_join(zend_object *object); static void php_swoole_thread_create(INTERNAL_FUNCTION_PARAMETERS, zval *zobject); +static int php_swoole_thread_stream_fileno(zval *zstream); +static bool php_swoole_thread_stream_restore(zend_long sockfd, zval *return_value); static thread_local zval thread_argv; static zend_long thread_resource_id = 0; @@ -146,6 +150,11 @@ void php_swoole_thread_minit(int module_number) { zend_declare_property_long(swoole_thread_ce, ZEND_STRL("id"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); zend_declare_class_constant_long( swoole_thread_ce, ZEND_STRL("HARDWARE_CONCURRENCY"), std::thread::hardware_concurrency()); + + zend_class_entry ce; + INIT_CLASS_ENTRY(ce, "Swoole\\Thread\\Stream", nullptr); + swoole_thread_stream_ce = zend_register_internal_class_ex(&ce, ZEND_STANDARD_CLASS_DEF_PTR); + zend_declare_property_long(swoole_thread_stream_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); } static PHP_METHOD(swoole_thread, __construct) { @@ -199,6 +208,24 @@ zend_string *php_swoole_thread_serialize(zval *zdata) { php_serialize_data_t var_hash; smart_str serialized_data = {0}; + if (ZVAL_IS_ARRAY(zdata)) { + zval *elem; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zdata), elem) { + ZVAL_DEREF(elem); + if (Z_TYPE_P(elem) != IS_RESOURCE) { + continue; + } + int sockfd = php_swoole_thread_stream_fileno(elem); + if (sockfd < 0) { + continue; + } + zval_ptr_dtor(elem); + object_init_ex(elem, swoole_thread_stream_ce); + zend_update_property_long(swoole_thread_stream_ce, SW_Z8_OBJ_P(elem), ZEND_STRL("fd"), sockfd); + } + ZEND_HASH_FOREACH_END(); + } + PHP_VAR_SERIALIZE_INIT(var_hash); php_var_serialize(&serialized_data, zdata, &var_hash); PHP_VAR_SERIALIZE_DESTROY(var_hash); @@ -223,6 +250,22 @@ bool php_swoole_thread_unserialize(zend_string *data, zval *zv) { swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", (zend_long) ((char *) p - ZSTR_VAL(data)), l); + } else { + if (ZVAL_IS_ARRAY(zv)) { + zval *elem; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zv), elem) { + ZVAL_DEREF(elem); + if (Z_TYPE_P(elem) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(elem), swoole_thread_stream_ce)) { + continue; + } + zend_long sockfd = zend::object_get_long(elem, ZEND_STRL("fd")); + zval_ptr_dtor(elem); + zval zstream; + php_swoole_thread_stream_restore(sockfd, &zstream); + ZVAL_COPY(elem, &zstream); + } + ZEND_HASH_FOREACH_END(); + } } return unserialized; } @@ -349,6 +392,28 @@ void php_swoole_thread_start(zend_string *file, zend_string *argv_serialized) { swoole_thread_clean(); } +static int php_swoole_thread_stream_fileno(zval *zstream) { + php_stream *stream; + int sockfd; + int cast_flags = PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL; + if ((php_stream_from_zval_no_verify(stream, zstream))) { + if (php_stream_cast(stream, cast_flags, (void **) &sockfd, 1) == SUCCESS && sockfd >= 0) { + return dup(sockfd); + } + } + return -1; +} + +static bool php_swoole_thread_stream_restore(zend_long sockfd, zval *return_value) { + std::string path = "php://fd/" + std::to_string(sockfd); + php_stream *stream = php_stream_open_wrapper_ex(path.c_str(), "", 0, NULL, NULL); + if (stream) { + php_stream_to_zval(stream, return_value); + return true; + } + return false; +} + static PHP_METHOD(swoole_thread, exec) { object_init_ex(return_value, swoole_thread_ce); php_swoole_thread_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, return_value); @@ -379,17 +444,11 @@ void ArrayItem::store(zval *zvalue) { case IS_NULL: break; case IS_RESOURCE: { - php_stream *stream; - int sock_fd; - int cast_flags = PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL; - if ((php_stream_from_zval_no_verify(stream, zvalue))) { - if (php_stream_cast(stream, cast_flags, (void **) &sock_fd, 1) == SUCCESS && sock_fd >= 0) { - value.lval = dup(sock_fd); - if (value.lval != -1) { - type = IS_STREAM_SOCKET; - break; - } - } + int sock_fd = php_swoole_thread_stream_fileno(zvalue); + if (sock_fd != -1) { + value.lval = sock_fd; + type = IS_STREAM_SOCKET; + break; } } /* no break */ @@ -424,14 +483,9 @@ void ArrayItem::fetch(zval *return_value) { case IS_STRING: RETVAL_NEW_STR(zend_string_init(ZSTR_VAL(value.str), ZSTR_LEN(value.str), 0)); break; - case IS_STREAM_SOCKET: { - std::string path = "php://fd/" + std::to_string(value.lval); - php_stream *stream = php_stream_open_wrapper_ex(path.c_str(), "", 0, NULL, NULL); - if (stream) { - php_stream_to_zval(stream, return_value); - } + case IS_STREAM_SOCKET: + php_swoole_thread_stream_restore(value.lval, return_value); break; - } case IS_SERIALIZED_OBJECT: php_swoole_thread_unserialize(value.serialized_object, return_value); break; diff --git a/tests/swoole_thread/stream_arg.phpt b/tests/swoole_thread/stream_arg.phpt new file mode 100644 index 00000000000..871049bd624 --- /dev/null +++ b/tests/swoole_thread/stream_arg.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_thread: stream as a thread argument +--SKIPIF-- + +--FILE-- +initFreePorts(); + +$tm->parentFunc = function () use ($tm) { + $fp = stream_socket_server('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + $thread = new Thread(__FILE__, $fp, 0); + var_dump('main thread'); + $thread->join(); +}; + +$tm->childFunc = function ($fp, $id) use ($tm) { + if ($id === 0) { + var_dump('child thread 0'); + $thread = new Thread(__FILE__, $fp, 1); + $conn = stream_socket_accept($fp, -1); + fwrite($conn, "hello world\n"); + fclose($conn); + fclose($fp); + $thread->join(); + } else { + var_dump('child thread 1'); + $client = stream_socket_client('tcp://127.0.0.1:' . $tm->getFreePort(), $errno, $errstr); + Assert::notEmpty($client); + $data = fread($client, 8192); + Assert::eq($data, "hello world\n"); + fclose($client); + } +}; + +$tm->run(); +?> +--EXPECT-- +string(11) "main thread" +string(14) "child thread 0" +string(14) "child thread 1" From b0cafc826bf4052585e947c062db6b4fd611e583 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Mon, 6 May 2024 20:28:20 +0800 Subject: [PATCH 102/103] optimize code --- ext-src/swoole_thread.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext-src/swoole_thread.cc b/ext-src/swoole_thread.cc index 783347668c7..0fb6cc50ee7 100644 --- a/ext-src/swoole_thread.cc +++ b/ext-src/swoole_thread.cc @@ -35,6 +35,7 @@ zend_class_entry *swoole_thread_ce; static zend_object_handlers swoole_thread_handlers; zend_class_entry *swoole_thread_stream_ce; +static zend_object_handlers swoole_thread_stream_handlers; static struct { char *path_translated; @@ -151,9 +152,8 @@ void php_swoole_thread_minit(int module_number) { zend_declare_class_constant_long( swoole_thread_ce, ZEND_STRL("HARDWARE_CONCURRENCY"), std::thread::hardware_concurrency()); - zend_class_entry ce; - INIT_CLASS_ENTRY(ce, "Swoole\\Thread\\Stream", nullptr); - swoole_thread_stream_ce = zend_register_internal_class_ex(&ce, ZEND_STANDARD_CLASS_DEF_PTR); + // only used for thread argument forwarding + SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_thread_stream, "Swoole\\Thread\\Stream"); zend_declare_property_long(swoole_thread_stream_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY); } From 9d59862c7ec070202c830841d9abd55ea2c32a74 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 9 May 2024 12:47:24 +0800 Subject: [PATCH 103/103] optimize code --- include/swoole_server.h | 8 ++++---- src/server/thread.cc | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/swoole_server.h b/include/swoole_server.h index bb00a664f09..e75f0185c86 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -464,10 +464,10 @@ class ThreadFactory : public BaseFactory { public: ThreadFactory(Server *server); ~ThreadFactory(); - void spawn_event_worker(int i); - void spawn_task_worker(int i); - void spawn_user_worker(int i); - void spawn_manager_thread(int i); + void spawn_event_worker(WorkerId i); + void spawn_task_worker(WorkerId i); + void spawn_user_worker(WorkerId i); + void spawn_manager_thread(WorkerId i); void wait(); bool start() override; bool shutdown() override; diff --git a/src/server/thread.cc b/src/server/thread.cc index 46fa1d57001..c576ce903e0 100644 --- a/src/server/thread.cc +++ b/src/server/thread.cc @@ -82,7 +82,7 @@ void ThreadFactory::create_thread(int i, _Callable fn) { threads_[i] = std::thread(fn); } -void ThreadFactory::spawn_event_worker(int i) { +void ThreadFactory::spawn_event_worker(WorkerId i) { create_thread(i, [=]() { swoole_set_process_type(SW_PROCESS_EVENTWORKER); swoole_set_thread_type(Server::THREAD_WORKER); @@ -96,7 +96,7 @@ void ThreadFactory::spawn_event_worker(int i) { }); } -void ThreadFactory::spawn_task_worker(int i) { +void ThreadFactory::spawn_task_worker(WorkerId i) { create_thread(i, [=]() { swoole_set_process_type(SW_PROCESS_TASKWORKER); swoole_set_thread_type(Server::THREAD_WORKER); @@ -120,7 +120,7 @@ void ThreadFactory::spawn_task_worker(int i) { }); } -void ThreadFactory::spawn_user_worker(int i) { +void ThreadFactory::spawn_user_worker(WorkerId i) { create_thread(i, [=]() { Worker *worker = server_->user_worker_list.at(i - server_->task_worker_num - server_->worker_num); swoole_set_process_type(SW_PROCESS_USERWORKER); @@ -134,7 +134,7 @@ void ThreadFactory::spawn_user_worker(int i) { }); } -void ThreadFactory::spawn_manager_thread(int i) { +void ThreadFactory::spawn_manager_thread(WorkerId i) { create_thread(i, [=]() { swoole_set_process_type(SW_PROCESS_MANAGER); swoole_set_thread_type(Server::THREAD_WORKER);