how to use AnyEvent::Handle on the child of AnyEvent::Run (aka portable_socketpair)?

Andrew Whatson whatson at gmail.com
Wed Nov 20 08:56:42 CET 2013


After further investigation, it seems the problem is caused by the
fact that constructing AnyEvent::Run with an "on_read" parameter
causes a read watcher for the socketpair to be created *before*
forking.  This watcher then exists in both the parent and child,
causing some weird behaviour.

A minor change to the script allows it to work on either backend.  The
original script passes on_read to the constructor:

  my $child = AnyEvent::Run->new(
    cmd => sub { ... },
    on_read => sub { ... },
  );

The work-around is to set on_read afterwards:

  my $child = AnyEvent::Run->new(cmd => sub { ... });
  $child->on_read(sub { ... });

This way the watchers are created after the fork, and everything works
as expected.

The AnyEvent documentation explains that using event-loops across
forks is risky business:

  https://metacpan.org/pod/AnyEvent#FORK

It seems to me that this behaviour could be considered a bug in
AnyEvent::Run.  The patch to fix it there is straightforward, we just
move AnyEvent::Handle construction into the parent section after the
call to fork.  The *real* mystery is how this read watcher is
interfering with the child's ability to read its STDIN.

Cheers,
Andrew Whatson

On 20 November 2013 13:49, Andrew Whatson <whatson at gmail.com> wrote:
> OK, I can replicate what you're seeing by selecting the pure-perl
> AnyEvent backend.  This is used as a fall-back when the EV module has
> not been installed.  If possible, I'd recommend installing EV, adding
> it to your list of things to install for AnyEvent, and not worrying
> about the pure-perl backend any more ;)  Once EV is installed, it will
> be used as the default backend - you can set PERL_ANYEVENT_MODEL=Perl
> to force the pure-perl backend if necessary.
>
> As for *why* the pure-perl backend exhibits this behaviour, that
> requires further investigation.
>
> On 20 November 2013 13:28, Fulko Hew <fulko.hew at gmail.com> wrote:
>>
>> On Tue, Nov 19, 2013 at 8:16 PM, Andrew Whatson <whatson at gmail.com> wrote:
>>>
>>> Anywhere before "first send to child" should work.  Without this, I
>>> see the following output sometimes:
>>>
>>>   parent rx: child timer
>>>   parent tx
>>>   parent rx: child timer
>>>   child rx: sent to child
>>>
>>> The "child timer\n" and "child rx: sent to child\n" messages are being
>>> received together, due to output buffering in the child process.
>>> Turning on autoflush forces the process to send each line separately.
>>>
>>> I hope this is the problem you're seeing - if not, please explain the
>>> problem in more detail.
>>
>>
>> I don't think its the same problem.
>> With or without the autoflush, I see the same results:
>>
>> parent tx first send
>> parent rx: child started
>> parent rx: child timer
>> parent tx
>> parent rx: child timer
>> parent rx: child timer
>> parent tx
>>
>> I think its a problem on the receiving end because if I change
>> the receiver to the code below (without the autoflush),
>> just to test the 'echo' part (without the child timer async sender)
>> the child _does_ receive and transmit.
>>
>> sub child_proc {
>>     while (1) {
>>         chomp (my $d = <STDIN>);
>>         print STDERR "child rx: $d\n";;
>>         send_msg_to_parent("echoing...$d");
>>         sleep 1;
>>     }
>> }
>>
>>
>> On 20 November 2013 10:40, Fulko Hew <fulko.hew at gmail.com> wrote:
>>> On Tue, Nov 19, 2013 at 7:16 PM, Andrew Whatson <whatson at gmail.com> wrote:
>>>>
>>>> Hi Fulko,
>>>>
>>>> I think you need to turn on autoflush on STDOUT.  After putting $|=1,
>>>
>>> Where did you put it, because no mater what I do,
>>> it doesn't make a difference for me.
>>>>
>>>> I get the following output which seems correct:
>>>>
>>>> parent tx 'first send'
>>>> parent rx: child started
>>>> parent rx: child rx: first send to child
>>>>
>>>> parent rx: child timer
>>>> parent tx
>>>> parent rx: child rx: sent to child
>>>>
>>>> parent rx: child timer
>>>> parent tx
>>>> parent rx: child timer
>>>> parent rx: child rx: sent to child
>>>>
>>>> parent rx: child timer
>>>> parent tx
>>>> parent rx: child rx: sent to child
>>>>
>>>> Regards,
>>>> Andrew Whatson
>>>>
>>>> On 20 November 2013 06:03, Fulko Hew <fulko.hew at gmail.com> wrote:
>>>> > I'm trying to use AnyEvent:Run, but I need to make my child smarter.
>>>> > and make it an AnyEvent style process that uses 'event driven I/O'.
>>>> > But I can't seem to get anywhere, and I'm obviously missing...
>>>> > something.
>>>> >
>>>> > I'm attempting the test example below.
>>>> >
>>>> > What I see is the child successfully sending and the parent receiving.
>>>> > But I'm not getting the parent-to-child pat,  and I don't know if its a
>>>> > problem in parent or in the child, or I'm just doing it wrong.
>>>> >
>>>> > use strict;
>>>> > use warnings;
>>>> > use Socket;
>>>> > use AnyEvent;
>>>> > use AnyEvent::Run;
>>>> > use AnyEvent::Handle;
>>>> >
>>>> > sub send_msg_to_parent { print "$_[0]\n"; }
>>>> >
>>>> > sub child_proc {
>>>> >     my $child_hdl = new AnyEvent::Handle (
>>>> >         fh  => \*STDIN,
>>>> >         on_read => sub {
>>>> >                        my $hdl = shift;
>>>> >                        my $d = $hdl->{rbuf};
>>>> >                        $hdl->{rbuf} = '';
>>>> >                        send_msg_to_parent("child rx: $d");
>>>> >                        },
>>>> >     );
>>>> >     my $child_timer = AnyEvent->timer (  # send a watchdog msg
>>>> >         after       => 2,
>>>> >         interval    => 2,
>>>> >         cb          => sub {send_msg_to_parent("child timer"),
>>>> >     );
>>>> >     send_msg_to_parent("child started");
>>>> >     my $child_cv = AnyEvent->condvar;
>>>> >     $child_cv->wait;
>>>> > }
>>>> >
>>>> >
>>>> > my $child = AnyEvent::Run->new(
>>>> >     cmd     => sub { child_proc() },
>>>> >     on_read => sub {
>>>> >                    my $hdl = shift;
>>>> >                    my $d = $hdl->{rbuf};
>>>> >                    $hdl->{rbuf} = '';
>>>> >                    print "parent rx: $d";
>>>> >                    },
>>>> >     on_error    => sub { print "child died\n"; },
>>>> > );
>>>> > my $parent_timer = AnyEvent->timer (
>>>> >     after       => 3,
>>>> >     interval    => 3,
>>>> >     cb          => sub { $child->push_write("sent to child\n"); print
>>>> > "parent tx\n";},
>>>> > );
>>>> > $child->push_write("first send to child\n"); print "parent tx 'first
>>>> > send'\n";
>>>> > my $cv = AnyEvent->condvar;
>>>> > $cv->wait;
>>>> >
>>>> >
>>>> > _______________________________________________
>>>> > anyevent mailing list
>>>> > anyevent at lists.schmorp.de
>>>> > http://lists.schmorp.de/mailman/listinfo/anyevent
>>>
>>>
>>



More information about the anyevent mailing list