Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cb29f01

Browse files
committed
Issue #22117: Add a new Python timestamp format _PyTime_t to pytime.h
In practice, _PyTime_t is a number of nanoseconds. Its C type is a 64-bit signed number. It's integer value is in the range [-2^63; 2^63-1]. In seconds, the range is around [-292 years; +292 years]. In term of Epoch timestamp (1970-01-01), it can store a date between 1677-09-21 and 2262-04-11. The API has a resolution of 1 nanosecond and use integer number. With a resolution on 1 nanosecond, 64-bit IEEE 754 floating point numbers loose precision after 194 days. It's not the case with this API. The drawback is overflow for values outside [-2^63; 2^63-1], but these values are unlikely for most Python modules, except of the datetime module. New functions: - _PyTime_GetMonotonicClock() - _PyTime_FromObject() - _PyTime_AsMilliseconds() - _PyTime_AsTimeval() This change uses these new functions in time.sleep() to avoid rounding issues. The new API will be extended step by step, and the old API will be removed step by step. Currently, some code is duplicated just to be able to move incrementally, instead of pushing a large change at once.
1 parent a766ddf commit cb29f01

3 files changed

Lines changed: 361 additions & 22 deletions

File tree

Include/pytime.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,40 @@ _PyTime_AddDouble(_PyTime_timeval *tv, double interval,
111111
Return 0 on success, raise an exception and return -1 on error. */
112112
PyAPI_FUNC(int) _PyTime_Init(void);
113113

114+
/****************** NEW _PyTime_t API **********************/
115+
116+
#ifdef PY_INT64_T
117+
typedef PY_INT64_T _PyTime_t;
118+
#else
119+
# error "_PyTime_t need signed 64-bit integer type"
120+
#endif
121+
122+
/* Convert a Python float or int to a timetamp.
123+
Raise an exception and return -1 on error, return 0 on success. */
124+
PyAPI_FUNC(int) _PyTime_FromObject(_PyTime_t *t,
125+
PyObject *obj,
126+
_PyTime_round_t round);
127+
128+
/* Convert timestamp to a number of milliseconds (10^-3 seconds). */
129+
PyAPI_FUNC(_PyTime_t)
130+
_PyTime_AsMilliseconds(_PyTime_t t,
131+
_PyTime_round_t round);
132+
133+
/* Convert a timestamp to a timeval structure. */
134+
PyAPI_FUNC(int) _PyTime_AsTimeval(_PyTime_t t,
135+
struct timeval *tv,
136+
_PyTime_round_t round);
137+
138+
/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
139+
The clock is not affected by system clock updates. The reference point of
140+
the returned value is undefined, so that only the difference between the
141+
results of consecutive calls is valid.
142+
143+
The function cannot fail. _PyTime_Init() ensures that a monotonic clock
144+
is available and works. */
145+
PyAPI_FUNC(_PyTime_t) _PyTime_GetMonotonicClock(void);
146+
147+
114148
#ifdef __cplusplus
115149
}
116150
#endif

Modules/timemodule.c

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
#endif /* !__WATCOMC__ || __QNX__ */
3232

3333
/* Forward declarations */
34-
static int floatsleep(double);
34+
static int pysleep(_PyTime_t);
3535
static PyObject* floattime(_Py_clock_info_t *info);
3636

3737
static PyObject *
@@ -218,17 +218,17 @@ Return the resolution (precision) of the specified clock clk_id.");
218218
#endif /* HAVE_CLOCK_GETTIME */
219219

220220
static PyObject *
221-
time_sleep(PyObject *self, PyObject *args)
221+
time_sleep(PyObject *self, PyObject *obj)
222222
{
223-
double secs;
224-
if (!PyArg_ParseTuple(args, "d:sleep", &secs))
223+
_PyTime_t secs;
224+
if (_PyTime_FromObject(&secs, obj, _PyTime_ROUND_UP))
225225
return NULL;
226226
if (secs < 0) {
227227
PyErr_SetString(PyExc_ValueError,
228228
"sleep length must be non-negative");
229229
return NULL;
230230
}
231-
if (floatsleep(secs) != 0)
231+
if (pysleep(secs) != 0)
232232
return NULL;
233233
Py_INCREF(Py_None);
234234
return Py_None;
@@ -1258,7 +1258,7 @@ static PyMethodDef time_methods[] = {
12581258
{"clock_settime", time_clock_settime, METH_VARARGS, clock_settime_doc},
12591259
{"clock_getres", time_clock_getres, METH_VARARGS, clock_getres_doc},
12601260
#endif
1261-
{"sleep", time_sleep, METH_VARARGS, sleep_doc},
1261+
{"sleep", time_sleep, METH_O, sleep_doc},
12621262
{"gmtime", time_gmtime, METH_VARARGS, gmtime_doc},
12631263
{"localtime", time_localtime, METH_VARARGS, localtime_doc},
12641264
{"asctime", time_asctime, METH_VARARGS, asctime_doc},
@@ -1379,34 +1379,30 @@ floattime(_Py_clock_info_t *info)
13791379
}
13801380

13811381

1382-
/* Implement floatsleep() for various platforms.
1382+
/* Implement pysleep() for various platforms.
13831383
When interrupted (or when another error occurs), return -1 and
13841384
set an exception; else return 0. */
13851385

13861386
static int
1387-
floatsleep(double secs)
1387+
pysleep(_PyTime_t secs)
13881388
{
1389-
_PyTime_timeval deadline, monotonic;
1389+
_PyTime_t deadline, monotonic;
13901390
#ifndef MS_WINDOWS
13911391
struct timeval timeout;
1392-
double frac;
13931392
int err = 0;
13941393
#else
1395-
double millisecs;
1394+
_PyTime_t millisecs;
13961395
unsigned long ul_millis;
13971396
DWORD rc;
13981397
HANDLE hInterruptEvent;
13991398
#endif
14001399

1401-
_PyTime_monotonic(&deadline);
1402-
_PyTime_AddDouble(&deadline, secs, _PyTime_ROUND_UP);
1400+
deadline = _PyTime_GetMonotonicClock() + secs;
14031401

14041402
do {
14051403
#ifndef MS_WINDOWS
1406-
frac = fmod(secs, 1.0);
1407-
secs = floor(secs);
1408-
timeout.tv_sec = (long)secs;
1409-
timeout.tv_usec = (long)(frac*1e6);
1404+
if (_PyTime_AsTimeval(secs, &timeout, _PyTime_ROUND_UP) < 0)
1405+
return -1;
14101406

14111407
Py_BEGIN_ALLOW_THREADS
14121408
err = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout);
@@ -1420,7 +1416,7 @@ floatsleep(double secs)
14201416
return -1;
14211417
}
14221418
#else
1423-
millisecs = secs * 1000.0;
1419+
millisecs = _PyTime_AsMilliseconds(secs, _PyTime_ROUND_UP);
14241420
if (millisecs > (double)ULONG_MAX) {
14251421
PyErr_SetString(PyExc_OverflowError,
14261422
"sleep length is too large");
@@ -1453,9 +1449,9 @@ floatsleep(double secs)
14531449
if (PyErr_CheckSignals())
14541450
return -1;
14551451

1456-
_PyTime_monotonic(&monotonic);
1457-
secs = _PyTime_INTERVAL(monotonic, deadline);
1458-
if (secs <= 0.0)
1452+
monotonic = _PyTime_GetMonotonicClock();
1453+
secs = deadline - monotonic;
1454+
if (secs <= 00)
14591455
break;
14601456
/* retry with the recomputed delay */
14611457
} while (1);

0 commit comments

Comments
 (0)