Skip to content

Commit 71968c5

Browse files
committed
ext/opcache: use pthread_mutex_t instead of a fcntl(F_SETLK)
pthread mutexes are lighter than fcntl(F_SETLK) because non-contending operations can avoid the system call. Note that this commit preserves the "lock_file" variable because it is used by ZendAccelerator.c - now that #10276 is merged, we may consider removing it.
1 parent 061fcdb commit 71968c5

File tree

2 files changed

+71
-13
lines changed

2 files changed

+71
-13
lines changed

ext/opcache/config.m4

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ if test "$PHP_OPCACHE" != "no"; then
2525

2626
AC_CHECK_HEADERS([stdatomic.h])
2727

28+
if test "$pthreads_working" = "yes"; then
29+
AC_CHECK_FUNC(pthread_mutexattr_setpshared, [
30+
AC_DEFINE(HAVE_PTHREAD_PROCESS_SHARED, 1, [Define to use pthread_mutexattr_setpshared(PTHREAD_PROCESS_SHARED)])
31+
if test -n "$ac_cv_pthreads_lib"; then
32+
PHP_EVAL_LIBLINE(-l$ac_cv_pthreads_lib, OPCACHE_SHARED_LIBADD)
33+
fi
34+
if test -n "$ac_cv_pthreads_cflags"; then
35+
PHP_EVAL_INCLINE($ac_cv_pthreads_cflags)
36+
fi
37+
], [])
38+
fi
39+
2840
if test "$PHP_HUGE_CODE_PAGES" = "yes"; then
2941
AC_DEFINE(HAVE_HUGE_CODE_PAGES, 1, [Define to enable copying PHP CODE pages into HUGE PAGES (experimental)])
3042
fi

ext/opcache/zend_shared_alloc.c

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,29 @@ static const char *g_shared_model;
4848
/* pointer to globals allocated in SHM and shared across processes */
4949
zend_smm_shared_globals *smm_shared_globals;
5050

51+
#ifdef HAVE_PTHREAD_PROCESS_SHARED
52+
53+
#include <pthread.h>
54+
55+
/**
56+
* This struct is allocated from shared memory and contains a
57+
* PTHREAD_PROCESS_SHARED mutex.
58+
*/
59+
struct opcache_locks {
60+
/**
61+
* The mutex for zend_shared_alloc_lock().
62+
*/
63+
pthread_mutex_t alloc;
64+
};
65+
66+
/**
67+
* Note, this variable should not be put in struct zend_accel_globals
68+
* because this is not a per-thread variable.
69+
*/
70+
static struct opcache_locks *opcache_locks;
71+
72+
#endif // HAVE_PTHREAD_PROCESS_SHARED
73+
5174
#ifndef ZEND_WIN32
5275
#ifdef ZTS
5376
static MUTEX_T zts_lock;
@@ -75,12 +98,24 @@ static const zend_shared_memory_handler_entry handler_table[] = {
7598
#ifndef ZEND_WIN32
7699
void zend_shared_alloc_create_lock(char *lockfile_path)
77100
{
78-
int val;
79-
80101
#ifdef ZTS
81102
zts_lock = tsrm_mutex_alloc();
82103
#endif
83104

105+
#ifdef HAVE_PTHREAD_PROCESS_SHARED
106+
opcache_locks = mmap(NULL, sizeof(*opcache_locks),
107+
PROT_READ|PROT_WRITE,
108+
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
109+
if (opcache_locks == MAP_FAILED) {
110+
zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Unable to create opcache_locks: %s (%d)", strerror(errno), errno);
111+
}
112+
113+
pthread_mutexattr_t mutexattr;
114+
pthread_mutexattr_init(&mutexattr);
115+
pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
116+
pthread_mutex_init(&opcache_locks->alloc, &mutexattr);
117+
#endif // HAVE_PTHREAD_PROCESS_SHARED
118+
84119
snprintf(lockfile_name, sizeof(lockfile_name), "%s/%sXXXXXX", lockfile_path, SEM_FILENAME_PREFIX);
85120
lock_file = mkstemp(lockfile_name);
86121
if (lock_file == -1) {
@@ -89,7 +124,7 @@ void zend_shared_alloc_create_lock(char *lockfile_path)
89124

90125
fchmod(lock_file, 0666);
91126

92-
val = fcntl(lock_file, F_GETFD, 0);
127+
int val = fcntl(lock_file, F_GETFD, 0);
93128
val |= FD_CLOEXEC;
94129
fcntl(lock_file, F_SETFD, val);
95130

@@ -301,6 +336,12 @@ void zend_shared_alloc_shutdown(void)
301336
}
302337
ZSMMG(shared_segments) = NULL;
303338
g_shared_alloc_handler = NULL;
339+
340+
#ifdef HAVE_PTHREAD_PROCESS_SHARED
341+
pthread_mutex_destroy(&opcache_locks->alloc);
342+
munmap(opcache_locks, sizeof(opcache_locks));
343+
#endif
344+
304345
#ifndef ZEND_WIN32
305346
close(lock_file);
306347

@@ -452,18 +493,20 @@ void zend_shared_alloc_lock(void)
452493
{
453494
ZEND_ASSERT(!ZCG(locked));
454495

455-
#ifndef ZEND_WIN32
496+
#if !defined(ZEND_WIN32) && defined(ZTS)
497+
tsrm_mutex_lock(zts_lock);
498+
#endif
499+
500+
#ifdef HAVE_PTHREAD_PROCESS_SHARED
501+
pthread_mutex_lock(&opcache_locks->alloc);
502+
#elif !defined(ZEND_WIN32)
456503
struct flock mem_write_lock;
457504

458505
mem_write_lock.l_type = F_WRLCK;
459506
mem_write_lock.l_whence = SEEK_SET;
460507
mem_write_lock.l_start = 0;
461508
mem_write_lock.l_len = 1;
462509

463-
#ifdef ZTS
464-
tsrm_mutex_lock(zts_lock);
465-
#endif
466-
467510
#if 0
468511
/* this will happen once per process, and will un-globalize mem_write_lock */
469512
if (mem_write_lock.l_pid == -1) {
@@ -491,7 +534,7 @@ void zend_shared_alloc_unlock(void)
491534
{
492535
ZEND_ASSERT(ZCG(locked));
493536

494-
#ifndef ZEND_WIN32
537+
#if !defined(ZEND_WIN32) && !defined(HAVE_PTHREAD_PROCESS_SHARED)
495538
struct flock mem_write_unlock;
496539

497540
mem_write_unlock.l_type = F_UNLCK;
@@ -502,16 +545,19 @@ void zend_shared_alloc_unlock(void)
502545

503546
ZCG(locked) = 0;
504547

505-
#ifndef ZEND_WIN32
548+
#ifdef HAVE_PTHREAD_PROCESS_SHARED
549+
pthread_mutex_unlock(&opcache_locks->alloc);
550+
#elif !defined(ZEND_WIN32)
506551
if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) {
507552
zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
508553
}
509-
#ifdef ZTS
510-
tsrm_mutex_unlock(zts_lock);
511-
#endif
512554
#else
513555
zend_shared_alloc_unlock_win32();
514556
#endif
557+
558+
#if !defined(ZEND_WIN32) && defined(ZTS)
559+
tsrm_mutex_unlock(zts_lock);
560+
#endif
515561
}
516562

517563
void zend_shared_alloc_init_xlat_table(void)

0 commit comments

Comments
 (0)