source: trunk/xcache/xc_mutex.c @ 1366

Last change on this file since 1366 was 1366, checked in by moo, 15 months ago

fixes #323: refix locking impl for threaded env

  • Property svn:eol-style set to native
File size: 8.2 KB
RevLine 
[1366]1#include "xc_mutex.h"
[1027]2#include "xcache.h"
[1]3#include <stdio.h>
4#include <string.h>
[36]5#include <stdlib.h>
[1186]6#ifdef ZEND_WIN32
7#   include <process.h>
[1199]8#else
9#   include <unistd.h>
10#   include <fcntl.h>
11#   include <errno.h>
[1186]12#endif
[1]13
[1366]14/* {{{ detect what type of mutex is needed */
[1206]15#ifdef ZTS
[1366]16#   define XC_MUTEX_NEED_TS
[1206]17#endif
[1204]18
[1366]19#ifdef HAVE_FORK
20#   define XC_MUTEX_NEED_INTERPROCESS
[1204]21#endif
22
[1366]23#if defined(XC_MUTEX_NEED_TS) && defined(XC_MUTEX_NEED_INTERPROCESS)
24/* the check is not even needed for non-threaded env */
25#   define XC_MUTEX_HAVE_INTERPROCESS_SWITCH
[1204]26#endif
27/* }}} */
28
[1366]29/* {{{ detect which mutex is needed */
30#if defined(XC_MUTEX_NEED_TS) && defined(XC_MUTEX_NEED_INTERPROCESS)
[1245]31#   ifdef PTHREADS
[1366]32#       define XC_MUTEX_USE_PTHREAD
[1206]33#       ifndef _POSIX_THREAD_PROCESS_SHARED
[1366]34#           define XC_MUTEX_USE_FCNTL
[1206]35#       endif
36#   else
[1366]37#       define XC_MUTEX_USE_TSRM
38#       define XC_MUTEX_USE_FCNTL
[1206]39#   endif
[1366]40#elif defined(XC_MUTEX_NEED_TS)
41#   define XC_MUTEX_USE_TSRM
42#elif defined(XC_MUTEX_NEED_INTERPROCESS)
43#   define XC_MUTEX_USE_FCNTL
[1206]44#else
[1366]45#   define XC_MUTEX_USE_NOOP
[1206]46#endif
47/* }}} */
[1204]48
[1366]49/* {{{ fcntl mutex impl */
50#ifdef XC_MUTEX_USE_FCNTL
[1]51#ifndef ZEND_WIN32
52typedef int HANDLE;
[62]53#   ifndef INVALID_HANDLE_VALUE
54#       define INVALID_HANDLE_VALUE -1
55#   endif
[928]56#else
57#   define close(h) CloseHandle(h)
58#   define open(filename, mode, permission) CreateFile(filename, \
59        GENERIC_READ | GENERIC_WRITE, \
60        FILE_SHARE_READ | FILE_SHARE_WRITE, \
61        NULL, \
62        OPEN_ALWAYS, \
63        FILE_ATTRIBUTE_NORMAL, \
64        NULL)
[1]65#endif
66
[1199]67typedef struct {
[1]68    HANDLE fd;
[1363]69#ifdef __CYGWIN__
70    /* store the path for unlink() later */
[1]71    char *pathname;
[1363]72#endif
[1366]73} xc_fcntl_mutex_t;
[1]74
75#ifndef ZEND_WIN32
[11]76#   define LCK_WR F_WRLCK
77#   define LCK_RD F_RDLCK
78#   define LCK_UN F_UNLCK
79#   define LCK_NB 0
[1366]80static inline int dolock(xc_fcntl_mutex_t *fcntl_mutex, int type)
[1198]81{
[1]82    int ret;
83    struct flock lock;
84
85    lock.l_type = type;
86    lock.l_start = 0;
87    lock.l_whence = SEEK_SET;
88    lock.l_len = 1;
89    lock.l_pid = 0;
90
91    do {
[1366]92        ret = fcntl(fcntl_mutex->fd, F_SETLKW, &lock);
[1]93    } while (ret < 0 && errno == EINTR);
94    return ret;
95}
96#else
97
98#   include <win32/flock.h>
99#   include <io.h>
100#   include <fcntl.h>
101#   include <sys/types.h>
102#   include <sys/stat.h>
[928]103#   undef errno
104#   define errno GetLastError()
[11]105#   define getuid() 0
106#   define LCK_WR LOCKFILE_EXCLUSIVE_LOCK
107#   define LCK_RD 0
108#   define LCK_UN 0
109#   define LCK_NB LOCKFILE_FAIL_IMMEDIATELY
[1366]110static inline int dolock(xc_fcntl_mutex_t *fcntl_mutex, int type)
[1198]111{
[1]112    static OVERLAPPED offset = {0, 0, 0, 0, NULL};
113
114    if (type == LCK_UN) {
[1366]115        return UnlockFileEx(fcntl_mutex->fd, 0, 1, 0, &offset);
[1]116    }
117    else {
[1366]118        return LockFileEx(fcntl_mutex->fd, type, 0, 1, 0, &offset);
[1]119    }
120}
[1199]121#endif
[1]122/* }}} */
123
[1366]124static zend_bool xc_fcntl_init(xc_fcntl_mutex_t *fcntl_mutex, const char *pathname) /* {{{ */
[1]125{
126    HANDLE fd;
[62]127    int size;
128    char *myname;
[1]129
130    if (pathname == NULL) {
[1154]131        static int instanceId = 0;
[62]132        const char default_tmpdir[] = { DEFAULT_SLASH, 't', 'm', 'p', '\0' };
133        const char *tmpdir;
134
135        tmpdir = getenv("TEMP");
136        if (!tmpdir) {
137            tmpdir = getenv("TMP");
138            if (!tmpdir) {
139                tmpdir = default_tmpdir;
140            }
141        }
[1366]142        size = strlen(tmpdir) + sizeof("/.xcache.mutex") - 1 + 3 * 10 + 100;
[282]143        myname = malloc(size);
[1366]144        snprintf(myname, size - 1, "%s%c.xcache.%d.%d.%d.mutex", tmpdir, DEFAULT_SLASH, (int) getuid(), (int) getpid(), ++instanceId);
[1]145        pathname = myname;
146    }
[62]147    else {
148        myname = NULL;
149    }
[1]150
[928]151    fd = open(pathname, O_RDWR|O_CREAT, 0666);
[1]152
[62]153    if (fd != INVALID_HANDLE_VALUE) {
[1366]154        fcntl_mutex->fd = fd;
[1363]155#ifdef __CYGWIN__
[1]156        size = strlen(pathname) + 1;
[1366]157        fcntl_mutex->pathname = malloc(size);
158        memcpy(fcntl_mutex->pathname, pathname, size);
[1363]159#else
160        unlink(pathname);
161#endif
[1]162    }
163    else {
[928]164        zend_error(E_ERROR, "xc_fcntl_create: open(%s, O_RDWR|O_CREAT, 0666) failed:", pathname);
[1366]165        fcntl_mutex = NULL;
[1]166    }
[62]167
168    if (myname) {
[282]169        free(myname);
[62]170    }
171
[1366]172    return fcntl_mutex ? 1 : 0;
[1]173}
174/* }}} */
[1366]175static void xc_fcntl_destroy(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */
[1198]176{
[1366]177    close(fcntl_mutex->fd);
[1]178#ifdef __CYGWIN__
[1366]179    unlink(fcntl_mutex->pathname);
180    free(fcntl_mutex->pathname);
[1]181#endif
182}
183/* }}} */
[1366]184static void xc_fcntl_mutex(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */
[1198]185{
[1366]186    if (dolock(fcntl_mutex, LCK_WR) < 0) {
187        zend_error(E_ERROR, "xc_fcntl_mutex failed errno:%d", errno);
[1]188    }
189}
190/* }}} */
[1366]191static void xc_fcntl_rdlock(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */
[1198]192{
[1366]193    if (dolock(fcntl_mutex, LCK_RD) < 0) {
194        zend_error(E_ERROR, "xc_fcntl_mutex failed errno:%d", errno);
[1]195    }
196}
197/* }}} */
[1366]198static void xc_fcntl_unlock(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */
[1198]199{
[1366]200    if (dolock(fcntl_mutex, LCK_UN) < 0) {
[928]201        zend_error(E_ERROR, "xc_fcntl_unlock failed errno:%d", errno);
[1]202    }
203}
204/* }}} */
[1366]205#endif /* XC_MUTEX_USE_FCNTL */
[1199]206
[1366]207struct _xc_mutex_t {
208#ifdef XC_MUTEX_USE_NOOP
[1199]209    int dummy;
[1206]210#endif
211
[1366]212#ifdef XC_MUTEX_HAVE_INTERPROCESS_SWITCH
213    zend_bool want_inter_process;  /* whether the lock is created for inter process usage */
[1206]214#endif
[1366]215    zend_bool shared; /* shared, or locally allocated */
[1199]216
[1366]217#ifdef XC_MUTEX_USE_TSRM
218    MUTEX_T tsrm_mutex;
[1206]219#endif
220
[1366]221#ifdef XC_MUTEX_USE_PTHREAD
[1204]222    pthread_mutex_t pthread_mutex;
[1206]223#endif
224
[1366]225#ifdef XC_MUTEX_USE_FCNTL
226    xc_fcntl_mutex_t fcntl_mutex;
[1199]227#endif
[1204]228
229#ifndef NDEBUG
230    zend_bool locked;
231#endif
[1199]232};
233
[1366]234#ifdef XC_MUTEX_HAVE_INTERPROCESS_SWITCH
235#   define xc_want_inter_process() (mutex->want_inter_process)
236#elif defined(HAVE_FORK)
237#   define xc_want_inter_process() 1
[1206]238#else
[1366]239#   define xc_want_inter_process() 0
[1206]240#endif
241
[1366]242size_t xc_mutex_size(void) /* {{{ */
[1199]243{
[1366]244    return sizeof(xc_mutex_t);
[1204]245}
246/* }}} */
[1366]247xc_mutex_t *xc_mutex_init(xc_mutex_t *const shared_mutex, const char *pathname, unsigned char want_inter_process) /* {{{ */
[1204]248{
[1366]249    xc_mutex_t *mutex = NULL;
250
251#ifndef HAVE_FORK
252    want_inter_process = 0;
[1206]253#endif
254
[1366]255    /* if interprocessed is needed, shared_mutex is required to be a pre-allocated memory on shm
256     * this function can always return non-shared memory if necessary despite shared memory is given
257     */
258
259    /* when inter-process is wanted, pthread lives in shm */
260#ifdef XC_MUTEX_USE_PTHREAD
261    if (want_inter_process) {
262        assert(shared_mutex);
263        mutex = shared_mutex;
264        mutex->shared = 1;
265    }
266    else
267#endif
[1206]268    {
[1366]269        /* all other mutex assumed live locally */
270        mutex = calloc(1, sizeof(*mutex));
271        mutex->shared = 0;
272#ifdef XC_MUTEX_HAVE_INTERPROCESS_SWITCH
273        mutex->want_inter_process = want_inter_process;
274#endif
275    }
276
277
278#ifdef XC_MUTEX_USE_PTHREAD
279    {
280        /* If you see mutex leak using valgrind, see xc_mutex_destroy function */
[1206]281        pthread_mutexattr_t psharedm;
282        pthread_mutexattr_init(&psharedm);
[1366]283        pthread_mutexattr_setpshared(&psharedm, xc_want_inter_process() ? PTHREAD_PROCESS_PRIVATE : PTHREAD_PROCESS_SHARED);
284        pthread_mutex_init(&mutex->pthread_mutex, &psharedm);
[1206]285    }
286#endif
287
[1366]288#ifdef XC_MUTEX_USE_TSRM
289    mutex->tsrm_mutex = tsrm_mutex_alloc();
[1204]290#endif
291
[1366]292#ifdef XC_MUTEX_USE_FCNTL
293    if (xc_want_inter_process()) {
294        xc_fcntl_init(&mutex->fcntl_mutex, pathname);
[1206]295    }
[1204]296#endif
297
298#ifndef NDEBUG
[1366]299    mutex->locked = 0;
[1204]300#endif
301
[1366]302    return mutex;
[1199]303}
304/* }}} */
[1366]305void xc_mutex_destroy(xc_mutex_t *mutex) /* {{{ */
[1199]306{
[1366]307    assert(mutex);
[1206]308
[1366]309    /* intended to not destroy mutex when mutex is shared between process */
310    if (!mutex->shared) {
311#ifdef XC_MUTEX_USE_PTHREAD
312        pthread_mutex_destroy(&mutex->pthread_mutex);
[1204]313#endif
314
[1366]315#ifdef XC_MUTEX_USE_TSRM
316        tsrm_mutex_free(mutex->tsrm_mutex);
[1199]317#endif
[1206]318    }
319
[1366]320#ifdef XC_MUTEX_USE_FCNTL
321    if (xc_want_inter_process()) {
322        xc_fcntl_destroy(&mutex->fcntl_mutex);
[1199]323    }
[1206]324#endif
325
[1366]326    if (!mutex->shared) {
327        free(mutex);
[1206]328    }
[1199]329}
330/* }}} */
[1366]331void xc_mutex_lock(xc_mutex_t *mutex) /* {{{ */
[1199]332{
[1366]333#ifdef XC_MUTEX_USE_PTHREAD
334    if (pthread_mutex_lock(&mutex->pthread_mutex) < 0) {
335        zend_error(E_ERROR, "xc_mutex failed errno:%d", errno);
[1206]336    }
337#endif
338
[1366]339#ifdef XC_MUTEX_USE_TSRM
340    if (tsrm_mutex_lock(mutex->tsrm_mutex) < 0) {
341        zend_error(E_ERROR, "xc_mutex failed errno:%d", errno);
[1199]342    }
[1206]343#endif
344
[1366]345#ifdef XC_MUTEX_USE_FCNTL
346    if (xc_want_inter_process()) {
347        xc_fcntl_mutex(&mutex->fcntl_mutex);
[1206]348    }
[1199]349#endif
[1204]350
351#ifndef NDEBUG
[1366]352    assert(!mutex->locked);
353    mutex->locked = 1;
354    assert(mutex->locked);
[1204]355#endif
[1199]356}
357/* }}} */
[1366]358void xc_mutex_unlock(xc_mutex_t *mutex) /* {{{ */
[1199]359{
[1204]360#ifndef NDEBUG
[1366]361    assert(mutex->locked);
362    mutex->locked = 0;
363    assert(!mutex->locked);
[1204]364#endif
365
[1366]366#ifdef XC_MUTEX_USE_FCNTL
367    if (xc_want_inter_process()) {
368        xc_fcntl_unlock(&mutex->fcntl_mutex);
[1206]369    }
[1199]370#endif
[1206]371
[1366]372#ifdef XC_MUTEX_USE_TSRM
373    if (tsrm_mutex_unlock(mutex->tsrm_mutex) < 0) {
374        zend_error(E_ERROR, "xc_mutex_unlock failed errno:%d", errno);
[1199]375    }
[1206]376#endif
377
[1366]378#ifdef XC_MUTEX_USE_PTHREAD
379    if (pthread_mutex_unlock(&mutex->pthread_mutex) < 0) {
380        zend_error(E_ERROR, "xc_mutex_unlock failed errno:%d", errno);
[1206]381    }
382#endif
[1199]383}
384/* }}} */
Note: See TracBrowser for help on using the repository browser.