- possible signal interruptions treated for sysv sems
[sip-router] / lock_ops.h
1 /* $Id$ */
2 /*
3  *
4  * Copyright (C) 2001-2003 Fhg Fokus
5  *
6  * This file is part of ser, a free SIP server.
7  *
8  * ser is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * For a license to use the ser software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * ser is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License 
24  * along with this program; if not, write to the Free Software 
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27
28 /*
29  *   ser locking library
30  *   WARNING: do not include this file directly, use instead locking.h
31  *   (unless you don't need to alloc/dealloc locks)
32  *
33  *  2002-12-16  created by andrei
34  *  2003-02-20  s/gen_lock_t/gen_lock_t/ to avoid a type conflict 
35  *               on solaris  (andrei)
36  *  2003-03-05  lock set support added for FAST_LOCK & SYSV (andrei)
37  *  2003-03-06  removed *_alloc,*_dealloc & moved them to lock_alloc.h
38  *              renamed locking.h to lock_ops.h (all this to solve
39  *              the locking.h<->shm_mem.h interdependency) (andrei)
40  *  2003-03-10  lock set support added also for PTHREAD_MUTEX & POSIX_SEM
41  *               (andrei)
42  *  2003-03-17  possible signal interruptions treated for sysv (andrei)
43  *
44 Implements:
45
46         simple locks:
47         -------------
48         gen_lock_t* lock_init(gen_lock_t* lock); - inits the lock
49         void    lock_destroy(gen_lock_t* lock);  - removes the lock (e.g sysv rmid)
50         void    lock_get(gen_lock_t* lock);      - lock (mutex down)
51         void    lock_release(gen_lock_t* lock);  - unlock (mutex up)
52         
53         lock sets: [implemented only for FL & SYSV so far]
54         ----------
55         lock_set_t* lock_set_init(lock_set_t* set);  - inits the lock set
56         void lock_set_destroy(lock_set_t* s);        - removes the lock set
57         void lock_set_get(lock_set_t* s, int i);     - locks sem i from the set
58         void lock_set_release(lock_set_t* s, int i)  - unlocks sem i from the set
59
60 WARNING: signals are not treated! (some locks are "awakened" by the signals)
61 */
62
63 #ifndef _lock_ops_h
64 #define _lock_ops_h
65
66
67 #ifdef FAST_LOCK
68 #include "fastlock.h"
69
70 typedef fl_lock_t gen_lock_t;
71
72
73 #define lock_destroy(lock) /* do nothing */ 
74
75 inline static gen_lock_t* lock_init(gen_lock_t* lock)
76 {
77         init_lock(*lock);
78         return lock;
79 }
80
81 #define lock_get(lock) get_lock(lock)
82 #define lock_release(lock) release_lock(lock)
83
84 #elif defined USE_PTHREAD_MUTEX
85 #include <pthread.h>
86
87 typedef pthread_mutex_t gen_lock_t;
88
89 #define lock_destroy(lock) /* do nothing */ 
90
91 inline static gen_lock_t* lock_init(gen_lock_t* lock)
92 {
93         if (pthread_mutex_init(lock, 0)==0) return lock;
94         else return 0;
95 }
96
97 #define lock_get(lock) pthread_mutex_lock(lock)
98 #define lock_release(lock) pthread_mutex_unlock(lock)
99
100
101
102 #elif defined USE_POSIX_SEM
103 #include <semaphore.h>
104
105 typedef sem_t gen_lock_t;
106
107 #define lock_destroy(lock) /* do nothing */ 
108
109 inline static gen_lock_t* lock_init(gen_lock_t* lock)
110 {
111         if (sem_init(lock, 1, 1)<0) return 0;
112         return lock;
113 }
114
115 #define lock_get(lock) sem_wait(lock)
116 #define lock_release(lock) sem_post(lock)
117
118
119 #elif defined USE_SYSV_SEM
120 #include <sys/ipc.h>
121 #include <sys/sem.h>
122
123 #if ((defined(HAVE_UNION_SEMUN) || defined(__GNU_LIBRARY__) )&& !defined(_SEM_SEMUN_UNDEFINED)) 
124         
125         /* union semun is defined by including sem.h */
126 #else
127         /* according to X/OPEN we have to define it ourselves */
128         union semun {
129                 int val;                      /* value for SETVAL */
130                 struct semid_ds *buf;         /* buffer for IPC_STAT, IPC_SET */
131                 unsigned short int *array;    /* array for GETALL, SETALL */
132                 struct seminfo *__buf;        /* buffer for IPC_INFO */
133         };
134 #endif
135
136 typedef int gen_lock_t;
137
138
139
140
141 inline static gen_lock_t* lock_init(gen_lock_t* lock)
142 {
143         union semun su;
144         
145         *lock=semget(IPC_PRIVATE, 1, 0700);
146         if (*lock==-1) return 0;
147         su.val=1;
148         if (semctl(*lock, 0, SETVAL, su)==-1){
149                 /* init error*/
150                 return 0;
151         }
152         return lock;
153 }
154
155 inline static void lock_destroy(gen_lock_t* lock)
156 {
157         semctl(*lock, 0, IPC_RMID, (union semun)(int)0);
158 }
159
160
161 inline static void lock_get(gen_lock_t* lock)
162 {
163         struct sembuf sop;
164
165         sop.sem_num=0;
166         sop.sem_op=-1; /* down */
167         sop.sem_flg=0; 
168 tryagain:
169         if (semop(*lock, &sop, 1)==-1){
170                 if (errno==EINTR){
171                         DBG("lock_get: signal received while waiting for on a mutex\n");
172                         goto tryagain;
173                 }else{
174                         LOG(L_CRIT, "ERROR: lock_get sysv: %s (%d)\n", strerror(errno),
175                                                 errno);
176                 }
177         }
178 }
179
180 inline static void lock_release(gen_lock_t* lock)
181 {
182         struct sembuf sop;
183         
184         sop.sem_num=0;
185         sop.sem_op=1; /* up */
186         sop.sem_flg=0; 
187 tryagain:
188         if (semop(*lock, &sop, 1)==-1){
189                 if (errno==EINTR){
190                         /* very improbable*/
191                         DBG("lock_release: signal received while releasing a mutex\n");
192                         goto tryagain;
193                 }else{
194                         LOG(L_CRIT, "ERROR: lock_release sysv: %s (%d)\n",
195                                         strerror(errno), errno);
196                 }
197         }
198 }
199
200
201 #else
202 #error "no locking method selected"
203 #endif
204
205
206 /* lock sets */
207
208 #if defined(FAST_LOCK) || defined(USE_PTHREAD_MUTEX) || defined(USE_POSIX_SEM)
209
210 struct lock_set_t_ {
211         long size;
212         gen_lock_t* locks;
213 }; /* must be  aligned (32 bits or 64 depending on the arch)*/
214 typedef struct lock_set_t_ lock_set_t;
215
216
217 #define lock_set_destroy(lock_set) /* do nothing */
218
219 inline static lock_set_t* lock_set_init(lock_set_t* s)
220 {
221         int r;
222         for (r=0; r<s->size; r++) if (lock_init(&s->locks[r])==0) return 0;
223         return s;
224 }
225
226 /* WARNING: no boundary checks!*/
227 #define lock_set_get(set, i) lock_get(&set->locks[i])
228 #define lock_set_release(set, i) lock_release(&set->locks[i])
229
230 #elif defined(USE_SYSV_SEM)
231
232 struct lock_set_t_ {
233         int size;
234         int semid;
235 };
236
237
238 typedef struct lock_set_t_ lock_set_t;
239 inline static lock_set_t* lock_set_init(lock_set_t* s)
240 {
241         union semun su;
242         int r;
243         
244         s->semid=semget(IPC_PRIVATE, s->size, 0700);
245         if (s->semid==-1){
246                 LOG(L_CRIT, "ERROR: lock_set_init (SYSV): semget failed: %s\n",
247                                 strerror(errno));
248                 return 0;
249         }
250         su.val=1;
251         for (r=0; r<s->size; r++){
252                 if (semctl(s->semid, r, SETVAL, su)==-1){
253                         LOG(L_CRIT, "ERROR: lock_set_init (SYSV): semctl failed on sem %d"
254                                         ": %s\n", r, strerror(errno));
255                         semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
256                         return 0;
257                 }
258         }
259         return s;
260 }
261
262 inline static void lock_set_destroy(lock_set_t* s)
263 {
264         semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
265 }
266
267 inline static void lock_set_get(lock_set_t* s, int n)
268 {
269         struct sembuf sop;
270         sop.sem_num=n;
271         sop.sem_op=-1; /* down */
272         sop.sem_flg=0;
273 tryagain:
274         if (semop(s->semid, &sop, 1)==-1){
275                 if (errno==EINTR){
276                         DBG("lock_set_get: signal received while waiting on a mutex\n");
277                         goto tryagain;
278                 }else{
279                         LOG(L_CRIT, "ERROR: lock_set_get sysv: %s (%d)\n",
280                                         strerror(errno), errno);
281                 }
282         }
283 }
284
285 inline static void lock_set_release(lock_set_t* s, int n)
286 {
287         struct sembuf sop;
288         sop.sem_num=n;
289         sop.sem_op=1; /* up */
290         sop.sem_flg=0;
291 tryagain:
292         if (semop(s->semid, &sop, 1)==-1){
293                 if (errno==EINTR){
294                         /* very improbable */
295                         DBG("lock_set_release: signal received while releasing mutex\n");
296                         goto tryagain;
297                 }else{
298                         LOG(L_CRIT, "ERROR: lock_set_release sysv: %s (%d)\n",
299                                         strerror(errno), errno);
300                 }
301         }
302 }
303 #else 
304 #error "no lock set method selected"
305 #endif
306
307
308 #endif