NAME

Linux::Event::Listen - Listening sockets for Linux::Event

SYNOPSIS

use v5.36;
use Linux::Event;
use Linux::Event::Listen;

my $loop = Linux::Event->new;

my $listen = Linux::Event::Listen->new(
  loop => $loop,
  host => '127.0.0.1',
  port => 3000,

  on_accept => sub ($loop, $client_fh, $peer, $listen) {
    # You own $client_fh. It is already non-blocking.
    # Attach whatever per-connection watchers you want:
    $loop->watch($client_fh,
      read => sub ($loop, $fh, $w) {
        my $buf;
        my $n = sysread($fh, $buf, 8192);
        if (!defined $n || $n == 0) {
          $w->cancel;
          close $fh;
          return;
        }
        # ... handle $buf ...
      },
    );
  },

  on_error => sub ($loop, $err, $listen) {
    warn "listener error ($err->{op}): $err->{error}\n";
  },
);

$loop->run;

DESCRIPTION

Linux::Event::Listen creates or wraps a listening socket and attaches it to a Linux::Event loop. When the socket becomes readable, it accepts connections and invokes your on_accept callback once per accepted client.

This distribution is intentionally small and policy-light:

  • It does not create per-connection watchers for you.

  • It guarantees the correct accept-drain behavior required for edge-triggered notification (accept until EAGAIN), with a fairness cap.

CONSTRUCTOR

new

my $listen = Linux::Event::Listen->new(%args);

Required:

  • loop

    A Linux::Event::Loop instance.

  • on_accept

    on_accept => sub ($loop, $client_fh, $peer, $listen) { ... }

    Called once per accepted connection.

You must also provide either:

  • fh

    A listening socket handle (already bound and in listen state), or

  • host and port

    TCP address to bind and listen on, or

  • path

    UNIX domain socket path to bind and listen on.

OPTIONS

on_accept

on_accept => sub ($loop, $client_fh, $peer, $listen) { ... }

Your callback is invoked once per accepted connection.

Ownership: you own $client_fh. This module never closes it.

on_error

on_error => sub ($loop, $err, $listen) { ... }

on_emfile

on_emfile => sub ($loop, $err, $listen) { ... }

Optional callback invoked when accept() fails with EMFILE or ENFILE. This is where production servers often implement "reserve FD" mitigation.

Optional callback invoked for accept-time errors and watcher error/hup events.

$err is a hashref with keys like:

{ op => 'accept', errno => ..., error => "...", fatal => 0 }

edge_triggered

edge_triggered => 1  # default

Sets the underlying watcher to edge-triggered mode. The accept loop always drains the accept queue until EAGAIN, which is required for correctness when edge triggered.

oneshot

oneshot => 0  # default

Passed through to the underlying watcher.

max_accept_per_tick

max_accept_per_tick => 128  # default

Upper bound on accepted connections per readable event callback.

If you set this option without explicitly setting edge_triggered, this module will default to level-triggered readiness so the cap cannot stall the listener.

If you explicitly enable edge_triggered and also cap accepts-per-tick, you must ensure you still eventually drain accept() until EAGAIN (for example by using a level-triggered listener, or designing your own re-arm strategy).

cloexec

cloexec => 1  # default

Sets FD_CLOEXEC on the listening socket and accepted sockets (best-effort).

nonblocking

nonblocking => 1  # default

Accepted sockets are set non-blocking (best-effort). The listening socket is also set non-blocking.

path

path => '/tmp/app.sock'

Bind and listen on a UNIX domain socket path.

unlink => 1

If true and path is used, unlink the path before binding (useful for restarts).

unlink_on_cancel => 1

If true and path is used, unlink the socket file when the listener is cancelled (or destroyed). Defaults to true for internally-created UNIX sockets.

owns_socket

If true, $listen->cancel will close the listening socket. Defaults to false when fh is provided, and true when the socket is created internally.

METHODS

fh

my $fh = $listen->fh;

Return the listening socket handle.

fd

my $fd = $listen->fd;

sockhost / sockport

my $host = $listen->sockhost;
my $port = $listen->sockport;

Convenience accessors that delegate to the underlying socket handle. For UNIX sockets these may return undef, depending on the underlying implementation.

Return the numeric file descriptor of the listening socket (or undef).

watcher

my $w = $listen->watcher;

is_running

if ($listen->is_running) { ... }

True if the underlying watcher is installed.

Return the underlying Linux::Event::Watcher.

pause / resume

$listen->pause;
$listen->resume;

Disable/enable read interest on the listening socket.

cancel

$listen->cancel;

Cancel the underlying watcher and, if owns_socket is true, close the listening socket.

ERROR HASH

When on_error or on_emfile is invoked, the second argument is a hashref describing the condition.

Common keys:

op     - one of: setup, accept, watch, on_accept
error  - string form of the error
errno  - numeric errno (only when the error came from C<$!>)
fatal  - boolean (true for setup failures)

Depending on how the listener was created, host/port or path may be present for op = setup errors.

ACCEPT LOOP SEMANTICS

When the listening socket is readable, this module attempts to accept connections in a loop until one of these conditions is met:

  • accept returns EAGAIN / EWOULDBLOCK (normal)

  • max_accept_per_tick is reached (fairness)

  • a non-retryable accept error occurs

The following errors are retried: EINTR, ECONNABORTED.

NOTES

SEE ALSO

Linux::Event, Linux::Event::Loop, Linux::Event::Watcher, IO::Socket::IP

AUTHOR

Joshua S. Day

LICENSE

Same terms as Perl itself.