comparison upreckon/_unixmodule.cpp @ 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 65b5c9390010
children 393b4689ac2f
comparison
equal deleted inserted replaced
252:756dacca888a 253:d06e57b182a9
1 // Copyright (c) 2011 Chortos-2 <chortos@inbox.lv> 1 // Copyright (c) 2011-2014 Chortos-2 <chortos@inbox.lv>
2 2
3 #include <Python.h> 3 #include <Python.h>
4 #include <structmember.h> 4 #include <structmember.h>
5 5
6 #ifdef HAVE_SYS_TYPES_H 6 #ifdef HAVE_SYS_TYPES_H
10 #ifdef HAVE_FCNTL_H 10 #ifdef HAVE_FCNTL_H
11 #include <fcntl.h> 11 #include <fcntl.h>
12 #endif 12 #endif
13 13
14 #include <limits.h> 14 #include <limits.h>
15
16 #ifdef __APPLE__
17 #include <mach/mach_time.h>
18 #endif
15 19
16 #ifdef HAVE_SIGNAL_H 20 #ifdef HAVE_SIGNAL_H
17 #include <signal.h> 21 #include <signal.h>
18 #endif 22 #endif
19 23
32 #include <sys/wait.h> 36 #include <sys/wait.h>
33 #endif 37 #endif
34 38
35 #ifdef HAVE_TERMIOS_H 39 #ifdef HAVE_TERMIOS_H
36 #include <termios.h> 40 #include <termios.h>
41 #endif
42
43 #ifdef HAVE_CLOCK_GETTIME
44 #include <time.h>
37 #endif 45 #endif
38 46
39 #if !(defined __cplusplus) && !(defined bool) 47 #if !(defined __cplusplus) && !(defined bool)
40 #ifdef HAVE_C99_BOOL 48 #ifdef HAVE_C99_BOOL
41 #define bool _Bool 49 #define bool _Bool
72 #endif 80 #endif
73 81
74 #ifndef Py_PYTIME_H 82 #ifndef Py_PYTIME_H
75 typedef struct timeval _PyTime_timeval; 83 typedef struct timeval _PyTime_timeval;
76 #ifndef GETTIMEOFDAY_NO_TZ 84 #ifndef GETTIMEOFDAY_NO_TZ
77 #define _PyTime_gettimeofday(tvp) gettimeofday((tvp), NULL) 85 #define _PyTime_gettimeofday(tvp) gettimeofday(tvp, NULL)
78 #else 86 #else
79 #define _PyTime_gettimeofday(tvp) gettimeofday((tvp)) 87 #define _PyTime_gettimeofday(tvp) gettimeofday(tvp)
80 #endif 88 #endif
81 #endif 89 #endif
90
91 static inline void timerget(_PyTime_timeval *tvp)
92 {
93 #if defined HAVE_CLOCK_GETTIME && (defined CLOCK_MONOTONIC_RAW || defined CLOCK_HIGHRES || defined CLOCK_MONOTONIC)
94 struct timespec ts;
95 #ifdef CLOCK_MONOTONIC_RAW
96 clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
97 #elif defined CLOCK_HIGHRES
98 clock_gettime(CLOCK_HIGHRES, &ts);
99 #else
100 clock_gettime(CLOCK_MONOTONIC, &ts);
101 #endif
102 tvp->tv_sec = ts.tv_sec;
103 tvp->tv_usec = ts.tv_nsec / 1000;
104 #elif defined __APPLE__
105 static mach_timebase_info_data_t base;
106 if (!base.numer)
107 {
108 mach_timebase_info(&base);
109 base.denom *= 1000;
110 }
111 uint64_t t = mach_absolute_time() * base.numer / base.denom;
112 tvp->tv_sec = t / 1000000;
113 tvp->tv_usec = t % 1000000;
114 #else
115 _PyTime_gettimeofday(tvp);
116 #endif
117 }
82 118
83 #if PY_MAJOR_VERSION >= 3 119 #if PY_MAJOR_VERSION >= 3
84 #define PyInt_AsLong PyLong_AsLong 120 #define PyInt_AsLong PyLong_AsLong
85 #define PyInt_FromLong PyLong_FromLong 121 #define PyInt_FromLong PyLong_FromLong
86 #define PyNumber_Int PyNumber_Long 122 #define PyNumber_Int PyNumber_Long
477 #ifdef __APPLE__ 513 #ifdef __APPLE__
478 if (posix_spawnp != NULL) 514 if (posix_spawnp != NULL)
479 { 515 {
480 #endif 516 #endif
481 spawn_errno = posix_spawnp(&pid, argv[0], NULL, NULL, argv, environ); 517 spawn_errno = posix_spawnp(&pid, argv[0], NULL, NULL, argv, environ);
482 _PyTime_gettimeofday(&tvstart); 518 timerget(&tvstart);
483 519
484 if (spawn_errno) 520 if (spawn_errno)
485 { 521 {
486 TESTEE_REPORT_STATUS(TESTEE_SPAWN_FAILED); 522 TESTEE_REPORT_STATUS(TESTEE_SPAWN_FAILED);
487 write(c2ppipe[1], &spawn_errno, sizeof spawn_errno); 523 write(c2ppipe[1], &spawn_errno, sizeof spawn_errno);
510 write(c2ppipe[1], &spawn_errno, sizeof spawn_errno); 546 write(c2ppipe[1], &spawn_errno, sizeof spawn_errno);
511 _exit(127); 547 _exit(127);
512 } 548 }
513 else 549 else
514 { 550 {
515 _PyTime_gettimeofday(&tvstart); 551 timerget(&tvstart);
516 } 552 }
517 } 553 }
518 #endif 554 #endif
519 TESTEE_REPORT_STATUS(TESTEE_SPAWNED); 555 TESTEE_REPORT_STATUS(TESTEE_SPAWNED);
520 write(c2ppipe[1], &tvstart, sizeof tvstart); 556 write(c2ppipe[1], &tvstart, sizeof tvstart);
527 while (waitpid(pid, &status, 0) != pid); 563 while (waitpid(pid, &status, 0) != pid);
528 #else 564 #else
529 while (wait(&status) != pid); 565 while (wait(&status) != pid);
530 #endif 566 #endif
531 567
532 _PyTime_gettimeofday(&tvend); 568 timerget(&tvend);
533 #if defined HAVE_SYS_RESOURCE_H && !(defined HAVE_WAIT4 || defined HAVE_WAIT3) 569 #if defined HAVE_SYS_RESOURCE_H && !(defined HAVE_WAIT4 || defined HAVE_WAIT3)
534 getrusage(RUSAGE_CHILDREN, &rusage); 570 getrusage(RUSAGE_CHILDREN, &rusage);
535 #endif 571 #endif
536 572
537 stats = zero_stats; 573 stats = zero_stats;
671 static char is_int(unsigned long long); 707 static char is_int(unsigned long long);
672 #endif 708 #endif
673 static two_chars is_int(...); 709 static two_chars is_int(...);
674 #endif 710 #endif
675 711
676 static inline bool timeval_to_attr(_PyTime_timeval *ptv, PyObject *obj, const char *attr) 712 static inline PyObject *timeval_to_obj(const _PyTime_timeval *ptv)
677 { 713 {
678 PyObject *value;
679 #ifdef __cplusplus 714 #ifdef __cplusplus
680 // If tv_sec has an integral type and !tv_usec, try to create a Python int 715 // If tv_sec has an integral type and !tv_usec, try to create a Python int
681 if (sizeof is_int(ptv->tv_sec) == sizeof(char) && !ptv->tv_usec) 716 if (sizeof is_int(ptv->tv_sec) == sizeof(char) && !ptv->tv_usec)
682 { 717 {
683 if (ptv->tv_sec <= LONG_MAX) 718 if (ptv->tv_sec <= LONG_MAX)
684 { 719 {
685 value = PyInt_FromLong(ptv->tv_sec); 720 return PyInt_FromLong(ptv->tv_sec);
686 } 721 }
687 // FIXME: signed/unsigned comparisons ruin everything 722 // FIXME: signed/unsigned comparisons ruin everything
688 #ifdef HAVE_LONG_LONG 723 #ifdef HAVE_LONG_LONG
689 else// if (ptv->tv_sec <= ULLONG_MAX) 724 else// if (ptv->tv_sec <= ULLONG_MAX)
690 { 725 {
691 value = PyLong_FromUnsignedLongLong(ptv->tv_sec); 726 return PyLong_FromUnsignedLongLong(ptv->tv_sec);
692 } 727 }
693 #else 728 #else
694 // else if (ptv->tv_sec <= ULONG_MAX) 729 // else if (ptv->tv_sec <= ULONG_MAX)
695 // { 730 // {
696 // value = PyLong_FromUnsignedLong(ptv->tv_sec); 731 // return PyLong_FromUnsignedLong(ptv->tv_sec);
697 // } 732 // }
698 //#endif 733 //#endif
699 else 734 else
700 { 735 {
701 value = PyFloat_FromDouble(ptv->tv_sec); 736 return PyFloat_FromDouble(ptv->tv_sec);
702 } 737 }
703 // 738 //
704 #endif 739 #endif
705 // 740 //
706 } 741 }
707 else 742 else
708 #endif 743 #endif
709 { 744 {
710 // TODO: use decimal.Decimal or fractions.Fraction 745 // TODO: use decimal.Decimal or fractions.Fraction
711 value = PyFloat_FromDouble(ptv->tv_sec + ptv->tv_usec * 0.000001); 746 return PyFloat_FromDouble(ptv->tv_sec + ptv->tv_usec * 0.000001);
712 } 747 }
748 }
749
750 static inline bool timeval_to_attr(const _PyTime_timeval *ptv, PyObject *obj, const char *attr)
751 {
752 PyObject *value = timeval_to_obj(ptv);
713 if (value == NULL) 753 if (value == NULL)
714 { 754 {
715 return false; 755 return false;
716 } 756 }
717 if (PyObject_SetAttrString(obj, attr, value) == -1) 757 if (PyObject_SetAttrString(obj, attr, value) == -1)
722 return true; 762 return true;
723 } 763 }
724 764
725 /* 765 /*
726 TODO/FIXME: 766 TODO/FIXME:
727 * Replace timeval->timespec and select->pselect if pselect is available
728 (preferably only if pselect is not a wrapper around select).
729 * File descriptors might be >= FD_SETSIZE? 767 * File descriptors might be >= FD_SETSIZE?
730 */ 768 */
731 static PyObject *_unix_call(PyObject *self, PyObject *args, PyObject *kwds) 769 static PyObject *_unix_call(PyObject *self, PyObject *args, PyObject *kwds)
732 { 770 {
733 PyObject *testcase = NULL, *obj; 771 PyObject *testcase = NULL, *obj;
737 _PyTime_timeval maxwalltime, maxcputime, timeout, time_start; 775 _PyTime_timeval maxwalltime, maxcputime, timeout, time_start;
738 Py_ssize_t maxmemory, r; 776 Py_ssize_t maxmemory, r;
739 size_t stats_read = 0; 777 size_t stats_read = 0;
740 fd_set readfds; 778 fd_set readfds;
741 char c; 779 char c;
742 bool have_maxwalltime; 780 bool have_maxwalltime, wall_time_exceeded;
743 781
744 if (kwds != NULL) 782 if (kwds != NULL)
745 { 783 {
746 testcase = PyDict_GetItemString(kwds, "case"); 784 testcase = PyDict_GetItemString(kwds, "case");
747 } 785 }
997 #endif 1035 #endif
998 FD_SET(c2ppipe[0], &readfds); 1036 FD_SET(c2ppipe[0], &readfds);
999 1037
1000 if (have_maxwalltime) 1038 if (have_maxwalltime)
1001 { 1039 {
1002 _PyTime_gettimeofday(&now); 1040 timerget(&now);
1003 if (timercmp(&time_end, &now, <)) 1041 if (timercmp(&time_end, &now, <))
1004 { 1042 {
1005 timerclear(&timeout); 1043 timerclear(&timeout);
1006 } 1044 }
1007 else 1045 else
1014 if (!s && timercmp(&time_end, &now, <)) 1052 if (!s && timercmp(&time_end, &now, <))
1015 { 1053 {
1016 close(c2ppipe[0]); 1054 close(c2ppipe[0]);
1017 TERM_TESTEE; 1055 TERM_TESTEE;
1018 Py_BLOCK_THREADS 1056 Py_BLOCK_THREADS
1057 timeval_to_attr(&now, testcase, "time_stopped");
1019 Py_DECREF(testcase); 1058 Py_DECREF(testcase);
1020 PyErr_SetObject(WallTimeLimitExceeded, NULL); 1059 PyErr_SetObject(WallTimeLimitExceeded, NULL);
1021 return NULL; 1060 return NULL;
1022 } 1061 }
1023 } 1062 }
1195 Py_DECREF(testcase); 1234 Py_DECREF(testcase);
1196 PyErr_SetString(PyExc_EnvironmentError, "unexpectedly early end of output from worker"); 1235 PyErr_SetString(PyExc_EnvironmentError, "unexpectedly early end of output from worker");
1197 return NULL; 1236 return NULL;
1198 } 1237 }
1199 1238
1200 if (timerisset(&maxwalltime) && timercmp(&stats.walltime, &maxwalltime, >))
1201 {
1202 Py_DECREF(testcase);
1203 PyErr_SetObject(WallTimeLimitExceeded, NULL);
1204 return NULL;
1205 }
1206
1207 obj = PyInt_FromLong(0); 1239 obj = PyInt_FromLong(0);
1208 if (obj == NULL) 1240 if (obj == NULL)
1209 { 1241 {
1210 Py_DECREF(testcase); 1242 Py_DECREF(testcase);
1211 return NULL; 1243 return NULL;
1215 Py_DECREF(testcase); 1247 Py_DECREF(testcase);
1216 return NULL; 1248 return NULL;
1217 } 1249 }
1218 Py_DECREF(obj); 1250 Py_DECREF(obj);
1219 1251
1252 wall_time_exceeded = timerisset(&maxwalltime) && timercmp(&stats.walltime, &maxwalltime, >);
1220 #if defined HAVE_SYS_RESOURCE_H || defined HAVE_WAIT4 || defined HAVE_WAIT3 1253 #if defined HAVE_SYS_RESOURCE_H || defined HAVE_WAIT4 || defined HAVE_WAIT3
1221 if (timerisset(&maxcputime) || !timerisset(&maxwalltime)) 1254 if (!wall_time_exceeded && (timerisset(&maxcputime) || !timerisset(&maxwalltime)))
1222 { 1255 {
1223 PyObject *cputls; 1256 PyObject *cputls;
1224 if (!timeval_to_attr(&stats.cputime, testcase, "time_stopped")) 1257 if (!timeval_to_attr(&stats.cputime, testcase, "time_stopped"))
1225 { 1258 {
1226 Py_DECREF(testcase); 1259 Py_DECREF(testcase);
1249 #endif 1282 #endif
1250 { 1283 {
1251 if (!timeval_to_attr(&stats.walltime, testcase, "time_stopped")) 1284 if (!timeval_to_attr(&stats.walltime, testcase, "time_stopped"))
1252 { 1285 {
1253 Py_DECREF(testcase); 1286 Py_DECREF(testcase);
1287 return NULL;
1288 }
1289 if (wall_time_exceeded)
1290 {
1291 Py_DECREF(testcase);
1292 PyErr_SetObject(WallTimeLimitExceeded, NULL);
1254 return NULL; 1293 return NULL;
1255 } 1294 }
1256 } 1295 }
1257 1296
1258 #if defined HAVE_SYS_RESOURCE_H || defined HAVE_WAIT4 || defined HAVE_WAIT3 1297 #if defined HAVE_SYS_RESOURCE_H || defined HAVE_WAIT4 || defined HAVE_WAIT3
1274 Py_DECREF(Popen_placeholder); 1313 Py_DECREF(Popen_placeholder);
1275 Py_DECREF(testcase); 1314 Py_DECREF(testcase);
1276 Py_RETURN_NONE; 1315 Py_RETURN_NONE;
1277 } 1316 }
1278 1317
1318 static PyObject *_unix_clock(PyObject *self)
1319 {
1320 _PyTime_timeval tv;
1321 timerget(&tv);
1322 return timeval_to_obj(&tv);
1323 }
1324
1279 static PyObject *_unix_pause(PyObject *self) 1325 static PyObject *_unix_pause(PyObject *self)
1280 { 1326 {
1281 #ifdef HAVE_TERMIOS_H 1327 #ifdef HAVE_TERMIOS_H
1282 if (catch_escape) 1328 if (catch_escape)
1283 { 1329 {
1298 } 1344 }
1299 1345
1300 static PyMethodDef _unixMethods[] = 1346 static PyMethodDef _unixMethods[] =
1301 { 1347 {
1302 { "call", (PyCFunction) _unix_call, METH_VARARGS | METH_KEYWORDS, "Call a process." }, 1348 { "call", (PyCFunction) _unix_call, METH_VARARGS | METH_KEYWORDS, "Call a process." },
1349 { "clock", (PyCFunction) _unix_clock, METH_NOARGS, "Return the current time in seconds since an unspecified reference point." },
1303 { "pause", (PyCFunction) _unix_pause, METH_NOARGS, "Block until a key is pressed." }, 1350 { "pause", (PyCFunction) _unix_pause, METH_NOARGS, "Block until a key is pressed." },
1304 { NULL } 1351 { NULL }
1305 }; 1352 };
1306 1353
1307 #ifdef USE_WAKEUP_FD 1354 #ifdef USE_WAKEUP_FD