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