NAME

Linux::Event::Loop - Linux-native epoll event loop

SYNOPSIS

use Linux::Event::Loop;
use Linux::Event::IO::Sock::Stream;

package Client;
use parent 'Linux::Event::IO::Sock::Stream';

sub on_data ($self, $bytes) {
    print $bytes;
}

package main;
my $loop = Linux::Event::Loop->new;
my $connection = $loop->add(Client->connect(
    host => '127.0.0.1',
    port => 9999,
));
$loop->run;

DESCRIPTION

Linux::Event::Loop owns the native epoll instance, descriptor registry, event buffer, shared timer source, and readiness dispatch. It is the only public loop class.

Public resource objects may be attached during construction with loop => $loop, or constructed detached and passed to add. add invokes the object's attachment implementation and returns that same object.

Ordered-byte resources may receive their application callbacks as subclass methods or constructor coderefs. This does not change Loop attachment or ownership; see docs/FIRST-CLASS-STREAM-CALLBACKS.md.

The public resource leaves are Linux::Event::IO::Pipe, Linux::Event::IO::TTY, Linux::Event::IO::Sock::Stream, Linux::Event::IO::Sock::Listener, Linux::Event::IO::Sock::Dgram, Linux::Event::Kernel::Timer, Linux::Event::Kernel::Signal, Linux::Event::Kernel::Event, and Linux::Event::Kernel::Process. A resource rejects attachment to a second Loop or attachment after reaching a terminal state.

watch is the low-level descriptor API. It registers immediately and returns an opaque native registration handle. The handle is not a public class or a subclassing API.

HIGH-LEVEL OBJECTS

add($object)

Attach a detached public I/O or Kernel resource and return that exact object. The object becomes owned by this Loop until its normal terminal lifecycle releases it.

The following styles are equivalent:

my $a = Client->connect(
    loop => $loop,
    host => '127.0.0.1',
    port => 9999,
);

my $b = $loop->add(Client->connect(
    host => '127.0.0.1',
    port => 9999,
));

A timer uses the same attachment contract:

package Delay;
use parent 'Linux::Event::Kernel::Timer';
sub on_timer ($self) { ... }

my $timer = $loop->add(Delay->new(after => 0.25));

The loop constructor option and add are both primary public APIs. Loop has no resource-specific factory hierarchy.

RAW DESCRIPTOR API

watch(fh => $fh, read => $callback) / watch(fd => $fd, read => $callback)

Register exactly one filehandle or integer descriptor. Supported options are:

  • read, write, error

    Coderefs for readable, writable, and terminal/error readiness. Only read and write control ordinary interest; terminal flags are always observed. For one returned event, callback order is error, read, then write. Cancellation after any callback suppresses the remaining callbacks for that event.

  • data

    An arbitrary retained value available through $registration->data.

  • no_args => 1

    Call readiness coderefs without an argument. By default each receives the opaque registration handle.

  • lean => 1

    With no_args, avoid retaining references used only by handle accessors. This is an expert registration-throughput optimization.

  • edge_triggered => 1

    Use EPOLLET. The callback must drain the descriptor until EAGAIN.

  • oneshot => 1

    Use EPOLLONESHOT. The application is responsible for its rearm policy.

Registering an fd that is already registered replaces its native registration with EPOLL_CTL_MOD. Cancelling the obsolete handle cannot remove the new registration.

watch_fd($fd, read => $callback)

Low-level positional form used by Linux::Event internals and specialized code. It creates the same native registration and has the same dispatch path as watch. Normal application code should prefer watch.

unwatch_fd($fd)

Cancel the current registration for $fd, if any. Prefer the registration's cancel method when the handle is available.

REGISTRATION METHODS

The opaque result of watch supports fd, fh, data, loop, lean, cancel, enable_read, disable_read, enable_write, and disable_write. cancel is idempotent and makes an obsolete handle inert, including after native watcher storage is reused. Cancellation releases the registration's retained Perl state. An fd-only registration returns undef from fh.

DRIVING THE LOOP

run

Wait and dispatch until stop is called.

run_once($timeout_ms = -1)

Run one epoll_wait. A negative timeout blocks indefinitely, zero polls, and a positive value is a maximum wait in milliseconds. Returns the number of events returned by epoll. A prior stop request does not suppress a later run_once call.

run_for($seconds)

Run against a monotonic deadline for the supplied non-negative number of seconds.

Only one driver method may be active for a given Loop. Calling run, run_once, or run_for recursively on that same Loop throws an exception; a callback may drive a different Loop. set_event_capacity is likewise rejected while its Loop is running or dispatching.

stop

Request that the active run or run_for return after the current dispatch work completes.

INTROSPECTION

running

True while this Loop is inside run, run_once, or run_for, including from a callback. This is an O(1) query of native driver state.

count

Return the number of current managed public resource objects. Opaque raw registrations and private helper objects are excluded.

has($object)

Return true only when the exact object is current in this Loop. An object owned by another Loop, detached, or terminal returns false.

objects

Return a new array reference containing the actual current managed resource objects. Order is unspecified. The query reads authoritative native and service registries without maintaining a duplicate public-object registry.

inspect($object)

Return a new type-specific snapshot. Every result contains type, class, and registered. A supported object which is not current in this Loop returns only those common fields with registered => 0. Current objects also include state and resource-specific fields. See docs/INTROSPECTION.md for the complete field table and the stable introspection type labels.

census

Return a new hash reference containing the documented introspection counts for ordered-byte resources, listeners, datagrams, timers, signals, eventfd notifications, and processes. See docs/INTROSPECTION.md for exact keys.

resources

Return a native resource snapshot: epoll and timer fds, total/public/internal registration counts, public registration fds, active timers, and current registry, timer-heap, and event-buffer capacities. timer_fd is undef until the first Linux::Event::Kernel::Timer creates the Loop's shared timer source. This scans native state and does not create resources.

why_alive

Return an array reference of actionable user-visible liveness reasons. Managed resource entries contain the same snapshot as inspect plus the exact object. Direct raw watch registrations appear as registration entries with their fd. Private backing registrations are not repeated as reasons.

pressure

Return conservative registrations, timers, and event_batch capacity and utilization snapshots. Event-batch maximum and utilization are undef until an epoll wait has completed. This is implementation pressure, not a synthesized health or latency score.

DIAGNOSTICS AND TUNING

stats returns counters for epoll waits, event classes, callbacks, registrations, timer scheduling and delivery, dispatch batching, and lifecycle activity. reset_stats resets them without changing profiling state. profile($boolean) returns the Loop and changes future nanosecond timing collection without resetting existing statistics. Statistics remain readable while profiling is disabled. Profiling changes the measured workload, so it should be disabled for normal benchmarks.

event_capacity returns the reusable epoll event-array capacity, default 8,192. set_event_capacity($capacity) accepts an integer from 1 through 1,048,576 while the Loop is neither running nor dispatching. A larger value can return more ready registrations from one epoll_wait; it also allocates a larger reusable array.

callback_scope_limit returns the maximum callbacks sharing one bounded Perl temporary scope, default 128. set_callback_scope_limit($limit) accepts an integer from 0 through 1,048,576. Zero uses one scope for the whole dispatch batch; a positive value rotates the scope after that many callbacks.

enable_watcher_reclaim($boolean = 1) toggles immediate watcher-structure recycling after dispatch. It defaults off and exposes an experimental native memory/throughput tradeoff. The measured defaults should normally remain unchanged unless application-specific benchmarks justify tuning them.

Loop tuning uses instance methods rather than subclass policy:

my $loop = Linux::Event::Loop->new;
$loop->set_event_capacity(16_384);
$loop->set_callback_scope_limit(256);
$loop->enable_watcher_reclaim(1); # experimental

INTERPRETER OWNERSHIP

A Loop and every native object it owns belong to the Perl interpreter that created them. They are not cloned into a new ithread. A cloned Linux::Event::Kernel::Event handle is deliberately restricted to signaling its owner through eventfd; it cannot manage the Loop, invoke callbacks, or access owner-interpreter data.

PLATFORM

Linux only. The implementation uses epoll directly.