NAME
Linux::Event::Loop - Run and coordinate Linux::Event resources
SYNOPSIS
use v5.36;
use Linux::Event::Loop;
use Linux::Event::Kernel::Timer;
my $loop = Linux::Event::Loop->new;
my $timer = Linux::Event::Kernel::Timer->new(
loop => $loop,
after => 1,
on_timer => sub ($timer) {
say "One second has passed";
$loop->stop;
},
);
$loop->run;
DESCRIPTION
Linux::Event::Loop is the heart of a Linux::Event application.
You create resources such as sockets, timers, processes, signals, or filesystem watches and attach them to a Loop.
The Loop then waits until something happens and calls the appropriate Perl callback.
A simple program usually follows this pattern:
my $loop = Linux::Event::Loop->new;
# Create resources and attach them to $loop.
$loop->run;
The Loop uses Linux epoll internally, but normal applications do not need to work with epoll directly.
CREATING A LOOP
Create a Loop with:
my $loop = Linux::Event::Loop->new;
A program may have more than one Loop, although most applications need only one.
Resources belong to the Loop they are attached to. A resource cannot normally be moved to another Loop after it has been attached.
ADDING RESOURCES
Most Linux::Event resources can be attached to a Loop in either of two ways.
The first is to supply the Loop when constructing the resource:
my $timer = Linux::Event::Kernel::Timer->new(
loop => $loop,
after => 1,
on_timer => sub ($timer) {
say "timer fired";
},
);
The second is to construct the resource first and add it afterward:
my $timer = Linux::Event::Kernel::Timer->new(
after => 1,
on_timer => sub ($timer) {
say "timer fired";
},
);
$loop->add($timer);
Both styles are normal Linux::Event APIs.
add($object)
$loop->add($object);
Attach a Linux::Event resource to this Loop.
add returns the same object, so this is also convenient:
my $timer = $loop->add(
Linux::Event::Kernel::Timer->new(
after => 1,
on_timer => sub ($timer) {
say "timer fired";
},
)
);
Once attached, the Loop keeps the resource alive until that resource reaches the end of its normal lifecycle.
For example, a Timer remains owned by the Loop until it is cancelled or finishes.
RUNNING THE LOOP
run
$loop->run;
Run the event loop until stop is requested.
While run is active, the Loop waits for events and dispatches callbacks as they become ready.
For many applications this is the only Loop-driving method that is needed.
stop
$loop->stop;
Ask an active run or run_for to finish after the current dispatch work is complete.
For example:
my $timer = Linux::Event::Kernel::Timer->new(
loop => $loop,
after => 5,
on_timer => sub ($timer) {
say "Finished";
$loop->stop;
},
);
$loop->run;
stop does not destroy the Loop. It may be driven again later.
run_for($seconds)
$loop->run_for(10);
Run the Loop for at most the supplied number of seconds.
The time is measured with a monotonic clock, so changes to the system wall clock do not change the deadline.
Fractional seconds may be used.
run_once($timeout_ms)
$loop->run_once(100);
Wait for and dispatch one batch of events.
The timeout is expressed in milliseconds:
A negative value waits indefinitely.
Zero does not wait.
A positive value is the maximum amount of time to wait.
If the argument is omitted, run_once waits indefinitely.
This method is useful when an application wants explicit control over each Loop turn.
DEFERRED WORK
defer($callback)
$loop->defer(sub {
say "Run this shortly";
});
defer schedules a callback to run on a later Loop turn.
It does not call the callback immediately.
This is useful when work needs to happen soon, but should not happen recursively inside the callback that is currently running.
For example:
sub on_message ($stream, $message) {
update_state($message);
$loop->defer(sub {
process_updated_state();
});
}
The deferred callback takes no arguments.
Callbacks are normally delivered in the order they were queued.
If a deferred callback queues another deferred callback, the new callback waits for a later deferred turn rather than running recursively during the current one.
Cancelling deferred work
defer returns a small one-shot handle:
my $pending = $loop->defer(sub {
expensive_work();
});
It supports:
$pending->cancel;
and:
$pending->is_active;
cancel is safe to call more than once.
Dropping your own reference to the handle does not cancel the callback. The Loop keeps pending deferred work alive until it runs, is cancelled, or the Loop is destroyed.
Errors in deferred callbacks
If a deferred callback dies, the exception propagates through the Loop driver.
Later deferred callbacks are left pending so they can still run if the application catches the exception and drives the Loop again.
Fairness
One deferred dispatch processes at most 1,024 queued entries.
If more work remains, the Loop schedules another turn.
This prevents a self-sustaining chain of deferred callbacks from indefinitely preventing sockets, timers, and other kernel events from running.
Threads and processes
defer schedules Perl callbacks only within the interpreter that owns the Loop.
It is not a cross-thread or cross-process callback queue.
To wake a Loop from another thread or process, pass the actual data through an appropriate shared queue or IPC mechanism and use Linux::Event::Kernel::Event to wake the Loop.
CHECKING LOOP STATE
running
if ($loop->running) {
...
}
Returns true while this Loop is currently inside run, run_for, run_once, or poll.
It is also true when called from a callback dispatched by one of those methods.
A Loop cannot recursively drive itself. Calling another driving method on the same Loop while it is already dispatching throws an exception.
A callback may, however, drive a different Loop.
LOOP-AWARE FORKING
fork(%disposition)
Linux::Event provides a managed form of fork for applications that need to fork after Loop resources already exist.
For example:
my $pid = $loop->fork(
share => [ $listener ],
clone => [ $timer ],
move => [ $connection ],
);
The return value follows Perl's normal fork convention:
The parent receives the child's positive PID.
The child receives zero.
A system
forkfailure returns undef and leaves$!set.
The important difference from calling CORE::fork directly is that Linux::Event rebuilds the child's event-loop infrastructure and applies an explicit plan for existing resources.
The three dispositions
Resources listed under share, clone, or move receive different treatment.
share
The resource remains active in both processes using intentionally shared underlying operating-system state.
For example, a listening socket can be shared so either process may accept new connections.
clone
The child gets its own independent equivalent resource.
The parent keeps its original resource.
For example, cloning a Timer gives the child its own timer scheduled for the same absolute monotonic deadline.
move
The resource becomes child-only.
The child first reconstructs the resource successfully. The parent then gives up its side of the resource before the child is released to continue.
Unlisted resources
Resources that are not listed are parent-only.
Their inherited copies in the child are made inactive without calling normal application lifecycle callbacks.
This makes the safe default explicit: a resource does not accidentally become active in both processes merely because fork duplicated the process.
Currently supported dispositions
The initial managed-fork support is intentionally conservative.
- Listener
-
Supports
shareandmove. - Timer
-
Supports
cloneandmove. - Inotify
-
Supports
cloneandmove. - Established plain Stream sockets
-
Support
move.shareis not supported for Stream connections.
Other public resource types currently use the default parent-only behavior in the child.
Pending connections, active resolver requests, and Stream transports that cannot be safely reconstructed cause managed fork to reject the operation.
When fork may be called
$loop->fork(...) may only be called while the Loop is quiescent.
It cannot be called while run, run_once, run_for, poll, or one of their callbacks is actively dispatching.
In particular, do not call it directly from an event callback.
Threads and fork
Managed fork assumes the application does not have unrelated live native threads at the time of the fork.
Linux::Event can prepare its own resolver service, but it cannot make arbitrary third-party native libraries or application-created thread state safe after a process fork.
Why ordinary CORE::fork is different
The Loop belongs to the process that created it.
After an ordinary CORE::fork, the child must not continue using the inherited Loop as though nothing happened.
Linux::Event detects attempts to drive, modify, introspect, or tune such an inherited parent Loop and throws instead of silently operating on unsafe copied reactor state.
Use $loop->fork(...) when existing Linux::Event resources must survive into the child.
LOW-LEVEL FILE DESCRIPTOR WATCHING
Most applications should use Linux::Event resource classes such as Stream, Listener, Timer, Signal, Process, and Inotify.
For specialized code, the Loop can also watch a file descriptor directly.
watch
A filehandle may be watched:
my $watch = $loop->watch(
fh => $fh,
read => sub ($watch) {
...
},
);
or an integer file descriptor may be used:
my $watch = $loop->watch(
fd => $fd,
read => sub ($watch) {
...
},
);
The returned value is an opaque registration handle.
Read, write, and error callbacks
watch accepts:
read-
Called when the descriptor is ready for reading.
write-
Called when the descriptor is ready for writing.
error-
Called for terminal or error readiness.
If one kernel event contains several kinds of readiness, callbacks run in this order:
error
read
write
If one callback cancels the registration, callbacks later in that sequence are not called for the same event.
data
An arbitrary value may be stored with the registration:
my $watch = $loop->watch(
fd => $fd,
data => $state,
read => sub ($watch) {
my $state = $watch->data;
...
},
);
no_args
Normally each readiness callback receives the registration handle.
Use:
no_args => 1
to call the callbacks without arguments.
edge_triggered
edge_triggered => 1
uses epoll edge-triggered readiness.
This is an advanced option. Code using it must completely drain the descriptor until it reaches EAGAIN.
oneshot
oneshot => 1
uses EPOLLONESHOT.
The application is responsible for deciding when and how to re-enable readiness.
lean
no_args => 1,
lean => 1,
avoids retaining some state that exists only to support registration-handle accessors.
This is an advanced optimization for code where registration throughput has been measured to matter.
One registration per descriptor
A Loop has one current low-level registration for a file descriptor.
Watching the same descriptor again replaces the current registration.
An older handle becomes obsolete and cannot accidentally remove the newer registration if it is later cancelled.
REGISTRATION HANDLE
The value returned by watch supports:
fd
fh
data
loop
lean
cancel
enable_read
disable_read
enable_write
disable_write
cancel
$watch->cancel;
Cancel the registration.
Cancellation is idempotent.
enable_read / disable_read
Enable or disable ordinary read interest.
enable_write / disable_write
Enable or disable ordinary write interest.
fd
Return the integer file descriptor.
fh
Return the retained Perl filehandle when the registration was created with fh.
An fd-only registration returns undef.
data
Return the value supplied with data.
loop
Return the owning Loop.
OTHER LOW-LEVEL METHODS
watch_fd
$loop->watch_fd($fd, read => sub { ... });
This is a lower-level positional form used primarily by Linux::Event internals and specialized code.
Normal application code should prefer watch.
unwatch_fd
$loop->unwatch_fd($fd);
Cancel the current registration for that descriptor, if one exists.
When a registration handle is already available, its cancel method is usually clearer.
USING LINUX::EVENT INSIDE ANOTHER EVENT LOOP
Linux::Event can be driven underneath another event system.
The two important methods are poll_fd and poll.
poll_fd
my $fd = $loop->poll_fd;
Return the epoll descriptor owned by this Linux::Event Loop.
That descriptor becomes readable when Linux::Event has kernel events waiting.
A foreign event loop can therefore watch it just like another readable file descriptor.
The returned fd is borrowed.
Do not close it.
Linux::Event owns it and will close it when the Loop is destroyed.
If another API requires a Perl filehandle, duplicate the descriptor rather than taking ownership of the original.
poll
my $events = $loop->poll;
Perform one nonblocking Linux::Event dispatch turn.
poll does not wait for events.
A typical foreign-loop adapter therefore does this:
1. Watch $loop->poll_fd for readability.
2. When it becomes readable, call $loop->poll once.
poll returns the number of events returned by epoll for that turn.
This is the supported foreign-loop integration boundary.
INTROSPECTION
Linux::Event provides several methods for asking what a Loop currently owns and why it is still alive.
These methods are primarily diagnostic tools.
count
my $count = $loop->count;
Return the number of current public Linux::Event resource objects.
Private helper registrations and raw watch registrations are not included.
has($object)
if ($loop->has($timer)) {
...
}
Return true when that exact resource is currently owned by this Loop.
objects
my $objects = $loop->objects;
Return a new array reference containing the current public resource objects.
The order is unspecified.
inspect($object)
my $info = $loop->inspect($object);
Return a snapshot describing a Linux::Event resource.
Every result contains basic fields such as its type, class, and whether it is currently registered with this Loop.
Active resources also include resource-specific information.
See docs/INTROSPECTION.md for the complete field definitions.
census
my $counts = $loop->census;
Return counts grouped by Linux::Event resource type.
See docs/INTROSPECTION.md for the exact keys.
resources
my $resources = $loop->resources;
Return a lower-level snapshot of Loop resources such as registrations, timer state, capacities, and backing descriptors.
This is useful when investigating what the reactor itself currently owns.
why_alive
my $reasons = $loop->why_alive;
Return an array reference explaining which user-visible resources are keeping the Loop active.
This is particularly useful when a program appears to have finished its work but still has live resources.
pressure
my $pressure = $loop->pressure;
Return capacity and utilization information for native registration, timer, and event-batch storage.
This describes implementation pressure. It is not a general performance, latency, or health score.
STATISTICS
stats
my $stats = $loop->stats;
Return Loop counters such as epoll waits, callbacks, registrations, timer activity, and dispatch activity.
reset_stats
$loop->reset_stats;
Reset diagnostic counters without changing the Loop's profiling setting.
profile($boolean)
$loop->profile(1);
Enable or disable nanosecond timing collection used by Loop statistics.
Profiling adds measurement overhead and should normally be disabled when running performance benchmarks.
ADVANCED TUNING
The default Loop settings are intended to work well for normal applications.
Change them only when application-specific measurements show a reason to do so.
event_capacity
my $capacity = $loop->event_capacity;
Return the reusable epoll event-array capacity.
The default is 8,192 events.
set_event_capacity($capacity)
$loop->set_event_capacity(16_384);
Change the event-array capacity.
The value must be between 1 and 1,048,576.
A larger value allows one epoll wait to return more ready registrations at once but also uses a larger reusable event array.
This setting cannot be changed while the Loop is running or dispatching.
callback_scope_limit
my $limit = $loop->callback_scope_limit;
Return the number of callbacks allowed to share one bounded Perl temporary scope.
The default is 128.
set_callback_scope_limit($limit)
$loop->set_callback_scope_limit(256);
Set the callback scope limit.
A value of zero allows the entire dispatch batch to use one scope.
Positive values rotate the scope after that many callbacks.
This is a performance and memory-lifetime tuning control and should normally be left at its measured default.
enable_watcher_reclaim
$loop->enable_watcher_reclaim(1);
Enable immediate recycling of native watcher structures after dispatch.
This is experimental and defaults to disabled.
It exposes a memory-versus-throughput tradeoff and should be changed only when application benchmarks justify it.
INTERPRETER OWNERSHIP
A Loop belongs to the Perl interpreter that created it.
The Loop itself and the native objects it owns are not cloned into another Perl ithread.
Linux::Event::Kernel::Event has limited support for being signaled from another thread, but that does not give the other thread access to the owning Loop or its Perl callbacks.
PUBLIC RESOURCE CLASSES
The main resource types that can be attached to a Loop are:
PLATFORM
Linux::Event::Loop runs only on Linux.
It uses Linux epoll directly.
SEE ALSO
Linux::Event, Linux::Event::Kernel::Timer, Linux::Event::IO::Sock::Stream, docs/INTROSPECTION.md.