my_stk_oflw_account
my_stk_oflw_account

Reputation: 409

How to call event loop from callback?

That is, let the event loop process the events currently in queue. In VBA the call is named DoEvents.

I want to do this in Perl. Preferably to do this in AnyEvent, because I have a script written with it. But I couldn't find any function like that in documentation.

I naively tried to implement it with a condvar, because documentation says that recv calls event loop, but it failed, here is sample code:

use strict;
use warnings;

use AnyEvent;
use AnyEvent::Strict;

sub _long_task;

my $t1 = AE::timer 0, 3, sub{print"task 1, with interval 3 seconds\n";};
my $t2 = AE::timer 0, 7, sub{print"task 2, with interval 7 seconds\n";};
my $t3 = AE::timer 0, 11, sub{print"task 3, with interval 11 seconds\n";};
my $t_long = AE::timer 0, 0, \&_long_task;

sub DoEvents()
{
    my $cv = AE::cv;
    my $t = AE::timer 0, 0, sub { $cv->send; };
    $cv->recv;
}

sub _long_task {
    print"long task: ENTERING\n";
    for(1..5) {
        print"long task: doing some work for 2 seconds\n";
        sleep 2;
        print"long task: calling DoEvents\n";
        DoEvents();
    }
    print"long task: EXITING, resheduling after 10 seconds\n";
    $t_long = AE::timer 10, 0, \&_long_task;
}

AE::cv->recv;

The output is:

task 1, with interval 3 seconds
task 2, with interval 7 seconds
task 3, with interval 11 seconds
long task: ENTERING
long task: doing some work for 2 seconds
long task: calling DoEvents
AnyEvent::CondVar: recursive blocking wait attempted at C:\Users\andreyi\Desktop\try_event_loop.pl line 18.

UPDATE: There are lines in AnyEvent.pm:

  $WAITING
     and Carp::croak "AnyEvent::CondVar: recursive blocking wait attempted";

If you comment them, the DoEvents() works.

However, it will be better to have solution that does not involve mofidication of this CPAN module.

Upvotes: 2

Views: 742

Answers (1)

my_stk_oflw_account
my_stk_oflw_account

Reputation: 409

Every problem has at least one simple solution (sometimes it is a dirty hack).

In my case this one seems to work. I added it to production code.

BEGIN { our $orig_Carp_croak = \&Carp::croak; }
sub DoEvents()
{
    my $cv = AE::cv;
    my $t = AE::timer 0, 0, $cv;
    no warnings 'redefine';
    local *Carp::croak = sub{
        (caller 1)[3] eq 'AnyEvent::CondVar::Base::recv'
            && $_[0] =~ /recursive blocking wait attempted/
            && return;
        goto \&{our $orig_Carp_croak};
    };
    $cv->recv;
}

UPDATE: For all callbacks that call DoEvents you will need to ensure that it they are not reentered. Like this:

our $entered_callbacks = {};
# ...
sub some_callback {
    $entered_callbacks->{some_callback} && return;
    local $entered_callbacks->{some_callback} = 1;
    ...;
}

The loops EV and AnyEvent::Loop have flaw that the callback is removed from queue only when it returned, not before it is called. It makes event loop not safe for reentry.

Upvotes: 1

Related Questions