source: trunk/xcache/xc_mutex.c

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

kill some warnings

  • 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    (void) want_inter_process; /* may be unused */
251
252#ifndef HAVE_FORK
253    want_inter_process = 0;
254#endif
255
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
269    {
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 */
282        pthread_mutexattr_t psharedm;
283        pthread_mutexattr_init(&psharedm);
284        pthread_mutexattr_setpshared(&psharedm, xc_want_inter_process() ? PTHREAD_PROCESS_PRIVATE : PTHREAD_PROCESS_SHARED);
285        pthread_mutex_init(&mutex->pthread_mutex, &psharedm);
286    }
287#endif
288
289#ifdef XC_MUTEX_USE_TSRM
290    mutex->tsrm_mutex = tsrm_mutex_alloc();
291#endif
292
293#ifdef XC_MUTEX_USE_FCNTL
294    if (xc_want_inter_process()) {
295        xc_fcntl_init(&mutex->fcntl_mutex, pathname);
296    }
297#endif
298
299#ifndef NDEBUG
300    mutex->locked = 0;
301#endif
302
303    return mutex;
304}
305/* }}} */
306void xc_mutex_destroy(xc_mutex_t *mutex) /* {{{ */
307{
308    assert(mutex);
309
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);
314#endif
315
316#ifdef XC_MUTEX_USE_TSRM
317        tsrm_mutex_free(mutex->tsrm_mutex);
318#endif
319    }
320
321#ifdef XC_MUTEX_USE_FCNTL
322    if (xc_want_inter_process()) {
323        xc_fcntl_destroy(&mutex->fcntl_mutex);
324    }
325#endif
326
327    if (!mutex->shared) {
328        free(mutex);
329    }
330}
331/* }}} */
332void xc_mutex_lock(xc_mutex_t *mutex) /* {{{ */
333{
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);
337    }
338#endif
339
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);
343    }
344#endif
345
346#ifdef XC_MUTEX_USE_FCNTL
347    if (xc_want_inter_process()) {
348        xc_fcntl_mutex(&mutex->fcntl_mutex);
349    }
350#endif
351
352#ifndef NDEBUG
353    assert(!mutex->locked);
354    mutex->locked = 1;
355    assert(mutex->locked);
356#endif
357}
358/* }}} */
359void xc_mutex_unlock(xc_mutex_t *mutex) /* {{{ */
360{
361#ifndef NDEBUG
362    assert(mutex->locked);
363    mutex->locked = 0;
364    assert(!mutex->locked);
365#endif
366
367#ifdef XC_MUTEX_USE_FCNTL
368    if (xc_want_inter_process()) {
369        xc_fcntl_unlock(&mutex->fcntl_mutex);
370    }
371#endif
372
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);
376    }
377#endif
378
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);
382    }
383#endif
384}
385/* }}} */
Note: See TracBrowser for help on using the repository browser.