From 43baee08ff8730f54f15e754107aa106e94722ae Mon Sep 17 00:00:00 2001 From: Niklas Keller Date: Sun, 5 Feb 2017 20:11:24 +0100 Subject: [PATCH 1/8] Implement hrtime() This adds hrtime() to provide access to the system's high-resolution timer. --- ext/standard/basic_functions.c | 9 +- ext/standard/config.m4 | 14 +- ext/standard/config.w32 | 5 +- ext/standard/hrtime.c | 59 +++++++ ext/standard/hrtime.h | 29 ++++ ext/standard/php_standard.h | 1 + ext/standard/tests/hrtime/hrtime.phpt | 20 +++ ext/standard/tests/hrtime/hrtime_error.phpt | 10 ++ ext/standard/timer.c | 174 ++++++++++++++++++++ ext/standard/timer.h | 76 +++++++++ 10 files changed, 386 insertions(+), 11 deletions(-) create mode 100644 ext/standard/hrtime.c create mode 100644 ext/standard/hrtime.h create mode 100644 ext/standard/tests/hrtime/hrtime.phpt create mode 100644 ext/standard/tests/hrtime/hrtime_error.phpt create mode 100644 ext/standard/timer.c create mode 100644 ext/standard/timer.h diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 56131e72ea4d..e99aa8ae5f33 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1449,6 +1449,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_http_response_code, 0, 0, 0) ZEND_ARG_INFO(0, response_code) ZEND_END_ARG_INFO() /* }}} */ +/* {{{ hrtime.c */ +ZEND_BEGIN_ARG_INFO(arginfo_hrtime, 0) +ZEND_END_ARG_INFO() +/* }}} */ /* {{{ html.c */ ZEND_BEGIN_ARG_INFO_EX(arginfo_htmlspecialchars, 0, 0, 1) ZEND_ARG_INFO(0, string) @@ -2982,6 +2986,8 @@ const zend_function_entry basic_functions[] = { /* {{{ */ PHP_FE(getrusage, arginfo_getrusage) #endif + PHP_FE(hrtime, arginfo_hrtime) + #ifdef HAVE_GETTIMEOFDAY PHP_FE(uniqid, arginfo_uniqid) #endif @@ -3707,6 +3713,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ #endif BASIC_MINIT_SUBMODULE(random) + BASIC_MINIT_SUBMODULE(hrtime) return SUCCESS; } @@ -4040,7 +4047,7 @@ PHP_FUNCTION(long2ip) ********************/ /* {{{ proto string getenv(string varname[, bool local_only] - Get the value of an environment variable or every available environment variable + Get the value of an environment variable or every available environment variable if no varname is present */ PHP_FUNCTION(getenv) { diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index 284e74e967ea..c7b5c65586f6 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -13,14 +13,14 @@ int main(int argc, char **argv) char *filename = tmpnam(NULL); char buffer[64]; int result = 0; - + FILE *fp = fopen(filename, "wb"); if (NULL == fp) return 0; fputs("line 1\n", fp); fputs("line 2\n", fp); fclose(fp); - + fp = fopen(filename, "rb+"); if (NULL == fp) return 0; @@ -58,7 +58,7 @@ if test "$ac_cv_func_crypt" = "no"; then AC_DEFINE(HAVE_CRYPT, 1, [ ]) ]) fi - + AC_CACHE_CHECK(for standard DES crypt, ac_cv_crypt_des,[ AC_TRY_RUN([ #if HAVE_UNISTD_H @@ -124,7 +124,7 @@ int main() { char salt[15], answer[40]; char *encrypted; - salt[0]='$'; salt[1]='1'; salt[2]='$'; + salt[0]='$'; salt[1]='1'; salt[2]='$'; salt[3]='r'; salt[4]='a'; salt[5]='s'; salt[6]='m'; salt[7]='u'; salt[8]='s'; salt[9]='l'; salt[10]='e'; salt[11]='$'; @@ -267,7 +267,7 @@ else AC_DEFINE_UNQUOTED(PHP_USE_PHP_CRYPT_R, 0, [Whether PHP has to use its own crypt_r for blowfish, des and ext des]) fi -dnl +dnl dnl Check for __attribute__ ((__aligned__)) support in the compiler dnl AC_CACHE_CHECK(whether the compiler supports aligned attribute, ac_cv_attribute_aligned,[ @@ -289,7 +289,7 @@ dnl dnl log2 could be used to improve the log function, however it requires C99. The check for log2 should be turned on, dnl as soon as we support C99. AC_CHECK_FUNCS(getcwd getwd asinh acosh atanh log1p hypot glob strfmon nice fpclass mempcpy strpncpy) -AC_FUNC_FNMATCH +AC_FUNC_FNMATCH dnl dnl Check if there is a support means of creating a new process @@ -455,7 +455,7 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32. http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \ var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \ filters.c proc_open.c streamsfuncs.c http.c password.c \ - random.c,,, + random.c timer.c hrtime.c,,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) PHP_ADD_MAKEFILE_FRAGMENT diff --git a/ext/standard/config.w32 b/ext/standard/config.w32 index 00b2166abe87..7580c2895e22 100644 --- a/ext/standard/config.w32 +++ b/ext/standard/config.w32 @@ -31,12 +31,11 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \ url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \ php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \ user_filters.c uuencode.c filters.c proc_open.c password.c \ - streamsfuncs.c http.c flock_compat.c random.c", false /* never shared */, + streamsfuncs.c http.c flock_compat.c random.c timer.c hrtime.c", false /* never shared */, '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); PHP_INSTALL_HEADERS("", "ext/standard"); if (PHP_MBREGEX != "no") { - CHECK_HEADER_ADD_INCLUDE("oniguruma.h", "CFLAGS_STANDARD", PHP_MBREGEX + ";ext\\mbstring\\oniguruma") + CHECK_HEADER_ADD_INCLUDE("oniguruma.h", "CFLAGS_STANDARD", PHP_MBREGEX + ";ext\\mbstring\\oniguruma") } ADD_MAKEFILE_FRAGMENT(); PHP_INSTALL_HEADERS("", "ext/standard"); - diff --git a/ext/standard/hrtime.c b/ext/standard/hrtime.c new file mode 100644 index 000000000000..71f0312b8c37 --- /dev/null +++ b/ext/standard/hrtime.c @@ -0,0 +1,59 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" + +#include "hrtime.h" +#include "timer.h" + +/* {{{ */ +PHP_MINIT_FUNCTION(hrtime) +{ + if (timer_lib_initialize()) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to initialize internal timer"); + return FAILURE; + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ */ +PHP_MSHUTDOWN_FUNCTION(hrtime) +{ + timer_lib_shutdown(); + + return SUCCESS; +} +/* }}} */ + +/* {{{ proto mixed hrtime() + Returns an integer containing the current high-resolution time in nanoseconds + counted from an arbitrary point in time */ +PHP_FUNCTION(hrtime) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + // Use double to avoid integer overflows as we don't know the order of magnitude + RETURN_LONG((uint64_t) ((double) 1000000000 * timer_current() / timer_ticks_per_second())); +} +/* }}} */ diff --git a/ext/standard/hrtime.h b/ext/standard/hrtime.h new file mode 100644 index 000000000000..b4183bc04398 --- /dev/null +++ b/ext/standard/hrtime.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef HRTIME_H +#define HRTIME_H + +PHP_MINIT_FUNCTION(hrtime); +PHP_MSHUTDOWN_FUNCTION(hrtime); + +PHP_FUNCTION(hrtime); + +#endif /* HRTIME_H */ diff --git a/ext/standard/php_standard.h b/ext/standard/php_standard.h index 5b0111f143c3..00e85d075ed9 100644 --- a/ext/standard/php_standard.h +++ b/ext/standard/php_standard.h @@ -27,6 +27,7 @@ #include "php_mail.h" #include "md5.h" #include "sha1.h" +#include "hrtime.h" #include "html.h" #include "exec.h" #include "file.h" diff --git a/ext/standard/tests/hrtime/hrtime.phpt b/ext/standard/tests/hrtime/hrtime.phpt new file mode 100644 index 000000000000..b72a48698a3f --- /dev/null +++ b/ext/standard/tests/hrtime/hrtime.phpt @@ -0,0 +1,20 @@ +--TEST-- +Test hrtime() returns nanoseconds +--FILE-- + 1000000000) { + print "fail"; +} else { + print "OK"; +} + +?> +--EXPECT-- +OK diff --git a/ext/standard/tests/hrtime/hrtime_error.phpt b/ext/standard/tests/hrtime/hrtime_error.phpt new file mode 100644 index 000000000000..344aa840ea7f --- /dev/null +++ b/ext/standard/tests/hrtime/hrtime_error.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test hrtime() errors +--FILE-- + +--EXPECTF-- +Warning: hrtime() expects exactly 0 parameters, 1 given in %s diff --git a/ext/standard/timer.c b/ext/standard/timer.c new file mode 100644 index 000000000000..b5a56034d3de --- /dev/null +++ b/ext/standard/timer.c @@ -0,0 +1,174 @@ +/* timer.h - Cross-platform timer library - Public Domain - 2011 Mattias Jansson / Rampant Pixels + * + * This library provides a cross-platform interface to measure + * elapsed time with (at least) millisecond accuracy. + * + * This library is put in the public domain; you can redistribute + * it and/or modify it without any restrictions. + * + */ + +#include "timer.h" + +#define TIMER_PLATFORM_WINDOWS 0 +#define TIMER_PLATFORM_APPLE 0 +#define TIMER_PLATFORM_POSIX 0 + +#if defined( _WIN32 ) || defined( _WIN64 ) +# undef TIMER_PLATFORM_WINDOWS +# define TIMER_PLATFORM_WINDOWS 1 +# define WIN32_LEAN_AND_MEAN +# include +#elif defined( __APPLE__ ) +# undef TIMER_PLATFORM_APPLE +# define TIMER_PLATFORM_APPLE 1 +# include +# include +static mach_timebase_info_data_t _timerlib_info; +static void absolutetime_to_nanoseconds (uint64_t mach_time, uint64_t* clock ) { *clock = mach_time * _timerlib_info.numer / _timerlib_info.denom; } +#else +# undef TIMER_PLATFORM_POSIX +# define TIMER_PLATFORM_POSIX 1 +# include +# include +# include +#endif + +static tick_t _timerlib_freq = 0; +static double _timerlib_oofreq = 0; + + +int timer_lib_initialize( void ) +{ +#if TIMER_PLATFORM_WINDOWS + tick_t unused; + if( !QueryPerformanceFrequency( (LARGE_INTEGER*)&_timerlib_freq ) || + !QueryPerformanceCounter( (LARGE_INTEGER*)&unused ) ) + return -1; +#elif TIMER_PLATFORM_APPLE + if( mach_timebase_info( &_timerlib_info ) ) + return -1; + _timerlib_freq = 1000000000ULL; +#elif TIMER_PLATFORM_POSIX + struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; + if( clock_gettime( CLOCK_MONOTONIC, &ts ) ) + return -1; + _timerlib_freq = 1000000000ULL; +#endif + + _timerlib_oofreq = 1.0 / (double)_timerlib_freq; + + return 0; +} + + +void timer_lib_shutdown( void ) +{ +} + + +tick_t timer_current( void ) +{ +#if TIMER_PLATFORM_WINDOWS + + tick_t curclock; + QueryPerformanceCounter( (LARGE_INTEGER*)&curclock ); + return curclock; + +#elif TIMER_PLATFORM_APPLE + + tick_t curclock = 0; + absolutetime_to_nanoseconds( mach_absolute_time(), &curclock ); + return curclock; + +#elif TIMER_PLATFORM_POSIX + + struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; + clock_gettime( CLOCK_MONOTONIC, &ts ); + return ( (uint64_t)ts.tv_sec * 1000000000ULL ) + ts.tv_nsec; + +#endif +} + + +tick_t timer_ticks_per_second( void ) +{ + return _timerlib_freq; +} + + +deltatime_t timer_elapsed( const tick_t t ) +{ + return (deltatime_t)( (double)timer_elapsed_ticks( t ) * _timerlib_oofreq ); +} + + +tick_t timer_elapsed_ticks( const tick_t t ) +{ + tick_t dt = 0; + +#if TIMER_PLATFORM_WINDOWS + + tick_t curclock = t; + QueryPerformanceCounter( (LARGE_INTEGER*)&curclock ); + dt = curclock - t; + +#elif TIMER_PLATFORM_APPLE + + tick_t curclock = t; + absolutetime_to_nanoseconds( mach_absolute_time(), &curclock ); + dt = curclock - t; + +#elif TIMER_PLATFORM_POSIX + + tick_t curclock; + struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; + clock_gettime( CLOCK_MONOTONIC, &ts ); + + curclock = ( (tick_t)ts.tv_sec * 1000000000ULL ) + ts.tv_nsec; + dt = curclock - t; + +#endif + + return dt; +} + + +deltatime_t timer_ticks_to_seconds( const tick_t dt ) +{ + return (deltatime_t)( (double)dt * _timerlib_oofreq ); +} + + +#if TIMER_PLATFORM_WINDOWS +struct __timeb64 { + __time64_t time; + unsigned short millitm; + short timezone; + short dstflag; + }; +_CRTIMP errno_t __cdecl _ftime64_s(_Out_ struct __timeb64 * _Time); +#endif + +tick_t timer_system( void ) +{ +#if TIMER_PLATFORM_WINDOWS + + struct __timeb64 tb; + _ftime64_s( &tb ); + return ( (tick_t)tb.time * 1000ULL ) + (tick_t)tb.millitm; + +#elif TIMER_PLATFORM_APPLE + + tick_t curclock = 0; + absolutetime_to_nanoseconds( mach_absolute_time(), &curclock ); + return ( curclock / 1000000ULL ); + +#elif TIMER_PLATFORM_POSIX + + struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; + clock_gettime( CLOCK_REALTIME, &ts ); + return ( (uint64_t)ts.tv_sec * 1000ULL ) + ( ts.tv_nsec / 1000000ULL ); + +#endif +} diff --git a/ext/standard/timer.h b/ext/standard/timer.h new file mode 100644 index 000000000000..f0eb603bd3f3 --- /dev/null +++ b/ext/standard/timer.h @@ -0,0 +1,76 @@ +/* timer.h - Cross-platform timer library - Public Domain - 2011 Mattias Jansson / Rampant Pixels + * + * This library provides a cross-platform interface to measure + * elapsed time with (at least) millisecond accuracy. + * + * This library is put in the public domain; you can redistribute + * it and/or modify it without any restrictions. + * + */ + +#pragma once + +/*! \file timer.h + Time measurements */ + +#if TIMER_COMPILE +#define TIMER_API +#else +#if __cplusplus +#define TIMER_API extern "C" +#else +#define TIMER_API extern +#endif +#endif + +#if defined( _WIN32 ) || defined( _WIN64 ) + +//! Tick type +typedef unsigned __int64 tick_t; + +#else + +#include +//! Tick type +typedef uint64_t tick_t; + +#endif + +//! Deltatime type (float or double) +//typedef float deltatime_t; +typedef double deltatime_t; + + +/*! Initialize timer library */ +TIMER_API int timer_lib_initialize( void ); + +/*! Shutdown timer library */ +TIMER_API void timer_lib_shutdown( void ); + +/*! Get current timestamp, in ticks of system-specific frequency (queryable with timer_ticks_per_second), measured from some system-specific base timestamp + and not in sync with other timestamps + \return Current timestamp */ +TIMER_API tick_t timer_current( void ); + +/*! Get elapsed time since given timestamp + \param t Timestamp + \return Number of seconds elapsed */ +TIMER_API deltatime_t timer_elapsed( const tick_t t ); + +/*! Get elapsed ticks since given timestamp + \param t Timestamp + \return Number of ticks elapsed */ +TIMER_API tick_t timer_elapsed_ticks( const tick_t t ); + +/*! Get timer frequency, as number of ticks per second + \return Ticks per second */ +TIMER_API tick_t timer_ticks_per_second( void ); + +/*! Get ticks as seconds (effectively calculating ticks/timer_ticks_per_second()) + \param dt Deltatime in ticks + \return Deltatime in seconds */ +TIMER_API deltatime_t timer_ticks_to_seconds( const tick_t dt ); + +/*! Get system time, in milliseconds since the epoch (UNIX time) + \return Current timestamp, in milliseconds */ +TIMER_API tick_t timer_system( void ); From 9d37a592f4d60fc5230b740f9170911cd6f763e7 Mon Sep 17 00:00:00 2001 From: Niklas Keller Date: Mon, 6 Feb 2017 12:25:26 +0100 Subject: [PATCH 2/8] Fix hrtime() proto --- ext/standard/hrtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/hrtime.c b/ext/standard/hrtime.c index 71f0312b8c37..70186479b45c 100644 --- a/ext/standard/hrtime.c +++ b/ext/standard/hrtime.c @@ -44,7 +44,7 @@ PHP_MSHUTDOWN_FUNCTION(hrtime) } /* }}} */ -/* {{{ proto mixed hrtime() +/* {{{ proto int hrtime() Returns an integer containing the current high-resolution time in nanoseconds counted from an arbitrary point in time */ PHP_FUNCTION(hrtime) From 009dcd471e088a57255fb83b82b2b3a510d4857e Mon Sep 17 00:00:00 2001 From: Niklas Keller Date: Mon, 6 Feb 2017 13:07:57 +0100 Subject: [PATCH 3/8] Merge used parts of timer.h library into hrtime.c --- ext/standard/config.m4 | 2 +- ext/standard/config.w32 | 2 +- ext/standard/hrtime.c | 84 +++++++++++++++++-- ext/standard/timer.c | 174 ---------------------------------------- ext/standard/timer.h | 76 ------------------ 5 files changed, 80 insertions(+), 258 deletions(-) delete mode 100644 ext/standard/timer.c delete mode 100644 ext/standard/timer.h diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index c7b5c65586f6..c86be26ad199 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -455,7 +455,7 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32. http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \ var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \ filters.c proc_open.c streamsfuncs.c http.c password.c \ - random.c timer.c hrtime.c,,, + random.c hrtime.c,,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) PHP_ADD_MAKEFILE_FRAGMENT diff --git a/ext/standard/config.w32 b/ext/standard/config.w32 index 7580c2895e22..c81b3393742c 100644 --- a/ext/standard/config.w32 +++ b/ext/standard/config.w32 @@ -31,7 +31,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \ url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \ php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \ user_filters.c uuencode.c filters.c proc_open.c password.c \ - streamsfuncs.c http.c flock_compat.c random.c timer.c hrtime.c", false /* never shared */, + streamsfuncs.c http.c flock_compat.c random.c hrtime.c", false /* never shared */, '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); PHP_INSTALL_HEADERS("", "ext/standard"); if (PHP_MBREGEX != "no") { diff --git a/ext/standard/hrtime.c b/ext/standard/hrtime.c index 70186479b45c..e3afbb816a37 100644 --- a/ext/standard/hrtime.c +++ b/ext/standard/hrtime.c @@ -19,14 +19,88 @@ /* $Id$ */ #include "php.h" - #include "hrtime.h" -#include "timer.h" + +/* - timer.h ------------------------------------------------------------------------------------ */ +/* The following code is based on: + timer.h - Cross-platform timer library - Public Domain - 2011 Mattias Jansson / Rampant Pixels */ + +#define TIMER_PLATFORM_WINDOWS 0 +#define TIMER_PLATFORM_APPLE 0 +#define TIMER_PLATFORM_POSIX 0 + +#if defined( _WIN32 ) || defined( _WIN64 ) +# undef TIMER_PLATFORM_WINDOWS +# define TIMER_PLATFORM_WINDOWS 1 +# define WIN32_LEAN_AND_MEAN +# include +#elif defined( __APPLE__ ) +# undef TIMER_PLATFORM_APPLE +# define TIMER_PLATFORM_APPLE 1 +# include +# include +static mach_timebase_info_data_t _timerlib_info; +static void absolutetime_to_nanoseconds (uint64_t mach_time, uint64_t* clock ) { *clock = mach_time * _timerlib_info.numer / _timerlib_info.denom; } +#else +# undef TIMER_PLATFORM_POSIX +# define TIMER_PLATFORM_POSIX 1 +# include +# include +# include +#endif + +static uint64_t _timer_freq = 0; + +static int _timer_init() +{ +#if TIMER_PLATFORM_WINDOWS + uint64_t unused; + if( !QueryPerformanceFrequency( (LARGE_INTEGER*)&_timer_freq ) || + !QueryPerformanceCounter( (LARGE_INTEGER*)&unused ) ) + return -1; +#elif TIMER_PLATFORM_APPLE + if( mach_timebase_info( &_timerlib_info ) ) + return -1; + _timer_freq = 1000000000ULL; +#elif TIMER_PLATFORM_POSIX + struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; + if( clock_gettime( CLOCK_MONOTONIC, &ts ) ) + return -1; + _timer_freq = 1000000000ULL; +#endif + + return 0; +} + +static uint64_t _timer_current() +{ +#if TIMER_PLATFORM_WINDOWS + + uint64_t curclock; + QueryPerformanceCounter( (LARGE_INTEGER*)&curclock ); + return curclock; + +#elif TIMER_PLATFORM_APPLE + + uint64_t curclock = 0; + absolutetime_to_nanoseconds( mach_absolute_time(), &curclock ); + return curclock; + +#elif TIMER_PLATFORM_POSIX + + struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; + clock_gettime( CLOCK_MONOTONIC, &ts ); + return ( (uint64_t)ts.tv_sec * 1000000000ULL ) + ts.tv_nsec; + +#endif +} + +/* - end of timer.h ----------------------------------------------------------------------------- */ /* {{{ */ PHP_MINIT_FUNCTION(hrtime) { - if (timer_lib_initialize()) { + if (_timer_init()) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to initialize internal timer"); return FAILURE; } @@ -38,8 +112,6 @@ PHP_MINIT_FUNCTION(hrtime) /* {{{ */ PHP_MSHUTDOWN_FUNCTION(hrtime) { - timer_lib_shutdown(); - return SUCCESS; } /* }}} */ @@ -54,6 +126,6 @@ PHP_FUNCTION(hrtime) } // Use double to avoid integer overflows as we don't know the order of magnitude - RETURN_LONG((uint64_t) ((double) 1000000000 * timer_current() / timer_ticks_per_second())); + RETURN_LONG((uint64_t) ((double) 1000000000 * _timer_current() / _timer_freq)); } /* }}} */ diff --git a/ext/standard/timer.c b/ext/standard/timer.c deleted file mode 100644 index b5a56034d3de..000000000000 --- a/ext/standard/timer.c +++ /dev/null @@ -1,174 +0,0 @@ -/* timer.h - Cross-platform timer library - Public Domain - 2011 Mattias Jansson / Rampant Pixels - * - * This library provides a cross-platform interface to measure - * elapsed time with (at least) millisecond accuracy. - * - * This library is put in the public domain; you can redistribute - * it and/or modify it without any restrictions. - * - */ - -#include "timer.h" - -#define TIMER_PLATFORM_WINDOWS 0 -#define TIMER_PLATFORM_APPLE 0 -#define TIMER_PLATFORM_POSIX 0 - -#if defined( _WIN32 ) || defined( _WIN64 ) -# undef TIMER_PLATFORM_WINDOWS -# define TIMER_PLATFORM_WINDOWS 1 -# define WIN32_LEAN_AND_MEAN -# include -#elif defined( __APPLE__ ) -# undef TIMER_PLATFORM_APPLE -# define TIMER_PLATFORM_APPLE 1 -# include -# include -static mach_timebase_info_data_t _timerlib_info; -static void absolutetime_to_nanoseconds (uint64_t mach_time, uint64_t* clock ) { *clock = mach_time * _timerlib_info.numer / _timerlib_info.denom; } -#else -# undef TIMER_PLATFORM_POSIX -# define TIMER_PLATFORM_POSIX 1 -# include -# include -# include -#endif - -static tick_t _timerlib_freq = 0; -static double _timerlib_oofreq = 0; - - -int timer_lib_initialize( void ) -{ -#if TIMER_PLATFORM_WINDOWS - tick_t unused; - if( !QueryPerformanceFrequency( (LARGE_INTEGER*)&_timerlib_freq ) || - !QueryPerformanceCounter( (LARGE_INTEGER*)&unused ) ) - return -1; -#elif TIMER_PLATFORM_APPLE - if( mach_timebase_info( &_timerlib_info ) ) - return -1; - _timerlib_freq = 1000000000ULL; -#elif TIMER_PLATFORM_POSIX - struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; - if( clock_gettime( CLOCK_MONOTONIC, &ts ) ) - return -1; - _timerlib_freq = 1000000000ULL; -#endif - - _timerlib_oofreq = 1.0 / (double)_timerlib_freq; - - return 0; -} - - -void timer_lib_shutdown( void ) -{ -} - - -tick_t timer_current( void ) -{ -#if TIMER_PLATFORM_WINDOWS - - tick_t curclock; - QueryPerformanceCounter( (LARGE_INTEGER*)&curclock ); - return curclock; - -#elif TIMER_PLATFORM_APPLE - - tick_t curclock = 0; - absolutetime_to_nanoseconds( mach_absolute_time(), &curclock ); - return curclock; - -#elif TIMER_PLATFORM_POSIX - - struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; - clock_gettime( CLOCK_MONOTONIC, &ts ); - return ( (uint64_t)ts.tv_sec * 1000000000ULL ) + ts.tv_nsec; - -#endif -} - - -tick_t timer_ticks_per_second( void ) -{ - return _timerlib_freq; -} - - -deltatime_t timer_elapsed( const tick_t t ) -{ - return (deltatime_t)( (double)timer_elapsed_ticks( t ) * _timerlib_oofreq ); -} - - -tick_t timer_elapsed_ticks( const tick_t t ) -{ - tick_t dt = 0; - -#if TIMER_PLATFORM_WINDOWS - - tick_t curclock = t; - QueryPerformanceCounter( (LARGE_INTEGER*)&curclock ); - dt = curclock - t; - -#elif TIMER_PLATFORM_APPLE - - tick_t curclock = t; - absolutetime_to_nanoseconds( mach_absolute_time(), &curclock ); - dt = curclock - t; - -#elif TIMER_PLATFORM_POSIX - - tick_t curclock; - struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; - clock_gettime( CLOCK_MONOTONIC, &ts ); - - curclock = ( (tick_t)ts.tv_sec * 1000000000ULL ) + ts.tv_nsec; - dt = curclock - t; - -#endif - - return dt; -} - - -deltatime_t timer_ticks_to_seconds( const tick_t dt ) -{ - return (deltatime_t)( (double)dt * _timerlib_oofreq ); -} - - -#if TIMER_PLATFORM_WINDOWS -struct __timeb64 { - __time64_t time; - unsigned short millitm; - short timezone; - short dstflag; - }; -_CRTIMP errno_t __cdecl _ftime64_s(_Out_ struct __timeb64 * _Time); -#endif - -tick_t timer_system( void ) -{ -#if TIMER_PLATFORM_WINDOWS - - struct __timeb64 tb; - _ftime64_s( &tb ); - return ( (tick_t)tb.time * 1000ULL ) + (tick_t)tb.millitm; - -#elif TIMER_PLATFORM_APPLE - - tick_t curclock = 0; - absolutetime_to_nanoseconds( mach_absolute_time(), &curclock ); - return ( curclock / 1000000ULL ); - -#elif TIMER_PLATFORM_POSIX - - struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; - clock_gettime( CLOCK_REALTIME, &ts ); - return ( (uint64_t)ts.tv_sec * 1000ULL ) + ( ts.tv_nsec / 1000000ULL ); - -#endif -} diff --git a/ext/standard/timer.h b/ext/standard/timer.h deleted file mode 100644 index f0eb603bd3f3..000000000000 --- a/ext/standard/timer.h +++ /dev/null @@ -1,76 +0,0 @@ -/* timer.h - Cross-platform timer library - Public Domain - 2011 Mattias Jansson / Rampant Pixels - * - * This library provides a cross-platform interface to measure - * elapsed time with (at least) millisecond accuracy. - * - * This library is put in the public domain; you can redistribute - * it and/or modify it without any restrictions. - * - */ - -#pragma once - -/*! \file timer.h - Time measurements */ - -#if TIMER_COMPILE -#define TIMER_API -#else -#if __cplusplus -#define TIMER_API extern "C" -#else -#define TIMER_API extern -#endif -#endif - -#if defined( _WIN32 ) || defined( _WIN64 ) - -//! Tick type -typedef unsigned __int64 tick_t; - -#else - -#include -//! Tick type -typedef uint64_t tick_t; - -#endif - -//! Deltatime type (float or double) -//typedef float deltatime_t; -typedef double deltatime_t; - - -/*! Initialize timer library */ -TIMER_API int timer_lib_initialize( void ); - -/*! Shutdown timer library */ -TIMER_API void timer_lib_shutdown( void ); - -/*! Get current timestamp, in ticks of system-specific frequency (queryable with timer_ticks_per_second), measured from some system-specific base timestamp - and not in sync with other timestamps - \return Current timestamp */ -TIMER_API tick_t timer_current( void ); - -/*! Get elapsed time since given timestamp - \param t Timestamp - \return Number of seconds elapsed */ -TIMER_API deltatime_t timer_elapsed( const tick_t t ); - -/*! Get elapsed ticks since given timestamp - \param t Timestamp - \return Number of ticks elapsed */ -TIMER_API tick_t timer_elapsed_ticks( const tick_t t ); - -/*! Get timer frequency, as number of ticks per second - \return Ticks per second */ -TIMER_API tick_t timer_ticks_per_second( void ); - -/*! Get ticks as seconds (effectively calculating ticks/timer_ticks_per_second()) - \param dt Deltatime in ticks - \return Deltatime in seconds */ -TIMER_API deltatime_t timer_ticks_to_seconds( const tick_t dt ); - -/*! Get system time, in milliseconds since the epoch (UNIX time) - \return Current timestamp, in milliseconds */ -TIMER_API tick_t timer_system( void ); From 2590c10a25bda73371fe138293ca0912672de273 Mon Sep 17 00:00:00 2001 From: Niklas Keller Date: Mon, 6 Feb 2017 17:14:26 +0100 Subject: [PATCH 4/8] Change hrtime return to double, improve feature checking --- ext/standard/basic_functions.c | 7 +++ ext/standard/hrtime.c | 68 +++++++++++++++++++-------- ext/standard/hrtime.h | 18 +++++-- ext/standard/tests/hrtime/hrtime.phpt | 14 +++--- 4 files changed, 78 insertions(+), 29 deletions(-) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index e99aa8ae5f33..be4471c75f0a 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1450,8 +1450,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_http_response_code, 0, 0, 0) ZEND_END_ARG_INFO() /* }}} */ /* {{{ hrtime.c */ +#if HRTIME_AVAILABLE ZEND_BEGIN_ARG_INFO(arginfo_hrtime, 0) ZEND_END_ARG_INFO() +#endif /* }}} */ /* {{{ html.c */ ZEND_BEGIN_ARG_INFO_EX(arginfo_htmlspecialchars, 0, 0, 1) @@ -2986,7 +2988,9 @@ const zend_function_entry basic_functions[] = { /* {{{ */ PHP_FE(getrusage, arginfo_getrusage) #endif +#if HRTIME_AVAILABLE PHP_FE(hrtime, arginfo_hrtime) +#endif #ifdef HAVE_GETTIMEOFDAY PHP_FE(uniqid, arginfo_uniqid) @@ -3713,7 +3717,10 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ #endif BASIC_MINIT_SUBMODULE(random) + +#if HRTIME_AVAILABLE BASIC_MINIT_SUBMODULE(hrtime) +#endif return SUCCESS; } diff --git a/ext/standard/hrtime.c b/ext/standard/hrtime.c index e3afbb816a37..f5032fd1c3d6 100644 --- a/ext/standard/hrtime.c +++ b/ext/standard/hrtime.c @@ -19,8 +19,11 @@ /* $Id$ */ #include "php.h" +#include "zend_exceptions.h" #include "hrtime.h" +#if HRTIME_AVAILABLE + /* - timer.h ------------------------------------------------------------------------------------ */ /* The following code is based on: timer.h - Cross-platform timer library - Public Domain - 2011 Mattias Jansson / Rampant Pixels */ @@ -29,24 +32,30 @@ #define TIMER_PLATFORM_APPLE 0 #define TIMER_PLATFORM_POSIX 0 -#if defined( _WIN32 ) || defined( _WIN64 ) +#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) && defined(CLOCK_MONOTONIC) + +# undef TIMER_PLATFORM_POSIX +# define TIMER_PLATFORM_POSIX 1 +# include +# include +# include + +#elif defined(_WIN32) || defined(_WIN64) + # undef TIMER_PLATFORM_WINDOWS # define TIMER_PLATFORM_WINDOWS 1 # define WIN32_LEAN_AND_MEAN # include -#elif defined( __APPLE__ ) + +#elif defined(__APPLE__) + # undef TIMER_PLATFORM_APPLE # define TIMER_PLATFORM_APPLE 1 # include # include static mach_timebase_info_data_t _timerlib_info; static void absolutetime_to_nanoseconds (uint64_t mach_time, uint64_t* clock ) { *clock = mach_time * _timerlib_info.numer / _timerlib_info.denom; } -#else -# undef TIMER_PLATFORM_POSIX -# define TIMER_PLATFORM_POSIX 1 -# include -# include -# include + #endif static uint64_t _timer_freq = 0; @@ -54,19 +63,28 @@ static uint64_t _timer_freq = 0; static int _timer_init() { #if TIMER_PLATFORM_WINDOWS + uint64_t unused; - if( !QueryPerformanceFrequency( (LARGE_INTEGER*)&_timer_freq ) || - !QueryPerformanceCounter( (LARGE_INTEGER*)&unused ) ) + if (!QueryPerformanceFrequency((LARGE_INTEGER*) &_timer_freq) || + !QueryPerformanceCounter((LARGE_INTEGER*) &unused)) { return -1; + } + #elif TIMER_PLATFORM_APPLE - if( mach_timebase_info( &_timerlib_info ) ) + + if (mach_timebase_info(&_timerlib_info)) { return -1; + } _timer_freq = 1000000000ULL; + #elif TIMER_PLATFORM_POSIX + struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; - if( clock_gettime( CLOCK_MONOTONIC, &ts ) ) + if (clock_gettime(CLOCK_MONOTONIC, &ts)) { return -1; + } _timer_freq = 1000000000ULL; + #endif return 0; @@ -77,20 +95,22 @@ static uint64_t _timer_current() #if TIMER_PLATFORM_WINDOWS uint64_t curclock; - QueryPerformanceCounter( (LARGE_INTEGER*)&curclock ); + QueryPerformanceCounter((LARGE_INTEGER*) &curclock); return curclock; #elif TIMER_PLATFORM_APPLE uint64_t curclock = 0; - absolutetime_to_nanoseconds( mach_absolute_time(), &curclock ); + absolutetime_to_nanoseconds(mach_absolute_time(), &curclock); return curclock; #elif TIMER_PLATFORM_POSIX struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; - clock_gettime( CLOCK_MONOTONIC, &ts ); - return ( (uint64_t)ts.tv_sec * 1000000000ULL ) + ts.tv_nsec; + if (clock_gettime(CLOCK_MONOTONIC, &ts)) { + return -1; + } + return ((uint64_t) ts.tv_sec * 1000000000ULL) + ts.tv_nsec; #endif } @@ -116,8 +136,8 @@ PHP_MSHUTDOWN_FUNCTION(hrtime) } /* }}} */ -/* {{{ proto int hrtime() - Returns an integer containing the current high-resolution time in nanoseconds +/* {{{ proto float hrtime() + Returns a float containing the current high-resolution time in seconds counted from an arbitrary point in time */ PHP_FUNCTION(hrtime) { @@ -125,7 +145,15 @@ PHP_FUNCTION(hrtime) return; } - // Use double to avoid integer overflows as we don't know the order of magnitude - RETURN_LONG((uint64_t) ((double) 1000000000 * _timer_current() / _timer_freq)); + uint64_t current_time = _timer_current(); + + if (current_time == -1) { + zend_throw_exception(zend_ce_error, "Failed to get current system time", 0); + return; + } + + RETURN_DOUBLE((double) current_time / _timer_freq); } /* }}} */ + +#endif /* HRTIME_AVAILABLE */ diff --git a/ext/standard/hrtime.h b/ext/standard/hrtime.h index b4183bc04398..e5a26c0051ff 100644 --- a/ext/standard/hrtime.h +++ b/ext/standard/hrtime.h @@ -21,9 +21,21 @@ #ifndef HRTIME_H #define HRTIME_H -PHP_MINIT_FUNCTION(hrtime); -PHP_MSHUTDOWN_FUNCTION(hrtime); +#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) && defined(CLOCK_MONOTONIC) +#define HRTIME_AVAILABLE 1 +#elif defined(_WIN32) || defined(_WIN64) +#define HRTIME_AVAILABLE 1 +#elif defined(__APPLE__) +#define HRTIME_AVAILABLE 1 +#else +#define HRTIME_AVAILABLE 0 +#endif -PHP_FUNCTION(hrtime); +#if HRTIME_AVAILABLE + PHP_MINIT_FUNCTION(hrtime); + PHP_MSHUTDOWN_FUNCTION(hrtime); + + PHP_FUNCTION(hrtime); +#endif /* HRTIME_AVAILABLE */ #endif /* HRTIME_H */ diff --git a/ext/standard/tests/hrtime/hrtime.phpt b/ext/standard/tests/hrtime/hrtime.phpt index b72a48698a3f..9972be90889c 100644 --- a/ext/standard/tests/hrtime/hrtime.phpt +++ b/ext/standard/tests/hrtime/hrtime.phpt @@ -1,15 +1,17 @@ --TEST-- -Test hrtime() returns nanoseconds +Test hrtime() aligns with microtime() --FILE-- 1000000000) { +$hrdiff = hrtime() - $hrtime; +$microdiff = microtime(1) - $microtime; + +if (abs($hrdiff - $microdiff) > 0.0001) { print "fail"; } else { print "OK"; From 9ff851246a9b0cd276b9950468978f47a6d06d94 Mon Sep 17 00:00:00 2001 From: Niklas Keller Date: Mon, 6 Feb 2017 17:34:10 +0100 Subject: [PATCH 5/8] Add hrtime support for HP-UX --- ext/standard/hrtime.c | 17 ++++++++++++++++- ext/standard/hrtime.h | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ext/standard/hrtime.c b/ext/standard/hrtime.c index f5032fd1c3d6..5cdd31b913ff 100644 --- a/ext/standard/hrtime.c +++ b/ext/standard/hrtime.c @@ -28,9 +28,10 @@ /* The following code is based on: timer.h - Cross-platform timer library - Public Domain - 2011 Mattias Jansson / Rampant Pixels */ +#define TIMER_PLATFORM_POSIX 0 #define TIMER_PLATFORM_WINDOWS 0 #define TIMER_PLATFORM_APPLE 0 -#define TIMER_PLATFORM_POSIX 0 +#define TIMER_PLATFORM_HPUX 0 #if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) && defined(CLOCK_MONOTONIC) @@ -56,6 +57,12 @@ static mach_timebase_info_data_t _timerlib_info; static void absolutetime_to_nanoseconds (uint64_t mach_time, uint64_t* clock ) { *clock = mach_time * _timerlib_info.numer / _timerlib_info.denom; } +#elif #elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) + +# undef TIMER_PLATFORM_HPUX +# define TIMER_PLATFORM_HPUX 1 +# include + #endif static uint64_t _timer_freq = 0; @@ -85,6 +92,10 @@ static int _timer_init() } _timer_freq = 1000000000ULL; +#elif TIMER_PLATFORM_HPUX + + _timer_freq = 1000000000ULL; + #endif return 0; @@ -112,6 +123,10 @@ static uint64_t _timer_current() } return ((uint64_t) ts.tv_sec * 1000000000ULL) + ts.tv_nsec; +#elif TIMER_PLATFORM_HPUX + + return (uint64_t) gethrtime(); + #endif } diff --git a/ext/standard/hrtime.h b/ext/standard/hrtime.h index e5a26c0051ff..ac8c92762d88 100644 --- a/ext/standard/hrtime.h +++ b/ext/standard/hrtime.h @@ -27,6 +27,8 @@ #define HRTIME_AVAILABLE 1 #elif defined(__APPLE__) #define HRTIME_AVAILABLE 1 +#elif #elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) +#define HRTIME_AVAILABLE 1 #else #define HRTIME_AVAILABLE 0 #endif From 14c9acaec2666ffe5595e26cddcf277049445a21 Mon Sep 17 00:00:00 2001 From: Niklas Keller Date: Mon, 6 Feb 2017 18:14:44 +0100 Subject: [PATCH 6/8] Add UNEXPECTED wrapping for hrtime failures --- ext/standard/hrtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/hrtime.c b/ext/standard/hrtime.c index 5cdd31b913ff..e1e5f7ef8ccf 100644 --- a/ext/standard/hrtime.c +++ b/ext/standard/hrtime.c @@ -162,7 +162,7 @@ PHP_FUNCTION(hrtime) uint64_t current_time = _timer_current(); - if (current_time == -1) { + if (UNEXPECTED(current_time == -1)) { zend_throw_exception(zend_ce_error, "Failed to get current system time", 0); return; } From 2b32f686882ba0a8202fe38a6295e440219dd92a Mon Sep 17 00:00:00 2001 From: Niklas Keller Date: Mon, 6 Feb 2017 18:16:45 +0100 Subject: [PATCH 7/8] Fix bogus elif --- ext/standard/hrtime.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/hrtime.h b/ext/standard/hrtime.h index ac8c92762d88..7756b46c55c1 100644 --- a/ext/standard/hrtime.h +++ b/ext/standard/hrtime.h @@ -27,7 +27,7 @@ #define HRTIME_AVAILABLE 1 #elif defined(__APPLE__) #define HRTIME_AVAILABLE 1 -#elif #elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) +#elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) #define HRTIME_AVAILABLE 1 #else #define HRTIME_AVAILABLE 0 From fe0bd6e83cd69b523e109fde04229f09687af0c7 Mon Sep 17 00:00:00 2001 From: Niklas Keller Date: Mon, 6 Feb 2017 18:52:07 +0100 Subject: [PATCH 8/8] Fix other bogus elif --- ext/standard/hrtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/hrtime.c b/ext/standard/hrtime.c index e1e5f7ef8ccf..fb7bbed61cdc 100644 --- a/ext/standard/hrtime.c +++ b/ext/standard/hrtime.c @@ -57,7 +57,7 @@ static mach_timebase_info_data_t _timerlib_info; static void absolutetime_to_nanoseconds (uint64_t mach_time, uint64_t* clock ) { *clock = mach_time * _timerlib_info.numer / _timerlib_info.denom; } -#elif #elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) +#elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__))) # undef TIMER_PLATFORM_HPUX # define TIMER_PLATFORM_HPUX 1