summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuel Dreyfus <manu@netbsd.org>2017-07-19 04:36:55 +0200
committerNikita Popov <nikita.ppv@gmail.com>2017-07-19 19:54:19 +0200
commitd25049cc1b74ae445d6521997a421a7462f1ad5b (patch)
tree04af1a5b08d8616857946a114e84f30883416d3a
parent6fcc7134f1d4aaf60e24202ed316c61636c5a95d (diff)
downloadphp-git-d25049cc1b74ae445d6521997a421a7462f1ad5b.tar.gz
Fixed bug #74851: Improve uniqid() performance
uniqid() relies on microsecond-precise system clock to produce an unique identifier. In order to avoid using the same value, the original implementation calls usleep(1) to wait for the next microsecond. Unfortunately, usleep() specification says "The suspension time may be longer than requested due to the scheduling of other activity by the system." Indeed, the pause may as as long as an entire execution slice, causing a uniqid() call to last more than 10 ms. This is fixed by replacing the usleep() call by time polling using gettimeofday() until the microsecond changes. Since the getttimeoday() system call lasts around a microsecond, only a small time is wasted calling multiple gettimeofday. On the benefit side, uniqid() performance in increased 10000 fold without changing its behavior.
-rw-r--r--NEWS4
-rw-r--r--ext/standard/uniqid.c29
2 files changed, 19 insertions, 14 deletions
diff --git a/NEWS b/NEWS
index 7ef1302edc..a1a9b4e334 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,10 @@ PHP NEWS
- SPL:
. Fixed bug #74669 (Unserialize ArrayIterator broken). (Andrew Nester)
+- Standard:
+ . Fixed bug #74851 (uniqid() without more_entropy performs badly).
+ (Emmanuel Dreyfus)
+
20 Jul 2017, PHP 7.2.0beta1
- Core:
diff --git a/ext/standard/uniqid.c b/ext/standard/uniqid.c
index 22173ae01d..e8f2f19520 100644
--- a/ext/standard/uniqid.c
+++ b/ext/standard/uniqid.c
@@ -38,17 +38,15 @@
#include "php_lcg.h"
#include "uniqid.h"
+#ifdef HAVE_GETTIMEOFDAY
+ZEND_TLS struct timeval prev_tv = { 0, 0 };
+
/* {{{ proto string uniqid([string prefix [, bool more_entropy]])
Generates a unique ID */
-#ifdef HAVE_GETTIMEOFDAY
PHP_FUNCTION(uniqid)
{
char *prefix = "";
-#if defined(__CYGWIN__)
- zend_bool more_entropy = 1;
-#else
zend_bool more_entropy = 0;
-#endif
zend_string *uniqid;
int sec, usec;
size_t prefix_len = 0;
@@ -60,17 +58,20 @@ PHP_FUNCTION(uniqid)
Z_PARAM_BOOL(more_entropy)
ZEND_PARSE_PARAMETERS_END();
-#if HAVE_USLEEP && !defined(PHP_WIN32)
if (!more_entropy) {
-#if defined(__CYGWIN__)
- php_error_docref(NULL, E_WARNING, "You must use 'more entropy' under CYGWIN");
- RETURN_FALSE;
-#else
- usleep(1);
-#endif
+ /* This implementation needs current microsecond to change,
+ * hence we poll time until it does. This is much faster than
+ * calling usleep(1) which may cause the kernel to schedule
+ * another process, causing a pause of around 10ms.
+ */
+ do {
+ (void)gettimeofday((struct timeval *) &tv, (struct timezone *) NULL);
+ } while (tv.tv_sec == prev_tv.tv_sec && tv.tv_usec == prev_tv.tv_usec);
+
+ prev_tv.tv_sec = tv.tv_sec;
+ prev_tv.tv_usec = tv.tv_usec;
}
-#endif
- gettimeofday((struct timeval *) &tv, (struct timezone *) NULL);
+
sec = (int) tv.tv_sec;
usec = (int) (tv.tv_usec % 0x100000);