source: trunk/xcache/xc_mutex.c

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

fixes #323: refix locking impl for threaded env

  • Property svn:eol-style set to native
File size: 8.2 KB
Line 
1#include "xc_mutex.h"
2#include "xcache.h"
3#include <stdio.h>
4#include <string.h>
5#include <stdlib.h>
6#ifdef ZEND_WIN32
7#   include <process.h>
8#else
9#   include <unistd.h>
10#   include <fcntl.h>
11#   include <errno.h>
12#endif
13
14/* {{{ detect what type of mutex is needed */
15#ifdef ZTS
16#   define XC_MUTEX_NEED_TS
17#endif
18
19#ifdef HAVE_FORK
20#   define XC_MUTEX_NEED_INTERPROCESS
21#endif
22
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
26#endif
27/* }}} */
28
29/* {{{ detect which mutex is needed */
30#if defined(XC_MUTEX_NEED_TS) && defined(XC_MUTEX_NEED_INTERPROCESS)
31#   ifdef PTHREADS
32#       define XC_MUTEX_USE_PTHREAD
33#       ifndef _POSIX_THREAD_PROCESS_SHARED
34#           define XC_MUTEX_USE_FCNTL
35#       endif
36#   else
37#       define XC_MUTEX_USE_TSRM
38#       define XC_MUTEX_USE_FCNTL
39#   endif
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
44#else
45#   define XC_MUTEX_USE_NOOP
46#endif
47/* }}} */
48
49/* {{{ fcntl mutex impl */
50#ifdef XC_MUTEX_USE_FCNTL
51#ifndef ZEND_WIN32
52typedef int HANDLE;
53#   ifndef INVALID_HANDLE_VALUE
54#       define INVALID_HANDLE_VALUE -1
55#   endif
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)
65#endif
66
67typedef struct {
68    HANDLE fd;
69#ifdef __CYGWIN__
70    /* store the path for unlink() later */
71    char *pathname;
72#endif
73} xc_fcntl_mutex_t;
74
75#ifndef ZEND_WIN32
76#   define LCK_WR F_WRLCK
77#   define LCK_RD F_RDLCK
78#   define LCK_UN F_UNLCK
79#   define LCK_NB 0
80static inline int dolock(xc_fcntl_mutex_t *fcntl_mutex, int type)
81{
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 {
92        ret = fcntl(fcntl_mutex->fd, F_SETLKW, &lock);
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>
103#   undef errno
104#   define errno GetLastError()
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
110static inline int dolock(xc_fcntl_mutex_t *fcntl_mutex, int type)
111{
112    static OVERLAPPED offset = {0, 0, 0, 0, NULL};
113
114    if (type == LCK_UN) {
115        return UnlockFileEx(fcntl_mutex->fd, 0, 1, 0, &offset);
116    }
117    else {
118        return LockFileEx(fcntl_mutex->fd, type, 0, 1, 0, &offset);
119    }
120}
121#endif
122/* }}} */
123
124static zend_bool xc_fcntl_init(xc_fcntl_mutex_t *fcntl_mutex, const char *pathname) /* {{{ */
125{
126    HANDLE fd;
127    int size;
128    char *myname;
129
130    if (pathname == NULL) {
131        static int instanceId = 0;
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        }
142        size = strlen(tmpdir) + sizeof("/.xcache.mutex") - 1 + 3 * 10 + 100;
143        myname = malloc(size);
144        snprintf(myname, size - 1, "%s%c.xcache.%d.%d.%d.mutex", tmpdir, DEFAULT_SLASH, (int) getuid(), (int) getpid(), ++instanceId);
145        pathname = myname;
146    }
147    else {
148        myname = NULL;
149    }
150
151    fd = open(pathname, O_RDWR|O_CREAT, 0666);
152
153    if (fd != INVALID_HANDLE_VALUE) {
154        fcntl_mutex->fd = fd;
155#ifdef __CYGWIN__
156        size = strlen(pathname) + 1;
157        fcntl_mutex->pathname = malloc(size);
158        memcpy(fcntl_mutex->pathname, pathname, size);
159#else
160        unlink(pathname);
161#endif
162    }
163    else {
164        zend_error(E_ERROR, "xc_fcntl_create: open(%s, O_RDWR|O_CREAT, 0666) failed:", pathname);
165        fcntl_mutex = NULL;
166    }
167
168    if (myname) {
169        free(myname);
170    }
171
172    return fcntl_mutex ? 1 : 0;
173}
174/* }}} */
175static void xc_fcntl_destroy(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */
176{
177    close(fcntl_mutex->fd);
178#ifdef __CYGWIN__
179    unlink(fcntl_mutex->pathname);
180    free(fcntl_mutex->pathname);
181#endif
182}
183/* }}} */
184static void xc_fcntl_mutex(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */
185{
186    if (dolock(fcntl_mutex, LCK_WR) < 0) {
187        zend_error(E_ERROR, "xc_fcntl_mutex failed errno:%d", errno);
188    }
189}
190/* }}} */
191static void xc_fcntl_rdlock(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */
192{
193    if (dolock(fcntl_mutex, LCK_RD) < 0) {
194        zend_error(E_ERROR, "xc_fcntl_mutex failed errno:%d", errno);
195    }
196}
197/* }}} */
198static void xc_fcntl_unlock(xc_fcntl_mutex_t *fcntl_mutex) /* {{{ */
199{
200    if (dolock(fcntl_mutex, LCK_UN) < 0) {
201        zend_error(E_ERROR, "xc_fcntl_unlock failed errno:%d", errno);
202    }
203}
204/* }}} */
205#endif /* XC_MUTEX_USE_FCNTL */
206
207struct _xc_mutex_t {
208#ifdef XC_MUTEX_USE_NOOP
209    int dummy;
210#endif
211
212#ifdef XC_MUTEX_HAVE_INTERPROCESS_SWITCH
213    zend_bool want_inter_process;  /* whether the lock is created for inter process usage */
214#endif
215    zend_bool shared; /* shared, or locally allocated */
216
217#ifdef XC_MUTEX_USE_TSRM
218    MUTEX_T tsrm_mutex;
219#endif
220
221#ifdef XC_MUTEX_USE_PTHREAD
222    pthread_mutex_t pthread_mutex;
223#endif
224
225#ifdef XC_MUTEX_USE_FCNTL
226    xc_fcntl_mutex_t fcntl_mutex;
227#endif
228
229#ifndef NDEBUG
230    zend_bool locked;
231#endif
232};
233
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
238#else
239#   define xc_want_inter_process() 0
240#endif
241
242size_t xc_mutex_size(void) /* {{{ */
243{
244    return sizeof(xc_mutex_t);
245}
246/* }}} */
247xc_mutex_t *xc_mutex_init(xc_mutex_t *const shared_mutex, const char *pathname, unsigned char want_inter_process) /* {{{ */
248{
249    xc_mutex_t *mutex = NULL;
250
251#ifndef HAVE_FORK
252    want_inter_process = 0;
253#endif
254
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
268    {
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 */
281        pthread_mutexattr_t psharedm;
282        pthread_mutexattr_init(&psharedm);
283        pthread_mutexattr_setpshared(&psharedm, xc_want_inter_process() ? PTHREAD_PROCESS_PRIVATE : PTHREAD_PROCESS_SHARED);
284        pthread_mutex_init(&mutex->pthread_mutex, &psharedm);
285    }
286#endif
287
288#ifdef XC_MUTEX_USE_TSRM
289    mutex->tsrm_mutex = tsrm_mutex_alloc();
290#endif
291
292#ifdef XC_MUTEX_USE_FCNTL
293    if (xc_want_inter_process()) {
294        xc_fcntl_init(&mutex->fcntl_mutex, pathname);
295    }
296#endif
297
298#ifndef NDEBUG
299    mutex->locked = 0;
300#endif
301
302    return mutex;
303}
304/* }}} */
305void xc_mutex_destroy(xc_mutex_t *mutex) /* {{{ */
306{
307    assert(mutex);
308
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);
313#endif
314
315#ifdef XC_MUTEX_USE_TSRM
316        tsrm_mutex_free(mutex->tsrm_mutex);
317#endif
318    }
319
320#ifdef XC_MUTEX_USE_FCNTL
321    if (xc_want_inter_process()) {
322        xc_fcntl_destroy(&mutex->fcntl_mutex);
323    }
324#endif
325
326    if (!mutex->shared) {
327        free(mutex);
328    }
329}
330/* }}} */
331void xc_mutex_lock(xc_mutex_t *mutex) /* {{{ */
332{
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);
336    }
337#endif
338
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);
342    }
343#endif
344
345#ifdef XC_MUTEX_USE_FCNTL
346    if (xc_want_inter_process()) {
347        xc_fcntl_mutex(&mutex->fcntl_mutex);
348    }
349#endif
350
351#ifndef NDEBUG
352    assert(!mutex->locked);
353    mutex->locked = 1;
354    assert(mutex->locked);
355#endif
356}
357/* }}} */
358void xc_mutex_unlock(xc_mutex_t *mutex) /* {{{ */
359{
360#ifndef NDEBUG
361    assert(mutex->locked);
362    mutex->locked = 0;
363    assert(!mutex->locked);
364#endif
365
366#ifdef XC_MUTEX_USE_FCNTL
367    if (xc_want_inter_process()) {
368        xc_fcntl_unlock(&mutex->fcntl_mutex);
369    }
370#endif
371
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);
375    }
376#endif
377
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);
381    }
382#endif
383}
384/* }}} */
Note: See TracBrowser for help on using the repository browser.