Coro::State#

📦 max

Coro::State - the engine under Coro: Perl-level threads (“coroutines”) with their own call stacks, switched cooperatively.

use Coro;

# Three coroutines take turns: each cede lets the next ready one run.
my @workers = map {
    my $id = $_;
    async {
        for my $step (1 .. 2) {
            print "worker $id, step $step\n";
            cede;
        }
        return "worker $id done";
    };
} 1 .. 3;

print $_->join, "\n" for @workers;     # wait for each, collect its result

# A semaphore makes the others wait while one holds it.
my $lock = Coro::Semaphore->new(1);
my $t = async { my $g = $lock->guard; print "got the lock\n" };
{
    my $g = $lock->guard;
    cede;                              # $t runs, and blocks on the lock
    print "releasing, ", scalar($lock->waiters), " waiting\n";
}
$t->join;

# The engine underneath: switch between two states by hand.
my ($main, $other);
$other = Coro::State->new(sub { print "in other\n"; $other->transfer($main) });
$main  = Coro::State->new;
$main->transfer($other);
print "back in main\n";

prints worker 1, step 1 to worker 3, step 1, then the three step 2 lines, the three done lines, releasing, 1 waiting, got the lock, in other and back in main.

Most programs use Coro and never touch Coro::State directly. Coro::State is the machinery: a coroutine for a sub, and switching from one to another. Coro’s scheduler, its coroutine objects, Coro::Semaphore and Coro::Signal are built on it, and their XS half lives here too.

Each coroutine has its own $_, @_, $@, $/, $1…, $^H and %^H, die and warn hooks ($Coro::State::DIEHOOK and WARNHOOK are the defaults), default output handle, local values and lexicals. A coroutine that switches away from inside a sort block, a tie method, a DESTROY or any other callback keeps a C stack of its own until it gets back out; the size of those stacks is Coro::State::cctx_stacksize, in pointers (default 16384, which is 128 KiB).

This is a native implementation of the XS half of Coro 6.57; its Coro.pm and every Coro::* module are used unchanged. use Coro exports async, async_pool, cede, schedule, terminate, current, unblock_sub, rouse_cb and rouse_wait; the PRIO_* constants and nready on request. Coro::State exports nothing. Coro::State::BACKEND names the stack-switch method: asm on x86-64, sjlj (setjmp/longjmp) elsewhere - what Coro’s own build picks on Linux.

Coroutines: the Coro functions#

  • async { ... } @args creates a coroutine running the block with @args, puts it in the ready queue and returns its Coro object. Returning from the block ends it; an uncaught die ends the program, as in the main program.

  • async_pool { ... } @args is the same with a coroutine from a pool of reused ones: faster, runs the block in an eval (a die warns), and never terminates, so it is not to be joined.

  • Coro->new(\&sub, @args) creates a coroutine without readying it.

  • cede puts the current coroutine at the end of the ready queue and runs the next one of the same or higher priority; Coro::cede_notself cedes to any ready one, whatever its priority.

  • schedule runs the next ready coroutine without readying the current one: the way to block until something calls ->ready.

  • terminate @values ends the current coroutine with those values.

  • Coro::nready is the number of coroutines in the ready queue.

  • rouse_cb returns a callback; rouse_wait blocks until that callback (or the one passed to it) has been called, and returns the arguments it was called with. They turn a callback API into a blocking call.

  • Coro::on_enter BLOCK and Coro::on_leave BLOCK run their block whenever the current coroutine is switched to or away from, while the enclosing scope lasts.

The methods of a Coro object $coro:

  • $coro->ready queues it; false when it already was. $coro->suspend keeps the scheduler from running it until $coro->resume.

  • $coro->join waits for it to end and returns its values (the block’s return value, or what terminate or cancel gave).

  • $coro->cancel(@values) ends it at once, from outside; $coro->safe_cancel(@values) ends it from inside, so its cleanup runs in its own context, and dies when it is not in a state where that can be done. $coro->throw($exception) makes it die with $exception when it next runs.

  • $coro->on_destroy(\&cb) calls cb with its values when it ends.

  • $coro->schedule_to and $coro->cede_to switch to it directly, bypassing the queue; cede_to readies the current coroutine first.

  • $coro->prio($new) and $coro->nice($change) set its priority (from PRIO_MIN, -4, to PRIO_MAX, 3; nice subtracts) and return the previous one.

  • $coro->is_new, is_ready, is_running, is_suspended, is_zombie (ended) and is_destroyed (the same) report its state.

Coro::Semaphore->new($count) has down (wait until the count is positive, then take one), up, try (take one if available), count, adjust($delta), waiters (the waiting coroutines, or their number in scalar context) and, from Coro’s Perl half, guard. Coro::Signal->new has wait, send (wake one waiter, or the next to wait), broadcast (wake every waiter) and awaited.

Coro::State#

  • Coro::State->new(\&sub, @args) makes a state that runs sub when first transferred to; Coro::State->new with no sub makes an empty one to save the current state in. A sub that returns ends the program.

  • $prev->transfer($next) saves the running state in $prev and continues $next.

  • $state->call(\&code) and $state->eval($string) run code in the context of another state (for debuggers; the code must not switch).

  • $state->swap_defsv and swap_defav exchange the current $_ or @_ with the state’s; $state->swap_sv(\$var, \$private) makes $var hold $private’s contents whenever that state runs (calling it again with the same two undoes it).

  • $state->cancel destroys the state.

  • $state->throw, is_new, is_zombie are as for Coro objects.

  • $state->rss is the memory its stacks use, in bytes; usecount the number of transfers to it.

  • Coro::State::enable_times($bool) turns on per-coroutine time accounting and returns the previous setting; $state->times then returns its real and CPU seconds.

  • Coro::State::list returns every state, Coro objects included.

  • $state->trace($flags) traces sub calls and statements in it, as Coro::Debug uses; $state->is_traced reads the flags.

  • C stacks: $state->has_cctx says whether it holds one; Coro::State::force_cctx gives the running state one; Coro::State::cctx_count and cctx_idle count those in use and cached; cctx_max_idle($n) and cctx_stacksize($n) set the cache size (default 4) and the size of new stacks, each returning the old value.

  • $state->clone dies “Coro::State->clone has not been configured into this installation of Coro”, as in a default Coro build.

The other Coro modules (Coro::Channel, Coro::RWLock, Coro::Specific, Coro::Timer, Coro::AnyEvent, Coro::Handle, Coro::Debug, …) are Coro’s Perl modules, unchanged.

Differences from upstream#

Two, both where perl 5.44 with XS Coro crashes (t/81-xs-native/Coro/390-or-better.t):

  • Coro::State::is_ready(bless {}, 'X') and the other methods called on a blessed hash that is not a coroutine die “Coro::State object required”, as they do for every other non-coroutine. Coro reads the magic of a hash that has none and crashes.

  • binmode $fh, ':cede' with no interval uses the 0.01-second interval Coro’s layer falls back to. Coro reads the missing argument and crashes.