bpo-41710: Add private _PyDeadline_Get() function (GH-28674)

Add a private C API for deadlines: add _PyDeadline_Init() and
_PyDeadline_Get() functions.

* Add _PyTime_Add() and _PyTime_Mul() functions which compute t1+t2
  and t1*t2 and clamp the result on overflow.
* _PyTime_MulDiv() now uses _PyTime_Add() and _PyTime_Mul().
This commit is contained in:
Victor Stinner 2021-10-01 13:29:25 +02:00 committed by GitHub
parent 54957f16a6
commit 833fdf126c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 177 additions and 109 deletions

View file

@ -182,7 +182,7 @@ _queue.SimpleQueue.get
cls: defining_class
/
block: bool = True
timeout: object = None
timeout as timeout_obj: object = None
Remove and return an item from the queue.
@ -198,11 +198,11 @@ in that case).
static PyObject *
_queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
int block, PyObject *timeout)
/*[clinic end generated code: output=1969aefa7db63666 input=5fc4d56b9a54757e]*/
int block, PyObject *timeout_obj)
/*[clinic end generated code: output=5c2cca914cd1e55b input=5b4047bfbc645ec1]*/
{
_PyTime_t endtime = 0;
_PyTime_t timeout_val;
_PyTime_t timeout;
PyObject *item;
PyLockStatus r;
PY_TIMEOUT_T microseconds;
@ -211,24 +211,25 @@ _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
/* Non-blocking */
microseconds = 0;
}
else if (timeout != Py_None) {
else if (timeout_obj != Py_None) {
/* With timeout */
if (_PyTime_FromSecondsObject(&timeout_val,
timeout, _PyTime_ROUND_CEILING) < 0)
if (_PyTime_FromSecondsObject(&timeout,
timeout_obj, _PyTime_ROUND_CEILING) < 0) {
return NULL;
if (timeout_val < 0) {
}
if (timeout < 0) {
PyErr_SetString(PyExc_ValueError,
"'timeout' must be a non-negative number");
return NULL;
}
microseconds = _PyTime_AsMicroseconds(timeout_val,
microseconds = _PyTime_AsMicroseconds(timeout,
_PyTime_ROUND_CEILING);
if (microseconds > PY_TIMEOUT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"timeout value is too large");
return NULL;
}
endtime = _PyTime_GetMonotonicClock() + timeout_val;
endtime = _PyDeadline_Init(timeout);
}
else {
/* Infinitely blocking */
@ -247,6 +248,7 @@ _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
r = PyThread_acquire_lock_timed(self->lock, microseconds, 1);
Py_END_ALLOW_THREADS
}
if (r == PY_LOCK_INTR && Py_MakePendingCalls() < 0) {
return NULL;
}
@ -258,12 +260,15 @@ _queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
return NULL;
}
self->locked = 1;
/* Adjust timeout for next iteration (if any) */
if (endtime > 0) {
timeout_val = endtime - _PyTime_GetMonotonicClock();
microseconds = _PyTime_AsMicroseconds(timeout_val, _PyTime_ROUND_CEILING);
if (microseconds > 0) {
timeout = _PyDeadline_Get(endtime);
microseconds = _PyTime_AsMicroseconds(timeout,
_PyTime_ROUND_CEILING);
}
}
/* BEGIN GIL-protected critical section */
assert(self->lst_pos < PyList_GET_SIZE(self->lst));
item = simplequeue_pop_item(self);

View file

@ -949,8 +949,9 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
timeout = GET_SOCKET_TIMEOUT(sock);
has_timeout = (timeout > 0);
if (has_timeout)
deadline = _PyTime_GetMonotonicClock() + timeout;
if (has_timeout) {
deadline = _PyDeadline_Init(timeout);
}
/* Actually negotiate SSL connection */
/* XXX If SSL_do_handshake() returns 0, it's also a failure. */
@ -965,7 +966,7 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
goto error;
if (has_timeout)
timeout = deadline - _PyTime_GetMonotonicClock();
timeout = _PyDeadline_Get(deadline);
if (err.ssl == SSL_ERROR_WANT_READ) {
sockstate = PySSL_select(sock, 0, timeout);
@ -2326,8 +2327,9 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
timeout = GET_SOCKET_TIMEOUT(sock);
has_timeout = (timeout > 0);
if (has_timeout)
deadline = _PyTime_GetMonotonicClock() + timeout;
if (has_timeout) {
deadline = _PyDeadline_Init(timeout);
}
sockstate = PySSL_select(sock, 1, timeout);
if (sockstate == SOCKET_HAS_TIMED_OUT) {
@ -2354,8 +2356,9 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
if (PyErr_CheckSignals())
goto error;
if (has_timeout)
timeout = deadline - _PyTime_GetMonotonicClock();
if (has_timeout) {
timeout = _PyDeadline_Get(deadline);
}
if (err.ssl == SSL_ERROR_WANT_READ) {
sockstate = PySSL_select(sock, 0, timeout);
@ -2494,7 +2497,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
timeout = GET_SOCKET_TIMEOUT(sock);
has_timeout = (timeout > 0);
if (has_timeout)
deadline = _PyTime_GetMonotonicClock() + timeout;
deadline = _PyDeadline_Init(timeout);
do {
PySSL_BEGIN_ALLOW_THREADS
@ -2506,8 +2509,9 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
if (PyErr_CheckSignals())
goto error;
if (has_timeout)
timeout = deadline - _PyTime_GetMonotonicClock();
if (has_timeout) {
timeout = _PyDeadline_Get(deadline);
}
if (err.ssl == SSL_ERROR_WANT_READ) {
sockstate = PySSL_select(sock, 0, timeout);
@ -2592,8 +2596,9 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
timeout = GET_SOCKET_TIMEOUT(sock);
has_timeout = (timeout > 0);
if (has_timeout)
deadline = _PyTime_GetMonotonicClock() + timeout;
if (has_timeout) {
deadline = _PyDeadline_Init(timeout);
}
while (1) {
PySSL_BEGIN_ALLOW_THREADS
@ -2626,8 +2631,9 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
continue;
}
if (has_timeout)
timeout = deadline - _PyTime_GetMonotonicClock();
if (has_timeout) {
timeout = _PyDeadline_Get(deadline);
}
/* Possibly retry shutdown until timeout or failure */
if (err.ssl == SSL_ERROR_WANT_READ)

View file

@ -84,13 +84,12 @@ lock_dealloc(lockobject *self)
static PyLockStatus
acquire_timed(PyThread_type_lock lock, _PyTime_t timeout)
{
PyLockStatus r;
_PyTime_t endtime = 0;
if (timeout > 0) {
endtime = _PyTime_GetMonotonicClock() + timeout;
endtime = _PyDeadline_Init(timeout);
}
PyLockStatus r;
do {
_PyTime_t microseconds;
microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_CEILING);
@ -114,7 +113,7 @@ acquire_timed(PyThread_type_lock lock, _PyTime_t timeout)
/* If we're using a timeout, recompute the timeout after processing
* signals, since those can take time. */
if (timeout > 0) {
timeout = endtime - _PyTime_GetMonotonicClock();
timeout = _PyDeadline_Get(endtime);
/* Check for negative values, since those mean block forever.
*/

View file

@ -139,7 +139,7 @@ PyDoc_STRVAR(_queue_SimpleQueue_get__doc__,
static PyObject *
_queue_SimpleQueue_get_impl(simplequeueobject *self, PyTypeObject *cls,
int block, PyObject *timeout);
int block, PyObject *timeout_obj);
static PyObject *
_queue_SimpleQueue_get(simplequeueobject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
@ -148,13 +148,13 @@ _queue_SimpleQueue_get(simplequeueobject *self, PyTypeObject *cls, PyObject *con
static const char * const _keywords[] = {"block", "timeout", NULL};
static _PyArg_Parser _parser = {"|pO:get", _keywords, 0};
int block = 1;
PyObject *timeout = Py_None;
PyObject *timeout_obj = Py_None;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&block, &timeout)) {
&block, &timeout_obj)) {
goto exit;
}
return_value = _queue_SimpleQueue_get_impl(self, cls, block, timeout);
return_value = _queue_SimpleQueue_get_impl(self, cls, block, timeout_obj);
exit:
return return_value;
@ -248,4 +248,4 @@ _queue_SimpleQueue_qsize(simplequeueobject *self, PyObject *Py_UNUSED(ignored))
exit:
return return_value;
}
/*[clinic end generated code: output=96cc57168d72aab1 input=a9049054013a1b77]*/
/*[clinic end generated code: output=acfaf0191d8935db input=a9049054013a1b77]*/

View file

@ -318,8 +318,9 @@ select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist,
if (omax > max) max = omax;
if (emax > max) max = emax;
if (tvp)
deadline = _PyTime_GetMonotonicClock() + timeout;
if (tvp) {
deadline = _PyDeadline_Init(timeout);
}
do {
Py_BEGIN_ALLOW_THREADS
@ -335,7 +336,7 @@ select_select_impl(PyObject *module, PyObject *rlist, PyObject *wlist,
goto finally;
if (tvp) {
timeout = deadline - _PyTime_GetMonotonicClock();
timeout = _PyDeadline_Get(deadline);
if (timeout < 0) {
/* bpo-35310: lists were unmodified -- clear them explicitly */
FD_ZERO(&ifdset);
@ -599,7 +600,7 @@ select_poll_poll_impl(pollObject *self, PyObject *timeout_obj)
}
if (timeout >= 0) {
deadline = _PyTime_GetMonotonicClock() + timeout;
deadline = _PyDeadline_Init(timeout);
}
}
@ -646,7 +647,7 @@ select_poll_poll_impl(pollObject *self, PyObject *timeout_obj)
}
if (timeout >= 0) {
timeout = deadline - _PyTime_GetMonotonicClock();
timeout = _PyDeadline_Get(deadline);
if (timeout < 0) {
poll_result = 0;
break;
@ -938,8 +939,9 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj)
dvp.dp_nfds = self->max_n_fds;
dvp.dp_timeout = (int)ms;
if (timeout >= 0)
deadline = _PyTime_GetMonotonicClock() + timeout;
if (timeout >= 0) {
deadline = _PyDeadline_Init(timeout);
}
do {
/* call devpoll() */
@ -956,7 +958,7 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj)
return NULL;
if (timeout >= 0) {
timeout = deadline - _PyTime_GetMonotonicClock();
timeout = _PyDeadline_Get(deadline);
if (timeout < 0) {
poll_result = 0;
break;
@ -1550,7 +1552,7 @@ select_epoll_poll_impl(pyEpoll_Object *self, PyObject *timeout_obj,
}
if (timeout >= 0) {
deadline = _PyTime_GetMonotonicClock() + timeout;
deadline = _PyDeadline_Init(timeout);
}
}
@ -1584,7 +1586,7 @@ select_epoll_poll_impl(pyEpoll_Object *self, PyObject *timeout_obj,
goto error;
if (timeout >= 0) {
timeout = deadline - _PyTime_GetMonotonicClock();
timeout = _PyDeadline_Get(deadline);
if (timeout < 0) {
nfds = 0;
break;
@ -2172,8 +2174,9 @@ select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist,
}
}
if (ptimeoutspec)
deadline = _PyTime_GetMonotonicClock() + timeout;
if (ptimeoutspec) {
deadline = _PyDeadline_Init(timeout);
}
do {
Py_BEGIN_ALLOW_THREADS
@ -2190,7 +2193,7 @@ select_kqueue_control_impl(kqueue_queue_Object *self, PyObject *changelist,
goto error;
if (ptimeoutspec) {
timeout = deadline - _PyTime_GetMonotonicClock();
timeout = _PyDeadline_Get(deadline);
if (timeout < 0) {
gotevents = 0;
break;

View file

@ -1221,11 +1221,7 @@ signal_sigtimedwait_impl(PyObject *module, sigset_t sigset,
PyObject *timeout_obj)
/*[clinic end generated code: output=59c8971e8ae18a64 input=87fd39237cf0b7ba]*/
{
struct timespec ts;
siginfo_t si;
int res;
_PyTime_t timeout, deadline, monotonic;
_PyTime_t timeout;
if (_PyTime_FromSecondsObject(&timeout,
timeout_obj, _PyTime_ROUND_CEILING) < 0)
return NULL;
@ -1235,12 +1231,16 @@ signal_sigtimedwait_impl(PyObject *module, sigset_t sigset,
return NULL;
}
deadline = _PyTime_GetMonotonicClock() + timeout;
_PyTime_t deadline = _PyDeadline_Init(timeout);
siginfo_t si;
do {
if (_PyTime_AsTimespec(timeout, &ts) < 0)
struct timespec ts;
if (_PyTime_AsTimespec(timeout, &ts) < 0) {
return NULL;
}
int res;
Py_BEGIN_ALLOW_THREADS
res = sigtimedwait(&sigset, &si, &ts);
Py_END_ALLOW_THREADS
@ -1259,10 +1259,10 @@ signal_sigtimedwait_impl(PyObject *module, sigset_t sigset,
if (PyErr_CheckSignals())
return NULL;
monotonic = _PyTime_GetMonotonicClock();
timeout = deadline - monotonic;
if (timeout < 0)
timeout = _PyDeadline_Get(deadline);
if (timeout < 0) {
break;
}
} while (1);
return fill_siginfo(&si);

View file

@ -840,18 +840,20 @@ sock_call_ex(PySocketSockObject *s,
if (deadline_initialized) {
/* recompute the timeout */
interval = deadline - _PyTime_GetMonotonicClock();
interval = _PyDeadline_Get(deadline);
}
else {
deadline_initialized = 1;
deadline = _PyTime_GetMonotonicClock() + timeout;
deadline = _PyDeadline_Init(timeout);
interval = timeout;
}
if (interval >= 0)
if (interval >= 0) {
res = internal_select(s, writing, interval, connect);
else
}
else {
res = 1;
}
}
else {
res = internal_select(s, writing, timeout, connect);
@ -4176,7 +4178,7 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
Py_buffer pbuf;
struct sock_send ctx;
int has_timeout = (s->sock_timeout > 0);
_PyTime_t interval = s->sock_timeout;
_PyTime_t timeout = s->sock_timeout;
_PyTime_t deadline = 0;
int deadline_initialized = 0;
PyObject *res = NULL;
@ -4195,14 +4197,14 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
if (has_timeout) {
if (deadline_initialized) {
/* recompute the timeout */
interval = deadline - _PyTime_GetMonotonicClock();
timeout = _PyDeadline_Get(deadline);
}
else {
deadline_initialized = 1;
deadline = _PyTime_GetMonotonicClock() + s->sock_timeout;
deadline = _PyDeadline_Init(timeout);
}
if (interval <= 0) {
if (timeout <= 0) {
PyErr_SetString(PyExc_TimeoutError, "timed out");
goto done;
}
@ -4211,7 +4213,7 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
ctx.buf = buf;
ctx.len = len;
ctx.flags = flags;
if (sock_call_ex(s, 1, sock_send_impl, &ctx, 0, NULL, interval) < 0)
if (sock_call_ex(s, 1, sock_send_impl, &ctx, 0, NULL, timeout) < 0)
goto done;
n = ctx.result;
assert(n >= 0);