9c05ff0442a8bfd95be7655c8bb2da4bb1944e63
[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 "condvar.h"
27 #include "mutex.h"
28 #include "lock_profiler.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
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 /**
50 * is this a recursiv emutex, implementing private_r_mutex_t?
51 */
52 bool recursive;
53
54 /**
55 * profiling info, if enabled
56 */
57 lock_profile_t profile;
58 };
59
60 /**
61 * private data of mutex, extended by recursive locking information
62 */
63 struct private_r_mutex_t {
64
65 /**
66 * Extends private_mutex_t
67 */
68 private_mutex_t generic;
69
70 /**
71 * thread which currently owns mutex
72 */
73 pthread_t thread;
74
75 /**
76 * times we have locked the lock, stored per thread
77 */
78 pthread_key_t times;
79 };
80
81 /**
82 * private data of condvar
83 */
84 struct private_condvar_t {
85
86 /**
87 * public functions
88 */
89 condvar_t public;
90
91 /**
92 * wrapped pthread condvar
93 */
94 pthread_cond_t condvar;
95
96 };
97
98
99
100 /**
101 * Implementation of mutex_t.lock.
102 */
103 static void lock(private_mutex_t *this)
104 {
105 int err;
106
107 profiler_start(&this->profile);
108 err = pthread_mutex_lock(&this->mutex);
109 if (err)
110 {
111 DBG1("!!! MUTEX LOCK ERROR: %s !!!", strerror(err));
112 }
113 profiler_end(&this->profile);
114 }
115
116 /**
117 * Implementation of mutex_t.unlock.
118 */
119 static void unlock(private_mutex_t *this)
120 {
121 int err;
122
123 err = pthread_mutex_unlock(&this->mutex);
124 if (err)
125 {
126 DBG1("!!! MUTEX UNLOCK ERROR: %s !!!", strerror(err));
127 }
128 }
129
130 /**
131 * Implementation of mutex_t.lock.
132 */
133 static void lock_r(private_r_mutex_t *this)
134 {
135 pthread_t self = pthread_self();
136
137 if (this->thread == self)
138 {
139 uintptr_t times;
140
141 /* times++ */
142 times = (uintptr_t)pthread_getspecific(this->times);
143 pthread_setspecific(this->times, (void*)times + 1);
144 }
145 else
146 {
147 lock(&this->generic);
148 this->thread = self;
149 /* times = 1 */
150 pthread_setspecific(this->times, (void*)1);
151 }
152 }
153
154 /**
155 * Implementation of mutex_t.unlock.
156 */
157 static void unlock_r(private_r_mutex_t *this)
158 {
159 uintptr_t times;
160
161 /* times-- */
162 times = (uintptr_t)pthread_getspecific(this->times);
163 pthread_setspecific(this->times, (void*)--times);
164
165 if (times == 0)
166 {
167 this->thread = 0;
168 unlock(&this->generic);
169 }
170 }
171
172 /**
173 * Implementation of mutex_t.destroy
174 */
175 static void mutex_destroy(private_mutex_t *this)
176 {
177 profiler_cleanup(&this->profile);
178 pthread_mutex_destroy(&this->mutex);
179 free(this);
180 }
181
182 /**
183 * Implementation of mutex_t.destroy for recursive mutex'
184 */
185 static void mutex_destroy_r(private_r_mutex_t *this)
186 {
187 profiler_cleanup(&this->generic.profile);
188 pthread_mutex_destroy(&this->generic.mutex);
189 pthread_key_delete(this->times);
190 free(this);
191 }
192
193 /*
194 * see header file
195 */
196 mutex_t *mutex_create(mutex_type_t type)
197 {
198 switch (type)
199 {
200 case MUTEX_TYPE_RECURSIVE:
201 {
202 private_r_mutex_t *this = malloc_thing(private_r_mutex_t);
203
204 this->generic.public.lock = (void(*)(mutex_t*))lock_r;
205 this->generic.public.unlock = (void(*)(mutex_t*))unlock_r;
206 this->generic.public.destroy = (void(*)(mutex_t*))mutex_destroy_r;
207
208 pthread_mutex_init(&this->generic.mutex, NULL);
209 pthread_key_create(&this->times, NULL);
210 this->generic.recursive = TRUE;
211 profiler_init(&this->generic.profile);
212 this->thread = 0;
213
214 return &this->generic.public;
215 }
216 case MUTEX_TYPE_DEFAULT:
217 default:
218 {
219 private_mutex_t *this = malloc_thing(private_mutex_t);
220
221 this->public.lock = (void(*)(mutex_t*))lock;
222 this->public.unlock = (void(*)(mutex_t*))unlock;
223 this->public.destroy = (void(*)(mutex_t*))mutex_destroy;
224
225 pthread_mutex_init(&this->mutex, NULL);
226 this->recursive = FALSE;
227 profiler_init(&this->profile);
228
229 return &this->public;
230 }
231 }
232 }
233
234
235
236 /**
237 * Implementation of condvar_t.wait.
238 */
239 static void _wait(private_condvar_t *this, private_mutex_t *mutex)
240 {
241 if (mutex->recursive)
242 {
243 private_r_mutex_t* recursive = (private_r_mutex_t*)mutex;
244
245 /* mutex owner gets cleared during condvar wait */
246 recursive->thread = 0;
247 pthread_cond_wait(&this->condvar, &mutex->mutex);
248 recursive->thread = pthread_self();
249 }
250 else
251 {
252 pthread_cond_wait(&this->condvar, &mutex->mutex);
253 }
254 }
255
256 /**
257 * Implementation of condvar_t.timed_wait_abs.
258 */
259 static bool timed_wait_abs(private_condvar_t *this, private_mutex_t *mutex,
260 timeval_t time)
261 {
262 struct timespec ts;
263 bool timed_out;
264
265 ts.tv_sec = time.tv_sec;
266 ts.tv_nsec = time.tv_usec * 1000;
267
268 if (mutex->recursive)
269 {
270 private_r_mutex_t* recursive = (private_r_mutex_t*)mutex;
271
272 recursive->thread = 0;
273 timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex,
274 &ts) == ETIMEDOUT;
275 recursive->thread = pthread_self();
276 }
277 else
278 {
279 timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex,
280 &ts) == ETIMEDOUT;
281 }
282 return timed_out;
283 }
284
285 /**
286 * Implementation of condvar_t.timed_wait.
287 */
288 static bool timed_wait(private_condvar_t *this, private_mutex_t *mutex,
289 u_int timeout)
290 {
291 timeval_t tv;
292 u_int s, ms;
293
294 time_monotonic(&tv);
295
296 s = timeout / 1000;
297 ms = timeout % 1000;
298
299 tv.tv_sec += s;
300 tv.tv_usec += ms * 1000;
301
302 if (tv.tv_usec > 1000000 /* 1s */)
303 {
304 tv.tv_usec -= 1000000;
305 tv.tv_sec++;
306 }
307 return timed_wait_abs(this, mutex, tv);
308 }
309
310 /**
311 * Implementation of condvar_t.signal.
312 */
313 static void _signal(private_condvar_t *this)
314 {
315 pthread_cond_signal(&this->condvar);
316 }
317
318 /**
319 * Implementation of condvar_t.broadcast.
320 */
321 static void broadcast(private_condvar_t *this)
322 {
323 pthread_cond_broadcast(&this->condvar);
324 }
325
326 /**
327 * Implementation of condvar_t.destroy
328 */
329 static void condvar_destroy(private_condvar_t *this)
330 {
331 pthread_cond_destroy(&this->condvar);
332 free(this);
333 }
334
335 /*
336 * see header file
337 */
338 condvar_t *condvar_create(condvar_type_t type)
339 {
340 switch (type)
341 {
342 case CONDVAR_TYPE_DEFAULT:
343 default:
344 {
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 #ifdef HAVE_PTHREAD_CONDATTR_INIT
355 {
356 pthread_condattr_t condattr;
357 pthread_condattr_init(&condattr);
358 #ifdef HAVE_CONDATTR_CLOCK_MONOTONIC
359 pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
360 #endif
361 pthread_cond_init(&this->condvar, &condattr);
362 pthread_condattr_destroy(&condattr);
363 }
364 #endif
365
366 return &this->public;
367 }
368 }
369 }
370