Python: нужно ли перехватывать EINTR в цикле чтения канала

ТЛ; др

Должен ли я обрабатывать ошибки EINTR "прервал системный вызов" при чтении канала в Python, и если да, то как мне проверить такой код?

Описание

В следе ниже, self._dataq это multiprocessing.Queue (технически я использую billiard библиотека, но я думаю, что они в основном один и тот же код). Подпроцесс Python иногда записывает данные на другой конец очереди. Я думаю, что произошло то, что системный вызов считывал канал, который питает очередь, и поступил сигнал - возможно, SIGINT из второго события Ctrl+C (первый 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...except блок выше должен ловить и игнорировать IOErrors, когда они вызваны прерванными системными вызовами. Если это сигнал, вызвавший ошибку EINTR, то просто возвращение в Python для запуска except IOError: оператор позволит запускать обработчики сигналов уровня Python.

Это верно? Если да, возможно ли проверить это изменение в моем коде? Для меня не очевидно, как бы я написал модульный тест, который не содержал бы серьезного состояния гонки.

2 ответа

Python 3.5 решает эту проблему, возлагая ответственность за EINTR в среде выполнения Python, а не в коде приложения. См. PEP 475 и список изменений Python 3.5.

Я собираюсь назвать это ошибкой в ​​Python. Я не могу найти документацию, которая multiprocessing.Queue разрешено поднимать IOError на EINTR (хотя имеет смысл поднять вопрос о других проблемах ввода-вывода, поэтому вам следует проверить атрибут errno возникшего исключения, прежде чем его игнорировать; см. errno), и он не отображается напрямую на любую низкоуровневую функцию C, которая бы обеспечивала такое поведение. Было некоторое обсуждение обработки всех EINTR s на уровне C, но это не сделало это в 3.4 (и я сомневаюсь, что оно вообще превратится в 2.x), так что это, вероятно, все еще справедливая игра для отчета.

Другие вопросы по тегам