simple mutex profiler
[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 #include "mutex.h"
19
20 #include <library.h>
21 #include <debug.h>
22
23 #include <pthread.h>
24 #include <sys/time.h>
25 #include <stdint.h>
26 #include <time.h>
27 #include <errno.h>
28
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
34 /**
35 * private data of mutex
36 */
37 struct private_mutex_t {
38
39 /**
40 * public functions
41 */
42 mutex_t public;
43
44 /**
45 * wrapped pthread mutex
46 */
47 pthread_mutex_t mutex;
48
49 #ifdef LOCK_PROFILER
50 /**
51 * how long threads have waited for the lock in this mutex so far
52 */
53 struct timeval waited;
54
55 /**
56 * creator of the mutex
57 */
58 void *stack[10];
59
60 /**
61 * number of pointers in stack
62 */
63 int stack_size;
64 #endif /* LOCK_PROFILER */
65
66 /**
67 * is this a recursiv emutex, implementing private_r_mutex_t?
68 */
69 bool recursive;
70 };
71
72 /**
73 * private data of mutex, extended by recursive locking information
74 */
75 struct private_r_mutex_t {
76
77 /**
78 * Extends private_mutex_t
79 */
80 private_mutex_t generic;
81
82 /**
83 * thread which currently owns mutex
84 */
85 pthread_t thread;
86
87 /**
88 * times we have locked the lock, stored per thread
89 */
90 pthread_key_t times;
91 };
92
93 /**
94 * private data of condvar
95 */
96 struct private_condvar_t {
97
98 /**
99 * public functions
100 */
101 condvar_t public;
102
103 /**
104 * wrapped pthread condvar
105 */
106 pthread_cond_t condvar;
107 };
108
109 #ifdef LOCK_PROFILER
110
111 #include <execinfo.h>
112
113 /**
114 * print mutex locking statistics
115 */
116 static void print_stats(private_mutex_t *this)
117 {
118 int i;
119
120 DBG1("waited %d.%06ds in mutex, created at:",
121 this->waited.tv_sec, this->waited.tv_usec);
122 for (i = 0; i < this->stack_size; i++)
123 {
124 DBG1(" %p", this->stack[i]);
125 }
126 }
127
128 static void init_stats(private_mutex_t *this)
129 {
130 this->stack_size = backtrace(this->stack, countof(this->stack));
131 timerclear(&this->waited);
132 }
133
134 /**
135 * Implementation of mutex_t.lock.
136 */
137 static void lock(private_mutex_t *this)
138 {
139 struct timeval start, end, diff;
140
141 gettimeofday(&start, NULL);
142 if (pthread_mutex_lock(&this->mutex))
143 {
144 DBG1("!!!! MUTEX %sLOCK ERROR, your code is buggy !!!", "");
145 }
146 gettimeofday(&end, NULL);
147
148 timersub(&end, &start, &diff);
149 timeradd(&this->waited, &diff, &this->waited);
150 }
151 #else /* !LOCK_PROFILER */
152
153 /** dummy implementations */
154 static void print_stats(private_mutex_t *this) {}
155 static void init_stats(private_mutex_t *this) {}
156
157 /**
158 * Implementation of mutex_t.lock.
159 */
160 static void lock(private_mutex_t *this)
161 {
162 if (pthread_mutex_lock(&this->mutex))
163 {
164 DBG1("!!!! MUTEX %sLOCK ERROR, your code is buggy !!!", "");
165 }
166 }
167 #endif /* LOCK_PROFILER */
168
169 /**
170 * Implementation of mutex_t.unlock.
171 */
172 static void unlock(private_mutex_t *this)
173 {
174 if (pthread_mutex_unlock(&this->mutex))
175 {
176 DBG1("!!!! MUTEX %sLOCK ERROR, your code is buggy !!!", "UN");
177 }
178 }
179
180 /**
181 * Implementation of mutex_t.lock.
182 */
183 static void lock_r(private_r_mutex_t *this)
184 {
185 pthread_t self = pthread_self();
186
187 if (this->thread == self)
188 {
189 uintptr_t times;
190
191 /* times++ */
192 times = (uintptr_t)pthread_getspecific(this->times);
193 pthread_setspecific(this->times, (void*)times + 1);
194 }
195 else
196 {
197 lock(&this->generic);
198 this->thread = self;
199 /* times = 1 */
200 pthread_setspecific(this->times, (void*)1);
201 }
202 }
203
204 /**
205 * Implementation of mutex_t.unlock.
206 */
207 static void unlock_r(private_r_mutex_t *this)
208 {
209 uintptr_t times;
210
211 /* times-- */
212 times = (uintptr_t)pthread_getspecific(this->times);
213 pthread_setspecific(this->times, (void*)--times);
214
215 if (times == 0)
216 {
217 this->thread = 0;
218 unlock(&this->generic);
219 }
220 }
221
222 /**
223 * Implementation of mutex_t.destroy
224 */
225 static void mutex_destroy(private_mutex_t *this)
226 {
227 print_stats(this);
228 pthread_mutex_destroy(&this->mutex);
229 free(this);
230 }
231
232 /**
233 * Implementation of mutex_t.destroy for recursive mutex'
234 */
235 static void mutex_destroy_r(private_r_mutex_t *this)
236 {
237 print_stats(&this->generic);
238 pthread_mutex_destroy(&this->generic.mutex);
239 pthread_key_delete(this->times);
240 free(this);
241 }
242
243 /*
244 * see header file
245 */
246 mutex_t *mutex_create(mutex_type_t type)
247 {
248 switch (type)
249 {
250 case MUTEX_RECURSIVE:
251 {
252 private_r_mutex_t *this = malloc_thing(private_r_mutex_t);
253
254 this->generic.public.lock = (void(*)(mutex_t*))lock_r;
255 this->generic.public.unlock = (void(*)(mutex_t*))unlock_r;
256 this->generic.public.destroy = (void(*)(mutex_t*))mutex_destroy_r;
257
258 pthread_mutex_init(&this->generic.mutex, NULL);
259 pthread_key_create(&this->times, NULL);
260 this->generic.recursive = TRUE;
261 init_stats(&this->generic);
262 this->thread = 0;
263
264 return &this->generic.public;
265 }
266 case MUTEX_DEFAULT:
267 default:
268 {
269 private_mutex_t *this = malloc_thing(private_mutex_t);
270
271 this->public.lock = (void(*)(mutex_t*))lock;
272 this->public.unlock = (void(*)(mutex_t*))unlock;
273 this->public.destroy = (void(*)(mutex_t*))mutex_destroy;
274
275 pthread_mutex_init(&this->mutex, NULL);
276 this->recursive = FALSE;
277 init_stats(this);
278
279 return &this->public;
280 }
281 }
282 }
283
284 /**
285 * Implementation of condvar_t.wait.
286 */
287 static void wait(private_condvar_t *this, private_mutex_t *mutex)
288 {
289 if (mutex->recursive)
290 {
291 private_r_mutex_t* recursive = (private_r_mutex_t*)mutex;
292
293 /* mutex owner gets cleared during condvar wait */
294 recursive->thread = 0;
295 pthread_cond_wait(&this->condvar, &mutex->mutex);
296 recursive->thread = pthread_self();
297 }
298 else
299 {
300 pthread_cond_wait(&this->condvar, &mutex->mutex);
301 }
302 }
303
304 /**
305 * Implementation of condvar_t.timed_wait.
306 */
307 static bool timed_wait(private_condvar_t *this, private_mutex_t *mutex,
308 u_int timeout)
309 {
310 struct timespec ts;
311 struct timeval tv;
312 u_int s, ms;
313 bool timed_out;
314
315 gettimeofday(&tv, NULL);
316
317 s = timeout / 1000;
318 ms = timeout % 1000;
319
320 ts.tv_sec = tv.tv_sec + s;
321 ts.tv_nsec = tv.tv_usec * 1000 + ms * 1000000;
322 if (ts.tv_nsec > 1000000000 /* 1s */)
323 {
324 ts.tv_nsec -= 1000000000;
325 ts.tv_sec++;
326 }
327 if (mutex->recursive)
328 {
329 private_r_mutex_t* recursive = (private_r_mutex_t*)mutex;
330
331 recursive->thread = 0;
332 timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex,
333 &ts) == ETIMEDOUT;
334 recursive->thread = pthread_self();
335 }
336 else
337 {
338 timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex,
339 &ts) == ETIMEDOUT;
340 }
341 return timed_out;
342 }
343
344 /**
345 * Implementation of condvar_t.signal.
346 */
347 static void signal(private_condvar_t *this)
348 {
349 pthread_cond_signal(&this->condvar);
350 }
351
352 /**
353 * Implementation of condvar_t.broadcast.
354 */
355 static void broadcast(private_condvar_t *this)
356 {
357 pthread_cond_broadcast(&this->condvar);
358 }
359
360 /**
361 * Implementation of condvar_t.destroy
362 */
363 static void condvar_destroy(private_condvar_t *this)
364 {
365 pthread_cond_destroy(&this->condvar);
366 free(this);
367 }
368
369 /*
370 * see header file
371 */
372 condvar_t *condvar_create(condvar_type_t type)
373 {
374 switch (type)
375 {
376 case CONDVAR_DEFAULT:
377 default:
378 {
379 private_condvar_t *this = malloc_thing(private_condvar_t);
380
381 this->public.wait = (void(*)(condvar_t*, mutex_t *mutex))wait;
382 this->public.timed_wait = (bool(*)(condvar_t*, mutex_t *mutex, u_int timeout))timed_wait;
383 this->public.signal = (void(*)(condvar_t*))signal;
384 this->public.broadcast = (void(*)(condvar_t*))broadcast;
385 this->public.destroy = (void(*)(condvar_t*))condvar_destroy;
386
387 pthread_cond_init(&this->condvar, NULL);
388
389 return &this->public;
390 }
391 }
392 }
393