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