Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Anyone have any idea why my AnyEvent timer is blocking Twiggy from handling requests? The initial response is served immediately, but any subsequent requests aren't handled until the timer is done running/deleted. Any ideas?
use strict; use Plack; use AnyEvent; our $timers = {}; my $app = sub { my $time = time(); $$timers{$time} = AE::timer 0, 0, sub { sleep 60; delete $$timers{$time} }; return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello World' ] +]; };

Replies are listed 'Best First'.
Re: AnyEvent code blocking Twiggy event loop
by basiliscos (Pilgrim) on Apr 27, 2015 at 06:51 UTC

    AE/Twiggi do not introduce any "magic": if your code is blocking and long-running, they "hang", just because that code is running. (Well, that's why there is workers: while one is blocked, some other could serve other clients meanwhile).

    Now, to your problem with executing external programs: try to execute it via AnyEvent::Util::fork_call, which executes program asynchronously without blocking main execution thread. Then in forked child you can use backticks to get the result, transform it, and return to main execution process (worker).

    WBR, basiliscos.
      I stumbled on to AnyEvent::Util::fork_call yesterday and it seems to work, but I'm still not understanding why AE::timer is blocking my main thread. My guess is that I'm interfering with another event loop, probably whatever one Twiggy uses to handle incoming requests. Also, is there any reason why code in timer or fork_call doesn't (seem to) execute at all under any other server (Starman, Gazelle, HTTP::Server::Simple, etc.)? Just a little something I noticed trying to piece together how this all works yesterday.

        Timer by itself blocks nothing, while sleep could do that; Although I think AE should prevent that as bad practice. To return/simulate delayed response, try

        use AE; my $app = sub { return sub { my $r = shift; my $t; $t = AE::timer 60, 0, sub { my $writer = $r->([200, ['Content-Type' => 'text/plain'] ] +); $writer->write('Hello World'); $writer->close; undef $t; }; }; };

        Also, please note, that not all servers are AE-compatible, i.e. they could use own loop mechanism, and there will be a problem with AE; e.g. starman.

        Another source of problem with fork: do not ever use any loop-related code in child (directly or indirectly), i.e. do system/exec ASAP.

        WBR, basiliscos.

        but I'm still not understanding why AE::timer is blocking my main thread.

        Its not AE::time, its the blocking system calls sleep/ipc/qx

        AE is cooperative multitasking meaning if any one part of the code decides to block, no other piece of code gets a chance to run

        Also, is there any reason why code in timer or fork_call doesn't (seem to) execute at all under any other server (Starman, Gazelle, HTTP::Server::Simple, etc.)?

        maybe you're trying stuff on win32 ... impossible to guess at reasons without code/diagnostics

Re: AnyEvent code blocking Twiggy event loop
by Anonymous Monk on Apr 26, 2015 at 23:56 UTC
    sleep is usually blocking ... you need to find the AE version of sleep
      Thought it might be that, but the problem still exists if I do something else that takes a long time. In my real world application I'm having issues with, I'm calling an external Python script with backticks.

        Maybe now is a good time to understand how nonblocking operations work and what the process model of AnyEvent and Twiggy is?

        Also see AnyEvent::Open3::Simple, but personally, I would just launch the other script as a detached process that writes its output to a logfile and then tail that logfile.