I believe that many users wanted an API to cancel the coroutine before, but it has not been added. Now it has been added in the v4.7 version:
For specific implementation, see: #4247 , #4249
New API & constants
Two new APIs have been added, namely
Co::cancel($cid): bool
Used to cancel a coroutine, but cannot initiate a cancel operation on the current coroutine
with
Co::isCanceled(): bool
Used to determine whether the current coroutine has been cancelled
Three new error codes have been added:
constant | meaning |
---|---|
SWOOLE_ERROR_CO_CANNOT_CANCEL | Coroutine cannot be cancelled |
SWOOLE_ERROR_CO_NOT_EXISTS | Coroutine does not exist |
SWOOLE_ERROR_CO_CANCELED | The coroutine has been cancelled |
Description
This API is used to cancel another or event callback
Only the coroutine in the cancelable operation can be cancelled. When a coroutine is successfully cancelled, the context will be immediately switched to the corresponding coroutine
Try to cancel a coroutine that is in an uncancelable operation, 060bedd43b9b39 will return Co::cancel()
succeeds, and true
false
,
swoole_last_error()
at this time, there may be two situations:
SWOOLE_ERROR_CO_NOT_EXISTS
does not exist 060bedd43b9b8f- The coroutine is in an
SWOOLE_ERROR_CO_CANNOT_CANCEL
You can use Co::isCanceled()
to determine whether the current operation is manually canceled. If manual cancellation ends normally, it will return to true
. If it fails, it will return to false
Currently, the cancellation of most of the coroutine APIs is basically supported, including:
- socket
- AsyncIO (fread, gethostbyname ...)
- sleep
- waitSignal
- wait/waitpid
- waitEvent
- Co::suspend/Co::yield
- channel
- native curl (SWOOLE_HOOK_NATIVE_CURL)
There are two uninterruptible scenarios
- A coroutine forced to be switched by the CPU interrupt scheduler
- During file lock operation
However, cancellation may be allowed in subsequent versions, so stay tuned
scenes to be used
The cancellation function based on the coroutine can be implemented on the user side:
- Overtime circuit breaker based on coroutine granularity
In the previous version, suspended coroutines cannot be actively scheduled. Co::resume()
Co::cancel()
is that not only the manual Co::yield()
coroutine can be cancelled, but all the coroutines that are allowed to be cancelled can be cancelled.
- Better API design
Unlike traditional PHP APIs with similar functions, a large number of APIs in Swoole have added timeout parameters. Of course, there are also some that are difficult to add or inappropriate to add timeout parameters, such as file operation series functions. Now everything is possible, yes. Realize the timeout of any IO operation at the PHP layer without relying on the underlying API design
Example
Let's take a look at some sample codes to understand the usage of coroutine cancellation:
Cannot initiate cancellation of the current coroutine and non-existent coroutine
A coroutine is automatically created in the coroutine container, and Co::cancel()
is called to cancel it. At this time, it cannot be cancelled. At the same time, there is only one coroutine in the coroutine container, and it is impossible to cancel a non-existent coroutine.
use Swoole\Coroutine;
use function Swoole\Coroutine\run;
run(function () {
assert(Coroutine::cancel(Coroutine::getCid()) === false);
assert(swoole_last_error() === SWOOLE_ERROR_CO_CANNOT_CANCEL);
assert(Coroutine::cancel(999) === false);
assert(swoole_last_error() === SWOOLE_ERROR_CO_NOT_EXISTS);
});
The following are three examples demonstrate in Co::suspend/Co::yield
, AsyncIO
and channel
use sleep
to forge timeout
after cancellation
Co::suspend/Co::yield
use Swoole\Coroutine;
use Swoole\Coroutine\System;
use function Swoole\Coroutine\run;
use function Swoole\Coroutine\go;
run(function () {
$cid = Coroutine::getCid();
go(function () use ($cid) {
System::sleep(0.002);
assert(Coroutine::cancel($cid) === true);
});
$retval = Coroutine::suspend();
echo "Done\n";
assert($retval === false);
assert(swoole_last_error() === SWOOLE_ERROR_CO_CANCELED);
});
AsyncIO
use Swoole\Coroutine;
use Swoole\Event;
use Swoole\Coroutine\System;
use function Swoole\Coroutine\run;
run(function () {
$cid = Coroutine::getCid();
Event::defer(function () use ($cid) {
assert(Coroutine::cancel($cid) === true);
});
$retval = System::gethostbyname('www.baidu.com');
echo "Done\n";
assert($retval === false);
assert(swoole_last_error() === SWOOLE_ERROR_AIO_CANCELED);
});
channel
use Swoole\Coroutine;
use Swoole\Coroutine\System;
use function Swoole\Coroutine\run;
use function Swoole\Coroutine\go;
run(function () {
$chan = new Coroutine\Channel(1);
$cid = Coroutine::getCid();
go(function () use ($cid) {
System::sleep(0.002);
assert(Coroutine::cancel($cid) === true);
});
assert($chan->push("hello world [1]", 100) === true);
assert(Coroutine::isCanceled() === false);
assert($chan->errCode === SWOOLE_CHANNEL_OK);
assert($chan->push("hello world [2]", 100) === false);
assert(Coroutine::isCanceled() === true);
assert($chan->errCode === SWOOLE_CHANNEL_CANCELED);
echo "Done\n";
});
When external use Co::cancel()
cancel the suspended state of a coroutine, the API called by the coroutine will immediately return to failure, and the program code will continue to execute downward.
By judging the return value and error code of the coroutine operation function/method, or using Co::isCanceled()
judge whether it is cancelled.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。