理系学生日記

おまえはいつまで学生気分なのか

AnyEvent でいくつものイベントを一つのイベントループで補足したいとき

$cv を Condition Variable だとすると、$cv->begin と $cv->end を組み合わせると良い。
$cv は AnyEvent::Condvar のインスタンスであるが、以下のように内部でいくつのイベントを待っているかのカウンタを持っており、begin はカウンタのインクリメント、end はデクリメントの役割を果たす。

sub begin {
   ++$_[0]{_ae_counter};
   $_[0]{_ae_end_cb} = $_[1] if @_ > 1;
}

sub end {
   return if --$_[0]{_ae_counter};
   &{ $_[0]{_ae_end_cb} || sub { $_[0]->send } };
}

この end を呼び出したときにカウンタが 0 になると自動的に send が呼ばれるようになっているため、この begin/end を組み合わせたときは(イベントループから何か値を返す場合を除き) send 呼び出しは明示的にはプログラム中に現れない。
値を返したいときには以下のように begin の引数に渡すコールバックで指定する。

$cv->begin( sub { shift->send( something to return ) } )

perldoc AnyEvent には、AnyEvent を用いた多数サーバに対する ping 疎通テストの例があり、begin/end の非常に分かりやすい解説になっている。
なお、begin で渡したコールバックは、それ以前の begin 呼び出しで渡していたコールバックを上書きする。そのため、コールバックをスタックのように積み上げていく場合は、それ用の仕組みを AnyEvent のユーザ側で用意する必要がある。