NAME

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

SYNOPSIS

use Linux::Event::Loop;

my $loop = Linux::Event::Loop->new;
my $stream = $loop->add(MyStream->connect(
    host => '127.0.0.1', # required
    port => 9999,        # required
));
$loop->run;

DESCRIPTION

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

High-level objects may be attached in either of two equivalent ways:

my $stream = MyStream->connect(
    loop => $loop,        # optional: attach immediately
    host => '127.0.0.1',  # required
    port => 9999,         # required
);

my $stream = MyStream->connect(
    host => '127.0.0.1', # required
    port => 9999,        # required
);
$loop->add($stream);

add invokes the concrete object's attachment implementation and returns the same object. Stream, Listener, Datagram, Timer, Signal, Wakeup, and Process reject 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)

Attaches a detached Linux::Event::Stream, Linux::Event::Listener, Linux::Event::Datagram, Linux::Event::Timer, Linux::Event::Signal, Linux::Event::Wakeup, or Linux::Event::Process and returns that exact object. The object becomes owned by this Loop. Attaching it again, attaching it to another Loop, or attaching a terminal object throws an exception.

The following are equivalent:

my $a = MyStream->connect(
    loop => $loop,       # optional: attach immediately
    host => '127.0.0.1', # required
    port => 9999,        # required
);
my $b = $loop->add(MyStream->connect(
    host => '127.0.0.1', # required
    port => 9999,        # required
));

my $timer = $loop->add(MyTimer->new(
    after => 0.25, # required one-shot delay
));

my $socket = $loop->add(MyDatagram->new(
    host => '0.0.0.0', # required
    port => 9999,      # required
));

The loop constructor option and add are both primary public APIs. Timer construction and scheduling deliberately use this generic attachment path; Loop has no Timer-specific factory methods.

RAW DESCRIPTOR API

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

Registers 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

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

  • lean => 1

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

  • edge_triggered => 1

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

  • oneshot => 1

    Uses 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)

Cancels 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

Waits and dispatches until stop is called.

run_once($timeout_ms = -1)

Runs 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)

Runs 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

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

INTROSPECTION

running

Returns 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

Returns the number of current managed public objects. Managed objects are Stream, Listener, Datagram, Timer, Signal, Wakeup, and Process instances. Opaque raw registrations and private helper objects are excluded. This query enumerates existing object and fd registries.

has($object)

Returns true only when the exact object is current in this Loop. An object owned by another Loop, or one which is detached or terminal, returns false. Identity is exact; the query enumerates the current object snapshot.

objects

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

inspect($object)

Returns 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 fields appropriate to Stream, Listener, Datagram, Timer, Signal, Wakeup, or Process. See docs/INTROSPECTION.md for the complete field table.

census

Returns a new hash reference containing counts for stream, listener, datagram, timer, signal, wakeup, and process. Every key is present even when its count is zero.

resources

Returns 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 Timer creates the Loop's shared timer source. This scans the native fd registry and does not create resources.

why_alive

Returns an array reference of actionable user-visible liveness reasons. Managed-object 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

Returns 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. The first API does not place a clock read around each callback. Profiling changes the measured workload, so it should be disabled for normal benchmarks.

event_capacity and set_event_capacity inspect or change the reusable event array. callback_scope_limit and set_callback_scope_limit control bounded Perl temporary scopes. enable_watcher_reclaim exposes an experimental native memory/throughput tradeoff. The measured defaults should normally remain unchanged.

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. Only a cloned Linux::Event::Wakeup handle may signal its owner through eventfd; it cannot manage the Loop, invoke callbacks, or access owner-interpreter data.

PLATFORM

Linux only. The implementation uses epoll directly.