Appropiate Coro/AnyEvent model?

Andrew Whatson whatson at gmail.com
Mon Nov 18 02:35:54 CET 2013


Hi Mark,

Sorry I don't have time for an in-depth reply right now, but I think I
see part of the problem at least:

>         $self->{sig} = AE::signal 'INT', sub {
>             warn 'got INT';
>             # Coro::terminate;                # spin/lockup
>             # $Coro::current->safe_cancel;    # spin/lockup
>             # $Coro::current->cancel;         # spin/lockup
>             # Coro::current->throw('stuff');  # kills program entirely
>             exit;
>         };

This callback does not run inside any of your threads.  It runs inside a
special event-handling thread.  By calling terminate or
$Coro::current->cancel, you're blowing up an internal thread for
whichever event loop you're using, which is not what you want.

It seems you want to do $coro->cancel on whatever thread do_stuff() is
running inside, well you need a reference to that thread.  You *could*
grab a reference to it inside do_stuff(), but who knows what thread that
is???  I would be very annoyed with a library which randomly canceled
threads I called it from.

>     my $coro = async {
>         my $client = Client->new;
>         $client->do_stuff;
>     };
>
>     $coro->join;
>
>     warn 'After join + terminate/cancel/throw';

It looks like you want $coro->join to return nicely.  The simplest way
to do that is to make $client->do_stuff() return nicely.  To do that,
you'll need to interrupt the do_stuff call in some way.

> After all of that I guess I can phrase my question better now: Can a
> coro thread stop itself?

Yes, but that's not what your code was trying to do.

Regards,
Andrew Whatson

On 18 November 2013 06:08, Mark Lawrence <nomad at null.net> wrote:
> Hi Andrew,
>
> Thanks for the reply.
>
>> You're on the right track, but it sounds like you're only waiting for
>> success inside do_stuff(), but because success never happens, it gets
>> stuck waiting forever.
>
> ... possibly, but my issue is not that my program is stopping while
> waiting for input or some true condition. The program stops responding
> to events and something somewhere (inside Coro?) attempts to use 100%
> of the cpu.
>
>> Seems that really you should be waiting for success OR error, and
>> have some way to tell the difference.  Here's one approach:
>
> ...
>
>>   sub do_stuff {
>>     ...
>>
>>     $self->{interrupt} = rouse_cb;
>>     AE::io $self->{child_stdout}, 0, $self->{interrupt};
>>
>>     rouse_wait $self->{interrupt};
>>     $self->{interrupt} = undef;
>
> The last thing I want to be doing inside the do_stuff() subroutine is
> waiting on possible errors. There is nothing event based about that
> approach.  Waiting on (normal) input (hence the use of Coro) is however
> fine.
>
> I have come up with better, tested example code which we can run and
> compare results with, which should hopefully show the problem clearer.
> This program simply echos any input received, and the coro should be
> stopped when an interrupt (^C) is received.
>
>     package Client;
>     use strict;
>     use warnings;
>     use Coro;
>     use Coro::Handle;
>
>     sub new {
>         my $class = shift;
>         bless my $self = {}, $class;
>
>         $self->{sig} = AE::signal 'INT', sub {
>             warn 'got INT';
>
>             # Uncomment one of the following to test
>
>             # Coro::terminate;                # spin/lockup
>             # $Coro::current->safe_cancel;    # spin/lockup
>             # $Coro::current->cancel;         # spin/lockup
>             # Coro::current->throw('stuff');  # kills program entirely
>
>             exit;
>         };
>
>         $self->{fh} = Coro::Handle->new_from_fh(*STDIN);
>         return $self;
>     }
>
>     sub do_stuff {
>         my $self = shift;
>
>         while ( my $line = $self->{fh}->readline ) {
>             print "Got: $line";
>         }
>         return;
>     }
>
>     package main;
>     use Coro;
>
>     my $coro = async {
>         my $client = Client->new;
>         $client->do_stuff;
>     };
>
>     $coro->join;
>
>     warn 'After join + terminate/cancel/throw';
>
> Uncomment any of the terminate/cancel options in the signal handler,
> and Coro decides that it doesn't want to behave nicely anymore.
>
> Stopping the coroutine from *outside* the coro however works fine:
>
>     package Client;
>     use strict;
>     use warnings;
>     use Coro::Handle;
>
>     sub new {
>         my $class = shift;
>         bless my $self = {}, $class;
>
>         $self->{fh} = Coro::Handle->new_from_fh(*STDIN);
>         return $self;
>     }
>
>     sub do_stuff {
>         my $self = shift;
>
>         while ( my $line = $self->{fh}->readline ) {
>             print "Got: $line";
>         }
>         return;
>     }
>
>     package main;
>     use Coro;
>     use AnyEvent;
>
>     my $cv = AnyEvent->condvar;
>
>     my $coro = async {
>         my $client = Client->new;
>         $client->do_stuff;
>         $cv->send;
>     };
>
>     my $w = AE::signal 'INT', sub {
>         warn 'got INT';
>
>         $coro->safe_cancel;
>         $cv->send;
>     };
>
>     $cv->recv;
>
>     warn 'After join + terminate/cancel/throw';
>
> After all of that I guess I can phrase my question better now: Can a
> coro thread stop itself?
>
> Possibly related to this whole issue is the question of why
> Coro::Handle doesn't deal with errors the same way that
> AnyEvent::Handle does. I had been hoping that Coro::Handle would have
> an on_error => sub {} capability.
>
> Mark.
> --
> Mark Lawrence



More information about the anyevent mailing list