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