3dc274e8c27f57b74dd73e72438814ff3e15c463
[strongswan.git] / src / libstrongswan / utils / mutex.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 *
15 * $Id$
16 */
17
18 #define _GNU_SOURCE
19 #include <pthread.h>
20 #include <sys/time.h>
21 #include <stdint.h>
22 #include <time.h>
23 #include <errno.h>
24
25 #include "mutex.h"
26
27 #include <library.h>
28 #include <debug.h>
29
30 typedef struct private_mutex_t private_mutex_t;
31 typedef struct private_r_mutex_t private_r_mutex_t;
32 typedef struct private_condvar_t private_condvar_t;
33 typedef struct private_rwlock_t private_rwlock_t;
34
35 #ifdef LOCK_PROFILER
36
37 /**
38 * Do not report mutexes with an overall waiting time smaller than this (in us)
39 */
40 #define PROFILE_TRESHHOLD 1000
41
42 #include <utils/backtrace.h>
43
44 typedef struct lock_profile_t lock_profile_t;
45
46 struct lock_profile_t {
47
48 /**
49 * how long threads have waited for the lock in this mutex so far
50 */
51 struct timeval waited;
52
53 /**
54 * backtrace where mutex has been created
55 */
56 backtrace_t *backtrace;
57 };
58
59 /**
60 * Print and cleanup mutex profiler
61 */
62 static void profiler_cleanup(lock_profile_t *profile)
63 {
64 if (profile->waited.tv_sec > 0 ||
65 profile->waited.tv_usec > PROFILE_TRESHHOLD)
66 {
67 fprintf(stderr, "%d.%06ds in lock created at:",
68 profile->waited.tv_sec, profile->waited.tv_usec);
69 profile->backtrace->log(profile->backtrace, stderr);
70 }
71 profile->backtrace->destroy(profile->backtrace);
72 }
73
74 /**
75 * Initialize mutex profiler
76 */
77 static void profiler_init(lock_profile_t *profile)
78 {
79 profile->backtrace = backtrace_create(3);
80 timerclear(&profile->waited);
81 }
82
83 #define profiler_start(profile) { \
84 struct timeval _start, _end, _diff; \
85 gettimeofday(&_start, NULL);
86
87 #define profiler_end(profile) \
88 gettimeofday(&_end, NULL); \
89 timersub(&_end, &_start, &_diff); \
90 timeradd(&(profile)->waited, &_diff, &(profile)->waited); }
91
92 #else /* !LOCK_PROFILER */
93
94 #define lock_profile_t struct {}
95 #define profiler_cleanup(...) {}
96 #define profiler_init(...) {}
97 #define profiler_start(...) {}
98 #define profiler_end(...) {}
99
100 #endif /* LOCK_PROFILER */
101
102 /**
103 * private data of mutex
104 */
105 struct private_mutex_t {
106
107 /**
108 * public functions
109 */
110 mutex_t public;
111
112 /**
113 * wrapped pthread mutex
114 */
115 pthread_mutex_t mutex;
116
117 /**
118 * is this a recursiv emutex, implementing private_r_mutex_t?
119 */
120 bool recursive;
121
122 /**
123 * profiling info, if enabled
124 */
125 lock_profile_t profile;
126 };
127
128 /**
129 * private data of mutex, extended by recursive locking information
130 */
131 struct private_r_mutex_t {
132
133 /**
134 * Extends private_mutex_t
135 */
136 private_mutex_t generic;
137
138 /**
139 * thread which currently owns mutex
140 */
141 pthread_t thread;
142
143 /**
144 * times we have locked the lock, stored per thread
145 */
146 pthread_key_t times;
147 };
148
149 /**
150 * private data of condvar
151 */
152 struct private_condvar_t {
153
154 /**
155 * public functions
156 */
157 condvar_t public;
158
159 /**
160 * wrapped pthread condvar
161 */
162 pthread_cond_t condvar;
163 };
164
165 /**
166 * private data of rwlock
167 */
168 struct private_rwlock_t {
169
170 /**
171 * public functions
172 */
173 rwlock_t public;
174
175 /**
176 * wrapped pthread rwlock
177 */
178 pthread_rwlock_t rwlock;
179
180 /**
181 * profiling info, if enabled
182 */
183 lock_profile_t profile;
184 };
185
186 /**
187 * Implementation of mutex_t.lock.
188 */
189 static void lock(private_mutex_t *this)
190 {
191 profiler_start(&this->profile);
192 if (pthread_mutex_lock(&this->mutex))
193 {
194 DBG1("!!!! MUTEX %sLOCK ERROR, your code is buggy !!!", "");
195 }
196 profiler_end(&this->profile);
197 }
198
199 /**
200 * Implementation of mutex_t.unlock.
201 */
202 static void unlock(private_mutex_t *this)
203 {
204 if (pthread_mutex_unlock(&this->mutex))
205 {
206 DBG1("!!!! MUTEX %sLOCK ERROR, your code is buggy !!!", "UN");
207 }
208 }
209
210 /**
211 * Implementation of mutex_t.lock.
212 */
213 static void lock_r(private_r_mutex_t *this)
214 {
215 pthread_t self = pthread_self();
216
217 if (this->thread == self)
218 {
219 uintptr_t times;
220
221 /* times++ */
222 times = (uintptr_t)pthread_getspecific(this->times);
223 pthread_setspecific(this->times, (void*)times + 1);
224 }
225 else
226 {
227 lock(&this->generic);
228 this->thread = self;
229 /* times = 1 */
230 pthread_setspecific(this->times, (void*)1);
231 }
232 }
233
234 /**
235 * Implementation of mutex_t.unlock.
236 */
237 static void unlock_r(private_r_mutex_t *this)
238 {
239 uintptr_t times;
240
241 /* times-- */
242 times = (uintptr_t)pthread_getspecific(this->times);
243 pthread_setspecific(this->times, (void*)--times);
244
245 if (times == 0)
246 {
247 this->thread = 0;
248 unlock(&this->generic);
249 }
250 }
251
252 /**
253 * Implementation of mutex_t.destroy
254 */
255 static void mutex_destroy(private_mutex_t *this)
256 {
257 profiler_cleanup(&this->profile);
258 pthread_mutex_destroy(&this->mutex);
259 free(this);
260 }
261
262 /**
263 * Implementation of mutex_t.destroy for recursive mutex'
264 */
265 static void mutex_destroy_r(private_r_mutex_t *this)
266 {
267 profiler_cleanup(&this->generic.profile);
268 pthread_mutex_destroy(&this->generic.mutex);
269 pthread_key_delete(this->times);
270 free(this);
271 }
272
273 /*
274 * see header file
275 */
276 mutex_t *mutex_create(mutex_type_t type)
277 {
278 switch (type)
279 {
280 case MUTEX_RECURSIVE:
281 {
282 private_r_mutex_t *this = malloc_thing(private_r_mutex_t);
283
284 this->generic.public.lock = (void(*)(mutex_t*))lock_r;
285 this->generic.public.unlock = (void(*)(mutex_t*))unlock_r;
286 this->generic.public.destroy = (void(*)(mutex_t*))mutex_destroy_r;
287
288 pthread_mutex_init(&this->generic.mutex, NULL);
289 pthread_key_create(&this->times, NULL);
290 this->generic.recursive = TRUE;
291 profiler_init(&this->generic.profile);
292 this->thread = 0;
293
294 return &this->generic.public;
295 }
296 case MUTEX_DEFAULT:
297 default:
298 {
299 private_mutex_t *this = malloc_thing(private_mutex_t);
300
301 this->public.lock = (void(*)(mutex_t*))lock;
302 this->public.unlock = (void(*)(mutex_t*))unlock;
303 this->public.destroy = (void(*)(mutex_t*))mutex_destroy;
304
305 pthread_mutex_init(&this->mutex, NULL);
306 this->recursive = FALSE;
307 profiler_init(&this->profile);
308
309 return &this->public;
310 }
311 }
312 }
313
314 /**
315 * Implementation of condvar_t.wait.
316 */
317 static void wait(private_condvar_t *this, private_mutex_t *mutex)
318 {
319 if (mutex->recursive)
320 {
321 private_r_mutex_t* recursive = (private_r_mutex_t*)mutex;
322
323 /* mutex owner gets cleared during condvar wait */
324 recursive->thread = 0;
325 pthread_cond_wait(&this->condvar, &mutex->mutex);
326 recursive->thread = pthread_self();
327 }
328 else
329 {
330 pthread_cond_wait(&this->condvar, &mutex->mutex);
331 }
332 }
333
334 /**
335 * Implementation of condvar_t.timed_wait.
336 */
337 static bool timed_wait(private_condvar_t *this, private_mutex_t *mutex,
338 u_int timeout)
339 {
340 struct timespec ts;
341 struct timeval tv;
342 u_int s, ms;
343 bool timed_out;
344
345 gettimeofday(&tv, NULL);
346
347 s = timeout / 1000;
348 ms = timeout % 1000;
349
350 ts.tv_sec = tv.tv_sec + s;
351 ts.tv_nsec = tv.tv_usec * 1000 + ms * 1000000;
352 if (ts.tv_nsec > 1000000000 /* 1s */)
353 {
354 ts.tv_nsec -= 1000000000;
355 ts.tv_sec++;
356 }
357 if (mutex->recursive)
358 {
359 private_r_mutex_t* recursive = (private_r_mutex_t*)mutex;
360
361 recursive->thread = 0;
362 timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex,
363 &ts) == ETIMEDOUT;
364 recursive->thread = pthread_self();
365 }
366 else
367 {
368 timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex,
369 &ts) == ETIMEDOUT;
370 }
371 return timed_out;
372 }
373
374 /**
375 * Implementation of condvar_t.signal.
376 */
377 static void signal(private_condvar_t *this)
378 {
379 pthread_cond_signal(&this->condvar);
380 }
381
382 /**
383 * Implementation of condvar_t.broadcast.
384 */
385 static void broadcast(private_condvar_t *this)
386 {
387 pthread_cond_broadcast(&this->condvar);
388 }
389
390 /**
391 * Implementation of condvar_t.destroy
392 */
393 static void condvar_destroy(private_condvar_t *this)
394 {
395 pthread_cond_destroy(&this->condvar);
396 free(this);
397 }
398
399 /*
400 * see header file
401 */
402 condvar_t *condvar_create(condvar_type_t type)
403 {
404 switch (type)
405 {
406 case CONDVAR_DEFAULT:
407 default:
408 {
409 private_condvar_t *this = malloc_thing(private_condvar_t);
410
411 this->public.wait = (void(*)(condvar_t*, mutex_t *mutex))wait;
412 this->public.timed_wait = (bool(*)(condvar_t*, mutex_t *mutex, u_int timeout))timed_wait;
413 this->public.signal = (void(*)(condvar_t*))signal;
414 this->public.broadcast = (void(*)(condvar_t*))broadcast;
415 this->public.destroy = (void(*)(condvar_t*))condvar_destroy;
416
417 pthread_cond_init(&this->condvar, NULL);
418
419 return &this->public;
420 }
421 }
422 }
423
424 /**
425 * Implementation of rwlock_t.read_lock
426 */
427 static void read_lock(private_rwlock_t *this)
428 {
429 profiler_start(&this->profile);
430 pthread_rwlock_rdlock(&this->rwlock);
431 profiler_end(&this->profile);
432 }
433
434 /**
435 * Implementation of rwlock_t.write_lock
436 */
437 static void write_lock(private_rwlock_t *this)
438 {
439 profiler_start(&this->profile);
440 pthread_rwlock_wrlock(&this->rwlock);
441 profiler_end(&this->profile);
442 }
443
444 /**
445 * Implementation of rwlock_t.try_write_lock
446 */
447 static bool try_write_lock(private_rwlock_t *this)
448 {
449 return pthread_rwlock_trywrlock(&this->rwlock) == 0;
450 }
451
452 /**
453 * Implementation of rwlock_t.unlock
454 */
455 static void rw_unlock(private_rwlock_t *this)
456 {
457 pthread_rwlock_unlock(&this->rwlock);
458 }
459
460 /**
461 * Implementation of rwlock_t.destroy
462 */
463 static void rw_destroy(private_rwlock_t *this)
464 {
465 pthread_rwlock_destroy(&this->rwlock);
466 profiler_cleanup(&this->profile);
467 free(this);
468 }
469
470 /*
471 * see header file
472 */
473 rwlock_t *rwlock_create(rwlock_type_t type)
474 {
475 switch (type)
476 {
477 case RWLOCK_DEFAULT:
478 default:
479 {
480 private_rwlock_t *this = malloc_thing(private_rwlock_t);
481
482 this->public.read_lock = (void(*)(rwlock_t*))read_lock;
483 this->public.write_lock = (void(*)(rwlock_t*))write_lock;
484 this->public.try_write_lock = (bool(*)(rwlock_t*))try_write_lock;
485 this->public.unlock = (void(*)(rwlock_t*))rw_unlock;
486 this->public.destroy = (void(*)(rwlock_t*))rw_destroy;
487
488 pthread_rwlock_init(&this->rwlock, NULL);
489 profiler_init(&this->profile);
490
491 return &this->public;
492 }
493 }
494 }
495