AnyEvent のコールバックの coro は main coro じゃないことを知り、coro の PID はアドレス値であることを知る修行

AnyEvent::timer の callback coro

AnyEvent::timer のコールバックが実行されるのはイベントループだからメイン coro なんだろうと思っていた。が、次のプログラムを走らせてみて、そうではないことを知った。

use strict;
use warnings;
use Coro;
use AnyEvent;

my $head = 'Coro=HASH(0x';
my $head_len = length $head;
sub p {
    my $coro = shift;
    my $addr = hex substr($coro, index($coro, $head) + $head_len, -1);
    print "coro ".$coro->desc." ".$coro.":$addr\n";
}

my $cv = AE::cv;
my $w = AE::timer 0, 0, sub {
    p $Coro::main;
    p $Coro::current;
    use Coro::Debug;
    Coro::Debug::command 'ps';
    print Coro::nready."\n";
    $cv->send;
};
$cv->recv;

__END__

実行結果は次のようになる。

[takeyuki@sunya ~]$ perl b.pl
coro [main::] Coro=HASH(0x907c3d4):151503828
coro [AnyEvent idle process] Coro=HASH(0x920839c):153125788
                 PID SC  RSS USES Description              Where
           151503828 -C  17k    0 [main::]                 [AnyEvent.pm:1719]
           151485828 N-  116    0 [coro manager]           -
           151485996 N-  116    0 [unblock_sub scheduler]  -
           153125788 UC 2484    1 [AnyEvent idle process]  [b.pl:19]
0

main coro とコールバックが実行されている coro は違っている。でもって ready queue は空。

coro の PID は、その coro の格納アドレスに等しいらしいことが、coro を文字列コンテキストで評価したときの "Coro=HASH(0xXXXXXXX)" の 16 進数部分を無理矢理 10 進数に直してみてわかった。じゃあ、Coro::Debug::command を使って eval したり bt したりできるのかな。後でやってみよう。

unblock_sub callback coro

上のプログラムのコールバックを sub から unblock_sub に変更すると次のような実行結果になる。

[takeyuki@sunya ~]$ perl b.pl
coro [main::] Coro=HASH(0x86013ec):140514284
coro [async_pool] Coro=HASH(0x8616864):140601444
                 PID SC  RSS USES Description              Where
           140514284 -C  17k    0 [main::]                 [AnyEvent.pm:1719]
           140496284 N-  116    0 [coro manager]           -
           140496452 R- 1436    1 [unblock_sub scheduler]  [Coro.pm:428]
           142136208 -- 1604    1 [AnyEvent idle process]  [Coro.pm:428]
           140601444 UC 2232    1 [async_pool]             [b.pl:19]
1

unblock_sub にすると coro が増えるのはわかっていたけど、description から判断するに、coro プールに所属する coro らしい。なるほど、それはいい。$Coro::POOL_SIZE を超えても async_pool するんだろうな。後でやってみよう。

無理矢理作り出した PID と Coro::Debug::command でやなことをしてみる

次のプログラムは停止しない。

use strict;
use warnings;
use Coro;

my $head = 'Coro=HASH(0x';
my $head_len = length $head;

sub pid {
    my $coro = shift;
    return hex substr($coro, index($coro, $head) + $head_len, -1);

}
sub cmd {
    use Coro::Debug;
    Coro::Debug::command shift;

}
my $m = $Coro::main;
my $c = async {
    while (1) {
        cede;
    }
};
$c->on_destroy(sub {print "terminated.\n"});
cede;
my $m_pid = pid $m;
my $c_pid = pid $c;
print "--- ps ---\n"; 
cmd 'ps';
print "--- backtrace ---\n"; 
cmd "bt $c_pid";
print "--- eval ---\n"; 
cmd "eval $c_pid \$Coro::current->desc";
cmd "cancel $c_pid";

print "--- backtrace ---\n"; 
cmd "bt $m_pid";
print "--- eval ---\n"; 
cmd "eval $m_pid \$Coro::current->desc";
cmd "cancel $m_pid";

実行結果。

[takeyuki@sunya ~]$ perl a.pl
--- ps ---
                 PID SC  RSS USES Description              Where
           143955172 UC  17k    1 [main::]                 [a.pl:15]
           143937172 N-  116    0 [coro manager]           -
           143937340 N-  116    0 [unblock_sub scheduler]  -
           143318332 R- 1436    1                          [a.pl:21]
           145577684 N-  116    0 [AnyEvent idle process]  -
--- backtrace ---
coroutine is at a.pl line 21
        main::__ANON__ called at /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi/Coro.pm line 428
        Coro::_coro_run called at a.pl line 0
--- eval ---
[main::] 
terminated.
--- backtrace ---
coroutine is at /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi/Coro/Debug.pm line 325
        Coro::Debug::command('bt 143955172') called at a.pl line 15
        main::cmd('bt 143955172') called at a.pl line 37
--- eval ---
[main::] 

実行結果からわかること。

  • backtrace は実行できる
  • eval は pid のコンテキストで実行されるとあるが、main coro で実行されているっぽい
  • cancel も実行できる
  • main coro を cancel するとプログラムが停止しない

今のところ、[coro manager] や [unblock_sub scheduler] の PID をプログラムの中から知る方法がわからない (ps して標準出力に吐かれるのを無理矢理パースすればできないこともないけど)。これらの PID を知ることができて、なおかつ、そのコンテキストで eval できるといろいろ面白い実験ができそうだ。