Further testing of locks.
[idzebra-moved-to-github.git] / util / flock.c
1 /* $Id: flock.c,v 1.11 2006-06-30 13:02:20 adam Exp $
2    Copyright (C) 1995-2006
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23
24 #include <stdio.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <sys/types.h>
30 #ifdef WIN32
31 #include <io.h>
32 #include <sys/locking.h>
33 #endif
34 #if HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #include <idzebra/flock.h>
39 #include <zebra-lock.h>
40 #include <yaz/xmalloc.h>
41 #include <yaz/log.h>
42
43 /** whether lock file desciptors are shared within one process' threads */
44 #define ZEBRA_FLOCK_SHARE_FD_IN_THREADS 1
45
46 /** whether we always lock on the file - even if rd/wr locks should do */
47 #define ZEBRA_FLOCK_FILE_LOCK_ALWAYS 1
48
49 /** whether we should also use pthreads locking to do "extra" rd/wr locks */
50 #define ZEBRA_FLOCK_EXTRA_RDWR_LOCKS 1
51
52 /** whether this module should debug */
53 #define DEBUG_FLOCK 0
54
55 /** have this module (mutex) been initialized? */
56 static int initialized = 0;
57
58 /** mutex for lock_list below */
59 Zebra_mutex lock_list_mutex;
60
61 /** our list of file locked files */
62 static struct zebra_lock_info *lock_list = 0;
63
64 /** the internal handle, with a pointer to each lock file info */
65 struct zebra_lock_handle {
66 #ifndef WIN32
67     /** so we can call zebra_lock_rdwr_wunlock or zebra_lock_lock_runlock */
68     int write_flag;
69 #endif
70     struct zebra_lock_info *p;
71 };
72
73 struct zebra_lock_info {
74     /** file descriptor */
75     int fd;
76     /** full path (xmalloc'ed) */
77     char *fname;
78     /** reference counter: number of zebra_lock_handles pointing to us */
79     int ref_count;
80 #ifndef WIN32
81     /** number of file write locks/read locks */
82     int no_file_write_lock;
83     int no_file_read_lock;
84 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
85     Zebra_lock_rdwr rdwr_lock;
86 #endif
87     Zebra_mutex file_mutex;
88 #endif
89     /** next in lock list */
90     struct zebra_lock_info *next;
91 };
92
93 static int log_level = 0;
94
95 char *zebra_mk_fname(const char *dir, const char *name)
96 {
97     int dlen = dir ? strlen(dir) : 0;
98     char *fname = xmalloc(dlen + strlen(name) + 3);
99     
100 #ifdef WIN32
101     if (dlen)
102     {
103         int last_one = dir[dlen-1];
104         
105         if (!strchr("/\\:", last_one))
106             sprintf(fname, "%s\\%s", dir, name);
107         else
108             sprintf(fname, "%s%s", dir, name);
109     }
110     else
111         sprintf(fname, "%s", name);
112 #else
113     if (dlen)
114     {
115         int last_one = dir[dlen-1];
116
117         if (!strchr("/", last_one))
118             sprintf(fname, "%s/%s", dir, name);
119         else
120             sprintf(fname, "%s%s", dir, name);
121     }
122     else
123         sprintf(fname, "%s", name);
124 #endif
125     return fname;
126 }
127
128 ZebraLockHandle zebra_lock_create(const char *dir, const char *name)
129 {
130     char *fname = zebra_mk_fname(dir, name);
131     struct zebra_lock_info *p = 0;
132     ZebraLockHandle h = 0;
133
134     assert(initialized);
135
136     zebra_mutex_lock(&lock_list_mutex);
137     /* see if we have the same filename in a global list of "lock files" */
138 #ifndef WIN32
139 #if ZEBRA_FLOCK_SHARE_FD_IN_THREADS
140     for (p = lock_list; p ; p = p->next)
141         if (!strcmp(p->fname, fname))
142             break;
143 #endif
144 #endif
145     if (!p)
146     {   /* didn't match (or we didn't want it to match! */
147         p = (struct zebra_lock_info *) xmalloc(sizeof(*p));
148         
149         p->ref_count = 0;
150 #ifdef WIN32
151         p->fd = open(name, O_BINARY|O_RDONLY);
152         if (p->fd == -1)
153             p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
154 #else
155         p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
156 #endif
157         if (p->fd == -1)
158         {
159             xfree(p);
160             yaz_log(YLOG_WARN | YLOG_ERRNO, 
161                     "zebra_lock_create fail fname=%s", fname);
162             p = 0;
163         }
164         else
165         {
166             p->fname = fname;
167             fname = 0;  /* fname buffer now owned by p->fname */
168 #ifndef WIN32
169 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
170             zebra_lock_rdwr_init(&p->rdwr_lock);
171 #endif
172             zebra_mutex_init(&p->file_mutex);
173             p->no_file_write_lock = 0;
174             p->no_file_read_lock = 0;
175 #endif
176             p->next = lock_list;
177             lock_list = p;
178         }
179     }
180     if (p)
181     {
182         /* we have lock info so we can make a handle pointing to that */
183         p->ref_count++;
184         h = (ZebraLockHandle) xmalloc(sizeof(*h));
185         h->p = p;
186 #ifndef WIN32
187         h->write_flag = 0;
188 #endif
189         yaz_log(log_level, "zebra_lock_create fd=%d p=%p fname=%s",
190                 h->p->fd, h, p->fname);
191     }
192     zebra_mutex_unlock(&lock_list_mutex);
193     xfree(fname); /* free it - if it's still there */
194
195     return h;
196 }
197
198 void zebra_lock_destroy(ZebraLockHandle h)
199 {
200     if (!h)
201         return;
202     yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s",
203             h->p->fd, h, h->p->fname);
204     zebra_mutex_lock(&lock_list_mutex);
205     yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s refcount=%d",
206             h->p->fd, h, h->p->fname, h->p->ref_count);
207     assert(h->p->ref_count > 0);
208     --(h->p->ref_count);
209     if (h->p->ref_count == 0)
210     {
211         /* must remove shared info from lock_list */
212         struct zebra_lock_info **hp = &lock_list;
213         while (*hp)
214         {
215             if (*hp == h->p)
216             {
217                 *hp = h->p->next;
218                 break;
219             }
220             else
221                 hp = &(*hp)->next;
222         }
223
224         yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s remove",
225                 h->p->fd, h, h->p->fname);
226
227 #ifndef WIN32
228 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
229         zebra_lock_rdwr_destroy(&h->p->rdwr_lock);
230 #endif
231         zebra_mutex_destroy(&h->p->file_mutex);
232 #endif
233         if (h->p->fd != -1)
234             close(h->p->fd);
235         xfree(h->p->fname);
236         xfree(h->p);
237     }
238     xfree(h);
239     zebra_mutex_unlock(&lock_list_mutex);
240 }
241
242 #ifndef WIN32
243 static int unixLock(int fd, int type, int cmd)
244 {
245     struct flock area;
246     int r;
247     area.l_type = type;
248     area.l_whence = SEEK_SET;
249     area.l_len = area.l_start = 0L;
250
251     yaz_log(log_level, "fcntl begin type=%d fd=%d", type, fd);
252     r = fcntl(fd, cmd, &area);
253     if (r == -1)
254         yaz_log(YLOG_WARN|YLOG_ERRNO, "fcntl FAIL type=%d fd=%d", type, fd);
255     else
256         yaz_log(log_level, "fcntl type=%d OK fd=%d", type, fd);
257     
258     return r;
259 }
260 #endif
261
262 int zebra_lock_w(ZebraLockHandle h)
263 {
264     int r;
265     int do_lock = ZEBRA_FLOCK_FILE_LOCK_ALWAYS;
266     yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s begin", 
267             h->p->fd, h, h->p->fname);
268     
269 #ifdef WIN32
270     while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
271         ;
272 #else
273     zebra_mutex_lock(&h->p->file_mutex);
274     if (h->p->no_file_write_lock == 0)
275         do_lock = 1;
276     h->p->no_file_write_lock++;
277     zebra_mutex_unlock(&h->p->file_mutex);
278     if (do_lock)
279     {
280         yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s 2", 
281                 h->p->fd, h, h->p->fname);
282         /* if there is already a read lock.. upgrade to write lock */
283         r = unixLock(h->p->fd, F_WRLCK, F_SETLKW);
284     }
285     yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s 3", 
286             h->p->fd, h, h->p->fname);
287
288 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
289     zebra_lock_rdwr_wlock(&h->p->rdwr_lock);
290 #endif
291     h->write_flag = 1;
292     yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s end", 
293             h->p->fd, h, h->p->fname);
294 #endif
295
296     return r;
297 }
298
299 int zebra_lock_r(ZebraLockHandle h)
300 {
301     int r;
302     int do_lock = ZEBRA_FLOCK_FILE_LOCK_ALWAYS;
303
304     yaz_log(log_level, "zebra_lock_r fd=%d p=%p fname=%s", 
305             h->p->fd, h, h->p->fname);
306 #ifdef WIN32
307     while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
308         ;
309 #else
310     zebra_mutex_lock(&h->p->file_mutex);
311     if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
312         do_lock = 1;
313     h->p->no_file_read_lock++;
314     zebra_mutex_unlock(&h->p->file_mutex);
315     
316     if (do_lock)
317     {
318         /* only read lock if no write locks already */
319         r = unixLock(h->p->fd, F_RDLCK, F_SETLKW);
320     }
321
322 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
323     zebra_lock_rdwr_rlock(&h->p->rdwr_lock);
324 #endif
325     h->write_flag = 0;
326 #endif
327     return r;
328 }
329
330 int zebra_unlock(ZebraLockHandle h)
331 {
332     int r = 0;
333     int do_unlock = ZEBRA_FLOCK_FILE_LOCK_ALWAYS;
334     yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s begin",
335             h->p->fd, h, h->p->fname);
336 #ifdef WIN32
337     r = _locking(h->p->fd, _LK_UNLCK, 1);
338 #else
339 #if ZEBRA_FLOCK_EXTRA_RDWR_LOCKS
340     if (h->write_flag)
341         zebra_lock_rdwr_wunlock(&h->p->rdwr_lock);
342     else
343         zebra_lock_rdwr_runlock(&h->p->rdwr_lock);
344 #endif
345
346     zebra_mutex_lock(&h->p->file_mutex);
347     if (h->write_flag)
348         h->p->no_file_write_lock--;
349     else
350         h->p->no_file_read_lock--;
351     if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
352         do_unlock = 1;
353     zebra_mutex_unlock(&h->p->file_mutex);
354
355     if (do_unlock)
356         r = unixLock(h->p->fd, F_UNLCK, F_SETLKW);
357     yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s end",
358             h->p->fd, h, h->p->fname);
359 #endif
360     return r;
361 }
362
363 void zebra_flock_init()
364 {
365     if (!initialized)
366     {
367         log_level = yaz_log_module_level("flock");
368 #if DEBUG_FLOCK
369         log_level = YLOG_LOG|YLOG_FLUSH;
370 #endif
371         initialized = 1;
372         zebra_mutex_init(&lock_list_mutex);
373     }
374     yaz_log(log_level, "zebra_flock_init");
375 }
376
377 /*
378  * Local variables:
379  * c-basic-offset: 4
380  * indent-tabs-mode: nil
381  * End:
382  * vim: shiftwidth=4 tabstop=8 expandtab
383  */
384