Skip to content

Commit f7ef45b

Browse files
authored
Support Co::cancel() (swoole#4247)
* socket/sleep * cancel api, yield/suspend * cancel AIO * rename, optimize * add tests, support cancel wait_signal, gethostbyname * optimize code, support cancel wait() * optimize tests, support cancel wait_event() * add Coroutine::isCanceled() * optimize, fix tests * Remove useless code * optimize naming, fix tests * Revert "Remove useless code" This reverts commit b64d2f2. * fix tests * fix tests[2]
1 parent 4eed1a6 commit f7ef45b

28 files changed

+562
-175
lines changed

core-tests/include/test_coroutine.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace swoole { namespace test {
1313
class coroutine
1414
{
1515
public:
16-
coroutine(const coroutine_func_t &_fn, void *_arg, int *_complete_num) :
16+
coroutine(const CoroutineFunc &_fn, void *_arg, int *_complete_num) :
1717
fn(_fn), arg(_arg), complete_num(_complete_num) { }
1818

1919
void start()
@@ -22,7 +22,7 @@ class coroutine
2222
(*complete_num)++;
2323
}
2424

25-
inline static void create(const coroutine_func_t &fn, void *arg, int *complete_num)
25+
inline static void create(const CoroutineFunc &fn, void *arg, int *complete_num)
2626
{
2727
auto test = new coroutine(fn, arg, complete_num);
2828

@@ -34,7 +34,7 @@ class coroutine
3434
ASSERT_GT(cid, 0);
3535
}
3636

37-
inline static void run(std::initializer_list<std::pair<coroutine_func_t, void*>> args)
37+
inline static void run(std::initializer_list<std::pair<CoroutineFunc, void*>> args)
3838
{
3939
int complete_num = 0;
4040
swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);
@@ -45,7 +45,7 @@ class coroutine
4545
swoole_event_wait();
4646
}
4747

48-
inline static void run(std::initializer_list<coroutine_func_t> fns)
48+
inline static void run(std::initializer_list<CoroutineFunc> fns)
4949
{
5050
int complete_num = 0;
5151
swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);
@@ -56,7 +56,7 @@ class coroutine
5656
swoole_event_wait();
5757
}
5858

59-
inline static void run(const coroutine_func_t &fn, void *arg = nullptr)
59+
inline static void run(const CoroutineFunc &fn, void *arg = nullptr)
6060
{
6161
int complete_num = 0;
6262
swoole_event_init(SW_EVENTLOOP_WAIT_EXIT);
@@ -65,7 +65,7 @@ class coroutine
6565
}
6666

6767
private:
68-
coroutine_func_t fn;
68+
CoroutineFunc fn;
6969
void *arg;
7070
int *complete_num;
7171
};

core-tests/src/coroutine/async.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ TEST(coroutine_async, usleep) {
1919
bool retval = swoole::coroutine::async(
2020
[](AsyncEvent *event) {
2121
usleep(1000);
22-
event->ret = magic_code;
22+
event->retval = magic_code;
2323
},
2424
ev);
2525
ASSERT_EQ(retval, true);
26-
ASSERT_EQ(ev.ret, magic_code);
26+
ASSERT_EQ(ev.retval, magic_code);
2727
});
2828
}
2929

core-tests/src/coroutine/system.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,14 @@ TEST(coroutine_system, flock) {
8080
swoole_event_wait();
8181
unlink(test_file);
8282
}
83+
84+
TEST(coroutine_system, cancel_sleep) {
85+
coroutine::run([](void *arg) {
86+
auto co = Coroutine::get_current_safe();
87+
Coroutine::create([co](void *){
88+
System::sleep(0.002);
89+
co->cancel();
90+
});
91+
System::sleep(1000);
92+
});
93+
}

ext-src/php_swoole.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,9 @@ PHP_MINIT_FUNCTION(swoole) {
657657
SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_PROTECT_STACK_FAILED", SW_ERROR_CO_PROTECT_STACK_FAILED);
658658
SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_STD_THREAD_LINK_ERROR", SW_ERROR_CO_STD_THREAD_LINK_ERROR);
659659
SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_DISABLED_MULTI_THREAD", SW_ERROR_CO_DISABLED_MULTI_THREAD);
660+
SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_CANNOT_CANCEL", SW_ERROR_CO_CANNOT_CANCEL);
661+
SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_NOT_EXISTS", SW_ERROR_CO_NOT_EXISTS);
662+
SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_CANCELED", SW_ERROR_CO_CANCELED);
660663

661664
/**
662665
* trace log

ext-src/php_swoole_library.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Generated by build-library.php, Please DO NOT modify!
33
*/
44

5-
/* $Id: b2d3486c3bca310c730de8575e71b27d2d0ee30d */
5+
/* $Id: 65ea83f61739de6203f1368150350fd3f91e3a7e */
66

77
static const char* swoole_library_source_constants =
88
"\n"
@@ -899,6 +899,19 @@ static const char* swoole_library_source_core_array_object =
899899
" }\n"
900900
"\n"
901901
" /**\n"
902+
" * @param mixed $key\n"
903+
" * @param mixed $default\n"
904+
" * @return ArrayObject|StringObject\n"
905+
" */\n"
906+
" public function getOr($key, $default = null)\n"
907+
" {\n"
908+
" if (!$this->exists($key)) {\n"
909+
" return $default;\n"
910+
" }\n"
911+
" return static::detectType($this->array[$key]);\n"
912+
" }\n"
913+
"\n"
914+
" /**\n"
902915
" * @return mixed\n"
903916
" */\n"
904917
" public function last()\n"
@@ -3375,7 +3388,7 @@ static const char* swoole_library_source_core_http_status =
33753388
" self::UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type',\n"
33763389
" self::REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested range not satisfiable',\n"
33773390
" self::EXPECTATION_FAILED => 'Expectation Failed',\n"
3378-
" self::MISDIRECTED_REQUEST => 'Unprocessable Entity',\n"
3391+
" self::MISDIRECTED_REQUEST => 'Misdirected Request',\n"
33793392
" self::UNPROCESSABLE_ENTITY => 'Unprocessable Entity',\n"
33803393
" self::LOCKED => 'Locked',\n"
33813394
" self::FAILED_DEPENDENCY => 'Failed Dependency',\n"
@@ -6947,7 +6960,7 @@ static const char* swoole_library_source_core_coroutine_functions =
69476960
"\n"
69486961
"function go(callable $fn, ...$args)\n"
69496962
"{\n"
6950-
" Coroutine::create($fn, ...$args);\n"
6963+
" return Coroutine::create($fn, ...$args);\n"
69516964
"}\n"
69526965
"\n"
69536966
"function defer(callable $fn)\n"

ext-src/swoole_coroutine.cc

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ SW_EXTERN_C_BEGIN
8888
static PHP_METHOD(swoole_coroutine, exists);
8989
static PHP_METHOD(swoole_coroutine, yield);
9090
static PHP_METHOD(swoole_coroutine, resume);
91+
static PHP_METHOD(swoole_coroutine, cancel);
92+
static PHP_METHOD(swoole_coroutine, isCanceled);
9193
static PHP_METHOD(swoole_coroutine, stats);
9294
static PHP_METHOD(swoole_coroutine, getCid);
9395
static PHP_METHOD(swoole_coroutine, getPcid);
@@ -113,6 +115,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_create, 0, 0, 1)
113115
ZEND_ARG_VARIADIC_INFO(0, params)
114116
ZEND_END_ARG_INFO()
115117

118+
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_cancel, 0, 0, 1)
119+
ZEND_ARG_INFO(0, cid)
120+
ZEND_END_ARG_INFO()
121+
116122
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_coroutine_resume, 0, 0, 1)
117123
ZEND_ARG_INFO(0, cid)
118124
ZEND_END_ARG_INFO()
@@ -160,6 +166,8 @@ static const zend_function_entry swoole_coroutine_methods[] =
160166
PHP_ME(swoole_coroutine_scheduler, getOptions, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
161167
PHP_ME(swoole_coroutine, exists, arginfo_swoole_coroutine_exists, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
162168
PHP_ME(swoole_coroutine, yield, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
169+
PHP_ME(swoole_coroutine, cancel, arginfo_swoole_coroutine_cancel, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
170+
PHP_ME(swoole_coroutine, isCanceled, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
163171
PHP_MALIAS(swoole_coroutine, suspend, yield, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
164172
PHP_ME(swoole_coroutine, resume, arginfo_swoole_coroutine_resume, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
165173
PHP_ME(swoole_coroutine, stats, arginfo_swoole_coroutine_void, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
@@ -352,7 +360,7 @@ void PHPCoroutine::activate() {
352360
/**
353361
* deactivate when reactor free.
354362
*/
355-
SwooleTG.reactor->add_destroy_callback(deactivate, nullptr);
363+
sw_reactor()->add_destroy_callback(deactivate, nullptr);
356364
Coroutine::activate();
357365
activated = true;
358366
}
@@ -954,9 +962,9 @@ PHP_FUNCTION(swoole_coroutine_defer) {
954962
PHPCoroutine::defer(defer_fci);
955963
}
956964

957-
PHP_METHOD(swoole_coroutine, stats) {
965+
static PHP_METHOD(swoole_coroutine, stats) {
958966
array_init(return_value);
959-
add_assoc_long_ex(return_value, ZEND_STRL("event_num"), SwooleTG.reactor ? SwooleTG.reactor->event_num : 0);
967+
add_assoc_long_ex(return_value, ZEND_STRL("event_num"), sw_reactor() ? sw_reactor()->event_num : 0);
960968
add_assoc_long_ex(
961969
return_value, ZEND_STRL("signal_listener_num"), SwooleTG.signal_listener_num + SwooleTG.co_signal_listener_num);
962970

@@ -994,7 +1002,7 @@ PHP_METHOD(swoole_coroutine, getPcid) {
9941002
RETURN_LONG(ret);
9951003
}
9961004

997-
PHP_METHOD(swoole_coroutine, getContext) {
1005+
static PHP_METHOD(swoole_coroutine, getContext) {
9981006
zend_long cid = 0;
9991007

10001008
ZEND_PARSE_PARAMETERS_START(0, 1)
@@ -1005,6 +1013,7 @@ PHP_METHOD(swoole_coroutine, getContext) {
10051013
PHPContext *task =
10061014
(PHPContext *) (EXPECTED(cid == 0) ? Coroutine::get_current_task() : Coroutine::get_task_by_cid(cid));
10071015
if (UNEXPECTED(!task)) {
1016+
swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS);
10081017
RETURN_NULL();
10091018
}
10101019
if (UNEXPECTED(task->context == (zend_object *) ~0)) {
@@ -1020,7 +1029,7 @@ PHP_METHOD(swoole_coroutine, getContext) {
10201029
RETURN_OBJ(task->context);
10211030
}
10221031

1023-
PHP_METHOD(swoole_coroutine, getElapsed) {
1032+
static PHP_METHOD(swoole_coroutine, getElapsed) {
10241033
zend_long cid = 0;
10251034
zend_long ret;
10261035

@@ -1033,7 +1042,7 @@ PHP_METHOD(swoole_coroutine, getElapsed) {
10331042
RETURN_LONG(ret);
10341043
}
10351044

1036-
PHP_METHOD(swoole_coroutine, exists) {
1045+
static PHP_METHOD(swoole_coroutine, exists) {
10371046
zend_long cid;
10381047

10391048
ZEND_PARSE_PARAMETERS_START(1, 1)
@@ -1043,7 +1052,7 @@ PHP_METHOD(swoole_coroutine, exists) {
10431052
RETURN_BOOL(Coroutine::get_by_cid(cid) != nullptr);
10441053
}
10451054

1046-
PHP_METHOD(swoole_coroutine, resume) {
1055+
static PHP_METHOD(swoole_coroutine, resume) {
10471056
long cid;
10481057
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &cid) == FAILURE) {
10491058
RETURN_FALSE;
@@ -1058,16 +1067,47 @@ PHP_METHOD(swoole_coroutine, resume) {
10581067
Coroutine *co = coroutine_iterator->second;
10591068
user_yield_coros.erase(cid);
10601069
co->resume();
1070+
10611071
RETURN_TRUE;
10621072
}
10631073

1064-
PHP_METHOD(swoole_coroutine, yield) {
1074+
static PHP_METHOD(swoole_coroutine, yield) {
10651075
Coroutine *co = Coroutine::get_current_safe();
10661076
user_yield_coros[co->get_cid()] = co;
1067-
co->yield();
1077+
1078+
Coroutine::CancelFunc cancel_fn = [](Coroutine *co){
1079+
user_yield_coros.erase(co->get_cid());
1080+
co->resume();
1081+
return true;
1082+
};
1083+
co->yield(&cancel_fn);
1084+
if (co->is_canceled()) {
1085+
swoole_set_last_error(SW_ERROR_CO_CANCELED);
1086+
RETURN_FALSE;
1087+
}
1088+
10681089
RETURN_TRUE;
10691090
}
10701091

1092+
static PHP_METHOD(swoole_coroutine, cancel) {
1093+
long cid;
1094+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &cid) == FAILURE) {
1095+
RETURN_FALSE;
1096+
}
1097+
1098+
Coroutine *co = swoole_coroutine_get(cid);
1099+
if (!co) {
1100+
swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS);
1101+
RETURN_FALSE;
1102+
}
1103+
RETURN_BOOL(co->cancel());
1104+
}
1105+
1106+
static PHP_METHOD(swoole_coroutine, isCanceled) {
1107+
Coroutine *co = Coroutine::get_current_safe();
1108+
RETURN_BOOL(co->is_canceled());
1109+
}
1110+
10711111
PHP_FUNCTION(swoole_test_kernel_coroutine) {
10721112
if (!PHPCoroutine::is_activated()) {
10731113
RETURN_FALSE;
@@ -1089,7 +1129,7 @@ PHP_FUNCTION(swoole_test_kernel_coroutine) {
10891129
});
10901130
}
10911131

1092-
PHP_METHOD(swoole_coroutine, getBackTrace) {
1132+
static PHP_METHOD(swoole_coroutine, getBackTrace) {
10931133
zend_long cid = 0;
10941134
zend_long options = DEBUG_BACKTRACE_PROVIDE_OBJECT;
10951135
zend_long limit = 0;
@@ -1106,6 +1146,7 @@ PHP_METHOD(swoole_coroutine, getBackTrace) {
11061146
} else {
11071147
PHPContext *task = (PHPContext *) PHPCoroutine::get_context_by_cid(cid);
11081148
if (UNEXPECTED(!task)) {
1149+
swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS);
11091150
RETURN_FALSE;
11101151
}
11111152
zend_execute_data *ex_backup = EG(current_execute_data);
@@ -1115,7 +1156,7 @@ PHP_METHOD(swoole_coroutine, getBackTrace) {
11151156
}
11161157
}
11171158

1118-
PHP_METHOD(swoole_coroutine, printBackTrace) {
1159+
static PHP_METHOD(swoole_coroutine, printBackTrace) {
11191160
zend_long cid = 0;
11201161
zend_long options = DEBUG_BACKTRACE_PROVIDE_OBJECT;
11211162
zend_long limit = 0;
@@ -1136,6 +1177,7 @@ PHP_METHOD(swoole_coroutine, printBackTrace) {
11361177
} else {
11371178
PHPContext *task = (PHPContext *) PHPCoroutine::get_context_by_cid(cid);
11381179
if (UNEXPECTED(!task)) {
1180+
swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS);
11391181
RETURN_FALSE;
11401182
}
11411183
zend_execute_data *ex_backup = EG(current_execute_data);
@@ -1145,7 +1187,7 @@ PHP_METHOD(swoole_coroutine, printBackTrace) {
11451187
}
11461188
}
11471189

1148-
PHP_METHOD(swoole_coroutine, list) {
1190+
static PHP_METHOD(swoole_coroutine, list) {
11491191
zval zlist;
11501192
array_init(&zlist);
11511193
for (auto &co : Coroutine::coroutines) {

ext-src/swoole_coroutine_system.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -645,12 +645,12 @@ PHP_METHOD(swoole_coroutine_system, waitSignal) {
645645
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
646646

647647
if (!System::wait_signal(signo, timeout)) {
648-
if (errno == EBUSY) {
648+
if (swoole_get_last_error() == EBUSY) {
649649
php_swoole_fatal_error(E_WARNING, "Unable to wait signal, async signal listener has been registered");
650-
} else if (errno == EINVAL) {
650+
} else if (swoole_get_last_error() == EINVAL) {
651651
php_swoole_fatal_error(E_WARNING, "Invalid signal [" ZEND_LONG_FMT "]", signo);
652652
}
653-
swoole_set_last_error(errno);
653+
errno = swoole_get_last_error();
654654
RETURN_FALSE;
655655
}
656656

@@ -676,6 +676,9 @@ PHP_METHOD(swoole_coroutine_system, waitEvent) {
676676
}
677677

678678
events = System::wait_event(fd, events, timeout);
679+
if (events < 0) {
680+
RETURN_FALSE;
681+
}
679682

680683
RETURN_LONG(events);
681684
}

include/swoole_async.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct AsyncEvent {
5050
/**
5151
* output
5252
*/
53-
ssize_t ret;
53+
ssize_t retval;
5454
int error;
5555
/**
5656
* internal use only
@@ -60,6 +60,10 @@ struct AsyncEvent {
6060
void *object;
6161
void (*handler)(AsyncEvent *event);
6262
void (*callback)(AsyncEvent *event);
63+
64+
bool catch_error() {
65+
return (error == SW_ERROR_AIO_TIMEOUT || error == SW_ERROR_AIO_CANCELED);
66+
}
6367
};
6468

6569
class AsyncThreads {

0 commit comments

Comments
 (0)