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