/src/strongswan/src/libstrongswan/threading/mutex.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2008-2012 Tobias Brunner |
3 | | * Copyright (C) 2008 Martin Willi |
4 | | * |
5 | | * Copyright (C) secunet Security Networks AG |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or modify it |
8 | | * under the terms of the GNU General Public License as published by the |
9 | | * Free Software Foundation; either version 2 of the License, or (at your |
10 | | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, but |
13 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
14 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
15 | | * for more details. |
16 | | */ |
17 | | |
18 | | #define _GNU_SOURCE |
19 | | #include <pthread.h> |
20 | | #include <stdint.h> |
21 | | #include <time.h> |
22 | | #include <errno.h> |
23 | | |
24 | | #include <library.h> |
25 | | #include <utils/debug.h> |
26 | | |
27 | | #include "thread.h" |
28 | | #include "condvar.h" |
29 | | #include "mutex.h" |
30 | | #include "lock_profiler.h" |
31 | | |
32 | | typedef struct private_mutex_t private_mutex_t; |
33 | | typedef struct private_r_mutex_t private_r_mutex_t; |
34 | | typedef struct private_condvar_t private_condvar_t; |
35 | | |
36 | | /** |
37 | | * private data of mutex |
38 | | */ |
39 | | struct private_mutex_t { |
40 | | |
41 | | /** |
42 | | * public functions |
43 | | */ |
44 | | mutex_t public; |
45 | | |
46 | | /** |
47 | | * wrapped pthread mutex |
48 | | */ |
49 | | pthread_mutex_t mutex; |
50 | | |
51 | | /** |
52 | | * is this a recursive mutex, implementing private_r_mutex_t? |
53 | | */ |
54 | | bool recursive; |
55 | | |
56 | | /** |
57 | | * profiling info, if enabled |
58 | | */ |
59 | | lock_profile_t profile; |
60 | | }; |
61 | | |
62 | | /** |
63 | | * private data of mutex, extended by recursive locking information |
64 | | */ |
65 | | struct private_r_mutex_t { |
66 | | |
67 | | /** |
68 | | * Extends private_mutex_t |
69 | | */ |
70 | | private_mutex_t generic; |
71 | | |
72 | | /** |
73 | | * thread which currently owns mutex |
74 | | */ |
75 | | thread_t *thread; |
76 | | |
77 | | /** |
78 | | * times the current thread locked the mutex |
79 | | */ |
80 | | u_int times; |
81 | | }; |
82 | | |
83 | | /** |
84 | | * private data of condvar |
85 | | */ |
86 | | struct private_condvar_t { |
87 | | |
88 | | /** |
89 | | * public functions |
90 | | */ |
91 | | condvar_t public; |
92 | | |
93 | | /** |
94 | | * wrapped pthread condvar |
95 | | */ |
96 | | pthread_cond_t condvar; |
97 | | |
98 | | }; |
99 | | |
100 | | |
101 | | METHOD(mutex_t, lock, void, |
102 | | private_mutex_t *this) |
103 | 114k | { |
104 | 114k | int err; |
105 | | |
106 | 114k | profiler_start(&this->profile); |
107 | 114k | err = pthread_mutex_lock(&this->mutex); |
108 | 114k | if (err) |
109 | 0 | { |
110 | 0 | DBG1(DBG_LIB, "!!! MUTEX LOCK ERROR: %s !!!", strerror(err)); |
111 | 0 | } |
112 | 114k | profiler_end(&this->profile); |
113 | 114k | } |
114 | | |
115 | | METHOD(mutex_t, unlock, void, |
116 | | private_mutex_t *this) |
117 | 114k | { |
118 | 114k | int err; |
119 | | |
120 | 114k | err = pthread_mutex_unlock(&this->mutex); |
121 | 114k | if (err) |
122 | 0 | { |
123 | 0 | DBG1(DBG_LIB, "!!! MUTEX UNLOCK ERROR: %s !!!", strerror(err)); |
124 | 0 | } |
125 | 114k | } |
126 | | |
127 | | METHOD(mutex_t, lock_r, void, |
128 | | private_r_mutex_t *this) |
129 | 0 | { |
130 | 0 | thread_t *self = thread_current(); |
131 | |
|
132 | 0 | if (cas_ptr(&this->thread, self, self)) |
133 | 0 | { |
134 | 0 | this->times++; |
135 | 0 | } |
136 | 0 | else |
137 | 0 | { |
138 | 0 | lock(&this->generic); |
139 | 0 | cas_ptr(&this->thread, NULL, self); |
140 | 0 | this->times = 1; |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | | METHOD(mutex_t, unlock_r, void, |
145 | | private_r_mutex_t *this) |
146 | 0 | { |
147 | 0 | if (--this->times == 0) |
148 | 0 | { |
149 | 0 | cas_ptr(&this->thread, thread_current(), NULL); |
150 | 0 | unlock(&this->generic); |
151 | 0 | } |
152 | 0 | } |
153 | | |
154 | | METHOD(mutex_t, mutex_destroy, void, |
155 | | private_mutex_t *this) |
156 | 131k | { |
157 | 131k | profiler_cleanup(&this->profile); |
158 | 131k | pthread_mutex_destroy(&this->mutex); |
159 | 131k | free(this); |
160 | 131k | } |
161 | | |
162 | | METHOD(mutex_t, mutex_destroy_r, void, |
163 | | private_r_mutex_t *this) |
164 | 16.3k | { |
165 | 16.3k | profiler_cleanup(&this->generic.profile); |
166 | 16.3k | pthread_mutex_destroy(&this->generic.mutex); |
167 | 16.3k | free(this); |
168 | 16.3k | } |
169 | | |
170 | | /* |
171 | | * see header file |
172 | | */ |
173 | | mutex_t *mutex_create(mutex_type_t type) |
174 | 147k | { |
175 | 147k | switch (type) |
176 | 147k | { |
177 | 16.3k | case MUTEX_TYPE_RECURSIVE: |
178 | 16.3k | { |
179 | 16.3k | private_r_mutex_t *this; |
180 | | |
181 | 16.3k | INIT(this, |
182 | 16.3k | .generic = { |
183 | 16.3k | .public = { |
184 | 16.3k | .lock = _lock_r, |
185 | 16.3k | .unlock = _unlock_r, |
186 | 16.3k | .destroy = _mutex_destroy_r, |
187 | 16.3k | }, |
188 | 16.3k | .recursive = TRUE, |
189 | 16.3k | }, |
190 | 16.3k | ); |
191 | | |
192 | 16.3k | pthread_mutex_init(&this->generic.mutex, NULL); |
193 | 16.3k | profiler_init(&this->generic.profile); |
194 | | |
195 | 16.3k | return &this->generic.public; |
196 | 0 | } |
197 | 131k | case MUTEX_TYPE_DEFAULT: |
198 | 131k | default: |
199 | 131k | { |
200 | 131k | private_mutex_t *this; |
201 | | |
202 | 131k | INIT(this, |
203 | 131k | .public = { |
204 | 131k | .lock = _lock, |
205 | 131k | .unlock = _unlock, |
206 | 131k | .destroy = _mutex_destroy, |
207 | 131k | }, |
208 | 131k | ); |
209 | | |
210 | 131k | pthread_mutex_init(&this->mutex, NULL); |
211 | 131k | profiler_init(&this->profile); |
212 | | |
213 | 131k | return &this->public; |
214 | 131k | } |
215 | 147k | } |
216 | 147k | } |
217 | | |
218 | | |
219 | | METHOD(condvar_t, wait_, void, |
220 | | private_condvar_t *this, private_mutex_t *mutex) |
221 | 0 | { |
222 | 0 | if (mutex->recursive) |
223 | 0 | { |
224 | 0 | private_r_mutex_t* recursive = (private_r_mutex_t*)mutex; |
225 | 0 | thread_t *self = thread_current(); |
226 | 0 | u_int times; |
227 | | |
228 | | /* keep track of the number of times this thread locked the mutex */ |
229 | 0 | times = recursive->times; |
230 | | /* mutex owner gets cleared during condvar wait */ |
231 | 0 | cas_ptr(&recursive->thread, self, NULL); |
232 | 0 | pthread_cond_wait(&this->condvar, &mutex->mutex); |
233 | 0 | cas_ptr(&recursive->thread, NULL, self); |
234 | 0 | recursive->times = times; |
235 | 0 | } |
236 | 0 | else |
237 | 0 | { |
238 | 0 | pthread_cond_wait(&this->condvar, &mutex->mutex); |
239 | 0 | } |
240 | 0 | } |
241 | | |
242 | | /* use the monotonic clock based version of this function if available */ |
243 | | #if defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) && \ |
244 | | !defined(HAVE_CONDATTR_CLOCK_MONOTONIC) |
245 | | #define pthread_cond_timedwait pthread_cond_timedwait_monotonic |
246 | | #endif |
247 | | |
248 | | METHOD(condvar_t, timed_wait_abs, bool, |
249 | | private_condvar_t *this, private_mutex_t *mutex, timeval_t time) |
250 | 0 | { |
251 | 0 | struct timespec ts; |
252 | 0 | bool timed_out; |
253 | |
|
254 | 0 | ts.tv_sec = time.tv_sec; |
255 | 0 | ts.tv_nsec = time.tv_usec * 1000; |
256 | |
|
257 | 0 | if (mutex->recursive) |
258 | 0 | { |
259 | 0 | private_r_mutex_t* recursive = (private_r_mutex_t*)mutex; |
260 | 0 | thread_t *self = thread_current(); |
261 | 0 | u_int times; |
262 | |
|
263 | 0 | times = recursive->times; |
264 | 0 | cas_ptr(&recursive->thread, self, NULL); |
265 | 0 | timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex, |
266 | 0 | &ts) == ETIMEDOUT; |
267 | 0 | cas_ptr(&recursive->thread, NULL, self); |
268 | 0 | recursive->times = times; |
269 | 0 | } |
270 | 0 | else |
271 | 0 | { |
272 | 0 | timed_out = pthread_cond_timedwait(&this->condvar, &mutex->mutex, |
273 | 0 | &ts) == ETIMEDOUT; |
274 | 0 | } |
275 | 0 | return timed_out; |
276 | 0 | } |
277 | | |
278 | | METHOD(condvar_t, timed_wait, bool, |
279 | | private_condvar_t *this, private_mutex_t *mutex, u_int timeout) |
280 | 0 | { |
281 | 0 | timeval_t tv; |
282 | 0 | u_int s, ms; |
283 | |
|
284 | 0 | time_monotonic(&tv); |
285 | |
|
286 | 0 | s = timeout / 1000; |
287 | 0 | ms = timeout % 1000; |
288 | |
|
289 | 0 | tv.tv_sec += s; |
290 | 0 | timeval_add_ms(&tv, ms); |
291 | 0 | return timed_wait_abs(this, mutex, tv); |
292 | 0 | } |
293 | | |
294 | | METHOD(condvar_t, signal_, void, |
295 | | private_condvar_t *this) |
296 | 32.7k | { |
297 | 32.7k | pthread_cond_signal(&this->condvar); |
298 | 32.7k | } |
299 | | |
300 | | METHOD(condvar_t, broadcast, void, |
301 | | private_condvar_t *this) |
302 | 16.3k | { |
303 | 16.3k | pthread_cond_broadcast(&this->condvar); |
304 | 16.3k | } |
305 | | |
306 | | METHOD(condvar_t, condvar_destroy, void, |
307 | | private_condvar_t *this) |
308 | 81.9k | { |
309 | 81.9k | pthread_cond_destroy(&this->condvar); |
310 | 81.9k | free(this); |
311 | 81.9k | } |
312 | | |
313 | | /* |
314 | | * see header file |
315 | | */ |
316 | | condvar_t *condvar_create(condvar_type_t type) |
317 | 81.9k | { |
318 | 81.9k | switch (type) |
319 | 81.9k | { |
320 | 81.9k | case CONDVAR_TYPE_DEFAULT: |
321 | 81.9k | default: |
322 | 81.9k | { |
323 | 81.9k | private_condvar_t *this; |
324 | | |
325 | 81.9k | INIT(this, |
326 | 81.9k | .public = { |
327 | 81.9k | .wait = (void*)_wait_, |
328 | 81.9k | .timed_wait = (void*)_timed_wait, |
329 | 81.9k | .timed_wait_abs = (void*)_timed_wait_abs, |
330 | 81.9k | .signal = _signal_, |
331 | 81.9k | .broadcast = _broadcast, |
332 | 81.9k | .destroy = _condvar_destroy, |
333 | 81.9k | } |
334 | 81.9k | ); |
335 | | |
336 | 81.9k | #ifdef HAVE_PTHREAD_CONDATTR_INIT |
337 | 81.9k | { |
338 | 81.9k | pthread_condattr_t condattr; |
339 | 81.9k | pthread_condattr_init(&condattr); |
340 | 81.9k | #ifdef HAVE_CONDATTR_CLOCK_MONOTONIC |
341 | 81.9k | pthread_condattr_setclock(&condattr, TIME_CLOCK_ID); |
342 | 81.9k | #endif |
343 | 81.9k | pthread_cond_init(&this->condvar, &condattr); |
344 | 81.9k | pthread_condattr_destroy(&condattr); |
345 | 81.9k | } |
346 | 81.9k | #endif |
347 | | |
348 | 81.9k | return &this->public; |
349 | 81.9k | } |
350 | 81.9k | } |
351 | 81.9k | } |
352 | | |