XCache is a fast, stable PHP opcode cacher that has been proven and is now running on production servers under high load.
https://xcache.lighttpd.net/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
384 lines
8.2 KiB
384 lines
8.2 KiB
#include "xc_mutex.h" |
|
#include "xcache.h" |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <stdlib.h> |
|
#ifdef ZEND_WIN32 |
|
# include <process.h> |
|
#else |
|
# include <unistd.h> |
|
# include <fcntl.h> |
|
# include <errno.h> |
|
#endif |
|
|
|
/* {{{ detect what type of mutex is needed */ |
|
#ifdef ZTS |
|
# define XC_MUTEX_NEED_TS |
|
#endif |
|
|
|
#ifdef HAVE_FORK |
|
# define XC_MUTEX_NEED_INTERPROCESS |
|
#endif |
|
|
|
#if defined(XC_MUTEX_NEED_TS) && defined(XC_MUTEX_NEED_INTERPROCESS) |
|
/* the check is not even needed for non-threaded env */ |
|
# define XC_MUTEX_HAVE_INTERPROCESS_SWITCH |
|
#endif |
|
/* }}} */ |
|
|
|
/* {{{ detect which mutex is needed */ |
|
#if defined(XC_MUTEX_NEED_TS) && defined(XC_MUTEX_NEED_INTERPROCESS) |
|
# ifdef PTHREADS |
|
# define XC_MUTEX_USE_PTHREAD |
|
# ifndef _POSIX_THREAD_PROCESS_SHARED |
|
# define XC_MUTEX_USE_FCNTL |
|
# endif |
|
# else |
|
# define XC_MUTEX_USE_TSRM |
|
# define XC_MUTEX_USE_FCNTL |
|
# endif |
|
#elif defined(XC_MUTEX_NEED_TS) |
|
# define XC_MUTEX_USE_TSRM |
|
#elif defined(XC_MUTEX_NEED_INTERPROCESS) |
|
# define XC_MUTEX_USE_FCNTL |
|
#else |
|
# define XC_MUTEX_USE_NOOP |
|
#endif |
|
/* }}} */ |
|
|
|
/* {{{ fcntl mutex impl */ |
|
#ifdef XC_MUTEX_USE_FCNTL |
|
#ifndef ZEND_WIN32 |
|
typedef int HANDLE; |
|
# ifndef INVALID_HANDLE_VALUE |
|
# define INVALID_HANDLE_VALUE -1 |
|
# endif |
|
#else |
|
# define close(h) CloseHandle(h) |
|
# define open(filename, mode, permission) CreateFile(filename, \ |
|
GENERIC_READ | GENERIC_WRITE, \ |
|
FILE_SHARE_READ | FILE_SHARE_WRITE, \ |
|
NULL, \ |
|
OPEN_ALWAYS, \ |
|
FILE_ATTRIBUTE_NORMAL, \ |
|
NULL) |
|
#endif |
|
|
|
typedef struct { |
|
HANDLE fd; |
|
#ifdef __CYGWIN__ |
|
/* store the path for unlink() later */ |
|
char *pathname; |
|
#endif |
|
} xc_fcntl_mutex_t; |
|
|
|
#ifndef ZEND_WIN32 |
|
# define LCK_WR F_WRLCK |
|
# define LCK_RD F_RDLCK |
|
# define LCK_UN F_UNLCK |
|
# define LCK_NB 0 |
|
static inline int dolock(xc_fcntl_mutex_t *fcntl_mutex, int type) |
|
{ |
|
int ret; |
|
struct flock lock; |
|
|
|
lock.l_type = type; |
|
lock.l_start = 0; |
|
lock.l_whence = SEEK_SET; |
|
lock.l_len = 1; |
|
lock.l_pid = 0; |
|
|
|
do { |
|
ret = fcntl(fcntl_mutex->fd, F_SETLKW, &lock); |
|
} while (ret < 0 && errno == EINTR); |
|
return ret; |
|
} |
|
#else |
|
|
|
# include <win32/flock.h> |
|
# include <io.h> |
|
# include <fcntl.h> |
|
# include <sys/types.h> |
|
# include <sys/stat.h> |
|
# undef errno |
|
# define errno GetLastError() |
|
# define getuid() 0 |
|
# define LCK_WR LOCKFILE_EXCLUSIVE_LOCK |
|
# define LCK_RD 0 |
|
# define LCK_UN 0 |
|
# define LCK_NB LOCKFILE_FAIL_IMMEDIATELY |
|
static inline int dolock(xc_fcntl_mutex_t *fcntl_mutex, int type) |
|
{ |
|
static OVERLAPPED offset = {0, 0, 0, 0, NULL}; |
|
|
|
if (type == LCK_UN) { |
|
return UnlockFileEx(fcntl_mutex->fd, 0, 1, 0, &offset); |
|
} |
|
else { |
|
return LockFileEx(fcntl_mutex->fd, type, 0, 1, 0, &offset); |
|
} |
|
} |
|
#endif |
|
/* }}} */ |
|
|
|
static zend_bool xc_fcntl_init(xc_fcntl_mutex_t *fcntl_mutex, const char *pathname) /* {{{ */ |
|
{ |
|
HANDLE fd; |
|
int size; |
|
char *myname; |
|
|
|
if (pathname == NULL) { |
|
static int instanceId = 0; |
|
const char default_tmpdir[] = { DEFAULT_SLASH, 't', 'm', 'p', '\0' }; |
|
const char *tmpdir; |
|
|
|
tmpdir = getenv("TEMP"); |
|
if (!tmpdir) { |
|
tmpdir = getenv("TMP"); |
|
if (!tmpdir) { |
|
tmpdir = default_tmpdir; |
|
} |
|
} |
|
size = strlen(tmpdir) + sizeof("/.xcache.mutex") - 1 + 3 * 10 + 100; |
|
myname = malloc(size); |
|
snprintf(myname, size - 1, "%s%c.xcache.%d.%d.%d.mutex", tmpdir, DEFAULT_SLASH, (int) getuid(), (int) getpid(), ++instanceId); |
|
pathname = myname; |
|
} |
|
else { |
|
myname = NULL; |
|
} |
|
|
|
fd = open(pathname, O_RDWR|O_CREAT, 0666); |
|
|
|
if (fd != INVALID_HANDLE_VALUE) { |
|
fcntl_mutex->fd = fd; |
|
#ifdef __CYGWIN__ |
|
size = strlen(pathname) + 1; |
|
fcntl_mutex->pathname = malloc(size); |
|
memcpy(fcntl_mutex->pathname, pathname, size); |
|
#else |
|
unlink(pathname); |
|
#endif |
|
} |
|
else { |
|
zend_error(E_ERROR, "xc_fcntl_create: open(%s, O_RDWR|O_CREAT, 0666) failed:", pathname); |
|
fcntl_mutex = NULL; |
|
} |
|
|
|
if (myname) { |
|
free(myname); |
|
} |
|
|
|
return fcntl_mutex ? 1 : 0; |
|
} |
|
/* }}} */ |
|
static void xc_fcntl_destroy(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */ |
|
{ |
|
close(fcntl_mutex->fd); |
|
#ifdef __CYGWIN__ |
|
unlink(fcntl_mutex->pathname); |
|
free(fcntl_mutex->pathname); |
|
#endif |
|
} |
|
/* }}} */ |
|
static void xc_fcntl_mutex(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */ |
|
{ |
|
if (dolock(fcntl_mutex, LCK_WR) < 0) { |
|
zend_error(E_ERROR, "xc_fcntl_mutex failed errno:%d", errno); |
|
} |
|
} |
|
/* }}} */ |
|
static void xc_fcntl_rdlock(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */ |
|
{ |
|
if (dolock(fcntl_mutex, LCK_RD) < 0) { |
|
zend_error(E_ERROR, "xc_fcntl_mutex failed errno:%d", errno); |
|
} |
|
} |
|
/* }}} */ |
|
static void xc_fcntl_unlock(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */ |
|
{ |
|
if (dolock(fcntl_mutex, LCK_UN) < 0) { |
|
zend_error(E_ERROR, "xc_fcntl_unlock failed errno:%d", errno); |
|
} |
|
} |
|
/* }}} */ |
|
#endif /* XC_MUTEX_USE_FCNTL */ |
|
|
|
struct _xc_mutex_t { |
|
#ifdef XC_MUTEX_USE_NOOP |
|
int dummy; |
|
#endif |
|
|
|
#ifdef XC_MUTEX_HAVE_INTERPROCESS_SWITCH |
|
zend_bool want_inter_process; /* whether the lock is created for inter process usage */ |
|
#endif |
|
zend_bool shared; /* shared, or locally allocated */ |
|
|
|
#ifdef XC_MUTEX_USE_TSRM |
|
MUTEX_T tsrm_mutex; |
|
#endif |
|
|
|
#ifdef XC_MUTEX_USE_PTHREAD |
|
pthread_mutex_t pthread_mutex; |
|
#endif |
|
|
|
#ifdef XC_MUTEX_USE_FCNTL |
|
xc_fcntl_mutex_t fcntl_mutex; |
|
#endif |
|
|
|
#ifndef NDEBUG |
|
zend_bool locked; |
|
#endif |
|
}; |
|
|
|
#ifdef XC_MUTEX_HAVE_INTERPROCESS_SWITCH |
|
# define xc_want_inter_process() (mutex->want_inter_process) |
|
#elif defined(HAVE_FORK) |
|
# define xc_want_inter_process() 1 |
|
#else |
|
# define xc_want_inter_process() 0 |
|
#endif |
|
|
|
size_t xc_mutex_size(void) /* {{{ */ |
|
{ |
|
return sizeof(xc_mutex_t); |
|
} |
|
/* }}} */ |
|
xc_mutex_t *xc_mutex_init(xc_mutex_t *const shared_mutex, const char *pathname, unsigned char want_inter_process) /* {{{ */ |
|
{ |
|
xc_mutex_t *mutex = NULL; |
|
|
|
#ifndef HAVE_FORK |
|
want_inter_process = 0; |
|
#endif |
|
|
|
/* if interprocessed is needed, shared_mutex is required to be a pre-allocated memory on shm |
|
* this function can always return non-shared memory if necessary despite shared memory is given |
|
*/ |
|
|
|
/* when inter-process is wanted, pthread lives in shm */ |
|
#ifdef XC_MUTEX_USE_PTHREAD |
|
if (want_inter_process) { |
|
assert(shared_mutex); |
|
mutex = shared_mutex; |
|
mutex->shared = 1; |
|
} |
|
else |
|
#endif |
|
{ |
|
/* all other mutex assumed live locally */ |
|
mutex = calloc(1, sizeof(*mutex)); |
|
mutex->shared = 0; |
|
#ifdef XC_MUTEX_HAVE_INTERPROCESS_SWITCH |
|
mutex->want_inter_process = want_inter_process; |
|
#endif |
|
} |
|
|
|
|
|
#ifdef XC_MUTEX_USE_PTHREAD |
|
{ |
|
/* If you see mutex leak using valgrind, see xc_mutex_destroy function */ |
|
pthread_mutexattr_t psharedm; |
|
pthread_mutexattr_init(&psharedm); |
|
pthread_mutexattr_setpshared(&psharedm, xc_want_inter_process() ? PTHREAD_PROCESS_PRIVATE : PTHREAD_PROCESS_SHARED); |
|
pthread_mutex_init(&mutex->pthread_mutex, &psharedm); |
|
} |
|
#endif |
|
|
|
#ifdef XC_MUTEX_USE_TSRM |
|
mutex->tsrm_mutex = tsrm_mutex_alloc(); |
|
#endif |
|
|
|
#ifdef XC_MUTEX_USE_FCNTL |
|
if (xc_want_inter_process()) { |
|
xc_fcntl_init(&mutex->fcntl_mutex, pathname); |
|
} |
|
#endif |
|
|
|
#ifndef NDEBUG |
|
mutex->locked = 0; |
|
#endif |
|
|
|
return mutex; |
|
} |
|
/* }}} */ |
|
void xc_mutex_destroy(xc_mutex_t *mutex) /* {{{ */ |
|
{ |
|
assert(mutex); |
|
|
|
/* intended to not destroy mutex when mutex is shared between process */ |
|
if (!mutex->shared) { |
|
#ifdef XC_MUTEX_USE_PTHREAD |
|
pthread_mutex_destroy(&mutex->pthread_mutex); |
|
#endif |
|
|
|
#ifdef XC_MUTEX_USE_TSRM |
|
tsrm_mutex_free(mutex->tsrm_mutex); |
|
#endif |
|
} |
|
|
|
#ifdef XC_MUTEX_USE_FCNTL |
|
if (xc_want_inter_process()) { |
|
xc_fcntl_destroy(&mutex->fcntl_mutex); |
|
} |
|
#endif |
|
|
|
if (!mutex->shared) { |
|
free(mutex); |
|
} |
|
} |
|
/* }}} */ |
|
void xc_mutex_lock(xc_mutex_t *mutex) /* {{{ */ |
|
{ |
|
#ifdef XC_MUTEX_USE_PTHREAD |
|
if (pthread_mutex_lock(&mutex->pthread_mutex) < 0) { |
|
zend_error(E_ERROR, "xc_mutex failed errno:%d", errno); |
|
} |
|
#endif |
|
|
|
#ifdef XC_MUTEX_USE_TSRM |
|
if (tsrm_mutex_lock(mutex->tsrm_mutex) < 0) { |
|
zend_error(E_ERROR, "xc_mutex failed errno:%d", errno); |
|
} |
|
#endif |
|
|
|
#ifdef XC_MUTEX_USE_FCNTL |
|
if (xc_want_inter_process()) { |
|
xc_fcntl_mutex(&mutex->fcntl_mutex); |
|
} |
|
#endif |
|
|
|
#ifndef NDEBUG |
|
assert(!mutex->locked); |
|
mutex->locked = 1; |
|
assert(mutex->locked); |
|
#endif |
|
} |
|
/* }}} */ |
|
void xc_mutex_unlock(xc_mutex_t *mutex) /* {{{ */ |
|
{ |
|
#ifndef NDEBUG |
|
assert(mutex->locked); |
|
mutex->locked = 0; |
|
assert(!mutex->locked); |
|
#endif |
|
|
|
#ifdef XC_MUTEX_USE_FCNTL |
|
if (xc_want_inter_process()) { |
|
xc_fcntl_unlock(&mutex->fcntl_mutex); |
|
} |
|
#endif |
|
|
|
#ifdef XC_MUTEX_USE_TSRM |
|
if (tsrm_mutex_unlock(mutex->tsrm_mutex) < 0) { |
|
zend_error(E_ERROR, "xc_mutex_unlock failed errno:%d", errno); |
|
} |
|
#endif |
|
|
|
#ifdef XC_MUTEX_USE_PTHREAD |
|
if (pthread_mutex_unlock(&mutex->pthread_mutex) < 0) { |
|
zend_error(E_ERROR, "xc_mutex_unlock failed errno:%d", errno); |
|
} |
|
#endif |
|
} |
|
/* }}} */
|
|
|