Skip to content

Commit 6e9f69f

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 e9c8621 commit 6e9f69f

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
@@ -23,6 +23,18 @@ if test "$PHP_OPCACHE" != "no"; then
2323
dnl Always build as shared extension
2424
ext_shared=yes
2525

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

ext/opcache/zend_shared_alloc.c

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

58+
#ifdef HAVE_PTHREAD_PROCESS_SHARED
59+
60+
#include <pthread.h>
61+
62+
/**
63+
* This struct is allocated from shared memory and contains a
64+
* PTHREAD_PROCESS_SHARED mutex.
65+
*/
66+
struct opcache_locks {
67+
/**
68+
* The mutex for zend_shared_alloc_lock().
69+
*/
70+
pthread_mutex_t alloc;
71+
};
72+
73+
/**
74+
* Note, this variable should not be put in struct zend_accel_globals
75+
* because this is not a per-thread variable.
76+
*/
77+
static struct opcache_locks *opcache_locks;
78+
79+
#endif // HAVE_PTHREAD_PROCESS_SHARED
80+
5881
#ifndef ZEND_WIN32
5982
#ifdef ZTS
6083
static MUTEX_T zts_lock;
@@ -82,12 +105,24 @@ static const zend_shared_memory_handler_entry handler_table[] = {
82105
#ifndef ZEND_WIN32
83106
void zend_shared_alloc_create_lock(char *lockfile_path)
84107
{
85-
int val;
86-
87108
#ifdef ZTS
88109
zts_lock = tsrm_mutex_alloc();
89110
#endif
90111

112+
#ifdef HAVE_PTHREAD_PROCESS_SHARED
113+
opcache_locks = mmap(NULL, sizeof(*opcache_locks),
114+
PROT_READ|PROT_WRITE,
115+
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
116+
if (opcache_locks == MAP_FAILED) {
117+
zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Unable to create opcache_locks: %s (%d)", strerror(errno), errno);
118+
}
119+
120+
pthread_mutexattr_t mutexattr;
121+
pthread_mutexattr_init(&mutexattr);
122+
pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);
123+
pthread_mutex_init(&opcache_locks->alloc, &mutexattr);
124+
#endif // HAVE_PTHREAD_PROCESS_SHARED
125+
91126
#if defined(__linux__) && defined(HAVE_MEMFD_CREATE)
92127
/* on Linux, we can use a memfd instead of a "real" file, so
93128
* we can do this without a writable filesystem and without
@@ -108,7 +143,7 @@ void zend_shared_alloc_create_lock(char *lockfile_path)
108143

109144
fchmod(lock_file, 0666);
110145

111-
val = fcntl(lock_file, F_GETFD, 0);
146+
int val = fcntl(lock_file, F_GETFD, 0);
112147
val |= FD_CLOEXEC;
113148
fcntl(lock_file, F_SETFD, val);
114149

@@ -320,6 +355,12 @@ void zend_shared_alloc_shutdown(void)
320355
}
321356
ZSMMG(shared_segments) = NULL;
322357
g_shared_alloc_handler = NULL;
358+
359+
#ifdef HAVE_PTHREAD_PROCESS_SHARED
360+
pthread_mutex_destroy(&opcache_locks->alloc);
361+
munmap(opcache_locks, sizeof(opcache_locks));
362+
#endif
363+
323364
#ifndef ZEND_WIN32
324365
close(lock_file);
325366

@@ -471,18 +512,20 @@ void zend_shared_alloc_lock(void)
471512
{
472513
ZEND_ASSERT(!ZCG(locked));
473514

474-
#ifndef ZEND_WIN32
515+
#if !defined(ZEND_WIN32) && defined(ZTS)
516+
tsrm_mutex_lock(zts_lock);
517+
#endif
518+
519+
#ifdef HAVE_PTHREAD_PROCESS_SHARED
520+
pthread_mutex_lock(&opcache_locks->alloc);
521+
#elif !defined(ZEND_WIN32)
475522
struct flock mem_write_lock;
476523

477524
mem_write_lock.l_type = F_WRLCK;
478525
mem_write_lock.l_whence = SEEK_SET;
479526
mem_write_lock.l_start = 0;
480527
mem_write_lock.l_len = 1;
481528

482-
#ifdef ZTS
483-
tsrm_mutex_lock(zts_lock);
484-
#endif
485-
486529
#if 0
487530
/* this will happen once per process, and will un-globalize mem_write_lock */
488531
if (mem_write_lock.l_pid == -1) {
@@ -510,7 +553,7 @@ void zend_shared_alloc_unlock(void)
510553
{
511554
ZEND_ASSERT(ZCG(locked));
512555

513-
#ifndef ZEND_WIN32
556+
#if !defined(ZEND_WIN32) && !defined(HAVE_PTHREAD_PROCESS_SHARED)
514557
struct flock mem_write_unlock;
515558

516559
mem_write_unlock.l_type = F_UNLCK;
@@ -521,16 +564,19 @@ void zend_shared_alloc_unlock(void)
521564

522565
ZCG(locked) = 0;
523566

524-
#ifndef ZEND_WIN32
567+
#ifdef HAVE_PTHREAD_PROCESS_SHARED
568+
pthread_mutex_unlock(&opcache_locks->alloc);
569+
#elif !defined(ZEND_WIN32)
525570
if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) {
526571
zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
527572
}
528-
#ifdef ZTS
529-
tsrm_mutex_unlock(zts_lock);
530-
#endif
531573
#else
532574
zend_shared_alloc_unlock_win32();
533575
#endif
576+
577+
#if !defined(ZEND_WIN32) && defined(ZTS)
578+
tsrm_mutex_unlock(zts_lock);
579+
#endif
534580
}
535581

536582
void zend_shared_alloc_init_xlat_table(void)

0 commit comments

Comments
 (0)