source: branches/3.0/xcache/xc_lock.c @ 1246

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

fixes #301: refix locking impl

  • Property svn:eol-style set to native
File size: 7.3 KB
RevLine 
[1027]1#include "xc_lock.h"
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
[1206]14/* {{{ detect what type of lock is needed */
15#ifdef ZTS
16#   define XC_LOCK_NEED_TS
17#endif
[1204]18
[1206]19#ifndef ZEND_WIN32
20#   define XC_LOCK_NEED_INTERPROCESS
[1204]21#endif
22
[1206]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
[1204]26#endif
27/* }}} */
28
[1206]29/* {{{ detect which lock is needed */
30#if defined(XC_LOCK_NEED_TS) && defined(XC_LOCK_NEED_INTERPROCESS)
[1246]31#   ifdef PTHREADS
[1206]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/* }}} */
[1204]48
[1206]49/* {{{ fcntl lock impl */
50#ifdef XC_LOCK_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;
69    char *pathname;
[1199]70} xc_fcntl_lock_t;
[1]71
72#ifndef ZEND_WIN32
[11]73#   define LCK_WR F_WRLCK
74#   define LCK_RD F_RDLCK
75#   define LCK_UN F_UNLCK
76#   define LCK_NB 0
[1199]77static inline int dolock(xc_fcntl_lock_t *lck, int type)
[1198]78{
[1]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>
[928]100#   undef errno
101#   define errno GetLastError()
[11]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
[1199]107static inline int dolock(xc_fcntl_lock_t *lck, int type)
[1198]108{
[1]109    static OVERLAPPED offset = {0, 0, 0, 0, NULL};
110
111    if (type == LCK_UN) {
[1204]112        return UnlockFileEx(lck->fd, 0, 1, 0, &offset);
[1]113    }
114    else {
[1204]115        return LockFileEx(lck->fd, type, 0, 1, 0, &offset);
[1]116    }
117}
[1199]118#endif
[1]119/* }}} */
120
[1199]121static zend_bool xc_fcntl_init(xc_fcntl_lock_t *lck, const char *pathname) /* {{{ */
[1]122{
123    HANDLE fd;
[62]124    int size;
125    char *myname;
[1]126
127    if (pathname == NULL) {
[1154]128        static int instanceId = 0;
[62]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;
[282]140        myname = malloc(size);
[1154]141        snprintf(myname, size - 1, "%s%c.xcache.%d.%d.%d.lock", tmpdir, DEFAULT_SLASH, (int) getuid(), (int) getpid(), ++instanceId);
[1]142        pathname = myname;
143    }
[62]144    else {
145        myname = NULL;
146    }
[1]147
[928]148    fd = open(pathname, O_RDWR|O_CREAT, 0666);
[1]149
[62]150    if (fd != INVALID_HANDLE_VALUE) {
[1]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 {
[928]161        zend_error(E_ERROR, "xc_fcntl_create: open(%s, O_RDWR|O_CREAT, 0666) failed:", pathname);
[62]162        lck = NULL;
[1]163    }
[62]164
165    if (myname) {
[282]166        free(myname);
[62]167    }
168
[1199]169    return lck ? 1 : 0;
[1]170}
171/* }}} */
[1199]172static void xc_fcntl_destroy(xc_fcntl_lock_t *lck) /* {{{ */
[1198]173{
[928]174    close(lck->fd);
[1]175#ifdef __CYGWIN__
176    unlink(lck->pathname);
177#endif
178    free(lck->pathname);
179}
180/* }}} */
[1199]181static void xc_fcntl_lock(xc_fcntl_lock_t *lck) /* {{{ */
[1198]182{
[1]183    if (dolock(lck, LCK_WR) < 0) {
[928]184        zend_error(E_ERROR, "xc_fcntl_lock failed errno:%d", errno);
[1]185    }
186}
187/* }}} */
[1199]188static void xc_fcntl_rdlock(xc_fcntl_lock_t *lck) /* {{{ */
[1198]189{
[1]190    if (dolock(lck, LCK_RD) < 0) {
[928]191        zend_error(E_ERROR, "xc_fcntl_lock failed errno:%d", errno);
[1]192    }
193}
194/* }}} */
[1199]195static void xc_fcntl_unlock(xc_fcntl_lock_t *lck) /* {{{ */
[1198]196{
[1]197    if (dolock(lck, LCK_UN) < 0) {
[928]198        zend_error(E_ERROR, "xc_fcntl_unlock failed errno:%d", errno);
[1]199    }
200}
201/* }}} */
[1206]202#endif /* XC_LOCK_USE_FCNTL */
[1199]203
204struct _xc_lock_t {
[1206]205#ifdef XC_LOCK_USE_NOOP
[1199]206    int dummy;
[1206]207#endif
208
209#ifdef XC_LOCK_USE_TSRM
[1199]210    MUTEX_T tsrm_mutex;
[1206]211#endif
[1199]212
[1206]213#ifdef XC_LOCK_HAVE_INTERPROCESS_SWITCH
214    zend_bool interprocess;
215#endif
216
217#ifdef XC_LOCK_USE_PTHREAD
[1204]218    pthread_mutex_t pthread_mutex;
[1206]219#endif
220
221#ifdef XC_LOCK_USE_FCNTL
[1199]222    xc_fcntl_lock_t fcntl_lock;
223#endif
[1204]224
225#ifndef NDEBUG
226    zend_bool locked;
227#endif
[1199]228};
229
[1206]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
[1204]236size_t xc_lock_size(void) /* {{{ */
[1199]237{
[1204]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{
[1206]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
[1199]257    lck->tsrm_mutex = tsrm_mutex_alloc();
[1204]258#endif
259
[1206]260#ifdef XC_LOCK_USE_FCNTL
261    if (XC_LOCK_INTERPROCESS) {
[1199]262        xc_fcntl_init(&lck->fcntl_lock, pathname);
[1206]263    }
[1204]264#endif
265
266#ifndef NDEBUG
267    lck->locked = 0;
268#endif
269
[1199]270    return lck;
271}
272/* }}} */
273void xc_lock_destroy(xc_lock_t *lck) /* {{{ */
274{
[1206]275#ifdef XC_LOCK_USE_PTHREAD
[1204]276    pthread_mutex_destroy(&lck->pthread_mutex);
[1206]277#endif
278
279#ifdef XC_LOCK_USE_TSRM
[1199]280    tsrm_mutex_free(lck->tsrm_mutex);
[1204]281#endif
282
[1206]283#ifdef XC_LOCK_USE_FCNTL
284    if (XC_LOCK_INTERPROCESS) {
[1199]285        xc_fcntl_destroy(&lck->fcntl_lock);
[1206]286    }
[1199]287#endif
288}
289/* }}} */
290void xc_lock(xc_lock_t *lck) /* {{{ */
291{
[1206]292#ifdef XC_LOCK_USE_PTHREAD
293    if (pthread_mutex_lock(&lck->pthread_mutex) < 0) {
294        zend_error(E_ERROR, "xc_lock failed errno:%d", errno);
295    }
296#endif
297
298#ifdef XC_LOCK_USE_TSRM
[1199]299    if (tsrm_mutex_lock(lck->tsrm_mutex) < 0) {
300        zend_error(E_ERROR, "xc_lock failed errno:%d", errno);
301    }
[1206]302#endif
303
304#ifdef XC_LOCK_USE_FCNTL
305    if (XC_LOCK_INTERPROCESS) {
[1199]306        xc_fcntl_lock(&lck->fcntl_lock);
[1206]307    }
[1199]308#endif
[1204]309
310#ifndef NDEBUG
311    assert(!lck->locked);
312    lck->locked = 1;
313    assert(lck->locked);
314#endif
[1199]315}
316/* }}} */
317void xc_rdlock(xc_lock_t *lck) /* {{{ */
318{
[1206]319#ifdef XC_LOCK_USE_PTHREAD
320    if (pthread_mutex_lock(&lck->pthread_mutex) < 0) {
321        zend_error(E_ERROR, "xc_rdlock failed errno:%d", errno);
322    }
323#endif
324
325#ifdef XC_LOCK_USE_TSRM
[1199]326    if (tsrm_mutex_lock(lck->tsrm_mutex) < 0) {
327        zend_error(E_ERROR, "xc_rdlock failed errno:%d", errno);
328    }
[1206]329#endif
330
331#ifdef XC_LOCK_USE_FCNTL
332    if (XC_LOCK_INTERPROCESS) {
[1199]333        xc_fcntl_lock(&lck->fcntl_lock);
[1206]334    }
[1199]335#endif
[1204]336
337#ifndef NDEBUG
338    assert(!lck->locked);
339    lck->locked = 1;
340    assert(lck->locked);
341#endif
[1199]342}
343/* }}} */
344void xc_unlock(xc_lock_t *lck) /* {{{ */
345{
[1204]346#ifndef NDEBUG
347    assert(lck->locked);
348    lck->locked = 0;
349    assert(!lck->locked);
350#endif
351
[1206]352#ifdef XC_LOCK_USE_FCNTL
353    if (XC_LOCK_INTERPROCESS) {
[1199]354        xc_fcntl_unlock(&lck->fcntl_lock);
[1206]355    }
[1199]356#endif
[1206]357
358#ifdef XC_LOCK_USE_TSRM
[1199]359    if (tsrm_mutex_unlock(lck->tsrm_mutex) < 0) {
360        zend_error(E_ERROR, "xc_unlock failed errno:%d", errno);
361    }
[1206]362#endif
363
364#ifdef XC_LOCK_USE_PTHREAD
365    if (pthread_mutex_unlock(&lck->pthread_mutex) < 0) {
366        zend_error(E_ERROR, "xc_unlock failed errno:%d", errno);
367    }
368#endif
[1199]369}
370/* }}} */
Note: See TracBrowser for help on using the repository browser.