Fugu

Fugu::Timeout(3p) Perl Library Manual Fugu::Timeout(3p)

Fugu::Timeout - run something under a time limit

    use Fugu::Timeout;
    my $result = Fugu::Timeout::bounded(10, sub { $client->connect });
    Fugu::Timeout::wait_until(60, 0.5, sub { -S $socket })
        or die "the daemon never came up\n";

Fugu::Timeout holds the two ways to run something under a time limit: bounded() sets an alarm, and wait_until() polls. An alarm interrupts a blocking system call that a poll loop cannot reach, and a poll loop leaves a signal handler alone. Neither can do the other's job, so both are here.

The two are plain functions, not methods. They keep no state and they never log.

"bounded($seconds, $code)" runs $code under a hard wall-clock deadline. A blocked call inside $code cannot stall the caller for longer than $seconds.

The guard is an alarm(3), so it interrupts a blocking system call that a poll loop cannot reach. That is why it exists next to wait_until(), which cannot do that.

A die inside $code propagates, with the alarm already cleared. Thus a real failure keeps its own message and is not reported as a timeout.

"wait_until($timeout, $interval, $code)" calls $code until it returns true, or until $timeout seconds pass. $interval is the pause between calls; its default is a quarter of a second.

$code runs at least once, even with a timeout of 0. A caller that asks "is it ready" always gets an answer about now.

The loop also stops when a signal arrives, through Fugu::Signal. A poll loop must not outlive the interrupt that told the program to stop.

bounded() returns what $code returned. It returns "undef" when the deadline elapsed.

wait_until() returns the first true value that $code gave. It returns "undef" on the timeout, and "undef" when a signal arrived.

This example bounds a guest interaction that can block for ever:

    my $out = Fugu::Timeout::bounded(15, sub {
        return $ssh->run_command('sync; sync; sync');
    });
    warn "the guest did not answer\n" unless defined $out;

This example waits for a port to open, and stops early on a "SIGINT":

    Fugu::Timeout::wait_until(120, 1, sub {
        IO::Socket::INET->new(PeerAddr => '127.0.0.1', PeerPort => $port);
    }) or die "port $port never opened\n";

bounded() lets a die from $code through unchanged. It has no error of its own: a deadline is a return value, not an exception.

wait_until() does not die.

alarm(3), Fugu::Process, Fugu::Signal

Dick Olsson <hi@senzilla.io>

bounded() uses alarm(3), which is process-global and has one pending deadline. A nested bounded() replaces the outer deadline and does not restore it. Do not nest the two.

An alarm interrupts a system call in progress. A caller whose $code writes to a file can therefore leave a partial write behind. Bound a read, a connect or a wait, and not a write that must be whole.

wait_until() cannot interrupt a $code that blocks. The timeout bounds the loop, not one call inside it. Use bounded() for that.

2026-10-16 OpenBSD