perl - Why won't AnyEvent::child callbacks ever run if interval timer events are always ready? -
update issue can resolved using fixes present in https://github.com/zbentley/anyevent-impl-perl-improved/tree/io-starvation
context:
i integrating anyevent otherwise-synchronous code. synchronous code needs install watchers (on timers, child processes, , files), wait @ least 1 watcher complete, synchronous/blocking/legacy stuff, , repeat.
i using pure-perl anyevent::loop
-based event loop, enough purposes @ point; of need signal/process/timer tracking.
the problem:
if have callback can block event loop moment, child-process-exit events/callbacks never fire. simplest example make watches child process , runs interval timer. interval timer blocking before finishes:
use anyevent; # start timer that, every 0.5 seconds, sleeps 1 second, prints "timer": $w2 = anyevent->timer( after => 0, interval => 0.5, cb => sub { sleep 1; # simulated blocking operation. if removed, works. "timer"; }, ); # fork off pid waits 1 second , exits: $pid = fork(); if ( $pid == 0 ) { sleep 1; exit; } # print "child" when child process exits: $w1 = anyevent->child( pid => $pid, cb => sub { "child"; }, ); anyevent->condvar->recv;
this code leaves child process zombied, , prints "timer" on , over, "ever" (i ran several minutes). if sleep 1
call removed callback timer, code works correctly , child process watcher fires expected.
i'd expect child watcher run (at point after child exited, , interval events in event queue ran, blocked, , finished), not.
the sleep 1
blocking operation. can replaced busy-wait or other thing takes long enough. doesn't need take second; appears need a) running during child-exit event/sigchld delivery, , b) result in interval being due run according wallclock.
questions:
why isn't anyevent ever running child-process watcher callback?
how can multiplex child-process-exit events interval events may block long next interval becomes due?
what i've tried:
my theory timer events become "ready" due time spent outside of event loop can indefinitely pre-empt other types of ready events (like child process watchers) somewhere inside anyevent. i've tried few things:
- using
anyevent::strict
doesn't surface errors or change behavior in way. - partial solution: removing interval event @ point does make child process watcher fire (as if there's internal event polling/queue population done inside anyevent happens if there no timer events "ready" according wallclock). drawbacks: in general case doesn't work, since i'd have know when child process had exited know when postpone intervals, tautological.
- partial solution: unlike child-process watchers, other interval timers seem able multiplex each other fine, can install manual call
waitpid
in interval timer check , reap children. drawbacks: child-waiting can artificially delayed (my use case involves lots of frequent process creation/destruction),anyevent::child
watchers are installed , fire auto-reap child , not tell interval/waitpid timer, requiring orchestration, , feels i'm misusing anyevent.
the interval time between start of each timer callback, i.e. not time between end of callback , start of next callback. setup timer interval 0.5 , action timer sleep 1 second. means once timer triggered triggered again , again because interval on after timer returned.
thus depending on implementation of event loop might happen no other events processed because busy running same timer on , over. don't know underlying event loop using (check $anyevent::model
) if @ source code of anyevent::loop (the loop pure perl implementation, i.e. model anyevent::impl::perl
) find following code:
if (@timer && $timer[0][0] <= $mnow) { { $timer = shift @timer; $timer->[1] && $timer->[1]($timer); } while @timer && $timer[0][0] <= $mnow;
as can see busy executing timers long there timers need run. , setup of interval (0.5) , behavior of timer (sleep 1 second) there timer needs executed.
if instead change timer there actual room processing of other events setting interval larger blocking time (like 2 seconds instead of 0.5) works fine:
... interval => 2, cb => sub { sleep 1; # simulated blocking operation. sleep less interval!! "timer"; ... timer child timer timer
Comments
Post a Comment