summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-03-28 02:52:05 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2015-03-28 02:52:05 (GMT)
commitf5faad2bf017db9e99845de29420476914f1ef1d (patch)
treebfe86d93267dd2c4cc49181a7289437e2ada4b64
parente245231fabb4c97521959e9ae1b45469bfad0d1e (diff)
downloadcpython-f5faad2bf017db9e99845de29420476914f1ef1d.zip
cpython-f5faad2bf017db9e99845de29420476914f1ef1d.tar.gz
cpython-f5faad2bf017db9e99845de29420476914f1ef1d.tar.bz2
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.
-rw-r--r--Include/pytime.h24
-rw-r--r--Modules/_threadmodule.c139
-rw-r--r--Python/pytime.c132
3 files changed, 76 insertions, 219 deletions
diff --git a/Include/pytime.h b/Include/pytime.h
index 3004289..3078d25 100644
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -74,24 +74,6 @@ PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
long *nsec,
_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 */
PyAPI_FUNC(void)
_PyTime_AddDouble(_PyTime_timeval *tv, double interval,
@@ -105,6 +87,8 @@ PyAPI_FUNC(int) _PyTime_Init(void);
#ifdef PY_INT64_T
typedef PY_INT64_T _PyTime_t;
+#define _PyTime_MIN PY_LLONG_MIN
+#define _PyTime_MAX PY_LLONG_MAX
#else
# error "_PyTime_t need signed 64-bit integer type"
#endif
@@ -125,6 +109,10 @@ PyAPI_FUNC(double) _PyTime_AsSecondsDouble(_PyTime_t t);
PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
_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
object. */
PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index 8f59e03..07b01f0 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -49,21 +49,18 @@ lock_dealloc(lockobject *self)
* timeout.
*/
static PyLockStatus
-acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
+acquire_timed(PyThread_type_lock lock, _PyTime_t timeout)
{
PyLockStatus r;
- _PyTime_timeval curtime;
- _PyTime_timeval endtime;
-
-
- if (microseconds > 0) {
- _PyTime_monotonic(&endtime);
- endtime.tv_sec += microseconds / (1000 * 1000);
- endtime.tv_usec += microseconds % (1000 * 1000);
- }
+ _PyTime_t endtime = 0;
+ _PyTime_t microseconds;
+ if (timeout > 0)
+ endtime = _PyTime_GetMonotonicClock() + timeout;
do {
+ microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_UP);
+
/* first a simple non-blocking try without releasing the GIL */
r = PyThread_acquire_lock_timed(lock, 0, 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
* signals, since those can take time. */
- if (microseconds > 0) {
- _PyTime_monotonic(&curtime);
- microseconds = ((endtime.tv_sec - curtime.tv_sec) * 1000000 +
- (endtime.tv_usec - curtime.tv_usec));
+ if (timeout > 0) {
+ timeout = endtime - _PyTime_GetMonotonicClock();
/* Check for negative values, since those mean block forever.
*/
- if (microseconds <= 0) {
+ if (timeout <= 0) {
r = PY_LOCK_FAILURE;
}
}
@@ -99,44 +94,60 @@ acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
return r;
}
-static PyObject *
-lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)
+static int
+lock_acquire_parse_args(PyObject *args, PyObject *kwds,
+ _PyTime_t *timeout)
{
char *kwlist[] = {"blocking", "timeout", NULL};
int blocking = 1;
- double timeout = -1;
- PY_TIMEOUT_T microseconds;
- PyLockStatus r;
+ PyObject *timeout_obj = NULL;
+ const _PyTime_t unset_timeout = _PyTime_FromNanoseconds(-1000000000);
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist,
- &blocking, &timeout))
- return NULL;
+ *timeout = unset_timeout ;
- if (!blocking && timeout != -1) {
- PyErr_SetString(PyExc_ValueError, "can't specify a timeout "
- "for a non-blocking call");
- return NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO:acquire", kwlist,
+ &blocking, &timeout_obj))
+ return -1;
+
+ 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) {
- PyErr_SetString(PyExc_ValueError, "timeout value must be "
- "strictly positive");
- return NULL;
+ if (*timeout < 0 && *timeout != unset_timeout) {
+ PyErr_SetString(PyExc_ValueError,
+ "timeout value must be positive");
+ return -1;
}
if (!blocking)
- microseconds = 0;
- else if (timeout == -1)
- microseconds = -1;
- else {
- timeout *= 1e6;
- if (timeout >= (double) PY_TIMEOUT_MAX) {
+ *timeout = 0;
+ else if (*timeout != unset_timeout) {
+ _PyTime_t microseconds;
+
+ microseconds = _PyTime_AsMicroseconds(*timeout, _PyTime_ROUND_UP);
+ if (microseconds >= PY_TIMEOUT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"timeout value is too large");
- return NULL;
+ return -1;
}
- microseconds = (PY_TIMEOUT_T) timeout;
}
+ return 0;
+}
+
+static PyObject *
+lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)
+{
+ _PyTime_t timeout;
+ PyLockStatus r;
- r = acquire_timed(self->lock_lock, microseconds);
+ if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
+ return NULL;
+
+ r = acquire_timed(self->lock_lock, timeout);
if (r == PY_LOCK_INTR) {
return NULL;
}
@@ -281,41 +292,13 @@ rlock_dealloc(rlockobject *self)
static PyObject *
rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
{
- char *kwlist[] = {"blocking", "timeout", NULL};
- int blocking = 1;
- double timeout = -1;
- PY_TIMEOUT_T microseconds;
+ _PyTime_t timeout;
long tid;
PyLockStatus r = PY_LOCK_ACQUIRED;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist,
- &blocking, &timeout))
+ if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
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();
if (self->rlock_count > 0 && tid == self->rlock_owner) {
unsigned long count = self->rlock_count + 1;
@@ -327,7 +310,7 @@ rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
self->rlock_count = count;
Py_RETURN_TRUE;
}
- r = acquire_timed(self->rlock_lock, microseconds);
+ r = acquire_timed(self->rlock_lock, timeout);
if (r == PY_LOCK_ACQUIRED) {
assert(self->rlock_count == 0);
self->rlock_owner = tid;
@@ -1362,7 +1345,9 @@ static struct PyModuleDef threadmodule = {
PyMODINIT_FUNC
PyInit__thread(void)
{
- PyObject *m, *d, *timeout_max;
+ PyObject *m, *d, *v;
+ double time_max;
+ double timeout_max;
/* Initialize types: */
if (PyType_Ready(&localdummytype) < 0)
@@ -1379,10 +1364,14 @@ PyInit__thread(void)
if (m == NULL)
return NULL;
- timeout_max = PyFloat_FromDouble(PY_TIMEOUT_MAX / 1000000);
- if (!timeout_max)
+ timeout_max = PY_TIMEOUT_MAX / 1000000;
+ time_max = floor(_PyTime_AsSecondsDouble(_PyTime_MAX));
+ timeout_max = Py_MIN(timeout_max, time_max);
+
+ v = PyFloat_FromDouble(timeout_max);
+ if (!v)
return NULL;
- if (PyModule_AddObject(m, "TIMEOUT_MAX", timeout_max) < 0)
+ if (PyModule_AddObject(m, "TIMEOUT_MAX", v) < 0)
return NULL;
/* Add a symbolic constant */
diff --git a/Python/pytime.c b/Python/pytime.c
index 9893116..aa64977 100644
--- a/Python/pytime.c
+++ b/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
error_time_t_overflow(void)
{
@@ -536,6 +414,12 @@ _PyTime_AsMilliseconds(_PyTime_t t, _PyTime_round_t 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
_PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
{
@@ -843,10 +727,6 @@ _PyTime_Init(void)
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 */
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0)
return -1;
return 0;