Author: Armin Rigo <[email protected]>
Branch: py3.5-eintr-pep475
Changeset: r88879:98fd8574f000
Date: 2016-12-04 19:29 +0100
http://bitbucket.org/pypy/pypy/changeset/98fd8574f000/

Log:    select.*() functions (the subset of the ones available on Linux)

diff --git a/pypy/module/select/interp_epoll.py 
b/pypy/module/select/interp_epoll.py
--- a/pypy/module/select/interp_epoll.py
+++ b/pypy/module/select/interp_epoll.py
@@ -7,6 +7,7 @@
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.error import exception_from_saved_errno
 from pypy.interpreter.typedef import TypeDef, GetSetProperty
+from pypy.interpreter import timeutils
 from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rtyper.tool import rffi_platform
 from rpython.rlib._rsocket_rffi import socketclose, FD_SETSIZE
@@ -156,9 +157,11 @@
     def descr_poll(self, space, timeout=-1.0, maxevents=-1):
         self.check_closed(space)
         if timeout < 0:
-            timeout = -1.0
+            end_time = 0.0
+            itimeout = -1
         else:
-            timeout *= 1000.0
+            end_time = timeutils.monotonic(space) + timeout
+            itimeout = int(timeout * 1000.0 + 0.999)
 
         if maxevents == -1:
             maxevents = FD_SETSIZE - 1
@@ -167,9 +170,18 @@
                         "maxevents must be greater than 0, not %d", maxevents)
 
         with lltype.scoped_alloc(rffi.CArray(epoll_event), maxevents) as evs:
-            nfds = epoll_wait(self.epfd, evs, maxevents, int(timeout))
-            if nfds < 0:
-                raise exception_from_saved_errno(space, space.w_IOError)
+            while True:
+                nfds = epoll_wait(self.epfd, evs, maxevents, itimeout)
+                if nfds < 0:
+                    if get_saved_errno() == errno.EINTR:
+                        space.getexecutioncontext().checksignals()
+                        if itimeout >= 0:
+                            timeout = end_time - timeutils.monotonic(space)
+                            timeout = max(timeout, 0.0)
+                            itimeout = int(timeout * 1000.0 + 0.999)
+                        continue
+                    raise exception_from_saved_errno(space, space.w_IOError)
+                break
 
             elist_w = [None] * nfds
             for i in xrange(nfds):
diff --git a/pypy/module/select/interp_kqueue.py 
b/pypy/module/select/interp_kqueue.py
--- a/pypy/module/select/interp_kqueue.py
+++ b/pypy/module/select/interp_kqueue.py
@@ -180,6 +180,7 @@
                             raise oefmt(space.w_ValueError,
                                         "Timeout must be None or >= 0, got %s",
                                         str(_timeout))
+                        XXX   # fix test_select_signal.py first, for PEP475!
                         sec = int(_timeout)
                         nsec = int(1e9 * (_timeout - sec))
                         rffi.setintfield(timeout, 'c_tv_sec', sec)
diff --git a/pypy/module/select/interp_select.py 
b/pypy/module/select/interp_select.py
--- a/pypy/module/select/interp_select.py
+++ b/pypy/module/select/interp_select.py
@@ -10,6 +10,7 @@
 from pypy.interpreter.gateway import (
     Unwrapper, WrappedDefault, interp2app, unwrap_spec)
 from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter import timeutils
 
 defaultevents = rpoll.POLLIN | rpoll.POLLOUT | rpoll.POLLPRI
 
@@ -49,8 +50,10 @@
 
     @unwrap_spec(w_timeout=WrappedDefault(None))
     def poll(self, space, w_timeout):
+        """WARNING: the timeout parameter is in **milliseconds**!"""
         if space.is_w(w_timeout, space.w_None):
             timeout = -1
+            end_time = 0
         else:
             # we want to be compatible with cpython and also accept things
             # that can be casted to integer (I think)
@@ -61,19 +64,29 @@
                 raise oefmt(space.w_TypeError,
                             "timeout must be an integer or None")
             timeout = space.c_int_w(w_timeout)
+            end_time = timeutils.monotonic(space) + timeout * 0.001
 
         if self.running:
             raise oefmt(space.w_RuntimeError, "concurrent poll() invocation")
-        self.running = True
-        try:
-            retval = rpoll.poll(self.fddict, timeout)
-        except rpoll.PollError as e:
-            message = e.get_msg()
-            raise OperationError(space.w_OSError,
-                                 space.newtuple([space.wrap(e.errno),
-                                                 space.wrap(message)]))
-        finally:
-            self.running = False
+        while True:
+            self.running = True
+            try:
+                retval = rpoll.poll(self.fddict, timeout)
+            except rpoll.PollError as e:
+                if e.errno == errno.EINTR:
+                    space.getexecutioncontext().checksignals()
+                    timeout = int((end_time - timeutils.monotonic(space))
+                                  * 1000.0 + 0.999)   # round up
+                    if timeout < 0:
+                        timeout = 0
+                    continue
+                message = e.get_msg()
+                raise OperationError(space.w_OSError,
+                                     space.newtuple([space.wrap(e.errno),
+                                                     space.wrap(message)]))
+            finally:
+                self.running = False
+            break
 
         retval_w = []
         for fd, revents in retval:
@@ -112,7 +125,7 @@
 
 
 def _call_select(space, iwtd_w, owtd_w, ewtd_w,
-                 ll_inl, ll_outl, ll_errl, ll_timeval):
+                 ll_inl, ll_outl, ll_errl, ll_timeval, timeout):
     fdlistin = fdlistout = fdlisterr = None
     nfds = -1
     if ll_inl:
@@ -122,13 +135,32 @@
     if ll_errl:
         fdlisterr, nfds = _build_fd_set(space, ewtd_w, ll_errl, nfds)
 
-    res = _c.select(nfds + 1, ll_inl, ll_outl, ll_errl, ll_timeval)
+    if ll_timeval:
+        end_time = timeutils.monotonic(space) + timeout
+    else:
+        end_time = 0.0
 
-    if res < 0:
-        errno = _c.geterrno()
-        msg = _c.socket_strerror_str(errno)
-        raise OperationError(space.w_OSError, space.newtuple([
-            space.wrap(errno), space.wrap(msg)]))
+    while True:
+        if ll_timeval:
+            i = int(timeout)
+            rffi.setintfield(ll_timeval, 'c_tv_sec', i)
+            rffi.setintfield(ll_timeval, 'c_tv_usec', int((timeout-i)*1000000))
+
+        res = _c.select(nfds + 1, ll_inl, ll_outl, ll_errl, ll_timeval)
+
+        if res >= 0:
+            break     # normal path
+        err = _c.geterrno()
+        if err != errno.EINTR:
+            msg = _c.socket_strerror_str(err)
+            raise OperationError(space.w_OSError, space.newtuple([
+                space.wrap(err), space.wrap(msg)]))
+        # got EINTR, automatic retry
+        space.getexecutioncontext().checksignals()
+        if timeout > 0.0:
+            timeout = end_time - timeutils.monotonic(space)
+            if timeout < 0.0:
+                timeout = 0.0
 
     resin_w = []
     resout_w = []
@@ -193,15 +225,12 @@
             ll_errl = lltype.malloc(_c.fd_set.TO, flavor='raw')
         if timeout >= 0.0:
             ll_timeval = rffi.make(_c.timeval)
-            i = int(timeout)
-            rffi.setintfield(ll_timeval, 'c_tv_sec', i)
-            rffi.setintfield(ll_timeval, 'c_tv_usec', int((timeout-i)*1000000))
 
         # Call this as a separate helper to avoid a large piece of code
         # in try:finally:.  Needed for calling further _always_inline_
         # helpers like _build_fd_set().
         return _call_select(space, iwtd_w, owtd_w, ewtd_w,
-                            ll_inl, ll_outl, ll_errl, ll_timeval)
+                            ll_inl, ll_outl, ll_errl, ll_timeval, timeout)
     finally:
         if ll_timeval:
             lltype.free(ll_timeval, flavor='raw')
diff --git a/pypy/module/select/test/test_select_signal.py 
b/pypy/module/select/test/test_select_signal.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/select/test/test_select_signal.py
@@ -0,0 +1,48 @@
+
+class AppTestSelectSignal:
+    spaceconfig = {
+        "usemodules": ['select', 'time', 'signal'],
+    }
+
+    def test_pep475_retry(self):
+        import select, time
+        import _signal as signal
+
+        def foo(*args):
+            signalled.append("ALARM")
+
+        # a list of functions that will do nothing more than sleep for 3
+        # seconds
+        cases = [(select.select, [], [], [], 3.0)]
+
+        if hasattr(select, 'poll'):
+            import posix
+            poll = select.poll()
+            cases.append((poll.poll, 3000))    # milliseconds
+
+        if hasattr(select, 'epoll'):
+            epoll = select.epoll()
+            cases.append((epoll.poll, 3.0))
+
+        if hasattr(select, 'kqueue'):
+            kqueue = select.kqueue()
+            cases.append((kqueue.control, [], 1, 3.0))
+
+        if hasattr(select, 'devpoll'):
+            raise NotImplementedError("write this test if we have devpoll")
+
+        for wait_for_three_seconds in cases:
+            print(wait_for_three_seconds[0])
+            signalled = []
+            signal.signal(signal.SIGALRM, foo)
+            try:
+                t1 = time.time()
+                signal.alarm(1)
+                wait_for_three_seconds[0](*wait_for_three_seconds[1:])
+                t2 = time.time()
+            finally:
+                signal.signal(signal.SIGALRM, signal.SIG_DFL)
+
+            print("result: signalled = %r in %s seconds" % (signalled, t2 - 
t1))
+            assert signalled != []
+            assert t2 - t1 > 2.99
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to