Preview of new features in the software v4.7 version Co:: cancel()

Time:2021-9-18

I believe that many users wanted an API to cancel the collaboration process before, but they didn’t add it. Now it is added in v4.7:

For specific implementation, see:#4247#4249

New API & constants

Two APIs have been added, namely

Co::cancel($cid): bool

It is used to cancel a collaboration, but cannot cancel the current collaboration

and

Co::isCanceled(): bool

It is used to judge whether the current collaboration is cancelled

Three error codes have been added:

constant meaning
SWOOLE_ERROR_CO_CANNOT_CANCEL The collaboration cannot be cancelled
SWOOLE_ERROR_CO_NOT_EXISTS Co process does not exist
SWOOLE_ERROR_CO_CANCELED The collaboration has been cancelled

explain

This API is used toA synergetic processperhapsevent callbacks Cancel another collaboration in.

Only a collaboration in cancelable operation can be cancelled. When a collaboration is successfully cancelled, the context will immediately switch to the corresponding collaboration

Attempt to cancel a collaboration in a non cancellable operation,Co::cancel()Return on successtrue, failure will be returnedfalse,

Called at this timeswoole_last_error(), there may be two situations:

  1. Co process does not existSWOOLE_ERROR_CO_NOT_EXISTS
  2. The collaboration is in a non cancellable stateSWOOLE_ERROR_CO_CANNOT_CANCEL

Can passCo::isCanceled()To determine whether the current operation is manually cancelled. If the manual cancellation ends normally, it will returntrue, if failed, will returnfalse

At present, it basically supports the cancellation of most collaboration APIs, including:

  1. socket
  2. AsyncIO (fread, gethostbyname …)
  3. sleep
  4. waitSignal
  5. wait/waitpid
  6. waitEvent
  7. Co::suspend/Co::yield
  8. channel
  9. native curl (SWOOLE_HOOK_NATIVE_CURL)

There are two non interruptible scenarios

  1. Coprocess forcibly switched by CPU interrupt scheduler
  2. During file lock operation

However, cancellation may also be allowed in subsequent versions. Please look forward to it

Usage scenario

Based on the function of collaborative process cancellation, it can be realized on the user side:

  • Timeout detection based on CO process granularity

In previous versions, suspended processes cannot be actively scheduled, andCo::cancel()FollowCo::resume()The difference is that you can not just cancel manualCo::yield()You can cancel all allowed collaborations.

  • Better API design

Different from the APIs with similar functions of traditional PHP, a large number of APIs in swoole have added timeout parameters. Of course, some are difficult or inappropriate to add timeout parameters, such as file operation series functions. Now everything is possible. The timeout of IO operation can be realized in 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 collaboration cancellation:

You cannot cancel the current collaboration or a collaboration that does not exist

When a collaboration is automatically created in the collaboration container, theCo::cancel()Cancel. You cannot cancel at this time; At the same time, there is only one process in the process container, and it is impossible to cancel a nonexistent process.

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 three examples demonstrateCo::suspend/Co::yieldAsyncIOandchannelUsed insleepTo forgetimeoutCancel after

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 used externallyCo::cancel()When the suspended state of a collaboration is cancelled, the API called by the collaboration will immediately return failure, and the program code will continue to execute downward.

By judging the return value and error code of the co process operation function / method, or useCo::isCanceled()Judge whether it has been cancelled.

Preview of new features in the software v4.7 version Co:: cancel()