NAME
Fugu::Timeout - run something under a time limit
SYNOPSIS
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";
DESCRIPTION
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
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
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.
RETURN VALUES
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.
EXAMPLES
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";
ERRORS
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.
SEE ALSO
alarm(3), Fugu::Process, Fugu::Signal
AUTHORS
Dick Olsson <hi@senzilla.io>
CAVEATS
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.