|
|
fea5c0 |
diff --git a/pyudev/_util.py b/pyudev/_util.py
|
|
|
fea5c0 |
index 266b161..3ce82b2 100644
|
|
|
fea5c0 |
--- a/pyudev/_util.py
|
|
|
fea5c0 |
+++ b/pyudev/_util.py
|
|
|
fea5c0 |
@@ -32,6 +32,7 @@
|
|
|
fea5c0 |
import os
|
|
|
fea5c0 |
import sys
|
|
|
fea5c0 |
import stat
|
|
|
fea5c0 |
+import errno
|
|
|
fea5c0 |
|
|
|
fea5c0 |
|
|
|
fea5c0 |
if sys.version_info[0] == 2:
|
|
|
fea5c0 |
@@ -141,3 +142,37 @@ def get_device_type(filename):
|
|
|
fea5c0 |
return 'block'
|
|
|
fea5c0 |
else:
|
|
|
fea5c0 |
raise ValueError('not a device file: {0!r}'.format(filename))
|
|
|
fea5c0 |
+
|
|
|
fea5c0 |
+
|
|
|
fea5c0 |
+def eintr_retry_call(func, *args, **kwargs):
|
|
|
fea5c0 |
+ """
|
|
|
fea5c0 |
+ Handle interruptions to an interruptible system call.
|
|
|
fea5c0 |
+
|
|
|
fea5c0 |
+ Run an interruptible system call in a loop and retry if it raises EINTR.
|
|
|
fea5c0 |
+ The signal calls that may raise EINTR prior to Python 3.5 are listed in
|
|
|
fea5c0 |
+ PEP 0475. Any calls to these functions must be wrapped in eintr_retry_call
|
|
|
fea5c0 |
+ in order to handle EINTR returns in older versions of Python.
|
|
|
fea5c0 |
+
|
|
|
fea5c0 |
+ This function is safe to use under Python 3.5 and newer since the wrapped
|
|
|
fea5c0 |
+ function will simply return without raising EINTR.
|
|
|
fea5c0 |
+
|
|
|
fea5c0 |
+ This function is based on _eintr_retry_call in python's subprocess.py.
|
|
|
fea5c0 |
+ """
|
|
|
fea5c0 |
+
|
|
|
fea5c0 |
+ # select.error inherits from Exception instead of OSError in Python 2
|
|
|
fea5c0 |
+ import select
|
|
|
fea5c0 |
+
|
|
|
fea5c0 |
+ while True:
|
|
|
fea5c0 |
+ try:
|
|
|
fea5c0 |
+ return func(*args, **kwargs)
|
|
|
fea5c0 |
+ except (OSError, IOError, select.error) as e:
|
|
|
fea5c0 |
+ # If this is not an IOError or OSError, it's the old select.error
|
|
|
fea5c0 |
+ # type, which means that the errno is only accessible via subscript
|
|
|
fea5c0 |
+ if isinstance(e, (OSError, IOError)):
|
|
|
fea5c0 |
+ error_code = e.errno
|
|
|
fea5c0 |
+ else:
|
|
|
fea5c0 |
+ error_code = e.args[0]
|
|
|
fea5c0 |
+
|
|
|
fea5c0 |
+ if error_code == errno.EINTR:
|
|
|
fea5c0 |
+ continue
|
|
|
fea5c0 |
+ raise
|
|
|
fea5c0 |
diff --git a/pyudev/monitor.py b/pyudev/monitor.py
|
|
|
fea5c0 |
index b1eb71c..d87dc2c 100644
|
|
|
fea5c0 |
--- a/pyudev/monitor.py
|
|
|
fea5c0 |
+++ b/pyudev/monitor.py
|
|
|
fea5c0 |
@@ -36,7 +36,7 @@ import select
|
|
|
fea5c0 |
from threading import Thread
|
|
|
fea5c0 |
from contextlib import closing
|
|
|
fea5c0 |
|
|
|
fea5c0 |
-from pyudev._util import ensure_byte_string, ensure_unicode_string, reraise
|
|
|
fea5c0 |
+from pyudev._util import ensure_byte_string, ensure_unicode_string, reraise, eintr_retry_call
|
|
|
fea5c0 |
|
|
|
fea5c0 |
from pyudev.core import Device
|
|
|
fea5c0 |
|
|
|
fea5c0 |
@@ -328,7 +328,7 @@ class Monitor(object):
|
|
|
fea5c0 |
with closing(select.epoll()) as notifier:
|
|
|
fea5c0 |
notifier.register(self, select.EPOLLIN)
|
|
|
fea5c0 |
while True:
|
|
|
fea5c0 |
- events = notifier.poll()
|
|
|
fea5c0 |
+ events = eintr_retry_call(notifier.poll)
|
|
|
fea5c0 |
for event in events:
|
|
|
fea5c0 |
yield self.receive_device()
|
|
|
fea5c0 |
|
|
|
fea5c0 |
@@ -399,7 +399,7 @@ class MonitorObserver(Thread):
|
|
|
fea5c0 |
# and on the monitor
|
|
|
fea5c0 |
notifier.register(self.monitor, select.EPOLLIN)
|
|
|
fea5c0 |
while True:
|
|
|
fea5c0 |
- for fd, _ in notifier.poll():
|
|
|
fea5c0 |
+ for fd, _ in eintr_retry_call(notifier.poll):
|
|
|
fea5c0 |
if fd == self._stop_event_source:
|
|
|
fea5c0 |
# in case of a stop event, close our pipe side, and
|
|
|
fea5c0 |
# return from the thread
|