-
Notifications
You must be signed in to change notification settings - Fork 7.9k
High resolution monotonic timer #2976
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
79c2021
88664d2
1372316
6a69256
6ad9424
cddc6ec
bdc9629
a2f912a
35965fe
f4803c4
1d1062a
49d1494
853403b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
/* | ||
+----------------------------------------------------------------------+ | ||
| PHP Version 7 | | ||
+----------------------------------------------------------------------+ | ||
| Copyright (c) 1997-2017 The PHP Group | | ||
+----------------------------------------------------------------------+ | ||
| This source file is subject to version 3.01 of the PHP license, | | ||
| that is bundled with this package in the file LICENSE, and is | | ||
| available through the world-wide-web at the following url: | | ||
| http://www.php.net/license/3_01.txt | | ||
| If you did not receive a copy of the PHP license and are unable to | | ||
| obtain it through the world-wide-web, please send a note to | | ||
| license@php.net so we can mail you a copy immediately. | | ||
+----------------------------------------------------------------------+ | ||
| Author: Niklas Keller <kelunik@php.net> | | ||
+----------------------------------------------------------------------+ | ||
*/ | ||
|
||
/* $Id$ */ | ||
|
||
#include "php.h" | ||
#include "hrtime.h" | ||
|
||
/* {{{ */ | ||
/* This file reuses code parts from the cross-platform timer library | ||
Public Domain - 2011 Mattias Jansson / Rampant Pixels */ | ||
|
||
#if PHP_HRTIME_PLATFORM_POSIX | ||
|
||
# include <unistd.h> | ||
# include <time.h> | ||
# include <string.h> | ||
|
||
#elif PHP_HRTIME_PLATFORM_WINDOWS | ||
|
||
# define WIN32_LEAN_AND_MEAN | ||
|
||
static double _timer_scale = .0; | ||
|
||
#elif PHP_HRTIME_PLATFORM_APPLE | ||
|
||
# include <mach/mach_time.h> | ||
# include <string.h> | ||
static mach_timebase_info_data_t _timerlib_info; | ||
|
||
#elif PHP_HRTIME_PLATFORM_HPUX | ||
|
||
# include <sys/time.h> | ||
|
||
#elif PHP_HRTIME_PLATFORM_AIX | ||
|
||
# include <sys/time.h> | ||
# include <sys/systemcfg.h> | ||
|
||
#endif | ||
|
||
#define NANO_IN_SEC 1000000000 | ||
/* }}} */ | ||
|
||
static int _timer_init() | ||
{/*{{{*/ | ||
#if PHP_HRTIME_PLATFORM_WINDOWS | ||
|
||
LARGE_INTEGER tf = {0}; | ||
if (!QueryPerformanceFrequency(&tf) || 0 == tf.QuadPart) { | ||
return -1; | ||
} | ||
_timer_scale = (double)NANO_IN_SEC / (php_hrtime_t)tf.QuadPart; | ||
|
||
#elif PHP_HRTIME_PLATFORM_APPLE | ||
|
||
if (mach_timebase_info(&_timerlib_info)) { | ||
return -1; | ||
} | ||
|
||
#elif PHP_HRTIME_PLATFORM_POSIX | ||
|
||
#if !_POSIX_MONOTONIC_CLOCK | ||
#ifdef _SC_MONOTONIC_CLOCK | ||
if (0 >= sysconf(_SC_MONOTONIC_CLOCK)) { | ||
return -1; | ||
} | ||
#endif | ||
#endif | ||
|
||
#elif PHP_HRTIME_PLATFORM_HPUX | ||
|
||
/* pass */ | ||
|
||
#elif PHP_HRTIME_PLATFORM_AIX | ||
|
||
/* pass */ | ||
|
||
#else | ||
/* Timer unavailable. */ | ||
return -1; | ||
#endif | ||
|
||
return 0; | ||
}/*}}}*/ | ||
|
||
/* {{{ */ | ||
PHP_MINIT_FUNCTION(hrtime) | ||
{ | ||
if (0 > _timer_init()) { | ||
php_error_docref(NULL, E_WARNING, "Failed to initialize high-resolution timer"); | ||
return FAILURE; | ||
} | ||
|
||
return SUCCESS; | ||
} | ||
/* }}} */ | ||
|
||
static zend_always_inline php_hrtime_t _timer_current(void) | ||
{/*{{{*/ | ||
#if PHP_HRTIME_PLATFORM_WINDOWS | ||
LARGE_INTEGER lt = {0}; | ||
QueryPerformanceCounter(<); | ||
return (php_hrtime_t)((php_hrtime_t)lt.QuadPart * _timer_scale); | ||
#elif PHP_HRTIME_PLATFORM_APPLE | ||
return (php_hrtime_t)mach_absolute_time() * _timerlib_info.numer / _timerlib_info.denom; | ||
#elif PHP_HRTIME_PLATFORM_POSIX | ||
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; | ||
if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) { | ||
return ((php_hrtime_t) ts.tv_sec * (php_hrtime_t)NANO_IN_SEC) + ts.tv_nsec; | ||
} | ||
return 0; | ||
#elif PHP_HRTIME_PLATFORM_HPUX | ||
return (php_hrtime_t) gethrtime(); | ||
#elif PHP_HRTIME_PLATFORM_AIX | ||
timebasestruct_t t; | ||
read_wall_time(&t, TIMEBASE_SZ); | ||
time_base_to_time(&t, TIMEBASE_SZ); | ||
return (php_hrtime_t) t.tb_high * (php_hrtime_t)NANO_IN_SEC + t.tb_low; | ||
#else | ||
return 0; | ||
#endif | ||
}/*}}}*/ | ||
|
||
#if ZEND_ENABLE_ZVAL_LONG64 | ||
#define PHP_RETURN_HRTIME(t) RETURN_LONG((zend_long)t) | ||
#else | ||
#ifdef _WIN32 | ||
# define HRTIME_U64A(i, s, len) _ui64toa_s(i, s, len, 10) | ||
#else | ||
# define HRTIME_U64A(i, s, len) \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we already have ZEND_LTOA? Why duplicate it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ZEND_LTOA depends on the platform and matches zend_long. The timer is saved in uint64_t, thus the macro is not suitable on 32-bit. |
||
do { \ | ||
int st = snprintf(s, len, "%llu", i); \ | ||
s[st] = '\0'; \ | ||
} while (0) | ||
#endif | ||
#define PHP_RETURN_HRTIME(t) do { \ | ||
char _a[ZEND_LTOA_BUF_LEN]; \ | ||
double _d; \ | ||
HRTIME_U64A(t, _a, ZEND_LTOA_BUF_LEN); \ | ||
_d = zend_strtod(_a, NULL); \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why we're converting to string and back here - can't we just do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The integral part of double can be overflown quite fast. With an up time of a couple of months, as soon as timestamp exceeds |
||
RETURN_DOUBLE(_d); \ | ||
} while (0) | ||
#endif | ||
|
||
/* {{{ proto mixed hrtime([bool get_as_number = false]) | ||
Returns an array of integers in form [seconds, nanoseconds] counted | ||
from an arbitrary point in time. If an optional boolean argument is | ||
passed, returns an integer on 64-bit platforms or float on 32-bit | ||
containing the current high-resolution time in nanoseconds. The | ||
delivered timestamp is monotonic and can not be adjusted. */ | ||
PHP_FUNCTION(hrtime) | ||
{ | ||
#if HRTIME_AVAILABLE | ||
zend_bool get_as_num = 0; | ||
php_hrtime_t t = _timer_current(); | ||
|
||
ZEND_PARSE_PARAMETERS_START(0, 1) | ||
Z_PARAM_OPTIONAL | ||
Z_PARAM_BOOL(get_as_num) | ||
ZEND_PARSE_PARAMETERS_END(); | ||
|
||
if (UNEXPECTED(get_as_num)) { | ||
PHP_RETURN_HRTIME(t); | ||
} else { | ||
array_init_size(return_value, 2); | ||
zend_hash_real_init(Z_ARRVAL_P(return_value), 1); | ||
add_next_index_long(return_value, (zend_long)(t / (php_hrtime_t)NANO_IN_SEC)); | ||
add_next_index_long(return_value, (zend_long)(t % (php_hrtime_t)NANO_IN_SEC)); | ||
} | ||
#else | ||
RETURN_FALSE | ||
#endif | ||
} | ||
/* }}} */ | ||
|
||
PHPAPI php_hrtime_t php_hrtime_current(void) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not used anywhere. Is it supposed to be? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done specifically to export the useful piece of the new API, so i'd say yes. It can be used by non core exts, etc. |
||
{/*{{{*/ | ||
return _timer_current(); | ||
}/*}}}*/ | ||
|
||
/* | ||
* Local variables: | ||
* tab-width: 4 | ||
* c-basic-offset: 4 | ||
* End: | ||
* vim600: sw=4 ts=4 fdm=marker | ||
* vim<600: sw=4 ts=4 | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If that fails for some reason, _timerlib_info would contain random values. Maybe it makes sense to initialize it to something safer? Not zeroes since _timerlib_info.denom is used as divisor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If MINIT fails and returns FAILURE, no functions are registered. I went explicitly this way, as otherwise one would need an additional condition in the function call, where the module init were healthy. IMO module init fail is an unusual situation, the userspace function not being available then is OK. And OFC no calculations would happen in this case.