NAME
Linux::Event::Loop - Linux-native event loop (epoll + timerfd + signalfd + eventfd + pidfd)
SYNOPSIS
use v5.36;
use Linux::Event;
my $loop = Linux::Event->new; # epoll backend, monotonic clock
# I/O watcher (read/write) with user data stored on the watcher:
my $conn = My::Conn->new(...);
my $w = $loop->watch($fh,
read => \&My::Conn::on_read,
write => \&My::Conn::on_write,
error => \&My::Conn::on_error, # optional
data => $conn, # optional (avoid closure captures)
edge_triggered => 0, # optional, default false
oneshot => 0, # optional, default false
);
$w->disable_write;
# Timers (monotonic)
my $id = $loop->after(0.250, sub ($loop) {
say "250ms later";
});
# Signals (signalfd): strict 4-arg callback
my $sub = $loop->signal('INT', sub ($loop, $sig, $count, $data) {
say "SIG$sig ($count)";
$loop->stop;
});
# Wakeups (eventfd): watch like a normal fd
my $waker = $loop->waker;
$loop->watch($waker->fh,
read => sub ($loop, $fh, $watcher) {
my $n = $waker->drain;
... handle non-fd work ...
},
);
# Pidfds (pidfd): one-shot exit notification
my $pid = fork() // die "fork: $!";
if ($pid == 0) { exit 42 }
my $psub = $loop->pid($pid, sub ($loop, $pid, $status, $data) {
require POSIX;
if (POSIX::WIFEXITED($status)) {
say "child $pid exited: " . POSIX::WEXITSTATUS($status);
}
});
$loop->run;
DESCRIPTION
Linux::Event::Loop is a minimal, Linux-native event loop that exposes Linux FD primitives cleanly and predictably. It is built around:
epoll(7)for I/O readinesstimerfd(2)for timerssignalfd(2)for signal deliveryeventfd(2)for explicit wakeupspidfd_open(2)(via Linux::FD::Pid) for process lifecycle notifications
Linux::Event is intentionally not a networking framework, protocol layer, retry/backoff engine, process supervisor, or socket abstraction. Ownership is explicit; there is no implicit close, and teardown operations are idempotent.
CONSTRUCTION
new(%opts) -> $loop
my $loop = Linux::Event->new(
backend => 'epoll', # default
clock => $clock, # optional; must provide tick/now_ns/etc.
timer => $timer, # optional; must provide after/disarm/read_ticks/fh
);
Options:
backendEither the string
'epoll'(default) or a backend object that implementswatch,unwatch, andrun_once.clockAn object implementing the clock interface used by the scheduler. By default, a monotonic clock is used.
timerAn object implementing the timerfd interface used by the loop. By default, Linux::Event::Timer is used.
RUNNING THE LOOP
run() / run_once($timeout_seconds) / stop()
run() enters the dispatch loop and continues until stop() is called.
run_once($timeout_seconds) runs at most one backend wait/dispatch cycle. The timeout is in seconds; fractions are allowed.
WATCHERS
watch($fh, %spec) -> Linux::Event::Watcher
Create (or replace) a watcher for a filehandle.
Watchers are keyed internally by file descriptor (fd). Calling watch() again for the same fd replaces the existing watcher atomically.
Supported keys in %spec:
read- coderef (optional)write- coderef (optional)error- coderef (optional). Called onEPOLLERR.data- user data (optional). Stored on the watcher to avoid closure captures.edge_triggered- boolean (optional, advanced). Defaults to false.oneshot- boolean (optional, advanced). Defaults to false.If true, the watcher uses
EPOLLONESHOT-style semantics: after an event is delivered, the fd is disabled inside the kernel until it is re-armed. Re-arming is typically done by forcing an epollMOD(for example by togglingdisable_read/enable_readordisable_write/enable_write).
Handlers are invoked as:
read => sub ($loop, $fh, $watcher) { ... }
write => sub ($loop, $fh, $watcher) { ... }
error => sub ($loop, $fh, $watcher) { ... }
unwatch($fh) -> bool
Remove the watcher for $fh. Returns true if a watcher was removed, false if $fh was not watched (or had no fd). Calling unwatch() multiple times is safe.
Dispatch contract
When the backend reports events for a file descriptor, the loop dispatches callbacks in this order (when applicable):
This order is frozen.
SIGNALS
signal($sig_or_list, $cb, %opt) -> Linux::Event::Signal::Subscription
Register a signal handler using Linux signalfd.
$sig_or_list may be a signal number (e.g. 2), a signal name ('INT' or 'SIGINT'), or an arrayref of those values.
Callback ABI (strict): the callback is always invoked with 4 arguments:
sub ($loop, $sig, $count, $data) { ... }
Only one handler is stored per signal; calling signal() again for the same signal replaces the previous handler.
Options:
data- arbitrary user value passed as the final callback argument.
Returns a subscription handle with an idempotent cancel method.
See Linux::Event::Signal.
WAKEUPS
waker() -> Linux::Event::Wakeup
Returns the loop's singleton waker object (an eventfd(2) handle) used to wake the loop from another thread or process.
The waker is created lazily on first use and is never destroyed for the lifetime of the loop.
The waker exposes a readable filehandle ($waker->fh) suitable for $loop->watch(...), and provides $waker->signal and $waker->drain methods. No watcher is installed automatically.
See Linux::Event::Wakeup.
PIDFDS
pid($pid, $cb, %opts) -> Linux::Event::Pid::Subscription
Registers a pidfd watcher for $pid.
Callback ABI (strict): the callback is always invoked with 4 arguments:
sub ($loop, $pid, $status, $data) { ... }
If reap => 1 (default), the loop attempts a non-blocking reap of the PID and passes a wait-status compatible value in $status. If reap => 0, no reap is attempted and $status is undef.
This is a one-shot subscription: after a defined status is observed and the callback is invoked, the subscription is automatically canceled.
Replacement semantics apply per PID: calling pid() again for the same $pid replaces the existing subscription.
See Linux::Event::Pid for full semantics and caveats (child-only reaping).
TIMERS
Timers use a monotonic clock.
after($seconds, $cb) -> $id
Schedule $cb to run after $seconds. Fractions are allowed.
Timer callbacks are invoked as:
sub ($loop) { ... }
at($deadline_seconds, $cb) -> $id
Schedule $cb at an absolute monotonic deadline in seconds (same timebase as the clock used by this loop). Fractions are allowed.
cancel($id) -> bool
Cancel a scheduled timer. Returns true if a timer was removed.
NOTES
Threading and forking helpers
Linux::Event intentionally does not provide $loop->thread or $loop->fork helpers. Concurrency helpers are policy-layer constructs and belong in separate distributions. The core provides primitives (waker, pid) that make such helpers straightforward to implement in user code.
VERSION
This document describes Linux::Event::Loop version 0.006.
AUTHOR
Joshua S. Day
LICENSE
Same terms as Perl itself.
STABILITY
As of version 0.006, the public API and the following contracts are frozen:
I/O watcher callback ABI and dispatch order (error, then read, then write)
Timer callback ABI (
($loop))Signal callback ABI (
($loop, $sig, $count, $data)) and replacement semantics per signalWakeup (waker) single-instance contract
Pid subscription callback ABI (
($loop, $pid, $status, $data)) and replacement semantics per PID
Future releases will be additive and will not change existing behavior.