d05b8674ec6634eac740ccc5c4f3c600dd9023e1
[strongswan.git] / src / libstrongswan / threading / mutex.c
1 /*
2 * Copyright (C) 2008-2009 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include <pthread.h>
19 #include <stdint.h>
20 #include <time.h>
21 #include <errno.h>
22
23 #include <library.h>
24 #include <debug.h>
25
26 #include "mutex.h"
27 #include "lock_profiler.h"
28
29 typedef struct private_mutex_t private_mutex_t;
30 typedef struct private_r_mutex_t private_r_mutex_t;
31 typedef struct private_condvar_t private_condvar_t;
32
33 /**
34 * private data of mutex
35 */
36 struct private_mutex_t {
37
38 /**
39 * public functions
40 */
41 mutex_t public;
42
43 /**
44 * wrapped pthread mutex
45 */
46 pthread_mutex_t mutex;
47
48 /**
49 * is this a recursiv emutex, implementing private_r_mutex_t?
50 */
51 bool recursive;
52
53 /**
54 * profiling info, if enabled
55 */
56 lock_profile_t profile;
57 };
58
59 /**
60 * private data of mutex, extended by recursive locking information
61 */
62 struct private_r_mutex_t {
63
64 /**
65 * Extends private_mutex_t
66 */
67 private_mutex_t generic;
68
69 /**
70 * thread which currently owns mutex
71 */
72 pthread_t thread;
73
74 /**
75 * times we have locked the lock, stored per thread
76 */
77 pthread_key_t times;
78 };
79
80 /**
81 * private data of condvar
82 */
83 struct private_condvar_t {
84
85 /**
86 * public functions
87 */
88 condvar_t public;
89
90 /**
91 * wrapped pthread condvar
92 */
93 pthread_cond_t condvar;
94
95 };
96
97
98
99 /**
100 * Implementation of mutex_t.lock.
101 */
102 static void lock(private_mutex_t *this)
103 {
104 int err;
105
106 profiler_start(&this->profile);
107 err = pthread_mutex_lock(&this->mutex);
108 if (err)
109 {
110 DBG1("!!! MUTEX LOCK ERROR: %s !!!", strerror(err));
111 }
112 profiler_end(&this->profile);
113 }
114
115 /**
116 * Implementation of mutex_t.unlock.
117 */
118 static void unlock(private_mutex_t *this)
119 {
120 int err;
121
122 err = pthread_mutex_unlock(&this->mutex);
123 if (err)
124 {
125 DBG1("!!! MUTEX UNLOCK ERROR: %s !!!", strerror(err));
126 }
127 }
128
129 /**
130 * Implementation of mutex_t.lock.
131 */
132 static void lock_r(private_r_mutex_t *this)
133 {
134 pthread_t self = pthread_self();
135
136 if (this->thread == self)
137 {
138 uintptr_t times;
139
140 /* times++ */
141 times = (uintptr_t)pthread_getspecific(this->times);
142 pthread_setspecific(this->times, (void*)times + 1);
143 }
144 else
145 {
146 lock(&this->generic);
147 this->thread = self;
148 /* times = 1 */
149 pthread_setspecific(this->times, (void*)1);
150 }
151 }
152
153 /**
154 * Implementation of mutex_t.unlock.
155 */
156 static void unlock_r(private_r_mutex_t *this)
157 {
158 uintptr_t times;
159
160 /* times-- */
161 times = (uintptr_t)pthread_getspecific(this->times);
162 pthread_setspecific(this->times, (void*)--times);
163
164 if (times == 0)
165 {
166 this->thread = 0;
167 unlock(&this->generic);
168 }
169 }
170
171 /**
172 * Implementation of mutex_t.destroy
173 */
174 static void mutex_destroy(private_mutex_t *this)
175 {
176 profiler_cleanup(&this->profile);
177 pthread_mutex_destroy(&this->mutex);
178 free(this);
179 }
180
181 /**
182 * Implementation of mutex_t.destroy for recursive mutex'
183 */
184 static void mutex_destroy_r(private_r_mutex_t *this)
185 {
186 profiler_cleanup(&this->generic.profile);
187 pthread_mutex_destroy(&this->generic.mutex);
188 pthread_key_delete(this->times);
189 free(this);
190 }
191
192 /*
193 * see header file
194 */
195 mutex_t *mutex_create(mutex_type_t type)
196 {
197 switch (type)
198 {
199 case MUTEX_TYPE_RECURSIVE:
200 {
201 private_r_mutex_t *this = malloc_thing(private_r_mutex_t);
202
203 this->generic.public.lock = (void(*)(mutex_t*))lock_r;
204 this->generic.public.unlock = (void(*)(mutex_t*))unlock_r;
205 this->generic.public.destroy = (void(*)(mutex_t*))mutex_destroy_r;
206
207 pthread_mutex_init(&this->generic.mutex, NULL);
208 pthread_key_create(&this->times, NULL);
209 this->generic.recursive = TRUE;
210 profiler_init(&this->generic.profile);
211 this->thread = 0;
212
213 return &this->generic.public;
214 }
215 case MUTEX_TYPE_DEFAULT:
216 default:
217 {
218 private_mutex_t *this = malloc_thing(private_mutex_t);
219
220 this->public.lock = (void(*)(mutex_t*))lock;
221 this->public.unlock = (void(*)(mutex_t*))unlock;
222 this->public.destroy = (void(*)(mutex_t*))mutex_destroy;
223
224 pthread_mutex_init(&this->mutex, NULL);
225 this->recursive = FALSE;
226 profiler_init(&this->profile);
227
228 return &this->public;
229 }
230 }
231 }
232
233
234
235 /**
236 * Implementation of condvar_t.wait.
237 */
238 static void _wait(private_condvar_t *this, private_mutex_t *mutex)
239 {
240 if (mutex->recursive)
241 {
242 private_r_mutex_t* recursive = (private_r_mutex_t*)mutex;
243
244 /* mutex owner gets cleared during condvar wait */
245 recursive->thread = 0;
246 pthread_cond_wait(&this->condvar, &mutex->mutex);
247 recursive->thread = pthread_self();
248 }
249 else
250 {
251 pthread_cond_wait(&this->condvar, &mutex->mutex);
252 }
253 }
254
255 /**
256 * Implementation of condvar_t.timed_wait_abs.
257 */
258 static bool timed_wait_abs(private_condvar_t *this, private_mutex_t *mutex,
259 timeval_t time)
260 {
261 struct timespec ts;
262 bool timed_out;
263
264 ts.tv_sec = time.tv_sec;
265 ts.tv_nsec = time.tv_usec * 1000;
266
267 if (mutex->recursive)
268 {
269 private_r_mutex_t* recursive = (private_r_mutex_t*)mutex;
270
271 recursive->thread = 0;
272 timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex,
273 &ts) == ETIMEDOUT;
274 recursive->thread = pthread_self();
275 }
276 else
277 {
278 timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex,
279 &ts) == ETIMEDOUT;
280 }
281 return timed_out;
282 }
283
284 /**
285 * Implementation of condvar_t.timed_wait.
286 */
287 static bool timed_wait(private_condvar_t *this, private_mutex_t *mutex,
288 u_int timeout)
289 {
290 timeval_t tv;
291 u_int s, ms;
292
293 time_monotonic(&tv);
294
295 s = timeout / 1000;
296 ms = timeout % 1000;
297
298 tv.tv_sec += s;
299 tv.tv_usec += ms * 1000;
300
301 if (tv.tv_usec > 1000000 /* 1s */)
302 {
303 tv.tv_usec -= 1000000;
304 tv.tv_sec++;
305 }
306 return timed_wait_abs(this, mutex, tv);
307 }
308
309 /**
310 * Implementation of condvar_t.signal.
311 */
312 static void _signal(private_condvar_t *this)
313 {
314 pthread_cond_signal(&this->condvar);
315 }
316
317 /**
318 * Implementation of condvar_t.broadcast.
319 */
320 static void broadcast(private_condvar_t *this)
321 {
322 pthread_cond_broadcast(&this->condvar);
323 }
324
325 /**
326 * Implementation of condvar_t.destroy
327 */
328 static void condvar_destroy(private_condvar_t *this)
329 {
330 pthread_cond_destroy(&this->condvar);
331 free(this);
332 }
333
334 /*
335 * see header file
336 */
337 condvar_t *condvar_create(condvar_type_t type)
338 {
339 switch (type)
340 {
341 case CONDVAR_TYPE_DEFAULT:
342 default:
343 {
344 pthread_condattr_t condattr;
345 private_condvar_t *this = malloc_thing(private_condvar_t);
346
347 this->public.wait = (void(*)(condvar_t*, mutex_t *mutex))_wait;
348 this->public.timed_wait = (bool(*)(condvar_t*, mutex_t *mutex, u_int timeout))timed_wait;
349 this->public.timed_wait_abs = (bool(*)(condvar_t*, mutex_t *mutex, timeval_t time))timed_wait_abs;
350 this->public.signal = (void(*)(condvar_t*))_signal;
351 this->public.broadcast = (void(*)(condvar_t*))broadcast;
352 this->public.destroy = (void(*)(condvar_t*))condvar_destroy;
353
354 pthread_condattr_init(&condattr);
355 #ifdef HAVE_CONDATTR_CLOCK_MONOTONIC
356 pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
357 #endif
358 pthread_cond_init(&this->condvar, &condattr);
359 pthread_condattr_destroy(&condattr);
360
361 return &this->public;
362 }
363 }
364 }
365