Experiments with file locks
[idzebra-moved-to-github.git] / util / flock.c
1 /* $Id: flock.c,v 1.10 2006-06-30 11:10:17 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 #define DEBUG_FLOCK 1
44
45 #define ALWAYS_FLOCK 1
46
47 /** have this module (mutex) been initialized? */
48 static int initialized = 0;
49
50 /** mutex for lock_list below */
51 Zebra_mutex lock_list_mutex;
52
53 /** our list of file locked files */
54 static struct zebra_lock_info *lock_list = 0;
55
56 /** the internal handle, with a pointer to each lock file info */
57 struct zebra_lock_handle {
58 #ifndef WIN32
59     /** so we can call zebra_lock_rdwr_wunlock or zebra_lock_lock_runlock */
60     int write_flag;
61 #endif
62     struct zebra_lock_info *p;
63 };
64
65 struct zebra_lock_info {
66     /** file descriptor */
67     int fd;
68     /** full path (xmalloc'ed) */
69     char *fname;
70     /** reference counter: number of zebra_lock_handles pointing to us */
71     int ref_count;
72 #ifndef WIN32
73     /** number of file write locks/read locks */
74     int no_file_write_lock;
75     int no_file_read_lock;
76     Zebra_lock_rdwr rdwr_lock;
77     Zebra_mutex file_mutex;
78 #endif
79     /** next in lock list */
80     struct zebra_lock_info *next;
81 };
82
83 static int log_level = 0;
84
85 char *zebra_mk_fname(const char *dir, const char *name)
86 {
87     int dlen = dir ? strlen(dir) : 0;
88     char *fname = xmalloc(dlen + strlen(name) + 3);
89     
90 #ifdef WIN32
91     if (dlen)
92     {
93         int last_one = dir[dlen-1];
94         
95         if (!strchr("/\\:", last_one))
96             sprintf(fname, "%s\\%s", dir, name);
97         else
98             sprintf(fname, "%s%s", dir, name);
99     }
100     else
101         sprintf(fname, "%s", name);
102 #else
103     if (dlen)
104     {
105         int last_one = dir[dlen-1];
106
107         if (!strchr("/", last_one))
108             sprintf(fname, "%s/%s", dir, name);
109         else
110             sprintf(fname, "%s%s", dir, name);
111     }
112     else
113         sprintf(fname, "%s", name);
114 #endif
115     return fname;
116 }
117
118 ZebraLockHandle zebra_lock_create(const char *dir, const char *name)
119 {
120     char *fname = zebra_mk_fname(dir, name);
121     struct zebra_lock_info *p = 0;
122     ZebraLockHandle h = 0;
123
124     assert(initialized);
125
126     zebra_mutex_lock(&lock_list_mutex);
127 #ifndef WIN32 
128     for (p = lock_list; p ; p = p->next)
129         if (!strcmp(p->fname, fname))
130             break;
131 #endif
132     if (!p)
133     {
134         p = (struct zebra_lock_info *) xmalloc(sizeof(*p));
135         
136         p->ref_count = 0;
137 #ifdef WIN32
138         p->fd = open(name, O_BINARY|O_RDONLY);
139         if (p->fd == -1)
140             p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
141 #else
142         p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
143 #endif
144         if (p->fd == -1)
145         {
146             xfree(p);
147             yaz_log(YLOG_WARN | YLOG_ERRNO, 
148                     "zebra_lock_create fail fname=%s", fname);
149             p = 0;
150         }
151         else
152         {
153             p->fname = fname;
154             fname = 0;  /* fname buffer now owned by p->fname */
155 #ifndef WIN32
156             zebra_lock_rdwr_init(&p->rdwr_lock);
157             zebra_mutex_init(&p->file_mutex);
158             p->no_file_write_lock = 0;
159             p->no_file_read_lock = 0;
160 #endif
161             p->next = lock_list;
162             lock_list = p;
163         }
164     }
165     if (p)
166     {
167         p->ref_count++;
168         h = (ZebraLockHandle) xmalloc(sizeof(*h));
169         h->p = p;
170 #ifndef WIN32
171         h->write_flag = 0;
172 #endif
173         yaz_log(log_level, "zebra_lock_create fd=%d p=%p fname=%s",
174                 h->p->fd, h, p->fname);
175     }
176     zebra_mutex_unlock(&lock_list_mutex);
177     xfree(fname); /* free it - if it's still there */
178
179     return h;
180 }
181
182 void zebra_lock_destroy(ZebraLockHandle h)
183 {
184     if (!h)
185         return;
186     yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s",
187             h->p->fd, h, h->p->fname);
188     zebra_mutex_lock(&lock_list_mutex);
189     yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s refcount=%d",
190             h->p->fd, h, h->p->fname, h->p->ref_count);
191     assert(h->p->ref_count > 0);
192     --(h->p->ref_count);
193     if (h->p->ref_count == 0)
194     {
195         struct zebra_lock_info **hp = &lock_list;
196         yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s remove",
197                 h->p->fd, h, h->p->fname);
198         while (*hp)
199         {
200             if (*hp == h->p)
201             {
202                 *hp = h->p->next;
203                 break;
204             }
205             else
206                 hp = &(*hp)->next;
207         }
208 #ifndef WIN32
209         zebra_lock_rdwr_destroy(&h->p->rdwr_lock);
210         zebra_mutex_destroy(&h->p->file_mutex);
211 #endif
212         if (h->p->fd != -1)
213             close(h->p->fd);
214         xfree(h->p->fname);
215         xfree(h->p);
216     }
217     xfree(h);
218     zebra_mutex_unlock(&lock_list_mutex);
219 }
220
221 #ifndef WIN32
222 static int unixLock(int fd, int type, int cmd)
223 {
224     struct flock area;
225     int r;
226     area.l_type = type;
227     area.l_whence = SEEK_SET;
228     area.l_len = area.l_start = 0L;
229
230     yaz_log(log_level, "fcntl begin type=%d fd=%d", type, fd);
231     r = fcntl(fd, cmd, &area);
232     if (r == -1)
233         yaz_log(YLOG_WARN|YLOG_ERRNO, "fcntl FAIL type=%d fd=%d", type, fd);
234     else
235         yaz_log(log_level, "fcntl type=%d OK fd=%d", type, fd);
236     
237     return r;
238 }
239 #endif
240
241 int zebra_lock_w(ZebraLockHandle h)
242 {
243     int r;
244     int do_lock = ALWAYS_FLOCK;
245     yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s begin", 
246             h->p->fd, h, h->p->fname);
247     
248 #ifdef WIN32
249     while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
250         ;
251 #else
252     zebra_mutex_lock(&h->p->file_mutex);
253     if (h->p->no_file_write_lock == 0)
254         do_lock = 1;
255     h->p->no_file_write_lock++;
256     zebra_mutex_unlock(&h->p->file_mutex);
257     if (do_lock)
258     {
259         yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s 2", 
260                 h->p->fd, h, h->p->fname);
261         /* if there is already a read lock.. upgrade to write lock */
262         r = unixLock(h->p->fd, F_WRLCK, F_SETLKW);
263     }
264     yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s 3", 
265             h->p->fd, h, h->p->fname);
266
267     zebra_lock_rdwr_wlock(&h->p->rdwr_lock);
268     h->write_flag = 1;
269     yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s end", 
270             h->p->fd, h, h->p->fname);
271 #endif
272
273     return r;
274 }
275
276 int zebra_lock_r(ZebraLockHandle h)
277 {
278     int r;
279     int do_lock = ALWAYS_FLOCK;
280
281     yaz_log(log_level, "zebra_lock_r fd=%d p=%p fname=%s", 
282             h->p->fd, h, h->p->fname);
283 #ifdef WIN32
284     while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
285         ;
286 #else
287     zebra_mutex_lock(&h->p->file_mutex);
288     if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
289         do_lock = 1;
290     h->p->no_file_read_lock++;
291     zebra_mutex_unlock(&h->p->file_mutex);
292     
293     if (do_lock)
294     {
295         /* only read lock if no write locks already */
296         r = unixLock(h->p->fd, F_RDLCK, F_SETLKW);
297     }
298
299     zebra_lock_rdwr_rlock(&h->p->rdwr_lock);
300     h->write_flag = 0;
301 #endif
302     return r;
303 }
304
305 int zebra_unlock(ZebraLockHandle h)
306 {
307     int r = 0;
308     int do_unlock = ALWAYS_FLOCK;
309     yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s begin",
310             h->p->fd, h, h->p->fname);
311 #ifdef WIN32
312     r = _locking(h->p->fd, _LK_UNLCK, 1);
313 #else
314     if (h->write_flag)
315         zebra_lock_rdwr_wunlock(&h->p->rdwr_lock);
316     else
317         zebra_lock_rdwr_runlock(&h->p->rdwr_lock);
318
319     zebra_mutex_lock(&h->p->file_mutex);
320     if (h->write_flag)
321         h->p->no_file_write_lock--;
322     else
323         h->p->no_file_read_lock--;
324     if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
325     {
326         do_unlock = 1;
327         yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s 2",
328                 h->p->fd, h, h->p->fname);
329     }
330     zebra_mutex_unlock(&h->p->file_mutex);
331
332     if (do_unlock)
333         r = unixLock(h->p->fd, F_UNLCK, F_SETLKW);
334     yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s end",
335             h->p->fd, h, h->p->fname);
336 #endif
337     return r;
338 }
339
340 void zebra_flock_init()
341 {
342     if (!initialized)
343     {
344         log_level = yaz_log_module_level("flock");
345 #if DEBUG_FLOCK
346         log_level = YLOG_LOG|YLOG_FLUSH;
347 #endif
348         initialized = 1;
349         zebra_mutex_init(&lock_list_mutex);
350     }
351     yaz_log(log_level, "zebra_flock_init");
352 }
353
354 /*
355  * Local variables:
356  * c-basic-offset: 4
357  * indent-tabs-mode: nil
358  * End:
359  * vim: shiftwidth=4 tabstop=8 expandtab
360  */
361