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