- - PR -
Perl の標準エラー出力を Process.getErrorStream() で読むとブロックしてしまう
| 投稿者 | 投稿内容 | ||||||||
|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2003-09-06 22:27
unibon です。こんにちわ。
Java から exec したプロセスが出力する標準エラー出力(stderr)を、 Java の側で Process.getErrorStream() で読むと、 ブロックしてしまって Java のプログラムが終わらなくて悩んでいます。 例としては、ActiveState の ActivePerl(perl.exe) の、 構文チェックと警告のオプション(-c と -w)を付けて呼び出した場合、 つぎのようになります。 (1) わざと誤った Perl のソースコードを渡すと、 Perl が構文チェックでエラーを出力しますが、 Java の側でそれを読みこめて特に問題なありません。 (2) 正しい Perl のソースコードを渡すと、 構文チェックで Perl がエラーをなにも返さないのですが、 そのようなときに Java の側で read(BufferedReader.readLine()) すると そこでブロックしてしまいます。 再現用のソースコードはつぎのとおりです。 簡単な Perl のソースコードをハードコーディングしてあり、 わざと間違ったコード(下記のように for を faor と間違えている)だと、 エラーの文字列がちゃんと取得できてブロックされることもないのですが、 faor を for に直すと、エラーの文字列が全然なくなってしまうためなのか、 ブロックされていつまでたっても終わりません。 #原理的には別に文字列がないからといってブロックされるわけではないはずですが。
なお、環境は Windows 98 + JDK 1.4.2_01 + ActivePerl 5.6.1 です。 背景としては、EPIC の Eclipse 用の Perl プラグイン http://e-p-i-c.sourceforge.net/ を Windows 98 で動かそうとしたところ構文チェックの機能がうまく動かず、 ソースコードを見てたらどうもこのあたりが絡んでいそうだなと思って、 上記のようなサンプルプログラムで試すと似たような挙動になったためです。 なお、このプラグインは以前に Windows XP で動かすとちゃんと動きました。 #上記の Java のサンプルプログラムは、 #私は都合によりすぐには Windows XP では試せないのですが。 | ||||||||
|
投稿日時: 2003-09-07 16:52
unibonさん、こんにちは。
なんか、execしたサブプロセスと、perlの受け渡しと、エラー入力の3者の どれかとどれかが原因でデッドロックしてるような気がします。 試しにというのは良くないかも知れませんが、エラーの入力を別 Threadで やってみたらどうでしょう。 | ||||||||
|
投稿日時: 2003-09-07 23:09
unibon です。こんにちわ。
ありがとうございます。 デッドロックということは2つ以上の個所で待ちが発生しているということでしょうかね。
私のアプリケーションの中では、 ブロックする(される?)個所は readLine の1個所だけですので、 単に Perl の標準エラー出力を入力とする Java のコード部分だけを別スレッドで動かしても、 別スレッドにしないのと違いがないと思います。 デッドロックに関与する2つ(以上)の登場人物が特定できないと、 難しそうですね。 もしかしたら、Perl の標準入力に Java から出力する段階や、 あるいはもっと以前の exec する段階で、 すでに別スレッドを使う必要があるのかも、と思えてきました。 しかし、そもそも、単にプロセスとパイプでやりとりしたいだけなのに、 別スレッドにする必要があるのかが良く分かりません。 [ メッセージ編集済み 編集者: unibon 編集日時 2003-09-07 23:13 ] | ||||||||
|
投稿日時: 2003-09-08 13:17
このperlプログラムは、正常に実行されると、標準出力に書き出しを行いますよね。
Java側で標準出力も読み出してあげないと、つまっちゃって止まってしまうのでは? | ||||||||
|
投稿日時: 2003-09-08 13:30
かなり外している可能性がありますが、
perlプログラムの最後に __END__ がないとperlプロセスが終了しないために止まるのではないでしょうか? | ||||||||
|
投稿日時: 2003-09-09 02:10
おそらくそうです。 こんなのがありますね。 http://www.gimlay.org/~javafaq/S103.html#S103-12 http://developer.java.sun.com/developer/bugParade/bugs/4062587.html 子プロセスのSTDOUT,STDERRをそれぞれ別のスレッドから吸い出してやるしかないようです。 手元のWindows2000, J2SDK1.4.1, ActivePerl5.8だと再現しないので、95系特有の問題なのかもしれません。 | ||||||||
|
投稿日時: 2003-09-09 02:13
__END__がなくてもperlはきちんと停止しますよ。 (オマケのPerlクイズ) __END__と__DATA__の違いを述べよ。(5点) [ メッセージ編集済み 編集者: サ 編集日時 2003-09-09 02:14 ] | ||||||||
|
投稿日時: 2003-09-09 16:15
Ken-Labです。
> 子プロセスのSTDOUT,STDERRをそれぞれ別のスレッドから吸い出してやるしかないようです。 確かにこれが図星のような気がします。 では、なぜENDリテラルの有無を問題にしたかと言いますと、サンプルを手で perl -c -w for ($i=0; $i < 10;$i++){ print $i; } と打った場合このままになり、__END__を打ってから -Syntax OK が戻り停止します。 このサンプルを誤って入力した場合は } を入力した直後 Syntax error at - line 1, near "0;" Syntax error at - line 1, near "++)" - had compilation errors. となり即停止します。普通ストリームをcloseした段階でperlが実行されるはずですが、 それが終端であると判断されなかった場合、perlから応答が得られず BufferedReader#readLineが入力待ちになるのでは??という推測をたてたためです。
__DATA__は、スクリプトの実行部分の終わりであることを意味する以外に、DATAファイル ハンドルをカレントパッケージの名前空間にOPENするのではないか、と思います。 具体的には、__END__も__DATA__も、このリテラルに続いてデータを置いた場合、 DATA を通じて取得できますが、requireされた側に置いた場合は__DATA__を使わないと 取得できなかったはずです。(ここでperl話を広げてもなんですので、このへんで。 [ メッセージ編集済み 編集者: Ken-Lab 編集日時 2003-09-09 16:23 ] [ メッセージ編集済み 編集者: Ken-Lab 編集日時 2003-09-09 17:03 ] | ||||||||
