source: trunk/xcache/xc_lock.c @ 1206

Last change on this file since 1206 was 1206, checked in by moo, 16 months ago

mutex code clean up

  • Property svn:eol-style set to native
File size: 7.3 KB
Line 
1#include "xc_lock.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 lock is needed */
15#ifdef ZTS
16#   define XC_LOCK_NEED_TS
17#endif
18
19#ifndef ZEND_WIN32
20#   define XC_LOCK_NEED_INTERPROCESS
21#endif
22
23#if defined(XC_LOCK_NEED_TS) && defined(XC_LOCK_NEED_INTERPROCESS)
24/* allow switching off interprocess support */
25#   define XC_LOCK_HAVE_INTERPROCESS_SWITCH
26#endif
27/* }}} */
28
29/* {{{ detect which lock is needed */
30#if defined(XC_LOCK_NEED_TS) && defined(XC_LOCK_NEED_INTERPROCESS)
31#   ifdef PTHREAD
32#       define XC_LOCK_USE_PTHREAD
33#       ifndef _POSIX_THREAD_PROCESS_SHARED
34#           define XC_LOCK_USE_FCNTL
35#       endif
36#   else
37#       define XC_LOCK_USE_TSRM
38#       define XC_LOCK_USE_FCNTL
39#   endif
40#elif defined(XC_LOCK_NEED_TS)
41#   define XC_LOCK_USE_TSRM
42#elif defined(XC_LOCK_NEED_INTERPROCESS)
43#   define XC_LOCK_USE_FCNTL
44#else
45#   define XC_LOCK_USE_NOOP
46#endif
47/* }}} */
48
49/* {{{ fcntl lock impl */
50#ifdef XC_LOCK_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    char *pathname;
70} xc_fcntl_lock_t;
71
72#ifndef ZEND_WIN32
73#   define LCK_WR F_WRLCK
74#   define LCK_RD F_RDLCK
75#   define LCK_UN F_UNLCK
76#   define LCK_NB 0
77static inline int dolock(xc_fcntl_lock_t *lck, int type)
78{
79    int ret;
80    struct flock lock;
81
82    lock.l_type = type;
83    lock.l_start = 0;
84    lock.l_whence = SEEK_SET;
85    lock.l_len = 1;
86    lock.l_pid = 0;
87
88    do {
89        ret = fcntl(lck->fd, F_SETLKW, &lock);
90    } while (ret < 0 && errno == EINTR);
91    return ret;
92}
93#else
94
95#   include <win32/flock.h>
96#   include <io.h>
97#   include <fcntl.h>
98#   include <sys/types.h>
99#   include <sys/stat.h>
100#   undef errno
101#   define errno GetLastError()
102#   define getuid() 0
103#   define LCK_WR LOCKFILE_EXCLUSIVE_LOCK
104#   define LCK_RD 0
105#   define LCK_UN 0
106#   define LCK_NB LOCKFILE_FAIL_IMMEDIATELY
107static inline int dolock(xc_fcntl_lock_t *lck, int type)
108{
109    static OVERLAPPED offset = {0, 0, 0, 0, NULL};
110
111    if (type == LCK_UN) {
112        return UnlockFileEx(lck->fd, 0, 1, 0, &offset);
113    }
114    else {
115        return LockFileEx(lck->fd, type, 0, 1, 0, &offset);
116    }
117}
118#endif
119/* }}} */
120
121static zend_bool xc_fcntl_init(xc_fcntl_lock_t *lck, const char *pathname) /* {{{ */
122{
123    HANDLE fd;
124    int size;
125    char *myname;
126
127    if (pathname == NULL) {
128        static int instanceId = 0;
129        const char default_tmpdir[] = { DEFAULT_SLASH, 't', 'm', 'p', '\0' };
130        const char *tmpdir;
131
132        tmpdir = getenv("TEMP");
133        if (!tmpdir) {
134            tmpdir = getenv("TMP");
135            if (!tmpdir) {
136                tmpdir = default_tmpdir;
137            }
138        }
139        size = strlen(tmpdir) + sizeof("/.xcache.lock") - 1 + 3 * 10 + 100;
140        myname = malloc(size);
141        snprintf(myname, size - 1, "%s%c.xcache.%d.%d.%d.lock", tmpdir, DEFAULT_SLASH, (int) getuid(), (int) getpid(), ++instanceId);
142        pathname = myname;
143    }
144    else {
145        myname = NULL;
146    }
147
148    fd = open(pathname, O_RDWR|O_CREAT, 0666);
149
150    if (fd != INVALID_HANDLE_VALUE) {
151
152#ifndef __CYGWIN__
153        unlink(pathname);
154#endif
155        lck->fd = fd;
156        size = strlen(pathname) + 1;
157        lck->pathname = malloc(size);
158        memcpy(lck->pathname, pathname, size);
159    }
160    else {
161        zend_error(E_ERROR, "xc_fcntl_create: open(%s, O_RDWR|O_CREAT, 0666) failed:", pathname);
162        lck = NULL;
163    }
164
165    if (myname) {
166        free(myname);
167    }
168
169    return lck ? 1 : 0;
170}
171/* }}} */
172static void xc_fcntl_destroy(xc_fcntl_lock_t *lck) /* {{{ */
173{
174    close(lck->fd);
175#ifdef __CYGWIN__
176    unlink(lck->pathname);
177#endif
178    free(lck->pathname);
179}
180/* }}} */
181static void xc_fcntl_lock(xc_fcntl_lock_t *lck) /* {{{ */
182{
183    if (dolock(lck, LCK_WR) < 0) {
184        zend_error(E_ERROR, "xc_fcntl_lock failed errno:%d", errno);
185    }
186}
187/* }}} */
188static void xc_fcntl_rdlock(xc_fcntl_lock_t *lck) /* {{{ */
189{
190    if (dolock(lck, LCK_RD) < 0) {
191        zend_error(E_ERROR, "xc_fcntl_lock failed errno:%d", errno);
192    }
193}
194/* }}} */
195static void xc_fcntl_unlock(xc_fcntl_lock_t *lck) /* {{{ */
196{
197    if (dolock(lck, LCK_UN) < 0) {
198        zend_error(E_ERROR, "xc_fcntl_unlock failed errno:%d", errno);
199    }
200}
201/* }}} */
202#endif /* XC_LOCK_USE_FCNTL */
203
204struct _xc_lock_t {
205#ifdef XC_LOCK_USE_NOOP
206    int dummy;
207#endif
208
209#ifdef XC_LOCK_USE_TSRM
210    MUTEX_T tsrm_mutex;
211#endif
212
213#ifdef XC_LOCK_HAVE_INTERPROCESS_SWITCH
214    zend_bool interprocess;
215#endif
216
217#ifdef XC_LOCK_USE_PTHREAD
218    pthread_mutex_t pthread_mutex;
219#endif
220
221#ifdef XC_LOCK_USE_FCNTL
222    xc_fcntl_lock_t fcntl_lock;
223#endif
224
225#ifndef NDEBUG
226    zend_bool locked;
227#endif
228};
229
230#ifdef XC_LOCK_HAVE_INTERPROCESS_SWITCH
231#   define XC_LOCK_INTERPROCESS (lck->interprocess)
232#else
233#   define XC_LOCK_INTERPROCESS 1
234#endif
235
236size_t xc_lock_size(void) /* {{{ */
237{
238    return sizeof(xc_lock_t);
239}
240/* }}} */
241xc_lock_t *xc_lock_init(xc_lock_t *lck, const char *pathname, unsigned char interprocess) /* {{{ */
242{
243#ifdef XC_LOCK_HAVE_INTERPROCESS_SWITCH
244    lck->interprocess = interprocess;
245#endif
246
247#ifdef XC_LOCK_USE_PTHREAD
248    {
249        pthread_mutexattr_t psharedm;
250        pthread_mutexattr_init(&psharedm);
251        pthread_mutexattr_setpshared(&psharedm, XC_LOCK_INTERPROCESS ? PTHREAD_PROCESS_PRIVATE : PTHREAD_PROCESS_SHARED);
252        pthread_mutex_init(&lck->pthread_mutex, &psharedm);
253    }
254#endif
255
256#ifdef XC_LOCK_USE_TSRM
257    lck->tsrm_mutex = tsrm_mutex_alloc();
258#endif
259
260#ifdef XC_LOCK_USE_FCNTL
261    if (XC_LOCK_INTERPROCESS) {
262        xc_fcntl_init(&lck->fcntl_lock, pathname);
263    }
264#endif
265
266#ifndef NDEBUG
267    lck->locked = 0;
268#endif
269
270    return lck;
271}
272/* }}} */
273void xc_lock_destroy(xc_lock_t *lck) /* {{{ */
274{
275#ifdef XC_LOCK_USE_PTHREAD
276    pthread_mutex_destroy(&lck->pthread_mutex);
277#endif
278
279#ifdef XC_LOCK_USE_TSRM
280    tsrm_mutex_free(lck->tsrm_mutex);
281    lck->tsrm_mutex = NULL;
282#endif
283
284#ifdef XC_LOCK_USE_FCNTL
285    if (XC_LOCK_INTERPROCESS) {
286        xc_fcntl_destroy(&lck->fcntl_lock);
287    }
288#endif
289}
290/* }}} */
291void xc_lock(xc_lock_t *lck) /* {{{ */
292{
293#ifdef XC_LOCK_USE_PTHREAD
294    if (pthread_mutex_lock(&lck->pthread_mutex) < 0) {
295        zend_error(E_ERROR, "xc_lock failed errno:%d", errno);
296    }
297#endif
298
299#ifdef XC_LOCK_USE_TSRM
300    if (tsrm_mutex_lock(lck->tsrm_mutex) < 0) {
301        zend_error(E_ERROR, "xc_lock failed errno:%d", errno);
302    }
303#endif
304
305#ifdef XC_LOCK_USE_FCNTL
306    if (XC_LOCK_INTERPROCESS) {
307        xc_fcntl_lock(&lck->fcntl_lock);
308    }
309#endif
310
311#ifndef NDEBUG
312    assert(!lck->locked);
313    lck->locked = 1;
314    assert(lck->locked);
315#endif
316}
317/* }}} */
318void xc_rdlock(xc_lock_t *lck) /* {{{ */
319{
320#ifdef XC_LOCK_USE_PTHREAD
321    if (pthread_mutex_lock(&lck->pthread_mutex) < 0) {
322        zend_error(E_ERROR, "xc_rdlock failed errno:%d", errno);
323    }
324#endif
325
326#ifdef XC_LOCK_USE_TSRM
327    if (tsrm_mutex_lock(lck->tsrm_mutex) < 0) {
328        zend_error(E_ERROR, "xc_rdlock failed errno:%d", errno);
329    }
330#endif
331
332#ifdef XC_LOCK_USE_FCNTL
333    if (XC_LOCK_INTERPROCESS) {
334        xc_fcntl_lock(&lck->fcntl_lock);
335    }
336#endif
337
338#ifndef NDEBUG
339    assert(!lck->locked);
340    lck->locked = 1;
341    assert(lck->locked);
342#endif
343}
344/* }}} */
345void xc_unlock(xc_lock_t *lck) /* {{{ */
346{
347#ifndef NDEBUG
348    assert(lck->locked);
349    lck->locked = 0;
350    assert(!lck->locked);
351#endif
352
353#ifdef XC_LOCK_USE_FCNTL
354    if (XC_LOCK_INTERPROCESS) {
355        xc_fcntl_unlock(&lck->fcntl_lock);
356    }
357#endif
358
359#ifdef XC_LOCK_USE_TSRM
360    if (tsrm_mutex_unlock(lck->tsrm_mutex) < 0) {
361        zend_error(E_ERROR, "xc_unlock failed errno:%d", errno);
362    }
363#endif
364
365#ifdef XC_LOCK_USE_PTHREAD
366    if (pthread_mutex_unlock(&lck->pthread_mutex) < 0) {
367        zend_error(E_ERROR, "xc_unlock failed errno:%d", errno);
368    }
369#endif
370}
371/* }}} */
Note: See TracBrowser for help on using the repository browser.