]> granicus.if.org Git - php/commitdiff
Implement high resolution monotonic timer function hrtime()
authorAnatol Belski <ab@php.net>
Sun, 7 Jan 2018 14:55:12 +0000 (15:55 +0100)
committerAnatol Belski <ab@php.net>
Sun, 7 Jan 2018 15:03:52 +0000 (16:03 +0100)
UPGRADING
UPGRADING.INTERNALS
ext/standard/basic_functions.c
ext/standard/config.m4
ext/standard/config.w32
ext/standard/hrtime.c [new file with mode: 0644]
ext/standard/hrtime.h [new file with mode: 0644]
ext/standard/php_standard.h
ext/standard/tests/hrtime/hrtime.phpt [new file with mode: 0644]
ext/standard/tests/hrtime/hrtime_array.phpt [new file with mode: 0644]

index 7413f7b2d1a3e39ac7df8cd0c65fff5a3f0b1b01..56b979be8e95c6c9133324947de052aeaf00ddfe 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -95,6 +95,17 @@ PCRE:
 6. New Functions
 ========================================
 
+Core:
+  . Added monotonic timer function hrtime([bool get_as_num]). It returns an
+    array of the form [seconds, nanoseconds] with the timestamp starting at
+    an unspecified point in the past. If the optional argument is passed as
+    true, the return value is an integer on 64-bit systems or float on
+    32-bit systems, representing the nanoseconds. The timestamp is not
+    adjustable and is not related to wall clock or time of day. The timers
+    are available under Linux, FreeBSD, Windows, Mac, SunOS, AIX and their
+    derivatives. If no required timers are provided by a corresponding
+    platform, the function returns false.
+
 Date:
   . Added the DateTime::createFromImmutable() method, which mirrors
     DateTimeImmutable::createFromMutable().
index 040498e38736c99eef33b05412f706754fbcb393..bf3a63b9b34a446fa942f9283485ea7ae0bda573 100644 (file)
@@ -12,6 +12,7 @@ PHP 7.3 INTERNALS UPGRADE NOTES
   i. RAND_RANGE()
   j. cast_object() with _IS_NUMBER
   k. zend_fcall_info_cache.initialized
+  l. php_hrtime_current()
 
 2. Build system changes
   a. Unix build system changes
@@ -95,6 +96,9 @@ PHP 7.3 INTERNALS UPGRADE NOTES
   k. zend_fcall_info_cache.initialized is removed. zend_fcall_info_cache is
      initialized if zend_fcall_info_cache.function_handler is set.
 
+  l. php_hrtime_current() delivers the number of nanoseconds since an uncertain
+     point in the past.
+
 ========================
 2. Build system changes
 ========================
index 3ebf9bc775da35579e3aff7998a336e76d0e35d5..2ae5cfb2593160e42f59b89a9d1d52b393953caf 100644 (file)
@@ -1453,6 +1453,11 @@ 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_ARG_INFO(0, get_as_number)
+ZEND_END_ARG_INFO()
+/* }}} */
 /* {{{ html.c */
 ZEND_BEGIN_ARG_INFO_EX(arginfo_htmlspecialchars, 0, 0, 1)
        ZEND_ARG_INFO(0, string)
@@ -2986,6 +2991,8 @@ static 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
@@ -3716,6 +3723,8 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */
 
        BASIC_MINIT_SUBMODULE(random)
 
+       BASIC_MINIT_SUBMODULE(hrtime)
+
        return SUCCESS;
 }
 /* }}} */
index a74df78a51e5c1420ea89a4a284c6c9359fe066c..6a5915b80ec549a4d76938fdbe3f3640d5dce7f2 100644 (file)
@@ -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
@@ -490,7 +490,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 net.c,,,
+                            random.c net.c hrtime.c,,,
                            -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
 
 PHP_ADD_MAKEFILE_FRAGMENT
index fe519b8db9ca33ab96c8b69675e62bd683cff666..e2fd7a28f198258a42f4f69924e26c99b5050b73 100644 (file)
@@ -36,12 +36,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 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 (file)
index 0000000..c32aef4
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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>                              |
+   | Author: Anatol Belski <ab@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(&lt);
+       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) \
+       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); \
+       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)
+{/*{{{*/
+       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
+ */
+
diff --git a/ext/standard/hrtime.h b/ext/standard/hrtime.h
new file mode 100644 (file)
index 0000000..f068124
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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>                              |
+   | Author: Anatol Belski <ab@php.net>                                   |
+   +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef HRTIME_H
+#define HRTIME_H
+
+#define PHP_HRTIME_PLATFORM_POSIX   0
+#define PHP_HRTIME_PLATFORM_WINDOWS 0
+#define PHP_HRTIME_PLATFORM_APPLE   0
+#define PHP_HRTIME_PLATFORM_HPUX    0
+#define PHP_HRTIME_PLATFORM_AIX     0
+
+#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) && defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC)
+# undef  PHP_HRTIME_PLATFORM_POSIX
+# define PHP_HRTIME_PLATFORM_POSIX 1
+#elif defined(_WIN32) || defined(_WIN64)
+# undef  PHP_HRTIME_PLATFORM_WINDOWS
+# define PHP_HRTIME_PLATFORM_WINDOWS 1
+#elif defined(__APPLE__)
+# undef  PHP_HRTIME_PLATFORM_APPLE
+# define PHP_HRTIME_PLATFORM_APPLE 1
+#elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__)))
+# undef  PHP_HRTIME_PLATFORM_HPUX
+# define PHP_HRTIME_PLATFORM_HPUX 1
+#elif defined(_AIX)
+# undef  PHP_HRTIME_PLATFORM_AIX
+# define PHP_HRTIME_PLATFORM_AIX 1
+#endif
+
+#define HRTIME_AVAILABLE (PHP_HRTIME_PLATFORM_POSIX || PHP_HRTIME_PLATFORM_WINDOWS || PHP_HRTIME_PLATFORM_APPLE || PHP_HRTIME_PLATFORM_HPUX || PHP_HRTIME_PLATFORM_AIX)
+
+typedef uint64_t php_hrtime_t;
+
+PHPAPI php_hrtime_t php_hrtime_current(void);
+
+PHP_MINIT_FUNCTION(hrtime);
+
+PHP_FUNCTION(hrtime);
+
+#endif /* HRTIME_H */
index 750507688b0df7c2dee42ee3c8f250bd71e57c53..eb7f42b082d3de2a01b7a82d1fe0550298ac6910 100644 (file)
@@ -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 (file)
index 0000000..bac127b
--- /dev/null
@@ -0,0 +1,26 @@
+--TEST--
+Test hrtime() aligns with microtime()
+--FILE--
+<?php
+
+$m0 = microtime(true);
+$h0 = hrtime(true);
+for ($i = 0; $i < 1024*1024; $i++);
+$h1 = hrtime(true);
+$m1 = microtime(true);
+
+$d0 = ($m1 - $m0)*1000000000.0;
+$d1 = $h1 - $h0;
+
+/* Relative uncertainty. */
+$d = abs($d0 - $d1)/$d1;
+
+if ($d > 0.05) {
+       print "FAIL, $d";
+} else {
+       print "OK, $d";
+}
+
+?>
+--EXPECTF--
+OK, %f
diff --git a/ext/standard/tests/hrtime/hrtime_array.phpt b/ext/standard/tests/hrtime/hrtime_array.phpt
new file mode 100644 (file)
index 0000000..c3b4b61
--- /dev/null
@@ -0,0 +1,15 @@
+--TEST--
+Test hrtime() return array
+--FILE--
+<?php
+
+var_dump(hrtime());
+
+?>
+--EXPECTF--
+array(2) {
+  [0]=>
+  int(%d)
+  [1]=>
+  int(%d)
+}