NAME

EV::Future - Minimalist and high-performance async control flow for EV

SYNOPSIS

use EV;
use EV::Future;

my @watchers;
EV::Future::parallel([
    sub { my $done = shift; push @watchers, EV::timer 0.1, 0, sub { print "Task 1 done\n"; $done->() } },
    sub { my $done = shift; push @watchers, EV::timer 0.2, 0, sub { print "Task 2 done\n"; $done->() } },
], sub {
    print "All parallel tasks finished\n";
});

EV::Future::parallel_limit([
    sub { my $done = shift; push @watchers, EV::timer 0.1, 0, sub { print "Task A\n"; $done->() } },
    sub { my $done = shift; push @watchers, EV::timer 0.2, 0, sub { print "Task B\n"; $done->() } },
    sub { my $done = shift; push @watchers, EV::timer 0.1, 0, sub { print "Task C\n"; $done->() } },
], 2, sub {
    print "All limited-parallel tasks finished\n";
});

EV::Future::series([
    sub { my $done = shift; print "Task 1 start\n"; $done->() },
    sub { my $done = shift; print "Task 2 start\n"; $done->() },
], sub {
    print "All series tasks finished\n";
});

EV::run;

DESCRIPTION

Focuses on performance and minimalism, offloading task management to XS.

TASKS

Each task is a coderef that receives a single argument: a "done" callback. The task MUST call this callback exactly once when it is finished.

Exceptions

If a task throws an exception (e.g., via die), the exception will be propagated immediately. EV::Future ensures that internal memory is cleaned up correctly in this case.

Double-calls

Calling the done callback more than once for a single task is considered incorrect usage. In safe mode (the default), EV::Future prevents catastrophic failure:

  • In parallel and parallel_limit, extra calls to a specific done callback are silently ignored.

  • In series, extra calls to a specific done callback are ignored. Only the done callback provided to the currently active task can advance the series.

In unsafe mode, these protections are not active. Double-calls will corrupt the internal completion counter, which may cause final_cb to be invoked multiple times or trigger use-after-free.

FUNCTIONS

parallel(\@tasks, \&final_cb, [$unsafe])

Executes all tasks concurrently. The final_cb is called once all tasks have invoked their done callback.

If the optional $unsafe flag is set to a true value, EV::Future will skip evaluating tasks inside a eval block and will reuse a single callback object for all tasks. This provides a massive performance boost (up to 100% faster) but bypasses per-task double-call protection and will cause issues if tasks throw exceptions. Use only when performance is critical and tasks are well-behaved.

parallel_limit(\@tasks, $limit, \&final_cb, [$unsafe])

Executes tasks concurrently, but with at most $limit tasks in-flight at any time. As each task completes, the next pending task is dispatched.

$limit is clamped to the range 1..scalar(@tasks). With $limit >= @tasks this behaves like parallel; with $limit == 1 it runs tasks sequentially.

The $unsafe flag has the same meaning as in parallel.

series(\@tasks, \&final_cb, [$unsafe])

Executes tasks one by one. The next task is only started after the current task calls its done callback.

If the optional $unsafe flag is set to a true value, error-checking overhead is bypassed for maximum performance. See parallel for warnings about exceptions.

To cancel the series and skip all subsequent tasks, pass a true value to the done callback:

series([
    sub { my $d = shift; $d->(1) }, # Cancel here
    sub { die "This will never run" },
], sub {
    print "Series finished early\n";
});

BENCHMARKS

1000 synchronous tasks, 5000 iterations (bench/comparison.pl):

--- PARALLEL (iterations/sec) ---
EV::Future (unsafe)     4,386
EV::Future (safe)       2,262
AnyEvent cv             1,027
Future::XS                982
Promise::XS                32

--- PARALLEL LIMIT 10 (iterations/sec) ---
EV::Future (unsafe)     4,673
EV::Future (safe)       2,688
Future::Utils fmap_void   431

--- SERIES (iterations/sec) ---
EV::Future (unsafe)     5,000
AnyEvent cv             3,185
EV::Future (safe)       2,591
Future::XS                893
Promise::XS               809

Safe mode allocates a per-task CV for double-call protection and wraps each dispatch in G_EVAL. Unsafe mode reuses a single shared CV and skips G_EVAL, roughly doubling throughput.

AUTHOR

vividsnow

LICENSE

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.