diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 406c9f3ccb..48d16671be 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1048,6 +1048,34 @@ void sw_php_exit(int status) { #endif } +bool sw_zval_is_serializable(zval *struc) { +again: + switch (Z_TYPE_P(struc)) { + case IS_OBJECT: { + if (Z_OBJCE_P(struc)->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) { + return false; + } + break; + } + case IS_ARRAY: { + zval *elem; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(struc), elem) { + if (!sw_zval_is_serializable(elem)) { + return false; + } + } + ZEND_HASH_FOREACH_END(); + break; + } + case IS_REFERENCE: + struc = Z_REFVAL_P(struc); + goto again; + default: + break; + } + return true; +} + static void sw_after_fork(void *args) { #ifdef ZEND_MAX_EXECUTION_TIMERS zend_max_execution_timer_init(); @@ -1478,7 +1506,7 @@ static PHP_FUNCTION(swoole_substr_unserialize) { if ((zend_long) buf_len <= offset) { RETURN_FALSE; } - if (length <= 0 || length > (zend_long)(buf_len - offset)) { + if (length <= 0 || length > (zend_long) (buf_len - offset)) { length = buf_len - offset; } zend::unserialize(return_value, buf + offset, length, options ? Z_ARRVAL_P(options) : NULL); @@ -1518,7 +1546,7 @@ static PHP_FUNCTION(swoole_substr_json_decode) { php_error_docref(nullptr, E_WARNING, "Offset must be less than the length of the string"); RETURN_NULL(); } - if (length <= 0 || length > (zend_long)(str_len - offset)) { + if (length <= 0 || length > (zend_long) (str_len - offset)) { length = str_len - offset; } /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */ diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 6f34049d6e..7e5ab5b23b 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -505,6 +505,8 @@ static inline bool sw_zval_is_process(zval *val) { return instanceof_function(Z_OBJCE_P(val), swoole_process_ce); } +bool sw_zval_is_serializable(zval *struc); + static inline bool sw_is_main_thread() { #ifdef SW_THREAD return tsrm_is_main_thread(); diff --git a/ext-src/php_swoole_server.h b/ext-src/php_swoole_server.h index 9a75a87c8d..0fe4904a0e 100644 --- a/ext-src/php_swoole_server.h +++ b/ext-src/php_swoole_server.h @@ -126,6 +126,7 @@ struct ServerObject { void register_callback(); void on_before_start(); + void copy_setting(zval *zsetting); }; struct TaskCo { diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index 400587a58a..47cb1f6867 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -235,6 +235,11 @@ static void server_free_object(zend_object *object) { zend_object_std_dtor(object); if (serv && serv->is_master()) { +#ifdef SW_THREAD + if (serv->is_thread_mode()) { + zend_string_release((zend_string *) serv->private_data_4); + } +#endif delete serv; } } @@ -808,6 +813,17 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort return zport; } +void ServerObject::copy_setting(zval *zsetting) { + zend_array *new_array = zend_array_dup(Z_ARRVAL_P(zsetting)); + zend_hash_apply(new_array, [](zval *el) -> int { + return sw_zval_is_serializable(el) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; + }); + zval znew_array; + ZVAL_ARR(&znew_array, new_array); + serv->private_data_4 = php_swoole_serialize(&znew_array); + zval_ptr_dtor(&znew_array); +} + void ServerObject::on_before_start() { /** * create swoole server @@ -995,6 +1011,12 @@ void ServerObject::on_before_start() { } } +#ifdef SW_THREAD + if (serv->is_thread_mode()) { + copy_setting(zsetting); + } +#endif + if (SWOOLE_G(enable_library)) { zend::function::call("\\Swoole\\Server\\Helper::onBeforeStart", 1, zobject); } @@ -2470,6 +2492,11 @@ static PHP_METHOD(swoole_server, getCallback) { static PHP_METHOD(swoole_server, listen) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (serv->is_worker_thread()) { + swoole_set_last_error(SW_ERROR_SERVER_UNRELATED_THREAD); + RETURN_FALSE; + } + if (serv->is_started()) { php_swoole_fatal_error(E_WARNING, "server is running, can't add listener"); RETURN_FALSE; @@ -2614,6 +2641,8 @@ static PHP_METHOD(swoole_server, start) { #ifdef SW_THREAD if (serv->is_worker_thread()) { + zval *zsetting = sw_zend_read_and_convert_property_array(Z_OBJCE_P(ZEND_THIS), zserv, ZEND_STRL("setting"), 0); + php_swoole_unserialize((zend_string *) serv->private_data_4, zsetting); worker_thread_fn(); RETURN_TRUE; } diff --git a/include/swoole_server.h b/include/swoole_server.h index e9beb91d0f..238bb0cd89 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -766,6 +766,7 @@ class Server { void *private_data_1 = nullptr; void *private_data_2 = nullptr; void *private_data_3 = nullptr; + void *private_data_4 = nullptr; Factory *factory = nullptr; Manager *manager = nullptr; diff --git a/scripts/format-changed-files.sh b/scripts/format-changed-files.sh new file mode 100755 index 0000000000..bde6775961 --- /dev/null +++ b/scripts/format-changed-files.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +changed_files=$(git status --porcelain | grep '^[ M].*\.cc$' | awk '{print $2}') + +if [ -z "$changed_files" ]; then + exit 0 +fi + +for file in $changed_files; do + echo "format $file" + clang-format -i "$file" +done + +echo "done" diff --git a/tests/swoole_thread/server/base.phpt b/tests/swoole_thread/server/base.phpt index 76303fe6c9..351c496994 100644 --- a/tests/swoole_thread/server/base.phpt +++ b/tests/swoole_thread/server/base.phpt @@ -8,6 +8,7 @@ skip_if_nts(); --FILE-- addProcess(new Swoole\Process(function ($process) use ($serv) { global $port; echo $queue->pop(-1); Co\run(function () use ($port) { - $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', $port, 2)); - $cli->send(json_encode(['type' => 'eof']) . "\r\n"); - Assert::eq($cli->recv(), "EOF\r\n"); + thread_server_test_eof_client($port); }); $atomic->set(0); echo "done\n"; diff --git a/tests/swoole_thread/server/functions.inc b/tests/swoole_thread/server/functions.inc new file mode 100644 index 0000000000..8ffe248537 --- /dev/null +++ b/tests/swoole_thread/server/functions.inc @@ -0,0 +1,12 @@ +set([ + 'open_eof_check' => true, + 'package_eof' => "\r\n", + ]); + Assert::assert($cli->connect('127.0.0.1', $port, 2)); + $cli->send(json_encode(['type' => 'eof']) . "\r\n"); + Assert::eq($cli->recv(), "EOF\r\n"); +} diff --git a/tests/swoole_thread/server/listen.phpt b/tests/swoole_thread/server/listen.phpt new file mode 100644 index 0000000000..408077f8a0 --- /dev/null +++ b/tests/swoole_thread/server/listen.phpt @@ -0,0 +1,71 @@ +--TEST-- +swoole_thread/server: listen +--SKIPIF-- + +--FILE-- +listen('127.0.0.1', $port, SWOOLE_SOCK_TCP); +$serv->set(array( + 'worker_num' => 2, + 'log_level' => SWOOLE_LOG_ERROR, + 'open_eof_check' => true, + 'package_eof' => "\r\n", + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(1); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + if ($workerId == 0) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$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 () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), 0); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + global $port; + echo $queue->pop(-1); + Co\run(function () use ($port) { + Co::join([ + Co\go(function () use ($port) { + thread_server_test_eof_client($port); + }), + Co\go(function () use ($port) { + thread_server_test_eof_client($port + 1); + }) + ]); + }); + $atomic->set(0); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown diff --git a/tests/swoole_thread/server/setting.phpt b/tests/swoole_thread/server/setting.phpt new file mode 100644 index 0000000000..19f7b53159 --- /dev/null +++ b/tests/swoole_thread/server/setting.phpt @@ -0,0 +1,53 @@ +--TEST-- +swoole_thread/server: setting +--SKIPIF-- + +--FILE-- +set(array( + 'worker_num' => N, + 'log_level' => SWOOLE_LOG_ERROR, + 'init_arguments' => function () { + global $queue, $atomic; + $queue = new Swoole\Thread\Queue(); + $atomic = new Swoole\Thread\Atomic(0); + return [$queue, $atomic]; + } +)); +$serv->on('WorkerStart', function (Swoole\Server $serv, $workerId) use ($port) { + [$queue, $atomic] = Thread::getArguments(); + Assert::isArray($serv->setting); + if ($atomic->add(1) == N) { + $queue->push("begin\n", Thread\Queue::NOTIFY_ALL); + } +}); +$serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { +}); +$serv->on('shutdown', function () { + global $queue, $atomic; + echo 'shutdown', PHP_EOL; + Assert::eq($atomic->get(), N); +}); +$serv->addProcess(new Swoole\Process(function ($process) use ($serv) { + [$queue, $atomic] = Thread::getArguments(); + echo $queue->pop(-1); + echo "done\n"; + $serv->shutdown(); +})); +$serv->start(); +?> +--EXPECT-- +begin +done +shutdown