changeset 253:d06e57b182a9

Embraced clock_gettime and mach_absolute_time in _unix Also made _unix wall clock time reports more precise; this was necessitated by the main changes.
author Oleg Oshmyan <chortos@inbox.lv>
date Fri, 14 Mar 2014 15:25:06 +0000
parents 756dacca888a
children 393b4689ac2f
files upreckon/_unixmodule.cpp
diffstat 1 files changed, 72 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/upreckon/_unixmodule.cpp	Sun Jan 19 01:32:21 2014 +0000
+++ b/upreckon/_unixmodule.cpp	Fri Mar 14 15:25:06 2014 +0000
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 Chortos-2 <chortos@inbox.lv>
+// Copyright (c) 2011-2014 Chortos-2 <chortos@inbox.lv>
 
 #include <Python.h>
 #include <structmember.h>
@@ -13,6 +13,10 @@
 
 #include <limits.h>
 
+#ifdef __APPLE__
+#include <mach/mach_time.h>
+#endif
+
 #ifdef HAVE_SIGNAL_H
 #include <signal.h>
 #endif
@@ -36,6 +40,10 @@
 #include <termios.h>
 #endif
 
+#ifdef HAVE_CLOCK_GETTIME
+#include <time.h>
+#endif
+
 #if !(defined __cplusplus) && !(defined bool)
 #ifdef HAVE_C99_BOOL
 #define bool _Bool
@@ -74,12 +82,40 @@
 #ifndef Py_PYTIME_H
 typedef struct timeval _PyTime_timeval;
 #ifndef GETTIMEOFDAY_NO_TZ
-#define _PyTime_gettimeofday(tvp) gettimeofday((tvp), NULL)
+#define _PyTime_gettimeofday(tvp) gettimeofday(tvp, NULL)
 #else
-#define _PyTime_gettimeofday(tvp) gettimeofday((tvp))
+#define _PyTime_gettimeofday(tvp) gettimeofday(tvp)
 #endif
 #endif
 
+static inline void timerget(_PyTime_timeval *tvp)
+{
+#if defined HAVE_CLOCK_GETTIME && (defined CLOCK_MONOTONIC_RAW || defined CLOCK_HIGHRES || defined CLOCK_MONOTONIC)
+	struct timespec ts;
+#ifdef CLOCK_MONOTONIC_RAW
+	clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+#elif defined CLOCK_HIGHRES
+	clock_gettime(CLOCK_HIGHRES, &ts);
+#else
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+#endif
+	tvp->tv_sec = ts.tv_sec;
+	tvp->tv_usec = ts.tv_nsec / 1000;
+#elif defined __APPLE__
+	static mach_timebase_info_data_t base;
+	if (!base.numer)
+	{
+		mach_timebase_info(&base);
+		base.denom *= 1000;
+	}
+	uint64_t t = mach_absolute_time() * base.numer / base.denom;
+	tvp->tv_sec = t / 1000000;
+	tvp->tv_usec = t % 1000000;
+#else
+	_PyTime_gettimeofday(tvp);
+#endif
+}
+
 #if PY_MAJOR_VERSION >= 3
 #define PyInt_AsLong PyLong_AsLong
 #define PyInt_FromLong PyLong_FromLong
@@ -479,7 +515,7 @@
 		{
 #endif
 			spawn_errno = posix_spawnp(&pid, argv[0], NULL, NULL, argv, environ);
-			_PyTime_gettimeofday(&tvstart);
+			timerget(&tvstart);
 			
 			if (spawn_errno)
 			{
@@ -512,7 +548,7 @@
 			}
 			else
 			{
-				_PyTime_gettimeofday(&tvstart);
+				timerget(&tvstart);
 			}
 		}
 #endif
@@ -529,7 +565,7 @@
 		while (wait(&status) != pid);
 #endif
 		
-		_PyTime_gettimeofday(&tvend);
+		timerget(&tvend);
 #if defined HAVE_SYS_RESOURCE_H && !(defined HAVE_WAIT4 || defined HAVE_WAIT3)
 		getrusage(RUSAGE_CHILDREN, &rusage);
 #endif
@@ -673,32 +709,31 @@
 static two_chars is_int(...);
 #endif
 
-static inline bool timeval_to_attr(_PyTime_timeval *ptv, PyObject *obj, const char *attr)
+static inline PyObject *timeval_to_obj(const _PyTime_timeval *ptv)
 {
-	PyObject *value;
 #ifdef __cplusplus
 	// If tv_sec has an integral type and !tv_usec, try to create a Python int
 	if (sizeof is_int(ptv->tv_sec) == sizeof(char) && !ptv->tv_usec)
 	{
 		if (ptv->tv_sec <= LONG_MAX)
 		{
-			value = PyInt_FromLong(ptv->tv_sec);
+			return PyInt_FromLong(ptv->tv_sec);
 		}
 		// FIXME: signed/unsigned comparisons ruin everything
 #ifdef HAVE_LONG_LONG
 		else// if (ptv->tv_sec <= ULLONG_MAX)
 		{
-			value = PyLong_FromUnsignedLongLong(ptv->tv_sec);
+			return PyLong_FromUnsignedLongLong(ptv->tv_sec);
 		}
 #else
 //		else if (ptv->tv_sec <= ULONG_MAX)
 //		{
-//			value = PyLong_FromUnsignedLong(ptv->tv_sec);
+//			return PyLong_FromUnsignedLong(ptv->tv_sec);
 //		}
 //#endif
 		else
 		{
-			value = PyFloat_FromDouble(ptv->tv_sec);
+			return PyFloat_FromDouble(ptv->tv_sec);
 		}
 //
 #endif
@@ -708,8 +743,13 @@
 #endif
 	{
 		// TODO: use decimal.Decimal or fractions.Fraction
-		value = PyFloat_FromDouble(ptv->tv_sec + ptv->tv_usec * 0.000001);
+		return PyFloat_FromDouble(ptv->tv_sec + ptv->tv_usec * 0.000001);
 	}
+}
+
+static inline bool timeval_to_attr(const _PyTime_timeval *ptv, PyObject *obj, const char *attr)
+{
+	PyObject *value = timeval_to_obj(ptv);
 	if (value == NULL)
 	{
 		return false;
@@ -724,8 +764,6 @@
 
 /*
 TODO/FIXME:
-* Replace timeval->timespec and select->pselect if pselect is available
- (preferably only if pselect is not a wrapper around select).
 * File descriptors might be >= FD_SETSIZE?
 */
 static PyObject *_unix_call(PyObject *self, PyObject *args, PyObject *kwds)
@@ -739,7 +777,7 @@
 	size_t stats_read = 0;
 	fd_set readfds;
 	char c;
-	bool have_maxwalltime;
+	bool have_maxwalltime, wall_time_exceeded;
 	
 	if (kwds != NULL)
 	{
@@ -999,7 +1037,7 @@
 		
 		if (have_maxwalltime)
 		{
-			_PyTime_gettimeofday(&now);
+			timerget(&now);
 			if (timercmp(&time_end, &now, <))
 			{
 				timerclear(&timeout);
@@ -1016,6 +1054,7 @@
 				close(c2ppipe[0]);
 				TERM_TESTEE;
 				Py_BLOCK_THREADS
+				timeval_to_attr(&now, testcase, "time_stopped");
 				Py_DECREF(testcase);
 				PyErr_SetObject(WallTimeLimitExceeded, NULL);
 				return NULL;
@@ -1197,13 +1236,6 @@
 		return NULL;
 	}
 	
-	if (timerisset(&maxwalltime) && timercmp(&stats.walltime, &maxwalltime, >))
-	{
-		Py_DECREF(testcase);
-		PyErr_SetObject(WallTimeLimitExceeded, NULL);
-		return NULL;
-	}
-	
 	obj = PyInt_FromLong(0);
 	if (obj == NULL)
 	{
@@ -1217,8 +1249,9 @@
 	}
 	Py_DECREF(obj);
 	
+	wall_time_exceeded = timerisset(&maxwalltime) && timercmp(&stats.walltime, &maxwalltime, >);
 #if defined HAVE_SYS_RESOURCE_H || defined HAVE_WAIT4 || defined HAVE_WAIT3
-	if (timerisset(&maxcputime) || !timerisset(&maxwalltime))
+	if (!wall_time_exceeded && (timerisset(&maxcputime) || !timerisset(&maxwalltime)))
 	{
 		PyObject *cputls;
 		if (!timeval_to_attr(&stats.cputime, testcase, "time_stopped"))
@@ -1253,6 +1286,12 @@
 			Py_DECREF(testcase);
 			return NULL;
 		}
+		if (wall_time_exceeded)
+		{
+			Py_DECREF(testcase);
+			PyErr_SetObject(WallTimeLimitExceeded, NULL);
+			return NULL;
+		}
 	}
 	
 #if defined HAVE_SYS_RESOURCE_H || defined HAVE_WAIT4 || defined HAVE_WAIT3
@@ -1276,6 +1315,13 @@
 	Py_RETURN_NONE;
 }
 
+static PyObject *_unix_clock(PyObject *self)
+{
+	_PyTime_timeval tv;
+	timerget(&tv);
+	return timeval_to_obj(&tv);
+}
+
 static PyObject *_unix_pause(PyObject *self)
 {
 #ifdef HAVE_TERMIOS_H
@@ -1300,6 +1346,7 @@
 static PyMethodDef _unixMethods[] =
 {
 	{ "call", (PyCFunction) _unix_call, METH_VARARGS | METH_KEYWORDS, "Call a process." },
+	{ "clock", (PyCFunction) _unix_clock, METH_NOARGS, "Return the current time in seconds since an unspecified reference point." },
 	{ "pause", (PyCFunction) _unix_pause, METH_NOARGS, "Block until a key is pressed." },
 	{ NULL }
 };