Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ 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.

Date:
. Added the DateTime::createFromImmutable() method, which mirrors
DateTimeImmutable::createFromMutable().
Expand Down
4 changes: 4 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
========================
Expand Down
9 changes: 9 additions & 0 deletions ext/standard/basic_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -3716,6 +3723,8 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */

BASIC_MINIT_SUBMODULE(random)

BASIC_MINIT_SUBMODULE(hrtime)

return SUCCESS;
}
/* }}} */
Expand Down
14 changes: 7 additions & 7 deletions ext/standard/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]='$';
Expand Down Expand Up @@ -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,[
Expand All @@ -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
Expand Down Expand Up @@ -483,7 +483,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
Expand Down
5 changes: 2 additions & 3 deletions ext/standard/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -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");

205 changes: 205 additions & 0 deletions ext/standard/hrtime.c
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;
Copy link
Contributor

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.

Copy link
Contributor Author

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.

}

#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) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we already have ZEND_LTOA? Why duplicate it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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); \
Copy link
Contributor

Choose a reason for hiding this comment

The 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 (double)? Yes, there's a potential for precision loss, but wouldn't it happen in string case too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 (2^52)/1000000000. The string conversion is the only way i see so far to do the correct conversion. Still it's 32-bit only. On 64-bit, even a signed 64-bit integral is fine for some hundreds of years of up time.

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not used anywhere. Is it supposed to be?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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
*/

Loading