Migrated mutex_t to INIT/METHOD macros.
[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 METHOD(mutex_t, lock, void,
100 private_mutex_t *this)
101 {
102 int err;
103
104 profiler_start(&this->profile);
105 err = pthread_mutex_lock(&this->mutex);
106 if (err)
107 {
108 DBG1(DBG_LIB, "!!! MUTEX LOCK ERROR: %s !!!", strerror(err));
109 }
110 profiler_end(&this->profile);
111 }
112
113 METHOD(mutex_t, unlock, void,
114 private_mutex_t *this)
115 {
116 int err;
117
118 err = pthread_mutex_unlock(&this->mutex);
119 if (err)
120 {
121 DBG1(DBG_LIB, "!!! MUTEX UNLOCK ERROR: %s !!!", strerror(err));
122 }
123 }
124
125 METHOD(mutex_t, lock_r, void,
126 private_r_mutex_t *this)
127 {
128 pthread_t self = pthread_self();
129
130 if (this->thread == self)
131 {
132 uintptr_t times;
133
134 /* times++ */
135 times = (uintptr_t)pthread_getspecific(this->times);
136 pthread_setspecific(this->times, (void*)times + 1);
137 }
138 else
139 {
140 lock(&this->generic);
141 this->thread = self;
142 /* times = 1 */
143 pthread_setspecific(this->times, (void*)1);
144 }
145 }
146
147 METHOD(mutex_t, unlock_r, void,
148 private_r_mutex_t *this)
149 {
150 uintptr_t times;
151
152 /* times-- */
153 times = (uintptr_t)pthread_getspecific(this->times);
154 pthread_setspecific(this->times, (void*)--times);
155
156 if (times == 0)
157 {
158 this->thread = 0;
159 unlock(&this->generic);
160 }
161 }
162
163 METHOD(mutex_t, mutex_destroy, void,
164 private_mutex_t *this)
165 {
166 profiler_cleanup(&this->profile);
167 pthread_mutex_destroy(&this->mutex);
168 free(this);
169 }
170
171 METHOD(mutex_t, mutex_destroy_r, void,
172 private_r_mutex_t *this)
173 {
174 profiler_cleanup(&this->generic.profile);
175 pthread_mutex_destroy(&this->generic.mutex);
176 pthread_key_delete(this->times);
177 free(this);
178 }
179
180 /*
181 * see header file
182 */
183 mutex_t *mutex_create(mutex_type_t type)
184 {
185 switch (type)
186 {
187 case MUTEX_TYPE_RECURSIVE:
188 {
189 private_r_mutex_t *this;
190
191 INIT(this,
192 .generic = {
193 .public = {
194 .lock = _lock_r,
195 .unlock = _unlock_r,
196 .destroy = _mutex_destroy_r,
197 },
198 .recursive = TRUE,
199 },
200 );
201
202 pthread_mutex_init(&this->generic.mutex, NULL);
203 pthread_key_create(&this->times, NULL);
204 profiler_init(&this->generic.profile);
205
206 return &this->generic.public;
207 }
208 case MUTEX_TYPE_DEFAULT:
209 default:
210 {
211 private_mutex_t *this;
212
213 INIT(this,
214 .public = {
215 .lock = _lock,
216 .unlock = _unlock,
217 .destroy = _mutex_destroy,
218 },
219 );
220
221 pthread_mutex_init(&this->mutex, NULL);
222 profiler_init(&this->profile);
223
224 return &this->public;
225 }
226 }
227 }
228
229
230 METHOD(condvar_t, wait_, void,
231 private_condvar_t *this, private_mutex_t *mutex)
232 {
233 if (mutex->recursive)
234 {
235 private_r_mutex_t* recursive = (private_r_mutex_t*)mutex;
236
237 /* mutex owner gets cleared during condvar wait */
238 recursive->thread = 0;
239 pthread_cond_wait(&this->condvar, &mutex->mutex);
240 recursive->thread = pthread_self();
241 }
242 else
243 {
244 pthread_cond_wait(&this->condvar, &mutex->mutex);
245 }
246 }
247
248 /* use the monotonic clock based version of this function if available */
249 #ifdef HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
250 #define pthread_cond_timedwait pthread_cond_timedwait_monotonic
251 #endif
252
253 METHOD(condvar_t, timed_wait_abs, bool,
254 private_condvar_t *this, private_mutex_t *mutex, timeval_t time)
255 {
256 struct timespec ts;
257 bool timed_out;
258
259 ts.tv_sec = time.tv_sec;
260 ts.tv_nsec = time.tv_usec * 1000;
261
262 if (mutex->recursive)
263 {
264 private_r_mutex_t* recursive = (private_r_mutex_t*)mutex;
265
266 recursive->thread = 0;
267 timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex,
268 &ts) == ETIMEDOUT;
269 recursive->thread = pthread_self();
270 }
271 else
272 {
273 timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex,
274 &ts) == ETIMEDOUT;
275 }
276 return timed_out;
277 }
278
279 METHOD(condvar_t, timed_wait, bool,
280 private_condvar_t *this, private_mutex_t *mutex, u_int timeout)
281 {
282 timeval_t tv;
283 u_int s, ms;
284
285 time_monotonic(&tv);
286
287 s = timeout / 1000;
288 ms = timeout % 1000;
289
290 tv.tv_sec += s;
291 tv.tv_usec += ms * 1000;
292
293 if (tv.tv_usec > 1000000 /* 1s */)
294 {
295 tv.tv_usec -= 1000000;
296 tv.tv_sec++;
297 }
298 return timed_wait_abs(this, mutex, tv);
299 }
300
301 METHOD(condvar_t, signal_, void,
302 private_condvar_t *this)
303 {
304 pthread_cond_signal(&this->condvar);
305 }
306
307 METHOD(condvar_t, broadcast, void,
308 private_condvar_t *this)
309 {
310 pthread_cond_broadcast(&this->condvar);
311 }
312
313 METHOD(condvar_t, condvar_destroy, void,
314 private_condvar_t *this)
315 {
316 pthread_cond_destroy(&this->condvar);
317 free(this);
318 }
319
320 /*
321 * see header file
322 */
323 condvar_t *condvar_create(condvar_type_t type)
324 {
325 switch (type)
326 {
327 case CONDVAR_TYPE_DEFAULT:
328 default:
329 {
330 private_condvar_t *this;
331
332 INIT(this,
333 .public = {
334 .wait = (void*)_wait_,
335 .timed_wait = (void*)_timed_wait,
336 .timed_wait_abs = (void*)_timed_wait_abs,
337 .signal = _signal_,
338 .broadcast = _broadcast,
339 .destroy = _condvar_destroy,
340 }
341 );
342
343 #ifdef HAVE_PTHREAD_CONDATTR_INIT
344 {
345 pthread_condattr_t condattr;
346 pthread_condattr_init(&condattr);
347 #ifdef HAVE_CONDATTR_CLOCK_MONOTONIC
348 pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
349 #endif
350 pthread_cond_init(&this->condvar, &condattr);
351 pthread_condattr_destroy(&condattr);
352 }
353 #endif
354
355 return &this->public;
356 }
357 }
358 }
359