Makefile.defs: version set 3.4.0-dev2
[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                         LOG(L_CRIT, "ERROR: lock_get sysv: %s (%d)\n", strerror(errno),
229                                                 errno);
230                         return -1;
231                 }
232         }
233         return 0;
234 }
235
236 inline static void lock_get(gen_lock_t* lock)
237 {
238         struct sembuf sop;
239
240         sop.sem_num=0;
241         sop.sem_op=-1; /* down */
242         sop.sem_flg=0; 
243 tryagain:
244         if (semop(*lock, &sop, 1)==-1){
245                 if (errno==EINTR){
246                         DBG("lock_get: signal received while waiting for on a mutex\n");
247                         goto tryagain;
248                 }else{
249                         LOG(L_CRIT, "ERROR: lock_get sysv: %s (%d)\n", strerror(errno),
250                                                 errno);
251                 }
252         }
253 }
254
255 inline static void lock_release(gen_lock_t* lock)
256 {
257         struct sembuf sop;
258         
259         sop.sem_num=0;
260         sop.sem_op=1; /* up */
261         sop.sem_flg=0; 
262 tryagain:
263         if (semop(*lock, &sop, 1)==-1){
264                 if (errno==EINTR){
265                         /* very improbable*/
266                         DBG("lock_release: signal received while releasing a mutex\n");
267                         goto tryagain;
268                 }else{
269                         LOG(L_CRIT, "ERROR: lock_release sysv: %s (%d)\n",
270                                         strerror(errno), errno);
271                 }
272         }
273 }
274
275
276 #else
277 #error "no locking method selected"
278 #endif
279
280
281 /* lock sets */
282
283 #if defined(FAST_LOCK) || defined(USE_PTHREAD_MUTEX) || \
284         defined(USE_POSIX_SEM) || defined(USE_FUTEX)
285 #define GEN_LOCK_T_PREFERRED
286 #define GEN_LOCK_T_PREFERED  /* backwards compat. */
287 #define GEN_LOCK_T_UNLIMITED
288 #define GEN_LOCK_SET_T_UNLIMITED
289
290 struct gen_lock_set_t_ {
291         long size;
292         gen_lock_t* locks;
293 }; /* must be  aligned (32 bits or 64 depending on the arch)*/
294 typedef struct gen_lock_set_t_ gen_lock_set_t;
295
296
297 #define lock_set_destroy(lock_set) /* do nothing */
298
299 inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s)
300 {
301         int r;
302         for (r=0; r<s->size; r++) if (lock_init(&s->locks[r])==0) return 0;
303         return s;
304 }
305
306 /* WARNING: no boundary checks!*/
307 #define lock_set_try(set, i) lock_try(&set->locks[i])
308 #define lock_set_get(set, i) lock_get(&set->locks[i])
309 #define lock_set_release(set, i) lock_release(&set->locks[i])
310
311 #elif defined(USE_SYSV_SEM)
312 #undef GEN_LOCK_T_PREFERRED
313 #undef GEN_LOCK_T_PREFERED  /* backwards compat. */
314 #undef GEN_LOCK_T_UNLIMITED
315 #undef GEN_LOCK_SET_T_UNLIMITED
316 #define GEN_LOCK_T_LIMITED
317 #define GEN_LOCK_SET_T_LIMITED
318
319 struct gen_lock_set_t_ {
320         int size;
321         int semid;
322 };
323
324
325 typedef struct gen_lock_set_t_ gen_lock_set_t;
326 inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s)
327 {
328         union semun su;
329         int r;
330         int euid;
331
332         euid=geteuid();
333         if (uid && uid!=euid)
334                 seteuid(uid); /* set euid to the cfg. requested one */
335         s->semid=semget(IPC_PRIVATE, s->size, 0700);
336         if (uid && uid!=euid)
337                 seteuid(euid); /* restore euid */
338         if (s->semid==-1){
339                 LOG(L_CRIT, "ERROR: lock_set_init (SYSV): semget (..., %d, 0700)"
340                                 " failed: %s\n",
341                                 s->size, strerror(errno));
342                 return 0;
343         }
344         su.val=1;
345         for (r=0; r<s->size; r++){
346                 if (semctl(s->semid, r, SETVAL, su)==-1){
347                         LOG(L_CRIT, "ERROR: lock_set_init (SYSV): semctl failed on sem %d"
348                                         ": %s\n", r, strerror(errno));
349                         semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
350                         return 0;
351                 }
352         }
353         return s;
354 }
355
356 inline static void lock_set_destroy(gen_lock_set_t* s)
357 {
358         semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
359 }
360
361
362 /* returns 0 if it "gets" the lock, -1 otherwise */
363 inline static int lock_set_try(gen_lock_set_t* s, int n)
364 {
365         struct sembuf sop;
366         
367         sop.sem_num=n;
368         sop.sem_op=-1; /* down */
369         sop.sem_flg=IPC_NOWAIT; 
370 tryagain:
371         if (semop(s->semid, &sop, 1)==-1){
372                 if (errno==EAGAIN){
373                         return -1;
374                 }else if (errno==EINTR){
375                         DBG("lock_get: signal received while waiting for on a mutex\n");
376                         goto tryagain;
377                 }else{
378                         LOG(L_CRIT, "ERROR: lock_get sysv: %s (%d)\n", strerror(errno),
379                                                 errno);
380                         return -1;
381                 }
382         }
383         return 0;
384 }
385
386
387 inline static void lock_set_get(gen_lock_set_t* s, int n)
388 {
389         struct sembuf sop;
390         sop.sem_num=n;
391         sop.sem_op=-1; /* down */
392         sop.sem_flg=0;
393 tryagain:
394         if (semop(s->semid, &sop, 1)==-1){
395                 if (errno==EINTR){
396                         DBG("lock_set_get: signal received while waiting on a mutex\n");
397                         goto tryagain;
398                 }else{
399                         LOG(L_CRIT, "ERROR: lock_set_get sysv: %s (%d)\n",
400                                         strerror(errno), errno);
401                 }
402         }
403 }
404
405 inline static void lock_set_release(gen_lock_set_t* s, int n)
406 {
407         struct sembuf sop;
408         sop.sem_num=n;
409         sop.sem_op=1; /* up */
410         sop.sem_flg=0;
411 tryagain:
412         if (semop(s->semid, &sop, 1)==-1){
413                 if (errno==EINTR){
414                         /* very improbable */
415                         DBG("lock_set_release: signal received while releasing mutex\n");
416                         goto tryagain;
417                 }else{
418                         LOG(L_CRIT, "ERROR: lock_set_release sysv: %s (%d)\n",
419                                         strerror(errno), errno);
420                 }
421         }
422 }
423 #else 
424 #error "no lock set method selected"
425 #endif
426
427
428 #endif