tl;dr
Python でパイプを読み取るときに、EINTR の「system call interrupted」エラーを処理する必要がありますか? その場合、そのようなコードをテストするにはどうすればよいですか?
説明
以下のトレースバックでself._dataq
は、multiprocessing.Queue
(技術的にはbilliard
ライブラリを使用していますが、基本的に同じコードだと思います)です。Python サブプロセスは、キューの反対側に書き込みを行うことがあります。何が起こったのかは、システム コールがキューにフィードするパイプを読み取っていて、シグナルが到着したことだと思います。おそらく、2 番目の Ctrl+C イベントからの SIGINT です (最初の SIGINT は、ユーザー^C
がロギング出力、およびシグナル ハンドラーがその SIGINT をキャッチしたことは、ログの WARNING メッセージで確認できます)。
[INFO 2014-03-05 14:16:06,000] Doing some work, la-dee-da
^C[WARNING 2014-03-05 14:16:07,344] Commencing shutdown. (Signal SIGINT, process 2469.). Press Ctrl+C again to exit immediately.
[DEBUG 2014-03-05 14:16:07,347] Terminating subprocess
Traceback (most recent call last):
[... a bunch of stuff omitted]
File "mycode.py", line 97, in __next__
result = self._dataq.get(timeout=0.1)
File "/usr/local/lib/python2.7/site-packages/billiard/queues.py", line 103, in get
if timeout < 0 or not self._poll(timeout):
IOError: [Errno 4] Interrupted system call
上記のトレースバックのステートメントresult = self._dataq.get(timeout=0.1)
は、次のようなループの途中にあります。ループの主な目的は、戻り始めるself._dataq
ときにからの読み取りをあきらめることができるようにすることです。self.timedout()
True
import queue
while True:
try:
result = self._dataq.get(timeout=0.1)
except queue.Empty:
if self.timedout():
self.close()
raise MyTimedoutError()
else:
break
質問
発生した理由についての私の理論IOError
が正しい場合、上記のtry
...ブロックは、システムコールの中断によって発生した sexcept
をキャッチして無視する必要があります。IOError
EINTR エラーの原因がシグナルである場合、Python に戻ってexcept IOError:
ステートメントを実行するだけで、Python レベルのシグナル ハンドラーを実行できます。
あれは正しいですか?もしそうなら、私のコードでこの変更をテストすることは可能ですか? 深刻な競合状態を含まない単体テストをどのように作成するかは、私には明らかではありません。