097d5bbdebd381eded5a0c85dbb2b11dc6a099d4
[sip-router] / lock_ops.h
1 /* $Id$ */
2 /*
3  *
4  * Copyright (C) 2001-2003 FhG Fokus
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 /*
20  *   ser locking library
21  *   WARNING: do not include this file directly, use instead locking.h
22  *   (unless you don't need to alloc/dealloc locks)
23  *
24  *  2002-12-16  created by andrei
25  *  2003-02-20  s/gen_lock_t/gen_lock_t/ to avoid a type conflict 
26  *               on solaris  (andrei)
27  *  2003-03-05  lock set support added for FAST_LOCK & SYSV (andrei)
28  *  2003-03-06  removed *_alloc,*_dealloc & moved them to lock_alloc.h
29  *              renamed locking.h to lock_ops.h (all this to solve
30  *              the locking.h<->shm_mem.h interdependency) (andrei)
31  *  2003-03-10  lock set support added also for PTHREAD_MUTEX & POSIX_SEM
32  *               (andrei)
33  *  2003-03-17  possible signal interruptions treated for sysv (andrei)
34  *  2004-07-28  s/lock_set_t/gen_lock_set_t/ because of a type conflict
35  *              on darwin (andrei)
36  *  2006-04-04  added lock_try(lock) and lock_set_try(s,i) (andrei)
37  *  2007-05-13  added futex support (andrei)
38  *
39 Implements:
40
41         simple locks:
42         -------------
43         gen_lock_t* lock_init(gen_lock_t* lock); - inits the lock
44         void    lock_destroy(gen_lock_t* lock);  - removes the lock (e.g sysv rmid)
45         void    lock_get(gen_lock_t* lock);      - lock (mutex down)
46         void    lock_release(gen_lock_t* lock);  - unlock (mutex up)
47         int     lock_try(gen_lock_t* lock);      - tries to get the lock, returns
48                                                     0 on success and !=0 on failure
49         
50         lock sets: 
51         ----------
52         gen_lock_set_t* lock_set_init(gen_lock_set_t* set);  - inits the lock set
53         void lock_set_destroy(gen_lock_set_t* s);        - removes the lock set
54         void lock_set_get(gen_lock_set_t* s, int i);     - locks sem i from the set
55         void lock_set_release(gen_lock_set_t* s, int i)  - unlocks sem i from the
56                                                            set
57         int  lock_set_try(gen_lock_set_t* s, int i);    - tries to lock the sem i,
58                                                           returns 0 on success and
59                                                           !=0 on failure
60         
61         defines:
62         --------
63         GEN_LOCK_T_PREFERRED - defined if using  arrays of gen_lock_t is as good as
64                               using a lock set (gen_lock_set_t). 
65                                                   In general is better to have the locks "close" or 
66                                                   inside the protected data structure rather then 
67                                                   having a separate array or lock set. However in some
68                                                   case (e.g. SYSV_LOCKS) is better to use lock sets,
69                                                   either due to lock number limitations, excesive 
70                                                   performance or memory overhead. In this cases
71                                                   GEN_LOCK_T_PREFERRED will not be defined.
72         GEN_LOCK_T_UNLIMITED - defined if there is no system imposed limit on
73                                the number of locks (other then the memory).
74         GEN_LOCK_SET_T_UNLIMITED
75                               - like above but for the size of a lock set.
76
77 WARNING: - lock_set_init may fail for large number of sems (e.g. sysv). 
78          - signals are not treated! (some locks are "awakened" by the signals)
79 */
80
81 #ifndef _lock_ops_h
82 #define _lock_ops_h
83
84 #ifdef USE_FUTEX
85 #include "futexlock.h"
86 /* if no native atomic ops support => USE_FUTEX will be undefined */
87 #endif
88
89
90 #ifdef USE_FUTEX
91
92 typedef futex_lock_t gen_lock_t;
93
94 #define lock_destroy(lock) /* do nothing */
95 #define lock_init(lock) futex_init(lock)
96 #define lock_try(lock)  futex_try(lock)
97 #define lock_get(lock)  futex_get(lock)
98 #define lock_release(lock) futex_release(lock)
99
100
101 #elif defined FAST_LOCK
102 #include "fastlock.h"
103
104 typedef fl_lock_t gen_lock_t;
105
106
107 #define lock_destroy(lock) /* do nothing */ 
108
109 inline static gen_lock_t* lock_init(gen_lock_t* lock)
110 {
111         init_lock(*lock);
112         return lock;
113 }
114
115 #define lock_try(lock) try_lock(lock)
116 #define lock_get(lock) get_lock(lock)
117 #define lock_release(lock) release_lock(lock)
118
119
120 #elif defined USE_PTHREAD_MUTEX
121 #include <pthread.h>
122
123 typedef pthread_mutex_t gen_lock_t;
124
125 #define lock_destroy(lock) /* do nothing */ 
126
127 inline static gen_lock_t* lock_init(gen_lock_t* lock)
128 {
129         if (pthread_mutex_init(lock, 0)==0) return lock;
130         else return 0;
131 }
132
133 #define lock_try(lock) pthread_mutex_trylock(lock)
134 #define lock_get(lock) pthread_mutex_lock(lock)
135 #define lock_release(lock) pthread_mutex_unlock(lock)
136
137
138
139 #elif defined USE_POSIX_SEM
140 #include <semaphore.h>
141
142 typedef sem_t gen_lock_t;
143
144 #define lock_destroy(lock) /* do nothing */ 
145
146 inline static gen_lock_t* lock_init(gen_lock_t* lock)
147 {
148         if (sem_init(lock, 1, 1)<0) return 0;
149         return lock;
150 }
151
152 #define lock_try(lock) sem_trywait(lock)
153 #define lock_get(lock) sem_wait(lock)
154 #define lock_release(lock) sem_post(lock)
155
156
157 #elif defined USE_SYSV_SEM
158 #include <sys/ipc.h>
159 #include <sys/sem.h>
160
161 #include <errno.h>
162 #include <string.h>
163 #include <sys/types.h>
164 #include <unistd.h>
165 #include "dprint.h"
166 #include "globals.h" /* uid */
167
168 #if ((defined(HAVE_UNION_SEMUN) || defined(__GNU_LIBRARY__) )&& !defined(_SEM_SEMUN_UNDEFINED)) 
169         
170         /* union semun is defined by including sem.h */
171 #else
172         /* according to X/OPEN we have to define it ourselves */
173         union semun {
174                 int val;                      /* value for SETVAL */
175                 struct semid_ds *buf;         /* buffer for IPC_STAT, IPC_SET */
176                 unsigned short int *array;    /* array for GETALL, SETALL */
177                 struct seminfo *__buf;        /* buffer for IPC_INFO */
178         };
179 #endif
180
181 typedef int gen_lock_t;
182
183
184
185
186 inline static gen_lock_t* lock_init(gen_lock_t* lock)
187 {
188         union semun su;
189         int euid;
190         
191         euid=geteuid();
192         if (uid && uid!=euid)
193                 seteuid(uid); /* set euid to the cfg. requested one */
194         *lock=semget(IPC_PRIVATE, 1, 0700);
195         if (uid && uid!=euid)
196                 seteuid(euid); /* restore it */
197         if (*lock==-1) return 0;
198         su.val=1;
199         if (semctl(*lock, 0, SETVAL, su)==-1){
200                 /* init error*/
201                 return 0;
202         }
203         return lock;
204 }
205
206 inline static void lock_destroy(gen_lock_t* lock)
207 {
208         semctl(*lock, 0, IPC_RMID, (union semun)(int)0);
209 }
210
211
212 /* returns 0 if it got the lock, -1 otherwise */
213 inline static int lock_try(gen_lock_t* lock)
214 {
215         struct sembuf sop;
216
217         sop.sem_num=0;
218         sop.sem_op=-1; /* down */
219         sop.sem_flg=IPC_NOWAIT; 
220 tryagain:
221         if (semop(*lock, &sop, 1)==-1){
222                 if (errno==EAGAIN){
223                         return -1;
224                 }else if (errno==EINTR){
225                         DBG("lock_get: signal received while waiting for on a mutex\n");
226                         goto tryagain;
227                 }else{
228                         LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
229                         return -1;
230                 }
231         }
232         return 0;
233 }
234
235 inline static void lock_get(gen_lock_t* lock)
236 {
237         struct sembuf sop;
238
239         sop.sem_num=0;
240         sop.sem_op=-1; /* down */
241         sop.sem_flg=0; 
242 tryagain:
243         if (semop(*lock, &sop, 1)==-1){
244                 if (errno==EINTR){
245                         DBG("lock_get: signal received while waiting for on a mutex\n");
246                         goto tryagain;
247                 }else{
248                         LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
249                 }
250         }
251 }
252
253 inline static void lock_release(gen_lock_t* lock)
254 {
255         struct sembuf sop;
256         
257         sop.sem_num=0;
258         sop.sem_op=1; /* up */
259         sop.sem_flg=0; 
260 tryagain:
261         if (semop(*lock, &sop, 1)==-1){
262                 if (errno==EINTR){
263                         /* very improbable*/
264                         DBG("lock_release: signal received while releasing a mutex\n");
265                         goto tryagain;
266                 }else{
267                         LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
268                 }
269         }
270 }
271
272
273 #else
274 #error "no locking method selected"
275 #endif
276
277
278 /* lock sets */
279
280 #if defined(FAST_LOCK) || defined(USE_PTHREAD_MUTEX) || \
281         defined(USE_POSIX_SEM) || defined(USE_FUTEX)
282 #define GEN_LOCK_T_PREFERRED
283 #define GEN_LOCK_T_PREFERED  /* backwards compat. */
284 #define GEN_LOCK_T_UNLIMITED
285 #define GEN_LOCK_SET_T_UNLIMITED
286
287 struct gen_lock_set_t_ {
288         long size;
289         gen_lock_t* locks;
290 }; /* must be  aligned (32 bits or 64 depending on the arch)*/
291 typedef struct gen_lock_set_t_ gen_lock_set_t;
292
293
294 #define lock_set_destroy(lock_set) /* do nothing */
295
296 inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s)
297 {
298         int r;
299         for (r=0; r<s->size; r++) if (lock_init(&s->locks[r])==0) return 0;
300         return s;
301 }
302
303 /* WARNING: no boundary checks!*/
304 #define lock_set_try(set, i) lock_try(&set->locks[i])
305 #define lock_set_get(set, i) lock_get(&set->locks[i])
306 #define lock_set_release(set, i) lock_release(&set->locks[i])
307
308 #elif defined(USE_SYSV_SEM)
309 #undef GEN_LOCK_T_PREFERRED
310 #undef GEN_LOCK_T_PREFERED  /* backwards compat. */
311 #undef GEN_LOCK_T_UNLIMITED
312 #undef GEN_LOCK_SET_T_UNLIMITED
313 #define GEN_LOCK_T_LIMITED
314 #define GEN_LOCK_SET_T_LIMITED
315
316 struct gen_lock_set_t_ {
317         int size;
318         int semid;
319 };
320
321
322 typedef struct gen_lock_set_t_ gen_lock_set_t;
323 inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s)
324 {
325         union semun su;
326         int r;
327         int euid;
328
329         euid=geteuid();
330         if (uid && uid!=euid)
331                 seteuid(uid); /* set euid to the cfg. requested one */
332         s->semid=semget(IPC_PRIVATE, s->size, 0700);
333         if (uid && uid!=euid)
334                 seteuid(euid); /* restore euid */
335         if (s->semid==-1){
336                 LM_CRIT("(SYSV): semget (..., %d, 0700) failed: %s\n",
337                                 s->size, strerror(errno));
338                 return 0;
339         }
340         su.val=1;
341         for (r=0; r<s->size; r++){
342                 if (semctl(s->semid, r, SETVAL, su)==-1){
343                         LM_CRIT("(SYSV): semctl failed on sem %d: %s\n", r, strerror(errno));
344                         semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
345                         return 0;
346                 }
347         }
348         return s;
349 }
350
351 inline static void lock_set_destroy(gen_lock_set_t* s)
352 {
353         semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
354 }
355
356
357 /* returns 0 if it "gets" the lock, -1 otherwise */
358 inline static int lock_set_try(gen_lock_set_t* s, int n)
359 {
360         struct sembuf sop;
361         
362         sop.sem_num=n;
363         sop.sem_op=-1; /* down */
364         sop.sem_flg=IPC_NOWAIT; 
365 tryagain:
366         if (semop(s->semid, &sop, 1)==-1){
367                 if (errno==EAGAIN){
368                         return -1;
369                 }else if (errno==EINTR){
370                         DBG("lock_get: signal received while waiting for on a mutex\n");
371                         goto tryagain;
372                 }else{
373                         LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
374                         return -1;
375                 }
376         }
377         return 0;
378 }
379
380
381 inline static void lock_set_get(gen_lock_set_t* s, int n)
382 {
383         struct sembuf sop;
384         sop.sem_num=n;
385         sop.sem_op=-1; /* down */
386         sop.sem_flg=0;
387 tryagain:
388         if (semop(s->semid, &sop, 1)==-1){
389                 if (errno==EINTR){
390                         DBG("lock_set_get: signal received while waiting on a mutex\n");
391                         goto tryagain;
392                 }else{
393                         LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
394                 }
395         }
396 }
397
398 inline static void lock_set_release(gen_lock_set_t* s, int n)
399 {
400         struct sembuf sop;
401         sop.sem_num=n;
402         sop.sem_op=1; /* up */
403         sop.sem_flg=0;
404 tryagain:
405         if (semop(s->semid, &sop, 1)==-1){
406                 if (errno==EINTR){
407                         /* very improbable */
408                         DBG("lock_set_release: signal received while releasing mutex\n");
409                         goto tryagain;
410                 }else{
411                         LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
412                 }
413         }
414 }
415 #else 
416 #error "no lock set method selected"
417 #endif
418
419
420 #endif