From e2ed7e6716f9564fcd8b13b823519baaa7256662 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Tue, 16 Jul 2019 21:14:57 +0200 Subject: Allow multiple cache instances per user/host on Windows Formerly, there was at most a single OPcache instance per user and the so called system ID (which is determined from the PHP version). Sometimes multiple OPcaches might be desired, though, particularly for unrelated CLI scripts, which may even be necessary (e.g. for our test suite in parallel mode). We therefore introduce a new INI directive `opcache.cache_id` which allows to configure independent OPcache instances for the same user. We also use `GetUserNameW()` instead of `php_win32_get_username()`, because the latter retrieves the user name encoded in the `default_charset`, which can obviously yield different results for different charsets, leading to OPcache "incompatibilities". Slightly worse, some characters may not even be encodeable in the `default_charset` and would be replaced by question marks, which could result in different users sharing the same OPcache. We also refactor, and re-use existing APIs to avoid duplicated code. --- .appveyor.yml | 2 +- ext/opcache/ZendAccelerator.c | 45 ++++++++++++++++++++++++++--------- ext/opcache/ZendAccelerator.h | 6 +++++ ext/opcache/shared_alloc_win32.c | 23 ++++-------------- ext/opcache/zend_accelerator_module.c | 6 +++++ ext/opcache/zend_file_cache.c | 21 +--------------- php.ini-development | 4 ++++ php.ini-production | 4 ++++ run-tests.php | 2 +- 9 files changed, 62 insertions(+), 51 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 909a9ad65a..ab888c3493 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -33,7 +33,7 @@ environment: PARALLEL: -j2 - THREAD_SAFE: 1 OPCACHE: 1 - PARALLEL: + PARALLEL: -j2 INTRINSICS: AVX services: diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 6fe08d6faa..0c3109ee50 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -45,6 +45,7 @@ #include "zend_file_cache.h" #include "ext/pcre/php_pcre.h" #include "ext/standard/md5.h" +#include "ext/hash/php_hash.h" #ifndef ZEND_WIN32 #include @@ -54,6 +55,7 @@ typedef int uid_t; typedef int gid_t; #include +#include #endif #ifndef ZEND_WIN32 @@ -110,6 +112,9 @@ zend_accel_shared_globals *accel_shared_globals = NULL; /* true globals, no need for thread safety */ char accel_system_id[32]; +#ifdef ZEND_WIN32 +char accel_uname_id[32]; +#endif zend_bool accel_startup_ok = 0; static char *zps_failure_reason = NULL; char *zps_api_failure_reason = NULL; @@ -2180,6 +2185,26 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) return zend_accel_load_script(persistent_script, from_shared_memory); } +#ifdef ZEND_WIN32 +static int accel_gen_uname_id(void) +{ + PHP_MD5_CTX ctx; + unsigned char digest[16]; + wchar_t uname[UNLEN + 1]; + DWORD unsize = UNLEN; + + if (!GetUserNameW(uname, &unsize)) { + return FAILURE; + } + PHP_MD5Init(&ctx); + PHP_MD5Update(&ctx, (void *) uname, (unsize - 1) * sizeof(wchar_t)); + PHP_MD5Update(&ctx, ZCG(accel_directives).cache_id, strlen(ZCG(accel_directives).cache_id)); + PHP_MD5Final(digest, &ctx); + php_hash_bin2hex(accel_uname_id, digest, sizeof digest); + return SUCCESS; +} +#endif + /* zend_stream_open_function() replacement for PHP 5.3 and above */ static int persistent_stream_open_function(const char *filename, zend_file_handle *handle) { @@ -2609,9 +2634,7 @@ static void accel_globals_ctor(zend_accel_globals *accel_globals) static void accel_gen_system_id(void) { PHP_MD5_CTX context; - unsigned char digest[16], c; - char *md5str = accel_system_id; - int i; + unsigned char digest[16]; PHP_MD5Init(&context); PHP_MD5Update(&context, PHP_VERSION, sizeof(PHP_VERSION)-1); @@ -2623,14 +2646,7 @@ static void accel_gen_system_id(void) PHP_MD5Update(&context, __TIME__, sizeof(__TIME__)-1); } PHP_MD5Final(digest, &context); - for (i = 0; i < 16; i++) { - c = digest[i] >> 4; - c = (c <= 9) ? c + '0' : c - 10 + 'a'; - md5str[i * 2] = c; - c = digest[i] & 0x0f; - c = (c <= 9) ? c + '0' : c - 10 + 'a'; - md5str[(i * 2) + 1] = c; - } + php_hash_bin2hex(accel_system_id, digest, sizeof digest); } #ifdef HAVE_HUGE_CODE_PAGES @@ -2821,6 +2837,13 @@ static int accel_startup(zend_extension *extension) return FAILURE; } +#ifdef ZEND_WIN32 + if (UNEXPECTED(accel_gen_uname_id() == FAILURE)) { + zps_startup_failure("Unable to get user name", NULL, accelerator_remove_cb); + return SUCCESS; + } +#endif + #ifdef HAVE_HUGE_CODE_PAGES if (ZCG(accel_directives).huge_code_pages && (strcmp(sapi_module.name, "cli") == 0 || diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index a0df1c148a..20ac84583d 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -185,6 +185,9 @@ typedef struct _zend_accel_directives { zend_bool huge_code_pages; #endif char *preload; +#ifdef ZEND_WIN32 + char *cache_id; +#endif } zend_accel_directives; typedef struct _zend_accel_globals { @@ -270,6 +273,9 @@ typedef struct _zend_accel_shared_globals { } zend_accel_shared_globals; extern char accel_system_id[32]; +#ifdef ZEND_WIN32 +extern char accel_uname_id[32]; +#endif extern zend_bool accel_startup_ok; extern zend_bool file_cache_only; #if ENABLE_FILE_CACHE_FALLBACK diff --git a/ext/opcache/shared_alloc_win32.c b/ext/opcache/shared_alloc_win32.c index 2d0cea1e97..35abe367cf 100644 --- a/ext/opcache/shared_alloc_win32.c +++ b/ext/opcache/shared_alloc_win32.c @@ -19,6 +19,7 @@ +----------------------------------------------------------------------+ */ +#include "php.h" #include "ZendAccelerator.h" #include "zend_shared_alloc.h" #include "zend_accelerator_util_funcs.h" @@ -67,38 +68,24 @@ static void zend_win_error_message(int type, char *msg, int err) static char *create_name_with_username(char *name) { - static char newname[MAXPATHLEN + UNLEN + 4 + 1 + 32]; - char *uname; - - uname = php_win32_get_username(); - if (!uname) { - return NULL; - } - snprintf(newname, sizeof(newname) - 1, "%s@%s@%.32s", name, uname, accel_system_id); - - free(uname); + static char newname[MAXPATHLEN + 32 + 4 + 1 + 32]; + snprintf(newname, sizeof(newname) - 1, "%s@%.32s@%.32s", name, accel_uname_id, accel_system_id); return newname; } static char *get_mmap_base_file(void) { - static char windir[MAXPATHLEN+UNLEN + 3 + sizeof("\\\\@") + 1 + 32]; - char *uname; + static char windir[MAXPATHLEN+ 32 + 3 + sizeof("\\\\@") + 1 + 32]; int l; - uname = php_win32_get_username(); - if (!uname) { - return NULL; - } GetTempPath(MAXPATHLEN, windir); l = strlen(windir); if ('\\' == windir[l-1]) { l--; } - snprintf(windir + l, sizeof(windir) - l - 1, "\\%s@%s@%.32s", ACCEL_FILEMAP_BASE, uname, accel_system_id); - free(uname); + snprintf(windir + l, sizeof(windir) - l - 1, "\\%s@%.32s@%.32s", ACCEL_FILEMAP_BASE, accel_uname_id, accel_system_id); return windir; } diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index bcbc927703..25f3cc5b81 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -321,6 +321,9 @@ ZEND_INI_BEGIN() STD_PHP_INI_BOOLEAN("opcache.huge_code_pages" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.huge_code_pages, zend_accel_globals, accel_globals) #endif STD_PHP_INI_ENTRY("opcache.preload" , "" , PHP_INI_SYSTEM, OnUpdateStringUnempty, accel_directives.preload, zend_accel_globals, accel_globals) +#if ZEND_WIN32 + STD_PHP_INI_ENTRY("opcache.cache_id" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.cache_id, zend_accel_globals, accel_globals) +#endif ZEND_INI_END() static int filename_is_in_cache(zend_string *filename) @@ -768,6 +771,9 @@ static ZEND_FUNCTION(opcache_get_configuration) add_assoc_bool(&directives, "opcache.huge_code_pages", ZCG(accel_directives).huge_code_pages); #endif add_assoc_string(&directives, "opcache.preload", STRING_NOT_NULL(ZCG(accel_directives).preload)); +#if ZEND_WIN32 + add_assoc_string(&directives, "opcache.cache_id", STRING_NOT_NULL(ZCG(accel_directives).cache_id)); +#endif add_assoc_zval(return_value, "directives", &directives); diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 2c6c16002e..b5bab36e4b 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -835,31 +835,13 @@ static char *zend_file_cache_get_bin_file_path(zend_string *script_path) memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path)); memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX)); #else - PHP_MD5_CTX ctx; - char md5uname[32]; - unsigned char digest[16], c; - size_t i; - char *uname = php_win32_get_username(); - - PHP_MD5Init(&ctx); - PHP_MD5Update(&ctx, uname, strlen(uname)); - PHP_MD5Final(digest, &ctx); - for (i = 0; i < 16; i++) { - c = digest[i] >> 4; - c = (c <= 9) ? c + '0' : c - 10 + 'a'; - md5uname[i * 2] = c; - c = digest[i] & 0x0f; - c = (c <= 9) ? c + '0' : c - 10 + 'a'; - md5uname[(i * 2) + 1] = c; - } - len = strlen(ZCG(accel_directives).file_cache); filename = emalloc(len + 33 + 33 + ZSTR_LEN(script_path) + sizeof(SUFFIX)); memcpy(filename, ZCG(accel_directives).file_cache, len); filename[len] = '\\'; - memcpy(filename + 1 + len, md5uname, 32); + memcpy(filename + 1 + len, accel_uname_id, 32); len += 1 + 32; filename[len] = '\\'; @@ -889,7 +871,6 @@ static char *zend_file_cache_get_bin_file_path(zend_string *script_path) memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path)); memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX)); } - free(uname); #endif return filename; diff --git a/php.ini-development b/php.ini-development index eb30bd389e..fd96f43534 100644 --- a/php.ini-development +++ b/php.ini-development @@ -1851,6 +1851,10 @@ ldap.max_links = -1 ; errors. ;opcache.mmap_base= +; Facilitates multiple OPcache instances per user (for Windows only). All PHP +; processes with the same cache ID and user share an OPcache instance. +;opcache.cache_id= + ; Enables and sets the second level cache directory. ; It should improve performance when SHM memory is full, at server restart or ; SHM reset. The default "" disables file based caching. diff --git a/php.ini-production b/php.ini-production index 229846698d..8883566156 100644 --- a/php.ini-production +++ b/php.ini-production @@ -1858,6 +1858,10 @@ ldap.max_links = -1 ; errors. ;opcache.mmap_base= +; Facilitates multiple OPcache instances per user (for Windows only). All PHP +; processes with the same cache ID and user share an OPcache instance. +;opcache.cache_id= + ; Enables and sets the second level cache directory. ; It should improve performance when SHM memory is full, at server restart or ; SHM reset. The default "" disables file based caching. diff --git a/run-tests.php b/run-tests.php index f54dc2d37a..58e8fc1dba 100755 --- a/run-tests.php +++ b/run-tests.php @@ -2076,7 +2076,7 @@ TEST $file } // Default ini settings - $ini_settings = array(); + $ini_settings = $workerID ? array('opcache.cache_id' => "worker$workerID") : array(); // Additional required extensions if (array_key_exists('EXTENSIONS', $section_text)) { -- cgit v1.2.1