Parallel::Preforkで「cannot start another process while you are in child process」
今日は、perlのParallel::Prefork(Parallel/Prefork.pm)で出たエラーについてメモ。
結論からいうと、今回のパターンは、Parallel::Preforkのバージョンと組み方の問題でした。
(※あくまでエラーメッセージの条件説明じゃなく、こんなパターンで出る場合があるよという感じで見てください)
では、簡単なテストスクリプトを書いてみます。
#!/usr/local/bin/env perl use strict; use warnings; use FindBin; use lib "$FindBin::Bin/Parallel-Prefork-0.08/lib/"; use Parallel::Prefork; my $pm = Parallel::Prefork->new({ max_workers => 2, trap_signals => { TERM => 'TERM', }, }); while ($pm->signal_received ne 'TERM') { $pm->start(sub { sleep(3); warn "end:" . time(); }); } $pm->wait_all_children();
これを実行すると、
cannot start another process while you are in child process at ...../Parallel/Prefork.pm line 42.
みたいなエラーが表示されます。
これは、手元にあった0.10未満のバージョンのParallel::Preforkを利用して、
0.10から実装された「- support new style: $pm->start(sub { … })」の形で
コードを実装してしまっている為です。
【0.10未満のParallel::Prefork】
$pm->start(sub{})に対応していないので、上記のコードだと、
何も処理されずにstartだけしてwhile分がまわります。
ここで問題なのは、Parallel::Prefoxkを正常終了するには、$pm->finishしないといけないという事。
この場合、$pm->start(sub{});で渡した内容が宙に浮きます。そして、finishが走る要素がなくなります。
sub start { my $self = shift; : : die 'cannot start another process while you are in child process' if $self->{in_child}; : }
0.10以上のParallel::Preforkを利用している場合には、上記の$pm->start(sub{})の中が
Parallel::Prefork側の$cbに入り、$cb->();実行後に、$self->finish();されているので、問題ない。
sub start { my ($self, $cb) = @_; : : if ($cb) { $cb->(); $self->finish(); } : }
ということで、コードをそのままに、libの読み込み先を
use lib "$FindBin::Bin/Parallel-Prefork-0.08/lib/"; ↓ use lib "$FindBin::Bin/Parallel-Prefork-0.13/lib/";
に変更したら、正常に動作しました。
まあ、通常はcpanmとかでParallel::Preforkを更新すればいいだけですね。
現時点の最新が、0.13なので、それにしてますが、多分Changesから見ると0.10以降なら何でも動くはず。
【もしも0.08のままいきたいなら】
コードを以下のように$pm->startの引数で渡すのを止めて、
かつ、$pm->finish;を明示的にするように修正すればいいだけですね
#!/usr/local/bin/env perl use strict; use warnings; use FindBin; use lib "$FindBin::Bin/Parallel-Prefork-0.08/lib/"; use Parallel::Prefork; my $pm = Parallel::Prefork->new({ max_workers => 2, trap_signals => { TERM => 'TERM', }, }); while ($pm->signal_received ne 'TERM') { $pm->start; sleep(3); warn "end:" . time(); $pm->finish; }
まあ、そもそもが、Parallel::Preforkの0.10以上を利用していたシステムを
0.10未満のサーバに移植した(古いサーバに移設とかあまり無いですよね)とかしないと発生しないので、
この現象を調べる事になる人は、ほとんどいないと思いますけど。