source: trunk/xcache/xc_mutex.c

Last change on this file was 1565, checked in by moo, 5 weeks ago

kill some warnings

  • 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;
[1565]250    (void) want_inter_process; /* may be unused */
[1366]251
252#ifndef HAVE_FORK
253    want_inter_process = 0;
[1206]254#endif
255
[1366]256    /* if interprocessed is needed, shared_mutex is required to be a pre-allocated memory on shm
257     * this function can always return non-shared memory if necessary despite shared memory is given
258     */
259
260    /* when inter-process is wanted, pthread lives in shm */
261#ifdef XC_MUTEX_USE_PTHREAD
262    if (want_inter_process) {
263        assert(shared_mutex);
264        mutex = shared_mutex;
265        mutex->shared = 1;
266    }
267    else
268#endif
[1206]269    {
[1366]270        /* all other mutex assumed live locally */
271        mutex = calloc(1, sizeof(*mutex));
272        mutex->shared = 0;
273#ifdef XC_MUTEX_HAVE_INTERPROCESS_SWITCH
274        mutex->want_inter_process = want_inter_process;
275#endif
276    }
277
278
279#ifdef XC_MUTEX_USE_PTHREAD
280    {
281        /* If you see mutex leak using valgrind, see xc_mutex_destroy function */
[1206]282        pthread_mutexattr_t psharedm;
283        pthread_mutexattr_init(&psharedm);
[1366]284        pthread_mutexattr_setpshared(&psharedm, xc_want_inter_process() ? PTHREAD_PROCESS_PRIVATE : PTHREAD_PROCESS_SHARED);
285        pthread_mutex_init(&mutex->pthread_mutex, &psharedm);
[1206]286    }
287#endif
288
[1366]289#ifdef XC_MUTEX_USE_TSRM
290    mutex->tsrm_mutex = tsrm_mutex_alloc();
[1204]291#endif
292
[1366]293#ifdef XC_MUTEX_USE_FCNTL
294    if (xc_want_inter_process()) {
295        xc_fcntl_init(&mutex->fcntl_mutex, pathname);
[1206]296    }
[1204]297#endif
298
299#ifndef NDEBUG
[1366]300    mutex->locked = 0;
[1204]301#endif
302
[1366]303    return mutex;
[1199]304}
305/* }}} */
[1366]306void xc_mutex_destroy(xc_mutex_t *mutex) /* {{{ */
[1199]307{
[1366]308    assert(mutex);
[1206]309
[1366]310    /* intended to not destroy mutex when mutex is shared between process */
311    if (!mutex->shared) {
312#ifdef XC_MUTEX_USE_PTHREAD
313        pthread_mutex_destroy(&mutex->pthread_mutex);
[1204]314#endif
315
[1366]316#ifdef XC_MUTEX_USE_TSRM
317        tsrm_mutex_free(mutex->tsrm_mutex);
[1199]318#endif
[1206]319    }
320
[1366]321#ifdef XC_MUTEX_USE_FCNTL
322    if (xc_want_inter_process()) {
323        xc_fcntl_destroy(&mutex->fcntl_mutex);
[1199]324    }
[1206]325#endif
326
[1366]327    if (!mutex->shared) {
328        free(mutex);
[1206]329    }
[1199]330}
331/* }}} */
[1366]332void xc_mutex_lock(xc_mutex_t *mutex) /* {{{ */
[1199]333{
[1366]334#ifdef XC_MUTEX_USE_PTHREAD
335    if (pthread_mutex_lock(&mutex->pthread_mutex) < 0) {
336        zend_error(E_ERROR, "xc_mutex failed errno:%d", errno);
[1206]337    }
338#endif
339
[1366]340#ifdef XC_MUTEX_USE_TSRM
341    if (tsrm_mutex_lock(mutex->tsrm_mutex) < 0) {
342        zend_error(E_ERROR, "xc_mutex failed errno:%d", errno);
[1199]343    }
[1206]344#endif
345
[1366]346#ifdef XC_MUTEX_USE_FCNTL
347    if (xc_want_inter_process()) {
348        xc_fcntl_mutex(&mutex->fcntl_mutex);
[1206]349    }
[1199]350#endif
[1204]351
352#ifndef NDEBUG
[1366]353    assert(!mutex->locked);
354    mutex->locked = 1;
355    assert(mutex->locked);
[1204]356#endif
[1199]357}
358/* }}} */
[1366]359void xc_mutex_unlock(xc_mutex_t *mutex) /* {{{ */
[1199]360{
[1204]361#ifndef NDEBUG
[1366]362    assert(mutex->locked);
363    mutex->locked = 0;
364    assert(!mutex->locked);
[1204]365#endif
366
[1366]367#ifdef XC_MUTEX_USE_FCNTL
368    if (xc_want_inter_process()) {
369        xc_fcntl_unlock(&mutex->fcntl_mutex);
[1206]370    }
[1199]371#endif
[1206]372
[1366]373#ifdef XC_MUTEX_USE_TSRM
374    if (tsrm_mutex_unlock(mutex->tsrm_mutex) < 0) {
375        zend_error(E_ERROR, "xc_mutex_unlock failed errno:%d", errno);
[1199]376    }
[1206]377#endif
378
[1366]379#ifdef XC_MUTEX_USE_PTHREAD
380    if (pthread_mutex_unlock(&mutex->pthread_mutex) < 0) {
381        zend_error(E_ERROR, "xc_mutex_unlock failed errno:%d", errno);
[1206]382    }
383#endif
[1199]384}
385/* }}} */
Note: See TracBrowser for help on using the repository browser.