Coroutine-safe exit()

Latest version: pecl install openswoole-26.2.0 | composer require openswoole/core:26.2.0

Since: OpenSwoole v26.2.0, PHP 8.4+

Description

In PHP 8.4, exit() was changed from an opcode to a function, which allows OpenSwoole to intercept it. When exit() is called inside a coroutine, OpenSwoole throws an openswoole_exit_exception instead of terminating the entire worker process.

This prevents a single coroutine from crashing the worker and affecting all other active connections and coroutines.

Example: exit() Inside a Coroutine

<?php
use OpenSwoole\Http\Server;
use OpenSwoole\Http\Request;
use OpenSwoole\Http\Response;

$server = new Server("0.0.0.0", 9501);

$server->on("request", function (Request $request, Response $response) {
    if ($request->get['action'] ?? '' === 'quit') {
        // On PHP 8.4+, this throws openswoole_exit_exception
        // instead of killing the worker process
        $response->end("Exiting coroutine\n");
        exit(0);
    }

    $response->end("Hello World\n");
});

$server->start();

Example: Catching exit() Exception

<?php
use OpenSwoole\Coroutine;

Coroutine::set([
    'hook_flags' => OpenSwoole\Runtime::HOOK_ALL,
]);

co::run(function () {
    go(function () {
        try {
            echo "Before exit\n";
            exit(1);
        } catch (\OpenSwoole\ExitException $e) {
            echo "Caught exit with status: " . $e->getStatus() . "\n";
        }
        // Coroutine continues after catching the exception
        echo "After exit catch\n";
    });

    go(function () {
        Co::sleep(0.1);
        // This coroutine is NOT affected by the exit() in the other coroutine
        echo "Other coroutine still running\n";
    });
});

Example: Legacy Behavior Comparison

<?php
// PHP < 8.4: exit() terminates the entire worker process
// PHP 8.4+ with OpenSwoole 26.2.0: exit() throws openswoole_exit_exception

co::run(function () {
    go(function () {
        echo "Coroutine 1: working...\n";
        Co::sleep(0.5);
        exit(0); // Throws exception, only this coroutine is terminated
    });

    go(function () {
        echo "Coroutine 2: working...\n";
        Co::sleep(1);
        echo "Coroutine 2: still alive!\n"; // This will execute
    });
});

Notes

  • This feature requires PHP 8.4 or later where exit() is a function rather than an opcode
  • On PHP 8.3 and below, exit() still terminates the worker process
  • The exception class is OpenSwoole\ExitException (or openswoole_exit_exception)
  • You can catch the exception to handle cleanup or logging before the coroutine terminates
  • Other coroutines running in the same worker are not affected
Last updated on February 28, 2026