4

I'm trying to build a timeout scenario in my Catalyst, AnyEvent, Websocket app. For that I'm using

AnyEvent->timer

which should be called after let's say a few seconds of inactivity (no more WS frames coming in).

The problem is, that my timer is never executed:

my $w = AnyEvent->timer (after => 3,
                         cb    => sub {
    warn "TIMEOUT!";
});

$self->{server} = Protocol::WebSocket::Handshake::Server->new_from_psgi(
                            $c->req->env) or die $c->log->fatal($!);

$self->{handle} = AnyEvent::Handle->new(
    fh => $c->req->io_fh,
    on_error => sub {
        my ($hd, $fatal, $msg) = @_;
        $clean_up->();
    }
);

die $c->log->fatal("WS Server error: '$_'")
        if $self->{server}->error;

$self->{server}->parse($self->{handle}->fh);
$self->{handle}->push_write($self->{server}->to_string);

$self->{handle}->on_read(sub {
    (my $frame = $self->{server}->build_frame)->append($_[0]->rbuf);

    while (my $frame_msg = $frame->next) {
        ...
    }

The timer callback is never executed. My guess would be, that the timer doesn't work inside another Event loop (AnyEvent::Handle)?

RandomAndy
  • 324
  • 1
  • 9
  • Could you make a runnable demonstration of the problem that doesn't use Protocol::WebSocket::Handshake::Server? (e.g. It could read from STDIN which could be piped from `perl -E'$|=1; say "abc"; sleep 4; say "def";`) Your problem is needlessly inaccessible (unless it relates to Protocol::WebSocket::Handshake::Server, in which case its something you should mention). – ikegami Nov 03 '14 at 15:09
  • If all the code posted in the question is in a function, then as soon as that function exits, $w goes out of scope, which cancels the timer. – TheAmigo May 02 '15 at 23:09

1 Answers1

3

Are you actually getting into the event loop for the timer to be processed? Your code snippet does not indicate this.

Also, AnyEvent::Handle has inactivity timeouts built-in:

       timeout => $fractional_seconds
           If non-zero, then this enables an "inactivity" timeout: whenever
           this many seconds pass without a successful read or write on the
           underlying file handle, the "on_timeout" callback will be invoked
           (and if that one is missing, a non-fatal "ETIMEDOUT" error will
           be raised).

           Note that timeout processing is also active when you currently do
           not have any outstanding read or write requests: If you plan to
           keep the connection idle then you should disable the timout
           temporarily or ignore the timeout in the "on_timeout" callback,
           in which case AnyEvent::Handle will simply restart the timeout.

           Zero (the default) disables this timeout.

       on_timeout => $cb->($handle)
           Called whenever the inactivity timeout passes. If you return from
           this callback, then the timeout will be reset as if some activity
           had happened, so this condition is not fatal in any way.
paulw1128
  • 454
  • 3
  • 14
  • 2
    Just a slight addendum to the inactivity timeouts - it's god to mention them because they might actually be a solution to the askers question, but I'd like to additionally point out that these timeouts are different than an overall timeout. The former might never trigger because the other side keeps sending data, without sending the right data needed to finish the request. – Remember Monica Jun 09 '18 at 06:12