python-pyudev/pyudev-eintr-retry.patch
David Shea 3457e9efa9 - Retry interrupted system calls (#1230773)
- Rearrange the build process to match current packaging recommendations
2015-06-12 09:40:51 -04:00

103 lines
3.9 KiB
Diff

diff -purN pyudev-0.16.1.orig/pyudev/monitor.py pyudev-0.16.1/pyudev/monitor.py
--- pyudev-0.16.1.orig/pyudev/monitor.py 2015-06-12 09:36:21.427469657 -0400
+++ pyudev-0.16.1/pyudev/monitor.py 2015-06-12 09:37:05.483447685 -0400
@@ -34,7 +34,7 @@ import select
from threading import Thread
from contextlib import closing
-from pyudev._util import ensure_byte_string
+from pyudev._util import ensure_byte_string, eintr_retry_call
from pyudev.core import Device
@@ -337,7 +337,7 @@ class Monitor(object):
.. versionadded:: 0.16
"""
- rlist, _, _ = select.select([self], [], [], timeout)
+ rlist, _, _ = eintr_retry_call(select.select, [self], [], [], timeout)
if self in rlist:
return self._receive_device()
else:
@@ -407,7 +407,7 @@ class Monitor(object):
with closing(select.epoll()) as notifier:
notifier.register(self, select.EPOLLIN)
while True:
- events = notifier.poll()
+ events = eintr_retry_call(notifier.poll)
for event in events:
device = self._receive_device()
yield device.action, device
@@ -503,14 +503,14 @@ class MonitorObserver(Thread):
# and on the monitor
notifier.register(self.monitor, select.EPOLLIN)
while True:
- for fd, _ in notifier.poll():
+ for fd, _ in eintr_retry_call(notifier.poll):
if fd == self._stop_event_source:
# in case of a stop event, close our pipe side, and
# return from the thread
os.close(self._stop_event_source)
return
else:
- device = self.monitor.poll(timeout=0)
+ device = eintr_retry_call(self.monitor.poll, timeout=0)
if device:
self._callback(device)
@@ -530,7 +530,7 @@ class MonitorObserver(Thread):
return
try:
# emit a stop event to the thread
- os.write(self._stop_event_sink, b'\x01')
+ eintr_retry_call(os.write, self._stop_event_sink, b'\x01')
finally:
# close the out-of-thread side of the pipe
os.close(self._stop_event_sink)
diff -purN pyudev-0.16.1.orig/pyudev/_util.py pyudev-0.16.1/pyudev/_util.py
--- pyudev-0.16.1.orig/pyudev/_util.py 2015-06-12 09:36:21.428469657 -0400
+++ pyudev-0.16.1/pyudev/_util.py 2015-06-12 09:36:45.923457440 -0400
@@ -32,6 +32,7 @@ from __future__ import (print_function,
import os
import sys
import stat
+import errno
if sys.version_info[0] == 2:
@@ -141,3 +142,34 @@ def get_device_type(filename):
return 'block'
else:
raise ValueError('not a device file: {0!r}'.format(filename))
+
+
+def eintr_retry_call(func, *args, **kwargs):
+ """
+ Handle interruptions to an interruptible system call.
+
+ Run an interruptible system call in a loop and retry if it raises EINTR.
+ The signal calls that my raise EINTR prior to Python 3.5 are listed in
+ PEP 0475. Any calls to these functions must be wrapped in eintr_retry_call
+ in order to handle EINTR returns in older versions of Python.
+
+ This function is based on _eintr_retry_call in python's subprocess.py.
+ """
+
+ # select.error inherits from Exception instead of OSError in Python 2
+ import select
+
+ while True:
+ try:
+ return func(*args, **kwargs)
+ except (OSError, IOError, select.error) as e:
+ # If this is not an IOError or OSError, it's the old select.error
+ # type, which means that the errno is only accessible via subscript
+ if isinstance(e, (OSError, IOError)):
+ error_code = e.errno
+ else:
+ error_code = e[0]
+
+ if error_code == errno.EINTR:
+ continue
+ raise