diff options
Diffstat (limited to 'Modules/timemodule.c')
-rw-r--r-- | Modules/timemodule.c | 955 |
1 files changed, 743 insertions, 212 deletions
diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 35162ff..161407d 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -1,13 +1,20 @@ /* Time module */ #include "Python.h" -#include "_time.h" #include <ctype.h> +#ifdef HAVE_SYS_TIMES_H +#include <sys/times.h> +#endif + #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> -#endif /* HAVE_SYS_TYPES_H */ +#endif + +#if defined(HAVE_SYS_RESOURCE_H) +#include <sys/resource.h> +#endif #ifdef QUICKWIN #include <io.h> @@ -21,19 +28,6 @@ #include <windows.h> #include "pythread.h" -/* helper to allow us to interrupt sleep() on Windows*/ -static HANDLE hInterruptEvent = NULL; -static BOOL WINAPI PyCtrlHandler(DWORD dwCtrlType) -{ - SetEvent(hInterruptEvent); - /* allow other default handlers to be called. - Default Python handler will setup the - KeyboardInterrupt exception. - */ - return FALSE; -} -static long main_thread; - #if defined(__BORLANDC__) /* These overrides not needed for Win32 */ #define timezone _timezone @@ -43,11 +37,6 @@ static long main_thread; #endif /* MS_WINDOWS */ #endif /* !__WATCOMC__ || __QNX__ */ -#if defined(MS_WINDOWS) && !defined(__BORLANDC__) -/* Win32 has better clock replacement; we have our own version below. */ -#undef HAVE_CLOCK -#endif /* MS_WINDOWS && !defined(__BORLANDC__) */ - #if defined(PYOS_OS2) #define INCL_DOS #define INCL_ERRORS @@ -58,23 +47,22 @@ static long main_thread; #include <sys/time.h> #endif +#if defined(__APPLE__) +#include <mach/mach_time.h> +#endif + /* Forward declarations */ static int floatsleep(double); -static double floattime(void); +static PyObject* floattime(_Py_clock_info_t *info); -/* For Y2K check */ -static PyObject *moddict = NULL; +#ifdef MS_WINDOWS +static OSVERSIONINFOEX winver; +#endif static PyObject * time_time(PyObject *self, PyObject *unused) { - double secs; - secs = floattime(); - if (secs == 0.0) { - PyErr_SetFromErrno(PyExc_IOError); - return NULL; - } - return PyFloat_FromDouble(secs); + return floattime(NULL); } PyDoc_STRVAR(time_doc, @@ -83,7 +71,7 @@ PyDoc_STRVAR(time_doc, Return the current time in seconds since the Epoch.\n\ Fractions of a second may be present if the system clock provides them."); -#ifdef HAVE_CLOCK +#if defined(HAVE_CLOCK) #ifndef CLOCKS_PER_SEC #ifdef CLK_TCK @@ -94,42 +82,82 @@ Fractions of a second may be present if the system clock provides them."); #endif static PyObject * -time_clock(PyObject *self, PyObject *unused) +floatclock(_Py_clock_info_t *info) { - return PyFloat_FromDouble(((double)clock()) / CLOCKS_PER_SEC); + clock_t value; + value = clock(); + if (value == (clock_t)-1) { + PyErr_SetString(PyExc_RuntimeError, + "the processor time used is not available " + "or its value cannot be represented"); + return NULL; + } + if (info) { + info->implementation = "clock()"; + info->resolution = 1.0 / (double)CLOCKS_PER_SEC; + info->monotonic = 1; + info->adjustable = 0; + } + return PyFloat_FromDouble((double)value / CLOCKS_PER_SEC); } #endif /* HAVE_CLOCK */ #if defined(MS_WINDOWS) && !defined(__BORLANDC__) -/* Due to Mark Hammond and Tim Peters */ -static PyObject * -time_clock(PyObject *self, PyObject *unused) +#define WIN32_PERF_COUNTER +/* Win32 has better clock replacement; we have our own version, due to Mark + Hammond and Tim Peters */ +static int +win_perf_counter(_Py_clock_info_t *info, PyObject **result) { - static LARGE_INTEGER ctrStart; - static double divisor = 0.0; + static LONGLONG cpu_frequency = 0; + static LONGLONG ctrStart; LARGE_INTEGER now; double diff; - if (divisor == 0.0) { + if (cpu_frequency == 0) { LARGE_INTEGER freq; - QueryPerformanceCounter(&ctrStart); + QueryPerformanceCounter(&now); + ctrStart = now.QuadPart; if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) { /* Unlikely to happen - this works on all intel machines at least! Revert to clock() */ - return PyFloat_FromDouble(((double)clock()) / - CLOCKS_PER_SEC); + *result = NULL; + return -1; } - divisor = (double)freq.QuadPart; + cpu_frequency = freq.QuadPart; } QueryPerformanceCounter(&now); - diff = (double)(now.QuadPart - ctrStart.QuadPart); - return PyFloat_FromDouble(diff / divisor); + diff = (double)(now.QuadPart - ctrStart); + if (info) { + info->implementation = "QueryPerformanceCounter()"; + info->resolution = 1.0 / (double)cpu_frequency; + info->monotonic = 1; + info->adjustable = 0; + } + *result = PyFloat_FromDouble(diff / (double)cpu_frequency); + return 0; } +#endif -#define HAVE_CLOCK /* So it gets included in the methods */ -#endif /* MS_WINDOWS && !defined(__BORLANDC__) */ +#if defined(WIN32_PERF_COUNTER) || defined(HAVE_CLOCK) +#define PYCLOCK +static PyObject* +pyclock(_Py_clock_info_t *info) +{ +#ifdef WIN32_PERF_COUNTER + PyObject *res; + if (win_perf_counter(info, &res) == 0) + return res; +#endif + return floatclock(info); +} + +static PyObject * +time_clock(PyObject *self, PyObject *unused) +{ + return pyclock(NULL); +} -#ifdef HAVE_CLOCK PyDoc_STRVAR(clock_doc, "clock() -> floating point number\n\ \n\ @@ -138,12 +166,93 @@ the first call to clock(). This has as much precision as the system\n\ records."); #endif +#ifdef HAVE_CLOCK_GETTIME +static PyObject * +time_clock_gettime(PyObject *self, PyObject *args) +{ + int ret; + int clk_id; + struct timespec tp; + + if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) + return NULL; + + ret = clock_gettime((clockid_t)clk_id, &tp); + if (ret != 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); +} + +PyDoc_STRVAR(clock_gettime_doc, +"clock_gettime(clk_id) -> floating point number\n\ +\n\ +Return the time of the specified clock clk_id."); + +static PyObject * +time_clock_settime(PyObject *self, PyObject *args) +{ + int clk_id; + PyObject *obj; + struct timespec tp; + int ret; + + if (!PyArg_ParseTuple(args, "iO:clock_settime", &clk_id, &obj)) + return NULL; + + if (_PyTime_ObjectToTimespec(obj, &tp.tv_sec, &tp.tv_nsec) == -1) + return NULL; + + ret = clock_settime((clockid_t)clk_id, &tp); + if (ret != 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(clock_settime_doc, +"clock_settime(clk_id, time)\n\ +\n\ +Set the time of the specified clock clk_id."); + +static PyObject * +time_clock_getres(PyObject *self, PyObject *args) +{ + int ret; + int clk_id; + struct timespec tp; + + if (!PyArg_ParseTuple(args, "i:clock_getres", &clk_id)) + return NULL; + + ret = clock_getres((clockid_t)clk_id, &tp); + if (ret != 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + + return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); +} + +PyDoc_STRVAR(clock_getres_doc, +"clock_getres(clk_id) -> floating point number\n\ +\n\ +Return the resolution (precision) of the specified clock clk_id."); +#endif /* HAVE_CLOCK_GETTIME */ + static PyObject * time_sleep(PyObject *self, PyObject *args) { double secs; if (!PyArg_ParseTuple(args, "d:sleep", &secs)) return NULL; + if (secs < 0) { + PyErr_SetString(PyExc_ValueError, + "sleep length must be non-negative"); + return NULL; + } if (floatsleep(secs) != 0) return NULL; Py_INCREF(Py_None); @@ -166,6 +275,10 @@ static PyStructSequence_Field struct_time_type_fields[] = { {"tm_wday", "day of week, range [0, 6], Monday is 0"}, {"tm_yday", "day of year, range [1, 366]"}, {"tm_isdst", "1 if summer time is in effect, 0 if not, and -1 if unknown"}, +#ifdef HAVE_STRUCT_TM_TM_ZONE + {"tm_zone", "abbreviation of timezone name"}, + {"tm_gmtoff", "offset from UTC in seconds"}, +#endif /* HAVE_STRUCT_TM_TM_ZONE */ {0} }; @@ -185,6 +298,7 @@ static PyStructSequence_Desc struct_time_type_desc = { static int initialized; static PyTypeObject StructTimeType; + static PyObject * tmtotuple(struct tm *p) { @@ -203,6 +317,11 @@ tmtotuple(struct tm *p) SET(6, (p->tm_wday + 6) % 7); /* Want Monday == 0 */ SET(7, p->tm_yday + 1); /* Want January, 1 == 1 */ SET(8, p->tm_isdst); +#ifdef HAVE_STRUCT_TM_TM_ZONE + PyStructSequence_SET_ITEM(v, 9, + PyUnicode_DecodeLocale(p->tm_zone, "surrogateescape")); + SET(10, p->tm_gmtoff); +#endif /* HAVE_STRUCT_TM_TM_ZONE */ #undef SET if (PyErr_Occurred()) { Py_XDECREF(v); @@ -212,55 +331,49 @@ tmtotuple(struct tm *p) return v; } -static PyObject * -time_convert(double when, struct tm * (*function)(const time_t *)) -{ - struct tm *p; - time_t whent = _PyTime_DoubleToTimet(when); - - if (whent == (time_t)-1 && PyErr_Occurred()) - return NULL; - errno = 0; - p = function(&whent); - if (p == NULL) { -#ifdef EINVAL - if (errno == 0) - errno = EINVAL; -#endif - return PyErr_SetFromErrno(PyExc_ValueError); - } - return tmtotuple(p); -} - /* Parse arg tuple that can contain an optional float-or-None value; format needs to be "|O:name". Returns non-zero on success (parallels PyArg_ParseTuple). */ static int -parse_time_double_args(PyObject *args, char *format, double *pwhen) +parse_time_t_args(PyObject *args, char *format, time_t *pwhen) { PyObject *ot = NULL; + time_t whent; if (!PyArg_ParseTuple(args, format, &ot)) return 0; - if (ot == NULL || ot == Py_None) - *pwhen = floattime(); + if (ot == NULL || ot == Py_None) { + whent = time(NULL); + } else { - double when = PyFloat_AsDouble(ot); - if (PyErr_Occurred()) + if (_PyTime_ObjectToTime_t(ot, &whent) == -1) return 0; - *pwhen = when; } + *pwhen = whent; return 1; } static PyObject * time_gmtime(PyObject *self, PyObject *args) { - double when; - if (!parse_time_double_args(args, "|O:gmtime", &when)) + time_t when; + struct tm buf, *local; + + if (!parse_time_t_args(args, "|O:gmtime", &when)) return NULL; - return time_convert(when, gmtime); + + errno = 0; + local = gmtime(&when); + if (local == NULL) { +#ifdef EINVAL + if (errno == 0) + errno = EINVAL; +#endif + return PyErr_SetFromErrno(PyExc_OSError); + } + buf = *local; + return tmtotuple(&buf); } PyDoc_STRVAR(gmtime_doc, @@ -268,15 +381,42 @@ PyDoc_STRVAR(gmtime_doc, tm_sec, tm_wday, tm_yday, tm_isdst)\n\ \n\ Convert seconds since the Epoch to a time tuple expressing UTC (a.k.a.\n\ -GMT). When 'seconds' is not passed in, convert the current time instead."); +GMT). When 'seconds' is not passed in, convert the current time instead.\n\ +\n\ +If the platform supports the tm_gmtoff and tm_zone, they are available as\n\ +attributes only."); + +static int +pylocaltime(time_t *timep, struct tm *result) +{ + struct tm *local; + + assert (timep != NULL); + local = localtime(timep); + if (local == NULL) { + /* unconvertible time */ +#ifdef EINVAL + if (errno == 0) + errno = EINVAL; +#endif + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + *result = *local; + return 0; +} static PyObject * time_localtime(PyObject *self, PyObject *args) { - double when; - if (!parse_time_double_args(args, "|O:localtime", &when)) + time_t when; + struct tm buf; + + if (!parse_time_t_args(args, "|O:localtime", &when)) + return NULL; + if (pylocaltime(&when, &buf) == -1) return NULL; - return time_convert(when, localtime); + return tmtotuple(&buf); } PyDoc_STRVAR(localtime_doc, @@ -307,53 +447,21 @@ gettmarg(PyObject *args, struct tm *p) &p->tm_hour, &p->tm_min, &p->tm_sec, &p->tm_wday, &p->tm_yday, &p->tm_isdst)) return 0; - - /* If year is specified with less than 4 digits, its interpretation - * depends on the accept2dyear value. - * - * If accept2dyear is true (default), a backward compatibility behavior is - * invoked as follows: - * - * - for 2-digit year, century is guessed according to POSIX rules for - * %y strptime format: 21st century for y < 69, 20th century - * otherwise. A deprecation warning is issued when century - * information is guessed in this way. - * - * - for 3-digit or negative year, a ValueError exception is raised. - * - * If accept2dyear is false (set by the program or as a result of a - * non-empty value assigned to PYTHONY2K environment variable) all year - * values are interpreted as given. - */ - if (y < 1000) { - PyObject *accept = PyDict_GetItemString(moddict, - "accept2dyear"); - if (accept != NULL) { - int acceptval = PyObject_IsTrue(accept); - if (acceptval == -1) - return 0; - if (acceptval) { - if (0 <= y && y < 69) - y += 2000; - else if (69 <= y && y < 100) - y += 1900; - else { - PyErr_SetString(PyExc_ValueError, - "year out of range"); - return 0; - } - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "Century info guessed for a 2-digit year.", 1) != 0) - return 0; - } - } - else - return 0; - } p->tm_year = y - 1900; p->tm_mon--; p->tm_wday = (p->tm_wday + 1) % 7; p->tm_yday--; +#ifdef HAVE_STRUCT_TM_TM_ZONE + if (Py_TYPE(args) == &StructTimeType) { + PyObject *item; + item = PyTuple_GET_ITEM(args, 9); + p->tm_zone = item == Py_None ? NULL : _PyUnicode_AsString(item); + item = PyTuple_GET_ITEM(args, 10); + p->tm_gmtoff = item == Py_None ? 0 : PyLong_AsLong(item); + if (PyErr_Occurred()) + return 0; + } +#endif /* HAVE_STRUCT_TM_TM_ZONE */ return 1; } @@ -470,7 +578,8 @@ time_strftime(PyObject *self, PyObject *args) if (tup == NULL) { time_t tt = time(NULL); - buf = *localtime(&tt); + if (pylocaltime(&tt, &buf) == -1) + return NULL; } else if (!gettmarg(tup, &buf) || !checktm(&buf)) return NULL; @@ -498,7 +607,7 @@ time_strftime(PyObject *self, PyObject *args) fmt = format; #else /* Convert the unicode string to an ascii one */ - format = PyUnicode_EncodeFSDefault(format_arg); + format = PyUnicode_EncodeLocale(format_arg, "surrogateescape"); if (format == NULL) return NULL; fmt = PyBytes_AS_STRING(format); @@ -549,7 +658,8 @@ time_strftime(PyObject *self, PyObject *args) #ifdef HAVE_WCSFTIME ret = PyUnicode_FromWideChar(outbuf, buflen); #else - ret = PyUnicode_DecodeFSDefaultAndSize(outbuf, buflen); + ret = PyUnicode_DecodeLocaleAndSize(outbuf, buflen, + "surrogateescape"); #endif PyMem_Free(outbuf); break; @@ -587,11 +697,12 @@ time_strptime(PyObject *self, PyObject *args) { PyObject *strptime_module = PyImport_ImportModuleNoBlock("_strptime"); PyObject *strptime_result; + _Py_IDENTIFIER(_strptime_time); if (!strptime_module) return NULL; - strptime_result = PyObject_CallMethod(strptime_module, - "_strptime_time", "O", args); + strptime_result = _PyObject_CallMethodId(strptime_module, + &PyId__strptime_time, "O", args); Py_DECREF(strptime_module); return strptime_result; } @@ -608,31 +719,20 @@ _asctime(struct tm *timeptr) { /* Inspired by Open Group reference implementation available at * http://pubs.opengroup.org/onlinepubs/009695399/functions/asctime.html */ - static char wday_name[7][3] = { + static char wday_name[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - static char mon_name[12][3] = { + static char mon_name[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - char buf[20]; /* 'Sun Sep 16 01:03:52\0' */ - int n; - - n = PyOS_snprintf(buf, sizeof(buf), "%.3s %.3s%3d %.2d:%.2d:%.2d", - wday_name[timeptr->tm_wday], - mon_name[timeptr->tm_mon], - timeptr->tm_mday, timeptr->tm_hour, - timeptr->tm_min, timeptr->tm_sec); - /* XXX: since the fields used by snprintf above are validated in checktm, - * the following condition should never trigger. We keep the check because - * historically fixed size buffer used in asctime was the source of - * crashes. */ - if (n + 1 != sizeof(buf)) { - PyErr_SetString(PyExc_ValueError, "unconvertible time"); - return NULL; - } - - return PyUnicode_FromFormat("%s %d", buf, 1900 + timeptr->tm_year); + return PyUnicode_FromFormat( + "%s %s%3d %.2d:%.2d:%.2d %d", + wday_name[timeptr->tm_wday], + mon_name[timeptr->tm_mon], + timeptr->tm_mday, timeptr->tm_hour, + timeptr->tm_min, timeptr->tm_sec, + 1900 + timeptr->tm_year); } static PyObject * @@ -645,7 +745,9 @@ time_asctime(PyObject *self, PyObject *args) return NULL; if (tup == NULL) { time_t tt = time(NULL); - buf = *localtime(&tt); + if (pylocaltime(&tt, &buf) == -1) + return NULL; + } else if (!gettmarg(tup, &buf) || !checktm(&buf)) return NULL; return _asctime(&buf); @@ -661,28 +763,13 @@ is used."); static PyObject * time_ctime(PyObject *self, PyObject *args) { - PyObject *ot = NULL; time_t tt; - struct tm *timeptr; - - if (!PyArg_UnpackTuple(args, "ctime", 0, 1, &ot)) + struct tm buf; + if (!parse_time_t_args(args, "|O:ctime", &tt)) return NULL; - if (ot == NULL || ot == Py_None) - tt = time(NULL); - else { - double dt = PyFloat_AsDouble(ot); - if (PyErr_Occurred()) - return NULL; - tt = _PyTime_DoubleToTimet(dt); - if (tt == (time_t)-1 && PyErr_Occurred()) - return NULL; - } - timeptr = localtime(&tt); - if (timeptr == NULL) { - PyErr_SetString(PyExc_ValueError, "unconvertible time"); + if (pylocaltime(&tt, &buf) == -1) return NULL; - } - return _asctime(timeptr); + return _asctime(&buf); } PyDoc_STRVAR(ctime_doc, @@ -715,7 +802,10 @@ time_mktime(PyObject *self, PyObject *tup) PyDoc_STRVAR(mktime_doc, "mktime(tuple) -> floating point number\n\ \n\ -Convert a time tuple in local time to seconds since the Epoch."); +Convert a time tuple in local time to seconds since the Epoch.\n\ +Note that mktime(gmtime(0)) will not generally return zero for most\n\ +time zones; instead the returned value will either be equal to that\n\ +of the timezone or altzone attributes on the time module."); #endif /* HAVE_MKTIME */ #ifdef HAVE_WORKING_TZSET @@ -755,6 +845,402 @@ the local timezone used by methods such as localtime, but this behaviour\n\ should not be relied on."); #endif /* HAVE_WORKING_TZSET */ +#if defined(MS_WINDOWS) || defined(__APPLE__) \ + || (defined(HAVE_CLOCK_GETTIME) \ + && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC))) +#define PYMONOTONIC +#endif + +#ifdef PYMONOTONIC +static PyObject* +pymonotonic(_Py_clock_info_t *info) +{ +#if defined(MS_WINDOWS) + static ULONGLONG (*GetTickCount64) (void) = NULL; + static ULONGLONG (CALLBACK *Py_GetTickCount64)(void); + static int has_getickcount64 = -1; + double result; + + if (has_getickcount64 == -1) { + /* GetTickCount64() was added to Windows Vista */ + if (winver.dwMajorVersion >= 6) { + HINSTANCE hKernel32; + hKernel32 = GetModuleHandleW(L"KERNEL32"); + *(FARPROC*)&Py_GetTickCount64 = GetProcAddress(hKernel32, + "GetTickCount64"); + has_getickcount64 = (Py_GetTickCount64 != NULL); + } + else + has_getickcount64 = 0; + } + + if (has_getickcount64) { + ULONGLONG ticks; + ticks = Py_GetTickCount64(); + result = (double)ticks * 1e-3; + } + else { + static DWORD last_ticks = 0; + static DWORD n_overflow = 0; + DWORD ticks; + + ticks = GetTickCount(); + if (ticks < last_ticks) + n_overflow++; + last_ticks = ticks; + + result = ldexp(n_overflow, 32); + result += ticks; + result *= 1e-3; + } + + if (info) { + DWORD timeAdjustment, timeIncrement; + BOOL isTimeAdjustmentDisabled, ok; + if (has_getickcount64) + info->implementation = "GetTickCount64()"; + else + info->implementation = "GetTickCount()"; + info->monotonic = 1; + ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, + &isTimeAdjustmentDisabled); + if (!ok) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + info->resolution = timeIncrement * 1e-7; + info->adjustable = 0; + } + return PyFloat_FromDouble(result); + +#elif defined(__APPLE__) + static mach_timebase_info_data_t timebase; + uint64_t time; + double secs; + + 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(); + secs = (double)time * timebase.numer / timebase.denom * 1e-9; + if (info) { + info->implementation = "mach_absolute_time()"; + info->resolution = (double)timebase.numer / timebase.denom * 1e-9; + info->monotonic = 1; + info->adjustable = 0; + } + return PyFloat_FromDouble(secs); + +#elif defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC)) + struct timespec tp; +#ifdef CLOCK_HIGHRES + const clockid_t clk_id = CLOCK_HIGHRES; + const char *function = "clock_gettime(CLOCK_HIGHRES)"; +#else + const clockid_t clk_id = CLOCK_MONOTONIC; + const char *function = "clock_gettime(CLOCK_MONOTONIC)"; +#endif + + if (clock_gettime(clk_id, &tp) != 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + if (info) { + struct timespec res; + info->monotonic = 1; + info->implementation = function; + info->adjustable = 0; + if (clock_getres(clk_id, &res) == 0) + info->resolution = res.tv_sec + res.tv_nsec * 1e-9; + else + info->resolution = 1e-9; + } + return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); +#endif +} + +static PyObject * +time_monotonic(PyObject *self, PyObject *unused) +{ + return pymonotonic(NULL); +} + +PyDoc_STRVAR(monotonic_doc, +"monotonic() -> float\n\ +\n\ +Monotonic clock, cannot go backward."); +#endif /* PYMONOTONIC */ + +static PyObject* +perf_counter(_Py_clock_info_t *info) +{ +#if defined(WIN32_PERF_COUNTER) || defined(PYMONOTONIC) + PyObject *res; +#endif +#if defined(WIN32_PERF_COUNTER) + static int use_perf_counter = 1; +#endif +#ifdef PYMONOTONIC + static int use_monotonic = 1; +#endif + +#ifdef WIN32_PERF_COUNTER + if (use_perf_counter) { + if (win_perf_counter(info, &res) == 0) + return res; + use_perf_counter = 0; + } +#endif + +#ifdef PYMONOTONIC + if (use_monotonic) { + res = pymonotonic(info); + if (res != NULL) + return res; + use_monotonic = 0; + PyErr_Clear(); + } +#endif + + return floattime(info); +} + +static PyObject * +time_perf_counter(PyObject *self, PyObject *unused) +{ + return perf_counter(NULL); +} + +PyDoc_STRVAR(perf_counter_doc, +"perf_counter() -> float\n\ +\n\ +Performance counter for benchmarking."); + +static PyObject* +py_process_time(_Py_clock_info_t *info) +{ +#if defined(MS_WINDOWS) + HANDLE process; + FILETIME creation_time, exit_time, kernel_time, user_time; + ULARGE_INTEGER large; + double total; + BOOL ok; + + process = GetCurrentProcess(); + ok = GetProcessTimes(process, &creation_time, &exit_time, &kernel_time, &user_time); + if (!ok) + return PyErr_SetFromWindowsErr(0); + + large.u.LowPart = kernel_time.dwLowDateTime; + large.u.HighPart = kernel_time.dwHighDateTime; + total = (double)large.QuadPart; + large.u.LowPart = user_time.dwLowDateTime; + large.u.HighPart = user_time.dwHighDateTime; + total += (double)large.QuadPart; + if (info) { + info->implementation = "GetProcessTimes()"; + info->resolution = 1e-7; + info->monotonic = 1; + info->adjustable = 0; + } + return PyFloat_FromDouble(total * 1e-7); +#else + +#if defined(HAVE_SYS_RESOURCE_H) + struct rusage ru; +#endif +#ifdef HAVE_TIMES + struct tms t; + static long ticks_per_second = -1; +#endif + +#if defined(HAVE_CLOCK_GETTIME) \ + && (defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_PROF)) + struct timespec tp; +#ifdef CLOCK_PROF + const clockid_t clk_id = CLOCK_PROF; + const char *function = "clock_gettime(CLOCK_PROF)"; +#else + const clockid_t clk_id = CLOCK_PROCESS_CPUTIME_ID; + const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)"; +#endif + + if (clock_gettime(clk_id, &tp) == 0) { + if (info) { + struct timespec res; + info->implementation = function; + info->monotonic = 1; + info->adjustable = 0; + if (clock_getres(clk_id, &res) == 0) + info->resolution = res.tv_sec + res.tv_nsec * 1e-9; + else + info->resolution = 1e-9; + } + return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); + } +#endif + +#if defined(HAVE_SYS_RESOURCE_H) + if (getrusage(RUSAGE_SELF, &ru) == 0) { + double total; + total = ru.ru_utime.tv_sec + ru.ru_utime.tv_usec * 1e-6; + total += ru.ru_stime.tv_sec + ru.ru_stime.tv_usec * 1e-6; + if (info) { + info->implementation = "getrusage(RUSAGE_SELF)"; + info->monotonic = 1; + info->adjustable = 0; + info->resolution = 1e-6; + } + return PyFloat_FromDouble(total); + } +#endif + +#ifdef HAVE_TIMES + if (times(&t) != (clock_t)-1) { + double total; + + if (ticks_per_second == -1) { +#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK) + ticks_per_second = sysconf(_SC_CLK_TCK); + if (ticks_per_second < 1) + ticks_per_second = -1; +#elif defined(HZ) + ticks_per_second = HZ; +#else + ticks_per_second = 60; /* magic fallback value; may be bogus */ +#endif + } + + if (ticks_per_second != -1) { + total = (double)t.tms_utime / ticks_per_second; + total += (double)t.tms_stime / ticks_per_second; + if (info) { + info->implementation = "times()"; + info->monotonic = 1; + info->adjustable = 0; + info->resolution = 1.0 / ticks_per_second; + } + return PyFloat_FromDouble(total); + } + } +#endif + + return floatclock(info); +#endif +} + +static PyObject * +time_process_time(PyObject *self, PyObject *unused) +{ + return py_process_time(NULL); +} + +PyDoc_STRVAR(process_time_doc, +"process_time() -> float\n\ +\n\ +Process time for profiling: sum of the kernel and user-space CPU time."); + + +static PyObject * +time_get_clock_info(PyObject *self, PyObject *args) +{ + char *name; + _Py_clock_info_t info; + PyObject *obj = NULL, *dict, *ns; + + if (!PyArg_ParseTuple(args, "s:get_clock_info", &name)) + return NULL; + +#ifdef Py_DEBUG + info.implementation = NULL; + info.monotonic = -1; + info.adjustable = -1; + info.resolution = -1.0; +#else + info.implementation = ""; + info.monotonic = 0; + info.adjustable = 0; + info.resolution = 1.0; +#endif + + if (strcmp(name, "time") == 0) + obj = floattime(&info); +#ifdef PYCLOCK + else if (strcmp(name, "clock") == 0) + obj = pyclock(&info); +#endif +#ifdef PYMONOTONIC + else if (strcmp(name, "monotonic") == 0) + obj = pymonotonic(&info); +#endif + else if (strcmp(name, "perf_counter") == 0) + obj = perf_counter(&info); + else if (strcmp(name, "process_time") == 0) + obj = py_process_time(&info); + else { + PyErr_SetString(PyExc_ValueError, "unknown clock"); + return NULL; + } + if (obj == NULL) + return NULL; + Py_DECREF(obj); + + dict = PyDict_New(); + if (dict == NULL) + return NULL; + + assert(info.implementation != NULL); + obj = PyUnicode_FromString(info.implementation); + if (obj == NULL) + goto error; + if (PyDict_SetItemString(dict, "implementation", obj) == -1) + goto error; + Py_CLEAR(obj); + + assert(info.monotonic != -1); + obj = PyBool_FromLong(info.monotonic); + if (obj == NULL) + goto error; + if (PyDict_SetItemString(dict, "monotonic", obj) == -1) + goto error; + Py_CLEAR(obj); + + assert(info.adjustable != -1); + obj = PyBool_FromLong(info.adjustable); + if (obj == NULL) + goto error; + if (PyDict_SetItemString(dict, "adjustable", obj) == -1) + goto error; + Py_CLEAR(obj); + + assert(info.resolution > 0.0); + assert(info.resolution <= 1.0); + obj = PyFloat_FromDouble(info.resolution); + if (obj == NULL) + goto error; + if (PyDict_SetItemString(dict, "resolution", obj) == -1) + goto error; + Py_CLEAR(obj); + + ns = _PyNamespace_New(dict); + Py_DECREF(dict); + return ns; + +error: + Py_DECREF(dict); + Py_XDECREF(obj); + return NULL; +} + +PyDoc_STRVAR(get_clock_info_doc, +"get_clock_info(name: str) -> dict\n\ +\n\ +Get information of the specified clock."); + static void PyInit_timezone(PyObject *m) { /* This code moved from PyInit_time wholesale to allow calling it from @@ -791,8 +1277,8 @@ PyInit_timezone(PyObject *m) { #endif /* PYOS_OS2 */ #endif PyModule_AddIntConstant(m, "daylight", daylight); - otz0 = PyUnicode_DecodeFSDefaultAndSize(tzname[0], strlen(tzname[0])); - otz1 = PyUnicode_DecodeFSDefaultAndSize(tzname[1], strlen(tzname[1])); + otz0 = PyUnicode_DecodeLocale(tzname[0], "surrogateescape"); + otz1 = PyUnicode_DecodeLocale(tzname[1], "surrogateescape"); PyModule_AddObject(m, "tzname", Py_BuildValue("(NN)", otz0, otz1)); #else /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/ #ifdef HAVE_STRUCT_TM_TM_ZONE @@ -843,14 +1329,38 @@ PyInit_timezone(PyObject *m) { Py_BuildValue("(zz)", _tzname[0], _tzname[1])); #endif /* __CYGWIN__ */ #endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/ + +#if defined(HAVE_CLOCK_GETTIME) + PyModule_AddIntMacro(m, CLOCK_REALTIME); +#ifdef CLOCK_MONOTONIC + PyModule_AddIntMacro(m, CLOCK_MONOTONIC); +#endif +#ifdef CLOCK_MONOTONIC_RAW + PyModule_AddIntMacro(m, CLOCK_MONOTONIC_RAW); +#endif +#ifdef CLOCK_HIGHRES + PyModule_AddIntMacro(m, CLOCK_HIGHRES); +#endif +#ifdef CLOCK_PROCESS_CPUTIME_ID + PyModule_AddIntMacro(m, CLOCK_PROCESS_CPUTIME_ID); +#endif +#ifdef CLOCK_THREAD_CPUTIME_ID + PyModule_AddIntMacro(m, CLOCK_THREAD_CPUTIME_ID); +#endif +#endif /* HAVE_CLOCK_GETTIME */ } static PyMethodDef time_methods[] = { {"time", time_time, METH_NOARGS, time_doc}, -#ifdef HAVE_CLOCK +#ifdef PYCLOCK {"clock", time_clock, METH_NOARGS, clock_doc}, #endif +#ifdef HAVE_CLOCK_GETTIME + {"clock_gettime", time_clock_gettime, METH_VARARGS, clock_gettime_doc}, + {"clock_settime", time_clock_settime, METH_VARARGS, clock_settime_doc}, + {"clock_getres", time_clock_getres, METH_VARARGS, clock_getres_doc}, +#endif {"sleep", time_sleep, METH_VARARGS, sleep_doc}, {"gmtime", time_gmtime, METH_VARARGS, gmtime_doc}, {"localtime", time_localtime, METH_VARARGS, localtime_doc}, @@ -866,6 +1376,12 @@ static PyMethodDef time_methods[] = { #ifdef HAVE_WORKING_TZSET {"tzset", time_tzset, METH_NOARGS, tzset_doc}, #endif +#ifdef PYMONOTONIC + {"monotonic", time_monotonic, METH_NOARGS, monotonic_doc}, +#endif + {"process_time", time_process_time, METH_NOARGS, process_time_doc}, + {"perf_counter", time_perf_counter, METH_NOARGS, perf_counter_doc}, + {"get_clock_info", time_get_clock_info, METH_VARARGS, get_clock_info_doc}, {NULL, NULL} /* sentinel */ }; @@ -881,7 +1397,7 @@ The actual value can be retrieved by calling gmtime(0).\n\ \n\ The other representation is a tuple of 9 integers giving local time.\n\ The tuple items are:\n\ - year (four digits, e.g. 1998)\n\ + year (including century, e.g. 1998)\n\ month (1-12)\n\ day (1-31)\n\ hours (0-23)\n\ @@ -933,51 +1449,65 @@ PyMODINIT_FUNC PyInit_time(void) { PyObject *m; - char *p; m = PyModule_Create(&timemodule); if (m == NULL) return NULL; - /* Accept 2-digit dates unless PYTHONY2K is set and non-empty */ - p = Py_GETENV("PYTHONY2K"); - PyModule_AddIntConstant(m, "accept2dyear", (long) (!p || !*p)); - /* If an embedded interpreter is shutdown and reinitialized the old - moddict was not decrefed on shutdown and the next import of this - module leads to a leak. Conditionally decref here to prevent that. - */ - Py_XDECREF(moddict); - /* Squirrel away the module's dictionary for the y2k check */ - moddict = PyModule_GetDict(m); - Py_INCREF(moddict); - /* Set, or reset, module variables like time.timezone */ PyInit_timezone(m); -#ifdef MS_WINDOWS - /* Helper to allow interrupts for Windows. - If Ctrl+C event delivered while not sleeping - it will be ignored. - */ - main_thread = PyThread_get_thread_ident(); - hInterruptEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - SetConsoleCtrlHandler( PyCtrlHandler, TRUE); -#endif /* MS_WINDOWS */ if (!initialized) { PyStructSequence_InitType(&StructTimeType, &struct_time_type_desc); + +#ifdef MS_WINDOWS + winver.dwOSVersionInfoSize = sizeof(winver); + if (!GetVersionEx((OSVERSIONINFO*)&winver)) { + Py_DECREF(m); + PyErr_SetFromWindowsErr(0); + return NULL; + } +#endif } Py_INCREF(&StructTimeType); +#ifdef HAVE_STRUCT_TM_TM_ZONE + PyModule_AddIntConstant(m, "_STRUCT_TM_ITEMS", 11); +#else + PyModule_AddIntConstant(m, "_STRUCT_TM_ITEMS", 9); +#endif PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType); initialized = 1; return m; } -static double -floattime(void) +static PyObject* +floattime(_Py_clock_info_t *info) { _PyTime_timeval t; - _PyTime_gettimeofday(&t); - return (double)t.tv_sec + t.tv_usec*0.000001; +#ifdef HAVE_CLOCK_GETTIME + struct timespec tp; + int ret; + + /* _PyTime_gettimeofday() does not use clock_gettime() + because it would require to link Python to the rt (real-time) + library, at least on Linux */ + ret = clock_gettime(CLOCK_REALTIME, &tp); + if (ret == 0) { + if (info) { + struct timespec res; + info->implementation = "clock_gettime(CLOCK_REALTIME)"; + info->monotonic = 0; + info->adjustable = 1; + if (clock_getres(CLOCK_REALTIME, &res) == 0) + info->resolution = res.tv_sec + res.tv_nsec * 1e-9; + else + info->resolution = 1e-9; + } + return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); + } +#endif + _PyTime_gettimeofday_info(&t, info); + return PyFloat_FromDouble((double)t.tv_sec + t.tv_usec * 1e-6); } @@ -992,23 +1522,28 @@ floatsleep(double secs) #if defined(HAVE_SELECT) && !defined(__EMX__) struct timeval t; double frac; + int err; + frac = fmod(secs, 1.0); secs = floor(secs); t.tv_sec = (long)secs; t.tv_usec = (long)(frac*1000000.0); Py_BEGIN_ALLOW_THREADS - if (select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t) != 0) { + err = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t); + Py_END_ALLOW_THREADS + if (err != 0) { #ifdef EINTR - if (errno != EINTR) { -#else - if (1) { + if (errno == EINTR) { + if (PyErr_CheckSignals()) + return -1; + } + else #endif - Py_BLOCK_THREADS + { PyErr_SetFromErrno(PyExc_IOError); return -1; } } - Py_END_ALLOW_THREADS #elif defined(__WATCOMC__) && !defined(__QNX__) /* XXX Can't interrupt this sleep */ Py_BEGIN_ALLOW_THREADS @@ -1029,18 +1564,14 @@ floatsleep(double secs) * by Guido, only the main thread can be interrupted. */ ul_millis = (unsigned long)millisecs; - if (ul_millis == 0 || - main_thread != PyThread_get_thread_ident()) + if (ul_millis == 0 || !_PyOS_IsMainThread()) Sleep(ul_millis); else { DWORD rc; + HANDLE hInterruptEvent = _PyOS_SigintEvent(); ResetEvent(hInterruptEvent); rc = WaitForSingleObject(hInterruptEvent, ul_millis); if (rc == WAIT_OBJECT_0) { - /* Yield to make sure real Python signal - * handler called. - */ - Sleep(1); Py_BLOCK_THREADS errno = EINTR; PyErr_SetFromErrno(PyExc_IOError); |