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
16 changes: 15 additions & 1 deletion ext/standard/basic_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_http_response_code, 0, 0, 0)
ZEND_ARG_INFO(0, response_code)
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)
ZEND_ARG_INFO(0, string)
Expand Down Expand Up @@ -2982,6 +2988,10 @@ 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)
#endif
Expand Down Expand Up @@ -3708,6 +3718,10 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */

BASIC_MINIT_SUBMODULE(random)

#if HRTIME_AVAILABLE
BASIC_MINIT_SUBMODULE(hrtime)
#endif

return SUCCESS;
}
/* }}} */
Expand Down Expand Up @@ -4040,7 +4054,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)
{
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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please revoke the WS change.

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 @@ -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 hrtime.c,,,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It should check for symbols and mark a fallback case, maybe falling back to gettimeofday() or anything better in the code. Specifically for POSIX - it should check for CLOCK_MONOTONIC define, as there can be variations in the implementations. I think, it's even better to check for different platforms explicitly - BSD, Solaris for sure, and maybe some other.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

As far as I know, CLOCK_MONOTONIC is available on FreeBSD, OpenBSD, Solaris, AIX, etc. There are just differences for CLOCK_MONOTONIC_RAW / CLOCK_MONOTONIC_PRECISE etc.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

At least one platform doesn't have it, as per the linked blog. Edge cases, still they should be covered them if they're known.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, HP-UX doesn't seem to support it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I added support for HP-UX just now. Any other edge cases we know of? I think the checks should be fine now with the conditional definition?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh, i thought we'd rather exclude it in configure, but works this way, too. Except probably only a few can test it :/ There are no cases, i'd be aware of otherwise.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That is not a so-easy subject to takle. Systems tend to be pretty different in their API for time measurement.
Also, you should have a read at http://nadeausoftware.com/articles/2012/03/c_c_tip_how_measure_cpu_time_benchmarking

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@jpauli That's talking about CPU time for benchmarking, that's not the use case I have. While benchmarking is another possible use case for hrtime(), the reason I created this PR and am interested in hrtime() is for event loops. I'm not even interested in a nanosecond resolution, it just happens to be provided. Milliseconds would be enough for my use case.

-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 @@ -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 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");

174 changes: 174 additions & 0 deletions ext/standard/hrtime.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
+----------------------------------------------------------------------+
| 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 "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 */

#define TIMER_PLATFORM_POSIX 0
#define TIMER_PLATFORM_WINDOWS 0
#define TIMER_PLATFORM_APPLE 0
#define TIMER_PLATFORM_HPUX 0

#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) && defined(CLOCK_MONOTONIC)

# undef TIMER_PLATFORM_POSIX
# define TIMER_PLATFORM_POSIX 1
# include <unistd.h>
# include <time.h>
# include <string.h>

#elif defined(_WIN32) || defined(_WIN64)

# undef TIMER_PLATFORM_WINDOWS
# define TIMER_PLATFORM_WINDOWS 1
# define WIN32_LEAN_AND_MEAN
# include <windows.h>

#elif defined(__APPLE__)

# undef TIMER_PLATFORM_APPLE
# define TIMER_PLATFORM_APPLE 1
# include <mach/mach_time.h>
# include <string.h>
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 (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__)))

# undef TIMER_PLATFORM_HPUX
# define TIMER_PLATFORM_HPUX 1
# include <sys/time.h>

#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;

#elif TIMER_PLATFORM_HPUX

_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 };
if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
return -1;
}
return ((uint64_t) ts.tv_sec * 1000000000ULL) + ts.tv_nsec;

#elif TIMER_PLATFORM_HPUX

return (uint64_t) gethrtime();

#endif
}

/* - end of timer.h ----------------------------------------------------------------------------- */

/* {{{ */
PHP_MINIT_FUNCTION(hrtime)
{
if (_timer_init()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For whatever reason, if the initialization has failed, the whole should fallback to something.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fail back to what? Depends on what we want to guarantee for this function. If we want to guarantee no jumps and monotony, we can't fallback to anything.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we don't want to make the timer a hard requirement we can either a) only register the function is the functionality is available or b) throw an exception if the function is called.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

My thought was falling back to getttimeofday() or alike, as it is supposed to be a rare case, and otherwise it needs one more condition check. Fine with being strict and exception, it solves the case as well.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

In that case we won't ever find the missing platforms and just think those are supported.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@jpauli If it fails in MINIT it fails with a fatal error. It doesn't make sense to continue if the initialization already failed. The issue with a notice or warning is that you need a dummy return value and nobody will check the return value.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I was talking about the case where we would replacing a failing init call by a gettimeofday() call. In such a case, we must return SUCCESS, and throw a Notice.

Copy link
Copy Markdown
Member Author

@kelunik kelunik Feb 9, 2017

Choose a reason for hiding this comment

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

There is no such case. If it can't use a monotonic time but compiled fine, it will result in a fatal error. It won't fall back to gettimeofday().

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ok, I thought we were talking about a possible replacement by gtod() if failing , so we forget such an idea.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@weltling wanted to do that. I don't want to do that, as it breaks the guarantee of monotony and might jump, while hrtime() usually doesn't do that.

php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to initialize internal timer");
return FAILURE;
}

return SUCCESS;
}
/* }}} */

/* {{{ */
PHP_MSHUTDOWN_FUNCTION(hrtime)
{
return SUCCESS;
}
/* }}} */

/* {{{ proto float hrtime()
Returns a float containing the current high-resolution time in seconds
counted from an arbitrary point in time */
PHP_FUNCTION(hrtime)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}

uint64_t current_time = _timer_current();

if (UNEXPECTED(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 */
43 changes: 43 additions & 0 deletions ext/standard/hrtime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
+----------------------------------------------------------------------+
| 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$ */

#ifndef HRTIME_H
#define HRTIME_H

#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
#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

#if HRTIME_AVAILABLE
PHP_MINIT_FUNCTION(hrtime);
PHP_MSHUTDOWN_FUNCTION(hrtime);

PHP_FUNCTION(hrtime);
#endif /* HRTIME_AVAILABLE */

#endif /* HRTIME_H */
1 change: 1 addition & 0 deletions ext/standard/php_standard.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
22 changes: 22 additions & 0 deletions ext/standard/tests/hrtime/hrtime.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Test hrtime() aligns with microtime()
--FILE--
<?php

$hrtime = hrtime();
$microtime = microtime(1);

usleep(500000);

$hrdiff = hrtime() - $hrtime;
$microdiff = microtime(1) - $microtime;

if (abs($hrdiff - $microdiff) > 0.0001) {
print "fail";
} else {
print "OK";
}

?>
--EXPECT--
OK
10 changes: 10 additions & 0 deletions ext/standard/tests/hrtime/hrtime_error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
Test hrtime() errors
--FILE--
<?php

hrtime(true);

?>
--EXPECTF--
Warning: hrtime() expects exactly 0 parameters, 1 given in %s