1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Niklas Keller <kelunik@php.net> |
14 | Author: Anatol Belski <ab@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #include "php.h"
19 #include "hrtime.h"
20
21 /* {{{ */
22 /* This file reuses code parts from the cross-platform timer library
23 Public Domain - 2011 Mattias Jansson / Rampant Pixels */
24
25 #if PHP_HRTIME_PLATFORM_POSIX
26
27 # include <unistd.h>
28 # include <time.h>
29 # include <string.h>
30
31 #elif PHP_HRTIME_PLATFORM_WINDOWS
32
33 # define WIN32_LEAN_AND_MEAN
34
35 static double _timer_scale = .0;
36
37 #elif PHP_HRTIME_PLATFORM_APPLE
38
39 # include <mach/mach_time.h>
40 # include <string.h>
41 static mach_timebase_info_data_t _timerlib_info;
42
43 #elif PHP_HRTIME_PLATFORM_HPUX
44
45 # include <sys/time.h>
46
47 #elif PHP_HRTIME_PLATFORM_AIX
48
49 # include <sys/time.h>
50 # include <sys/systemcfg.h>
51
52 #endif
53
54 #define NANO_IN_SEC 1000000000
55 /* }}} */
56
_timer_init(void)57 static int _timer_init(void)
58 {/*{{{*/
59 #if PHP_HRTIME_PLATFORM_WINDOWS
60
61 LARGE_INTEGER tf = {0};
62 if (!QueryPerformanceFrequency(&tf) || 0 == tf.QuadPart) {
63 return -1;
64 }
65 _timer_scale = (double)NANO_IN_SEC / (php_hrtime_t)tf.QuadPart;
66
67 #elif PHP_HRTIME_PLATFORM_APPLE
68
69 if (mach_timebase_info(&_timerlib_info)) {
70 return -1;
71 }
72
73 #elif PHP_HRTIME_PLATFORM_POSIX
74
75 #if !_POSIX_MONOTONIC_CLOCK
76 #ifdef _SC_MONOTONIC_CLOCK
77 if (0 >= sysconf(_SC_MONOTONIC_CLOCK)) {
78 return -1;
79 }
80 #endif
81 #endif
82
83 #elif PHP_HRTIME_PLATFORM_HPUX
84
85 /* pass */
86
87 #elif PHP_HRTIME_PLATFORM_AIX
88
89 /* pass */
90
91 #else
92 /* Timer unavailable. */
93 return -1;
94 #endif
95
96 return 0;
97 }/*}}}*/
98
99 /* {{{ */
PHP_MINIT_FUNCTION(hrtime)100 PHP_MINIT_FUNCTION(hrtime)
101 {
102 if (0 > _timer_init()) {
103 php_error_docref(NULL, E_WARNING, "Failed to initialize high-resolution timer");
104 return FAILURE;
105 }
106
107 return SUCCESS;
108 }
109 /* }}} */
110
_timer_current(void)111 static zend_always_inline php_hrtime_t _timer_current(void)
112 {/*{{{*/
113 #if PHP_HRTIME_PLATFORM_WINDOWS
114 LARGE_INTEGER lt = {0};
115 QueryPerformanceCounter(<);
116 return (php_hrtime_t)((php_hrtime_t)lt.QuadPart * _timer_scale);
117 #elif PHP_HRTIME_PLATFORM_APPLE
118 return (php_hrtime_t)mach_absolute_time() * _timerlib_info.numer / _timerlib_info.denom;
119 #elif PHP_HRTIME_PLATFORM_POSIX
120 struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
121 if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) {
122 return ((php_hrtime_t) ts.tv_sec * (php_hrtime_t)NANO_IN_SEC) + ts.tv_nsec;
123 }
124 return 0;
125 #elif PHP_HRTIME_PLATFORM_HPUX
126 return (php_hrtime_t) gethrtime();
127 #elif PHP_HRTIME_PLATFORM_AIX
128 timebasestruct_t t;
129 read_wall_time(&t, TIMEBASE_SZ);
130 time_base_to_time(&t, TIMEBASE_SZ);
131 return (php_hrtime_t) t.tb_high * (php_hrtime_t)NANO_IN_SEC + t.tb_low;
132 #else
133 return 0;
134 #endif
135 }/*}}}*/
136
137 #ifdef ZEND_ENABLE_ZVAL_LONG64
138 #define PHP_RETURN_HRTIME(t) RETURN_LONG((zend_long)t)
139 #else
140 #ifdef _WIN32
141 # define HRTIME_U64A(i, s, len) _ui64toa_s(i, s, len, 10)
142 #else
143 # define HRTIME_U64A(i, s, len) \
144 do { \
145 int st = snprintf(s, len, "%llu", i); \
146 s[st] = '\0'; \
147 } while (0)
148 #endif
149 #define PHP_RETURN_HRTIME(t) do { \
150 char _a[ZEND_LTOA_BUF_LEN]; \
151 double _d; \
152 HRTIME_U64A(t, _a, ZEND_LTOA_BUF_LEN); \
153 _d = zend_strtod(_a, NULL); \
154 RETURN_DOUBLE(_d); \
155 } while (0)
156 #endif
157
158 /* {{{ Returns an array of integers in form [seconds, nanoseconds] counted
159 from an arbitrary point in time. If an optional boolean argument is
160 passed, returns an integer on 64-bit platforms or float on 32-bit
161 containing the current high-resolution time in nanoseconds. The
162 delivered timestamp is monotonic and cannot be adjusted. */
PHP_FUNCTION(hrtime)163 PHP_FUNCTION(hrtime)
164 {
165 #if HRTIME_AVAILABLE
166 bool get_as_num = 0;
167 php_hrtime_t t = _timer_current();
168
169 ZEND_PARSE_PARAMETERS_START(0, 1)
170 Z_PARAM_OPTIONAL
171 Z_PARAM_BOOL(get_as_num)
172 ZEND_PARSE_PARAMETERS_END();
173
174 if (UNEXPECTED(get_as_num)) {
175 PHP_RETURN_HRTIME(t);
176 } else {
177 array_init_size(return_value, 2);
178 zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
179 add_next_index_long(return_value, (zend_long)(t / (php_hrtime_t)NANO_IN_SEC));
180 add_next_index_long(return_value, (zend_long)(t % (php_hrtime_t)NANO_IN_SEC));
181 }
182 #else
183 RETURN_FALSE;
184 #endif
185 }
186 /* }}} */
187
php_hrtime_current(void)188 PHPAPI php_hrtime_t php_hrtime_current(void)
189 {/*{{{*/
190 return _timer_current();
191 }/*}}}*/
192