root / tags / 1.0.8 / xapian-core / backends / flint / flint_lock.cc

Revision 9989, 5.7 kB (checked in by olly, 12 months ago)

backends/flint/flint_lock.cc,backends/flint/flint_lock.h,
bin/xapian-check.cc: Apply tweaked version of patch for OS/2 support
by Yuri Dario.
AUTHORS: Add Yuri Dario.
PLATFORMS: Mention OS/2.

  • Property svn:eol-style set to native
Line 
1/* flint_lock.cc: database locking for flint backend.
2 *
3 * Copyright (C) 2005,2006,2007 Olly Betts
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
18 * USA
19 */
20
21#include <config.h>
22
23#include "flint_lock.h"
24
25#ifndef __WIN32__
26#include "safeerrno.h"
27
28#include "safefcntl.h"
29#include <unistd.h>
30#include <stdlib.h>
31#include <sys/types.h>
32// FIXME:1.1: It's unclear why this workaround is needed here, yet not needed
33// in configure or the other files which include sys/socket.h and use
34// SOCKLEN_T.  The commit comment doesn't help, and there's no obvious related
35// email thread.  I think the way forward is to drop this in 1.1.0, and if it
36// is required, we can work out why this file is different and either fix that
37// or add a "safesyssocket.h" header to replace <sys/socket.h> uses with.
38#ifdef _NEWLIB_VERSION
39// Workaround bug in newlib (at least some versions) - socklen_t doesn't
40// get defined if you just "#include <sys/socket.h>".
41#include <netinet/in.h>
42#endif
43#include <sys/socket.h>
44#include <sys/wait.h>
45#include <signal.h>
46#endif
47
48#include "omassert.h"
49
50#ifdef __CYGWIN__
51#include <sys/cygwin.h>
52#endif
53
54FlintLock::reason
55FlintLock::lock(bool exclusive) {
56    // Currently we only support exclusive locks.
57    (void)exclusive;
58    Assert(exclusive);
59#if defined __CYGWIN__ || defined __WIN32__
60    Assert(hFile == INVALID_HANDLE_VALUE);
61#ifdef __CYGWIN__
62    char fnm[MAX_PATH];
63    cygwin_conv_to_win32_path(filename.c_str(), fnm);
64#else
65    const char *fnm = filename.c_str();
66#endif
67    hFile = CreateFile(fnm, GENERIC_WRITE, FILE_SHARE_READ,
68                       NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
69    if (hFile != INVALID_HANDLE_VALUE) return SUCCESS;
70    if (GetLastError() == ERROR_ALREADY_EXISTS) return INUSE;
71    return UNKNOWN;
72#elif defined __EMX__
73    APIRET rc;
74    ULONG ulAction;
75    rc = DosOpen((PCSZ)filename.c_str(), &hFile, &ulAction, 0, FILE_NORMAL,
76                 OPEN_ACTION_OPEN_IF_EXISTS  | OPEN_ACTION_CREATE_IF_NEW,
77                 OPEN_SHARE_DENYWRITE | OPEN_ACCESS_WRITEONLY,
78                 NULL);
79    if (rc == NO_ERROR) return SUCCESS;
80    if (rc == ERROR_ACCESS_DENIED) return INUSE;
81    return UNKNOWN;
82#else
83    Assert(fd == -1);
84    int lockfd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0600);
85    if (lockfd < 0) return UNKNOWN; // Couldn't open lockfile.
86
87    int fds[2];
88    if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) < 0) {
89        // Couldn't create socketpair.
90        close(lockfd);
91        return UNKNOWN;
92    }
93
94    pid_t child = fork();
95    if (child == 0) {
96        // Child process.
97        close(fds[0]);
98
99        reason why = SUCCESS;
100        {
101            struct flock fl;
102            fl.l_type = F_WRLCK;
103            fl.l_whence = SEEK_SET;
104            fl.l_start = 0;
105            fl.l_len = 1;
106            while (fcntl(lockfd, F_SETLK, &fl) == -1) {
107                if (errno != EINTR) {
108                    // Lock failed - translate known errno values into a reason
109                    // code.
110                    if (errno == EACCES || errno == EAGAIN) {
111                        why = INUSE;
112                    } else if (errno == ENOLCK) {
113                        why = UNSUPPORTED;
114                    } else {
115                        _exit(0);
116                    }
117                    break;
118                }
119            }
120        }
121
122        {
123            // Tell the parent if we got the lock, and if not, why not.
124            char ch = static_cast<char>(why);
125            while (write(fds[1], &ch, 1) < 0) {
126                // EINTR means a signal interrupted us, so retry.
127                // Otherwise we're DOOMED!  The best we can do is just exit
128                // and the parent process should get EOF and know the lock
129                // failed.
130                if (errno != EINTR) _exit(1);
131            }
132            if (why != SUCCESS) _exit(0);
133        }
134
135        //shutdown(fds[1], 1); // Disable further sends.
136        // Connect pipe to stdin.
137        dup2(fds[1], 0);
138        // FIXME: use special statically linked helper instead of cat.
139        execl("/bin/cat", "/bin/cat", (void*)NULL);
140        // Emulate cat ourselves (we try to avoid this to reduce VM overhead).
141        char ch;
142        while (read(0, &ch, 1) != 0) { /* Do nothing */ }
143        _exit(0);
144    }
145
146    close(lockfd);
147
148    if (child == -1) {
149        // Couldn't fork.
150        close(fds[0]);
151        close(fds[1]);
152        return UNKNOWN;
153    }
154
155    // Parent process.
156    close(fds[1]);
157    while (true) {
158        char ch;
159        int n = read(fds[0], &ch, 1);
160        if (n == 1) {
161            reason why = static_cast<reason>(ch);
162            if (why == SUCCESS) break; // Got the lock.
163            close(fds[0]);
164            return why;
165        }
166        if (n == 0 || errno != EINTR) {
167            // EOF means the lock failed; we also treat unexpected errors from
168            // read() the same way.
169            close(fds[0]);
170            return UNKNOWN;
171        }
172    }
173    //shutdown(fds[0], 0); // Disable further receives.
174    fd = fds[0];
175    pid = child;
176    return SUCCESS;
177#endif
178}
179
180void
181FlintLock::release() {
182#if defined __CYGWIN__ || defined __WIN32__
183    if (hFile == INVALID_HANDLE_VALUE) return;
184    CloseHandle(hFile);
185    hFile = INVALID_HANDLE_VALUE;
186#elif defined __EMX__
187    if (hFile == NULLHANDLE) return;
188    DosClose(hFile);
189    hFile = NULLHANDLE;
190#else
191    if (fd < 0) return;
192    close(fd);
193    fd = -1;
194    // The only likely error from kill is ESRCH.  The other possibilities
195    // (according to the Linux man page) are EINVAL (invalid signal) and EPERM
196    // (don't have permission to SIGHUP the process) but in none of the cases
197    // does calling waitpid do us any good!
198    if (kill(pid, SIGHUP) == 0) {
199        int status;
200        while (waitpid(pid, &status, 0) < 0) {
201            if (errno != EINTR) break;
202        }
203    }
204#endif
205}
Note: See TracBrowser for help on using the browser.