diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2015-03-20 11:54:28 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2015-03-20 11:54:28 (GMT) |
commit | a453cd8d85f583914a1bbb8d5054306bbbafdb7c (patch) | |
tree | 13410366cc0465611601372e268c11bd1579b6db /Modules/signalmodule.c | |
parent | a3c0202eb574bd3c09bc81b74227e146503fff94 (diff) | |
download | cpython-a453cd8d85f583914a1bbb8d5054306bbbafdb7c.zip cpython-a453cd8d85f583914a1bbb8d5054306bbbafdb7c.tar.gz cpython-a453cd8d85f583914a1bbb8d5054306bbbafdb7c.tar.bz2 |
Issue #23715: signal.sigwaitinfo() and signal.sigtimedwait() are now retried
when interrupted by a signal not in the *sigset* parameter, if the signal
handler does not raise an exception. signal.sigtimedwait() recomputes the
timeout with a monotonic clock when it is retried.
Remove test_signal.test_sigwaitinfo_interrupted() because sigwaitinfo() doesn't
raise InterruptedError anymore if it is interrupted by a signal not in its
sigset parameter.
Diffstat (limited to 'Modules/signalmodule.c')
-rw-r--r-- | Modules/signalmodule.c | 73 |
1 files changed, 47 insertions, 26 deletions
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 3ad8ebb..5dba8b1 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -934,6 +934,7 @@ signal_sigwaitinfo(PyObject *self, PyObject *args) sigset_t set; siginfo_t si; int err; + int async_err = 0; if (!PyArg_ParseTuple(args, "O:sigwaitinfo", &signals)) return NULL; @@ -941,11 +942,14 @@ signal_sigwaitinfo(PyObject *self, PyObject *args) if (iterable_to_sigset(signals, &set)) return NULL; - Py_BEGIN_ALLOW_THREADS - err = sigwaitinfo(&set, &si); - Py_END_ALLOW_THREADS + do { + Py_BEGIN_ALLOW_THREADS + err = sigwaitinfo(&set, &si); + Py_END_ALLOW_THREADS + } while (err == -1 + && errno == EINTR && !(async_err = PyErr_CheckSignals())); if (err == -1) - return PyErr_SetFromErrno(PyExc_OSError); + return (!async_err) ? PyErr_SetFromErrno(PyExc_OSError) : NULL; return fill_siginfo(&si); } @@ -962,25 +966,19 @@ Returns a struct_siginfo containing information about the signal."); static PyObject * signal_sigtimedwait(PyObject *self, PyObject *args) { - PyObject *signals, *timeout; - struct timespec buf; + PyObject *signals; + double timeout, frac; + struct timespec ts; sigset_t set; siginfo_t si; - time_t tv_sec; - long tv_nsec; - int err; + int res; + _PyTime_timeval deadline, monotonic; - if (!PyArg_ParseTuple(args, "OO:sigtimedwait", + if (!PyArg_ParseTuple(args, "Od:sigtimedwait", &signals, &timeout)) return NULL; - if (_PyTime_ObjectToTimespec(timeout, &tv_sec, &tv_nsec, - _PyTime_ROUND_DOWN) == -1) - return NULL; - buf.tv_sec = tv_sec; - buf.tv_nsec = tv_nsec; - - if (buf.tv_sec < 0 || buf.tv_nsec < 0) { + if (timeout < 0) { PyErr_SetString(PyExc_ValueError, "timeout must be non-negative"); return NULL; } @@ -988,15 +986,38 @@ signal_sigtimedwait(PyObject *self, PyObject *args) if (iterable_to_sigset(signals, &set)) return NULL; - Py_BEGIN_ALLOW_THREADS - err = sigtimedwait(&set, &si, &buf); - Py_END_ALLOW_THREADS - if (err == -1) { - if (errno == EAGAIN) - Py_RETURN_NONE; - else - return PyErr_SetFromErrno(PyExc_OSError); - } + _PyTime_monotonic(&deadline); + _PyTime_AddDouble(&deadline, timeout, _PyTime_ROUND_UP); + + do { + frac = fmod(timeout, 1.0); + timeout = floor(timeout); + ts.tv_sec = (long)timeout; + ts.tv_nsec = (long)(frac*1e9); + + Py_BEGIN_ALLOW_THREADS + res = sigtimedwait(&set, &si, &ts); + Py_END_ALLOW_THREADS + + if (res != -1) + break; + + if (errno != EINTR) { + if (errno == EAGAIN) + Py_RETURN_NONE; + else + return PyErr_SetFromErrno(PyExc_OSError); + } + + /* sigtimedwait() was interrupted by a signal (EINTR) */ + if (PyErr_CheckSignals()) + return NULL; + + _PyTime_monotonic(&monotonic); + timeout = _PyTime_INTERVAL(monotonic, deadline); + if (timeout <= 0.0) + break; + } while (1); return fill_siginfo(&si); } |