Issue #22117: The thread module uses the new _PyTime_t timestamp API
Add also a new _PyTime_AsMicroseconds() function. threading.TIMEOUT_MAX is now be smaller: only 292 years instead of 292,271 years on 64-bit system for example. Sorry, your threads will hang a *little bit* shorter. Call me if you want to ensure that your locks wait longer, I can share some tricks with you.
This commit is contained in:
parent
e245231fab
commit
f5faad2bf0
|
@ -74,24 +74,6 @@ PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
|
||||||
long *nsec,
|
long *nsec,
|
||||||
_PyTime_round_t);
|
_PyTime_round_t);
|
||||||
|
|
||||||
/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
|
|
||||||
The clock is not affected by system clock updates. The reference point of
|
|
||||||
the returned value is undefined, so that only the difference between the
|
|
||||||
results of consecutive calls is valid.
|
|
||||||
|
|
||||||
The function never fails. _PyTime_Init() ensures that a monotonic clock
|
|
||||||
is available and works. */
|
|
||||||
PyAPI_FUNC(void) _PyTime_monotonic(
|
|
||||||
_PyTime_timeval *tp);
|
|
||||||
|
|
||||||
/* Similar to _PyTime_monotonic(), fill also info (if set) with information of
|
|
||||||
the function used to get the time.
|
|
||||||
|
|
||||||
Return 0 on success, raise an exception and return -1 on error. */
|
|
||||||
PyAPI_FUNC(int) _PyTime_monotonic_info(
|
|
||||||
_PyTime_timeval *tp,
|
|
||||||
_Py_clock_info_t *info);
|
|
||||||
|
|
||||||
/* Add interval seconds to tv */
|
/* Add interval seconds to tv */
|
||||||
PyAPI_FUNC(void)
|
PyAPI_FUNC(void)
|
||||||
_PyTime_AddDouble(_PyTime_timeval *tv, double interval,
|
_PyTime_AddDouble(_PyTime_timeval *tv, double interval,
|
||||||
|
@ -105,6 +87,8 @@ PyAPI_FUNC(int) _PyTime_Init(void);
|
||||||
|
|
||||||
#ifdef PY_INT64_T
|
#ifdef PY_INT64_T
|
||||||
typedef PY_INT64_T _PyTime_t;
|
typedef PY_INT64_T _PyTime_t;
|
||||||
|
#define _PyTime_MIN PY_LLONG_MIN
|
||||||
|
#define _PyTime_MAX PY_LLONG_MAX
|
||||||
#else
|
#else
|
||||||
# error "_PyTime_t need signed 64-bit integer type"
|
# error "_PyTime_t need signed 64-bit integer type"
|
||||||
#endif
|
#endif
|
||||||
|
@ -125,6 +109,10 @@ PyAPI_FUNC(double) _PyTime_AsSecondsDouble(_PyTime_t t);
|
||||||
PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
|
PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
|
||||||
_PyTime_round_t round);
|
_PyTime_round_t round);
|
||||||
|
|
||||||
|
/* Convert timestamp to a number of microseconds (10^-6 seconds). */
|
||||||
|
PyAPI_FUNC(_PyTime_t) _PyTime_AsMicroseconds(_PyTime_t t,
|
||||||
|
_PyTime_round_t round);
|
||||||
|
|
||||||
/* Convert timestamp to a number of nanoseconds (10^-9 seconds) as a Python int
|
/* Convert timestamp to a number of nanoseconds (10^-9 seconds) as a Python int
|
||||||
object. */
|
object. */
|
||||||
PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
|
PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
|
||||||
|
|
|
@ -49,21 +49,18 @@ lock_dealloc(lockobject *self)
|
||||||
* timeout.
|
* timeout.
|
||||||
*/
|
*/
|
||||||
static PyLockStatus
|
static PyLockStatus
|
||||||
acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
|
acquire_timed(PyThread_type_lock lock, _PyTime_t timeout)
|
||||||
{
|
{
|
||||||
PyLockStatus r;
|
PyLockStatus r;
|
||||||
_PyTime_timeval curtime;
|
_PyTime_t endtime = 0;
|
||||||
_PyTime_timeval endtime;
|
_PyTime_t microseconds;
|
||||||
|
|
||||||
|
|
||||||
if (microseconds > 0) {
|
|
||||||
_PyTime_monotonic(&endtime);
|
|
||||||
endtime.tv_sec += microseconds / (1000 * 1000);
|
|
||||||
endtime.tv_usec += microseconds % (1000 * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (timeout > 0)
|
||||||
|
endtime = _PyTime_GetMonotonicClock() + timeout;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_UP);
|
||||||
|
|
||||||
/* first a simple non-blocking try without releasing the GIL */
|
/* first a simple non-blocking try without releasing the GIL */
|
||||||
r = PyThread_acquire_lock_timed(lock, 0, 0);
|
r = PyThread_acquire_lock_timed(lock, 0, 0);
|
||||||
if (r == PY_LOCK_FAILURE && microseconds != 0) {
|
if (r == PY_LOCK_FAILURE && microseconds != 0) {
|
||||||
|
@ -82,14 +79,12 @@ acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
|
||||||
|
|
||||||
/* If we're using a timeout, recompute the timeout after processing
|
/* If we're using a timeout, recompute the timeout after processing
|
||||||
* signals, since those can take time. */
|
* signals, since those can take time. */
|
||||||
if (microseconds > 0) {
|
if (timeout > 0) {
|
||||||
_PyTime_monotonic(&curtime);
|
timeout = endtime - _PyTime_GetMonotonicClock();
|
||||||
microseconds = ((endtime.tv_sec - curtime.tv_sec) * 1000000 +
|
|
||||||
(endtime.tv_usec - curtime.tv_usec));
|
|
||||||
|
|
||||||
/* Check for negative values, since those mean block forever.
|
/* Check for negative values, since those mean block forever.
|
||||||
*/
|
*/
|
||||||
if (microseconds <= 0) {
|
if (timeout <= 0) {
|
||||||
r = PY_LOCK_FAILURE;
|
r = PY_LOCK_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,44 +94,60 @@ acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static int
|
||||||
lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)
|
lock_acquire_parse_args(PyObject *args, PyObject *kwds,
|
||||||
|
_PyTime_t *timeout)
|
||||||
{
|
{
|
||||||
char *kwlist[] = {"blocking", "timeout", NULL};
|
char *kwlist[] = {"blocking", "timeout", NULL};
|
||||||
int blocking = 1;
|
int blocking = 1;
|
||||||
double timeout = -1;
|
PyObject *timeout_obj = NULL;
|
||||||
PY_TIMEOUT_T microseconds;
|
const _PyTime_t unset_timeout = _PyTime_FromNanoseconds(-1000000000);
|
||||||
PyLockStatus r;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist,
|
*timeout = unset_timeout ;
|
||||||
&blocking, &timeout))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!blocking && timeout != -1) {
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO:acquire", kwlist,
|
||||||
PyErr_SetString(PyExc_ValueError, "can't specify a timeout "
|
&blocking, &timeout_obj))
|
||||||
"for a non-blocking call");
|
return -1;
|
||||||
return NULL;
|
|
||||||
|
if (timeout_obj
|
||||||
|
&& _PyTime_FromSecondsObject(timeout, timeout_obj, _PyTime_ROUND_UP) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!blocking && *timeout != unset_timeout ) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"can't specify a timeout for a non-blocking call");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
if (timeout < 0 && timeout != -1) {
|
if (*timeout < 0 && *timeout != unset_timeout) {
|
||||||
PyErr_SetString(PyExc_ValueError, "timeout value must be "
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"strictly positive");
|
"timeout value must be positive");
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
if (!blocking)
|
if (!blocking)
|
||||||
microseconds = 0;
|
*timeout = 0;
|
||||||
else if (timeout == -1)
|
else if (*timeout != unset_timeout) {
|
||||||
microseconds = -1;
|
_PyTime_t microseconds;
|
||||||
else {
|
|
||||||
timeout *= 1e6;
|
microseconds = _PyTime_AsMicroseconds(*timeout, _PyTime_ROUND_UP);
|
||||||
if (timeout >= (double) PY_TIMEOUT_MAX) {
|
if (microseconds >= PY_TIMEOUT_MAX) {
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
"timeout value is too large");
|
"timeout value is too large");
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
microseconds = (PY_TIMEOUT_T) timeout;
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
r = acquire_timed(self->lock_lock, microseconds);
|
static PyObject *
|
||||||
|
lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
_PyTime_t timeout;
|
||||||
|
PyLockStatus r;
|
||||||
|
|
||||||
|
if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
r = acquire_timed(self->lock_lock, timeout);
|
||||||
if (r == PY_LOCK_INTR) {
|
if (r == PY_LOCK_INTR) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -281,41 +292,13 @@ rlock_dealloc(rlockobject *self)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
|
rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
char *kwlist[] = {"blocking", "timeout", NULL};
|
_PyTime_t timeout;
|
||||||
int blocking = 1;
|
|
||||||
double timeout = -1;
|
|
||||||
PY_TIMEOUT_T microseconds;
|
|
||||||
long tid;
|
long tid;
|
||||||
PyLockStatus r = PY_LOCK_ACQUIRED;
|
PyLockStatus r = PY_LOCK_ACQUIRED;
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist,
|
if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
|
||||||
&blocking, &timeout))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (!blocking && timeout != -1) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "can't specify a timeout "
|
|
||||||
"for a non-blocking call");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (timeout < 0 && timeout != -1) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "timeout value must be "
|
|
||||||
"strictly positive");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!blocking)
|
|
||||||
microseconds = 0;
|
|
||||||
else if (timeout == -1)
|
|
||||||
microseconds = -1;
|
|
||||||
else {
|
|
||||||
timeout *= 1e6;
|
|
||||||
if (timeout >= (double) PY_TIMEOUT_MAX) {
|
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
|
||||||
"timeout value is too large");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
microseconds = (PY_TIMEOUT_T) timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
tid = PyThread_get_thread_ident();
|
tid = PyThread_get_thread_ident();
|
||||||
if (self->rlock_count > 0 && tid == self->rlock_owner) {
|
if (self->rlock_count > 0 && tid == self->rlock_owner) {
|
||||||
unsigned long count = self->rlock_count + 1;
|
unsigned long count = self->rlock_count + 1;
|
||||||
|
@ -327,7 +310,7 @@ rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
|
||||||
self->rlock_count = count;
|
self->rlock_count = count;
|
||||||
Py_RETURN_TRUE;
|
Py_RETURN_TRUE;
|
||||||
}
|
}
|
||||||
r = acquire_timed(self->rlock_lock, microseconds);
|
r = acquire_timed(self->rlock_lock, timeout);
|
||||||
if (r == PY_LOCK_ACQUIRED) {
|
if (r == PY_LOCK_ACQUIRED) {
|
||||||
assert(self->rlock_count == 0);
|
assert(self->rlock_count == 0);
|
||||||
self->rlock_owner = tid;
|
self->rlock_owner = tid;
|
||||||
|
@ -1362,7 +1345,9 @@ static struct PyModuleDef threadmodule = {
|
||||||
PyMODINIT_FUNC
|
PyMODINIT_FUNC
|
||||||
PyInit__thread(void)
|
PyInit__thread(void)
|
||||||
{
|
{
|
||||||
PyObject *m, *d, *timeout_max;
|
PyObject *m, *d, *v;
|
||||||
|
double time_max;
|
||||||
|
double timeout_max;
|
||||||
|
|
||||||
/* Initialize types: */
|
/* Initialize types: */
|
||||||
if (PyType_Ready(&localdummytype) < 0)
|
if (PyType_Ready(&localdummytype) < 0)
|
||||||
|
@ -1379,10 +1364,14 @@ PyInit__thread(void)
|
||||||
if (m == NULL)
|
if (m == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
timeout_max = PyFloat_FromDouble(PY_TIMEOUT_MAX / 1000000);
|
timeout_max = PY_TIMEOUT_MAX / 1000000;
|
||||||
if (!timeout_max)
|
time_max = floor(_PyTime_AsSecondsDouble(_PyTime_MAX));
|
||||||
|
timeout_max = Py_MIN(timeout_max, time_max);
|
||||||
|
|
||||||
|
v = PyFloat_FromDouble(timeout_max);
|
||||||
|
if (!v)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (PyModule_AddObject(m, "TIMEOUT_MAX", timeout_max) < 0)
|
if (PyModule_AddObject(m, "TIMEOUT_MAX", v) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Add a symbolic constant */
|
/* Add a symbolic constant */
|
||||||
|
|
132
Python/pytime.c
132
Python/pytime.c
|
@ -119,128 +119,6 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise)
|
|
||||||
{
|
|
||||||
#ifdef Py_DEBUG
|
|
||||||
static _PyTime_timeval last = {0, -1};
|
|
||||||
#endif
|
|
||||||
#if defined(MS_WINDOWS)
|
|
||||||
ULONGLONG result;
|
|
||||||
|
|
||||||
assert(info == NULL || raise);
|
|
||||||
|
|
||||||
result = GetTickCount64();
|
|
||||||
|
|
||||||
tp->tv_sec = result / SEC_TO_MS;
|
|
||||||
tp->tv_usec = (result % SEC_TO_MS) * MS_TO_US;
|
|
||||||
|
|
||||||
if (info) {
|
|
||||||
DWORD timeAdjustment, timeIncrement;
|
|
||||||
BOOL isTimeAdjustmentDisabled, ok;
|
|
||||||
info->implementation = "GetTickCount64()";
|
|
||||||
info->monotonic = 1;
|
|
||||||
ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
|
|
||||||
&isTimeAdjustmentDisabled);
|
|
||||||
if (!ok) {
|
|
||||||
PyErr_SetFromWindowsErr(0);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
info->resolution = timeIncrement * 1e-7;
|
|
||||||
info->adjustable = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
static mach_timebase_info_data_t timebase;
|
|
||||||
uint64_t time;
|
|
||||||
|
|
||||||
if (timebase.denom == 0) {
|
|
||||||
/* According to the Technical Q&A QA1398, mach_timebase_info() cannot
|
|
||||||
fail: https://developer.apple.com/library/mac/#qa/qa1398/ */
|
|
||||||
(void)mach_timebase_info(&timebase);
|
|
||||||
}
|
|
||||||
|
|
||||||
time = mach_absolute_time();
|
|
||||||
|
|
||||||
/* nanoseconds => microseconds */
|
|
||||||
time /= US_TO_NS;
|
|
||||||
/* apply timebase factor */
|
|
||||||
time *= timebase.numer;
|
|
||||||
time /= timebase.denom;
|
|
||||||
tp->tv_sec = time / SEC_TO_US;
|
|
||||||
tp->tv_usec = time % SEC_TO_US;
|
|
||||||
|
|
||||||
if (info) {
|
|
||||||
info->implementation = "mach_absolute_time()";
|
|
||||||
info->resolution = (double)timebase.numer / timebase.denom * 1e-9;
|
|
||||||
info->monotonic = 1;
|
|
||||||
info->adjustable = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
struct timespec ts;
|
|
||||||
#ifdef CLOCK_HIGHRES
|
|
||||||
const clockid_t clk_id = CLOCK_HIGHRES;
|
|
||||||
const char *implementation = "clock_gettime(CLOCK_HIGHRES)";
|
|
||||||
#else
|
|
||||||
const clockid_t clk_id = CLOCK_MONOTONIC;
|
|
||||||
const char *implementation = "clock_gettime(CLOCK_MONOTONIC)";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
assert(info == NULL || raise);
|
|
||||||
|
|
||||||
if (clock_gettime(clk_id, &ts) != 0) {
|
|
||||||
if (raise) {
|
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
tp->tv_sec = 0;
|
|
||||||
tp->tv_usec = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info) {
|
|
||||||
struct timespec res;
|
|
||||||
info->monotonic = 1;
|
|
||||||
info->implementation = implementation;
|
|
||||||
info->adjustable = 0;
|
|
||||||
if (clock_getres(clk_id, &res) != 0) {
|
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
|
|
||||||
}
|
|
||||||
tp->tv_sec = ts.tv_sec;
|
|
||||||
tp->tv_usec = ts.tv_nsec / US_TO_NS;
|
|
||||||
#endif
|
|
||||||
assert(0 <= tp->tv_usec && tp->tv_usec < SEC_TO_US);
|
|
||||||
#ifdef Py_DEBUG
|
|
||||||
/* monotonic clock cannot go backward */
|
|
||||||
assert(last.tv_usec == -1
|
|
||||||
|| tp->tv_sec > last.tv_sec
|
|
||||||
|| (tp->tv_sec == last.tv_sec && tp->tv_usec >= last.tv_usec));
|
|
||||||
last = *tp;
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
_PyTime_monotonic(_PyTime_timeval *tp)
|
|
||||||
{
|
|
||||||
if (pymonotonic(tp, NULL, 0) < 0) {
|
|
||||||
/* cannot happen, _PyTime_Init() checks that pymonotonic() works */
|
|
||||||
assert(0);
|
|
||||||
tp->tv_sec = 0;
|
|
||||||
tp->tv_usec = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
_PyTime_monotonic_info(_PyTime_timeval *tp, _Py_clock_info_t *info)
|
|
||||||
{
|
|
||||||
return pymonotonic(tp, info, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
error_time_t_overflow(void)
|
error_time_t_overflow(void)
|
||||||
{
|
{
|
||||||
|
@ -536,6 +414,12 @@ _PyTime_AsMilliseconds(_PyTime_t t, _PyTime_round_t round)
|
||||||
return _PyTime_Multiply(t, 1000, round);
|
return _PyTime_Multiply(t, 1000, round);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_PyTime_t
|
||||||
|
_PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round)
|
||||||
|
{
|
||||||
|
return _PyTime_Multiply(t, 1000 * 1000, round);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
|
_PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
|
||||||
{
|
{
|
||||||
|
@ -842,10 +726,6 @@ _PyTime_Init(void)
|
||||||
if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0)
|
if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* ensure that the operating system provides a monotonic clock */
|
|
||||||
if (_PyTime_monotonic_info(&tv, NULL) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* ensure that the operating system provides a monotonic clock */
|
/* ensure that the operating system provides a monotonic clock */
|
||||||
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0)
|
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
Loading…
Reference in New Issue