/[svn]/linuxsampler/trunk/src/common/Condition.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/common/Condition.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3290 - (show annotations) (download)
Fri Jun 23 12:24:58 2017 UTC (6 years, 9 months ago) by schoenebeck
File size: 18526 byte(s)
* Revised fundamental C++ classes "Thread", "Mutex" and
  "Condition" which fixes potential undefined behavior
  (note: this addresses mainly the POSIX implementation,
   Win32 is untested yet and would also need an update).
* Bumped version (2.0.0.svn64).

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2017 Christian Schoenebeck *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #include <sys/time.h>
25
26 #include "Condition.h"
27
28 #include "global_private.h"
29
30 #if CONFIG_PTHREAD_TESTCANCEL
31 #include "Thread.h"
32 #endif
33
34 namespace LinuxSampler {
35
36 // *************** Internal data types (for Windows only) ***************
37 // *
38
39 #if defined(WIN32)
40
41 typedef HANDLE win32thread_mutex_t;
42
43 //NOTE: since pthread_condattr_t is not needed in the Condition class so just set it to int
44 typedef int win32thread_condattr_t;
45
46 typedef struct {
47 unsigned long tv_sec;
48 unsigned long tv_nsec;
49 } win32_timespec;
50
51
52
53 // *************** ConditionInternal (for Windows only) ***************
54 // *
55
56 class ConditionInternal {
57 public:
58
59 static
60 int win32thread_cond_init (Condition::win32thread_cond_t *cv, const win32thread_condattr_t *)
61 {
62 dmsg(7,("win32thread_cond_init:\n"));
63 cv->waiters_count_ = 0;
64 cv->was_broadcast_ = 0;
65 cv->sema_ = CreateSemaphore (NULL, // no security
66 0, // initially 0
67 0x7fffffff, // max count
68 NULL); // unnamed
69 dmsg(7,("win32thread_cond_init: after CreateSemaphore retval=%d\n",cv->sema_));
70 InitializeCriticalSection (&cv->waiters_count_lock_);
71 cv->waiters_done_ = CreateEvent (NULL, // no security
72 FALSE, // auto-reset
73 FALSE, // non-signaled initially
74 NULL); // unnamed
75 dmsg(7,("win32thread_cond_init: after CreateEvent retval=%d\n",cv->waiters_done_));
76
77 return 0;
78 }
79
80 static
81 int win32thread_cond_destroy(Condition::win32thread_cond_t *cv) {
82 dmsg(7,("win32thread_cond_destroy ptr=%d\n",cv));
83 CloseHandle(cv->waiters_done_);
84 DeleteCriticalSection(&cv->waiters_count_lock_);
85 CloseHandle(cv->sema_);
86 return 0;
87 }
88
89 static
90 int win32thread_cond_timedwait (Condition::win32thread_cond_t *cv, win32thread_mutex_t *external_mutex, win32_timespec *timeout)
91 {
92 dmsg(7,("win32thread_cond_timedwait: external mutex=%d BEGIN!\n",external_mutex));
93 // Avoid race conditions.
94 dmsg(7,("win32thread_cond_timedwait: before EnterCriticalSection (&cv->waiters_count_lock_) cv->waiters_count_=%d\n",cv->waiters_count_));
95 EnterCriticalSection (&cv->waiters_count_lock_);
96 cv->waiters_count_++;
97 LeaveCriticalSection (&cv->waiters_count_lock_);
98 dmsg(7,("win32thread_cond_timedwait: after LeaveCriticalSection (&cv->waiters_count_lock_) cv->waiters_count_=%d\n",cv->waiters_count_));
99
100
101 // This call atomically releases the mutex and waits on the
102 // semaphore until <pthread_cond_signal> or <pthread_cond_broadcast>
103 // are called by another thread.
104
105 DWORD dwMilliseconds;
106 if(timeout->tv_sec || timeout->tv_nsec) {
107 dwMilliseconds = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000;
108 }
109 else {
110 dmsg(7,("win32thread_cond_timedwait: dwMilliseconds = INFINITE\n"));
111 dwMilliseconds = INFINITE;
112 }
113 dmsg(7,("win32thread_cond_timedwait: before SignalObjectAndWait(*external_mutex, cv->sema_, dwMilliseconds, FALSE), dwMilliseconds=%d\n",dwMilliseconds));
114 DWORD res;
115 res = SignalObjectAndWait (*external_mutex, cv->sema_, dwMilliseconds, FALSE);
116 dmsg(7,("win32thread_cond_timedwait: after SignalObjectAndWait, res=%d\n",res));
117 if(res == WAIT_TIMEOUT) return -1;
118
119 // Reacquire lock to avoid race conditions.
120 dmsg(7,("win32thread_cond_timedwait: before EnterCriticalSection (2) (&cv->waiters_count_lock_) cv->waiters_count=%d\n",cv->waiters_count_));
121 EnterCriticalSection (&cv->waiters_count_lock_);
122 dmsg(7,("win32thread_cond_timedwait: after EnterCriticalSection (2) (&cv->waiters_count_lock_) cv->waiters_count=%d\n",cv->waiters_count_));
123
124 // We're no longer waiting...
125 cv->waiters_count_--;
126
127 // Check to see if we're the last waiter after <pthread_cond_broadcast>.
128 int last_waiter = cv->was_broadcast_ && cv->waiters_count_ == 0;
129
130 LeaveCriticalSection (&cv->waiters_count_lock_);
131 dmsg(7,("win32thread_cond_timedwait: after LeaveCriticalSection (2) (&cv->waiters_count_lock_) last_waiter=%d\n",last_waiter));
132
133 // If we're the last waiter thread during this particular broadcast
134 // then let all the other threads proceed.
135 if (last_waiter) {
136 // This call atomically signals the <waiters_done_> event and waits until
137 // it can acquire the <external_mutex>. This is required to ensure fairness.
138 dmsg(7,("win32thread_cond_timedwait: before SignalObjectAndWait (cv->waiters_done_, *external_mutex, dwMilliseconds, FALSE) \n"));
139 res = SignalObjectAndWait (cv->waiters_done_, *external_mutex, dwMilliseconds, FALSE);
140 dmsg(7,("win32thread_cond_timedwait: after SignalObjectAndWait (cv->waiters_done_, *external_mutex, dwMilliseconds, FALSE) res=%d\n",res));
141 if(res == WAIT_TIMEOUT) {
142 dmsg(7,("win32thread_cond_timedwait: after SignalObjectAndWait: WAIT_TIMEOUT! returning -1\n"));
143 return -1;
144 }
145 }
146 else {
147 // Always regain the external mutex since that's the guarantee we
148 // give to our callers.
149 dmsg(7,("win32thread_cond_timedwait: before WaitForSingleObject (*external_mutex, dwMilliseconds)\n"));
150 res = WaitForSingleObject (*external_mutex, dwMilliseconds);
151 dmsg(7,("win32thread_cond_timedwait: after WaitForSingleObject (*external_mutex, dwMilliseconds) res=%d\n",res));
152 if(res == WAIT_TIMEOUT) {
153 dmsg(7,("win32thread_cond_timedwait: after WaitForSingleObject: WAIT_TIMEOUT ! returning -1\n"));
154 return -1;
155 }
156 }
157 dmsg(7,("win32thread_cond_timedwait: all OK. returning 0\n"));
158 return 0;
159 }
160
161 static
162 int win32thread_cond_broadcast (Condition::win32thread_cond_t *cv)
163 {
164 DWORD res;
165 dmsg(7,("win32thread_cond_broadcast: cv=%d\n",cv));
166 dmsg(7,("win32thread_cond_broadcast: before EnterCriticalSection (&cv->waiters_count_lock_)\n"));
167 // This is needed to ensure that <waiters_count_> and <was_broadcast_> are
168 // consistent relative to each other.
169 EnterCriticalSection (&cv->waiters_count_lock_);
170 int have_waiters = 0;
171
172 dmsg(7,("win32thread_cond_broadcast: cv->waiters_count_=%d\n",cv->waiters_count_));
173 if (cv->waiters_count_ > 0) {
174 dmsg(7,("win32thread_cond_broadcast: cv->waiters_count > 0 !\n"));
175 // We are broadcasting, even if there is just one waiter...
176 // Record that we are broadcasting, which helps optimize
177 // <pthread_cond_wait> for the non-broadcast case.
178 cv->was_broadcast_ = 1;
179 have_waiters = 1;
180 }
181 dmsg(7,("win32thread_cond_broadcast: cv->was_broadcast_=%d have_waiters=%d\n",cv->was_broadcast_,have_waiters));
182
183 if (have_waiters) {
184 // Wake up all the waiters atomically.
185 dmsg(7,("win32thread_cond_broadcast: have_waiters ! before ReleaseSemaphore (cv->sema_, cv->waiters_count_, 0);\n"));
186 ReleaseSemaphore (cv->sema_, cv->waiters_count_, 0);
187
188 LeaveCriticalSection (&cv->waiters_count_lock_);
189 dmsg(7,("win32thread_cond_broadcast: have_waiters ! after LeaveCriticalSection (&cv->waiters_count_lock_)\n"));
190
191 // Wait for all the awakened threads to acquire the counting
192 // semaphore.
193 dmsg(7,("win32thread_cond_broadcast: before WaitForSingleObject (cv->waiters_done_, INFINITE)\n"));
194 res = WaitForSingleObject (cv->waiters_done_, INFINITE);
195 dmsg(7,("win32thread_cond_broadcast: after WaitForSingleObject (cv->waiters_done_, INFINITE) res=%d\n",res));
196 // This assignment is okay, even without the <waiters_count_lock_> held
197 // because no other waiter threads can wake up to access it.
198 cv->was_broadcast_ = 0;
199 }
200 else {
201 LeaveCriticalSection (&cv->waiters_count_lock_);
202 dmsg(7,("win32thread_cond_broadcast: after LeaveCriticalSection (&cv->waiters_count_lock_)\n"));
203 }
204 dmsg(7,("win32thread_cond_broadcast: all OK, returning 0.\n"));
205 return 0;
206 }
207
208 /* TODO: the following two functions are currently not used yet
209 static
210 int win32thread_cond_signal (Condition::win32thread_cond_t *cv)
211 {
212
213 dmsg(7,("win32thread_cond_signal cv=%d\n",cv));
214 dmsg(7,("win32thread_cond_signal before EnterCriticalSection (&cv->waiters_count_lock_)\n"));
215 EnterCriticalSection (&cv->waiters_count_lock_);
216 int have_waiters = cv->waiters_count_ > 0;
217 LeaveCriticalSection (&cv->waiters_count_lock_);
218 dmsg(7,("win32thread_cond_signal after LeaveCriticalSection (&cv->waiters_count_lock_)\n"));
219
220 dmsg(7,("win32thread_cond_signal have_waiters=%d\n",have_waiters));
221 // If there aren't any waiters, then this is a no-op.
222 if (have_waiters) {
223 dmsg(7,("win32thread_cond have_waiters is TRUE, before ReleaseSemaphore (cv->sema_, 1, 0)\n"));
224 ReleaseSemaphore (cv->sema_, 1, 0);
225 }
226 dmsg(7,("win32thread_cond_signal: all OK. returning 0\n"));
227 return 0;
228 }
229
230 static
231 int win32thread_cond_wait (Condition::win32thread_cond_t *cv, win32thread_mutex_t *external_mutex) {
232 dmsg(7,("win32thread_cond_wait: (calls win32thread_cond_timedwait) cv=%d external_mutex=%d\n",cv, external_mutex));
233 win32_timespec timeout;
234 timeout.tv_sec = 0;
235 timeout.tv_nsec = 0;
236 return win32thread_cond_timedwait (cv, external_mutex, &timeout);
237 }
238 */
239
240 }; // class ConditionInternal
241
242 #endif // WIN32
243
244
245
246 // *************** Condition ***************
247 // *
248
249 Condition::Condition(bool bInitialCondition, Mutex::type_t mutexType)
250 : Mutex(mutexType)
251 {
252 dmsg(7,("Condition:: constructor, bInitialCondition=%d\n", bInitialCondition));
253 #if defined(WIN32)
254 ConditionInternal::win32thread_cond_init(&__win32_true_condition, NULL);
255 ConditionInternal::win32thread_cond_init(&__win32_false_condition, NULL);
256 #else
257 pthread_cond_init(&__posix_true_condition, NULL);
258 pthread_cond_init(&__posix_false_condition, NULL);
259 #endif
260 bCondition = bInitialCondition;
261 }
262
263 Condition::~Condition() {
264 #if defined(WIN32)
265 ConditionInternal::win32thread_cond_destroy(&__win32_true_condition);
266 ConditionInternal::win32thread_cond_destroy(&__win32_false_condition);
267 #else
268 pthread_cond_destroy(&__posix_true_condition);
269 pthread_cond_destroy(&__posix_false_condition);
270 #endif
271 }
272
273 #ifndef WIN32
274 namespace {
275 // If the thread is cancelled while waiting for the condition
276 // variable, the mutex will be locked. It needs to be unlocked so
277 // the Condition can be reused if the thread is restarted.
278 void condition_cleanup(void* c) {
279 static_cast<Condition*>(c)->Unlock();
280 }
281 }
282 #endif
283
284 int Condition::WaitIfInternal(bool bLock, bool bCondition, long TimeoutSeconds, long TimeoutNanoSeconds) {
285 dmsg(7,("Condition::WaitIfInternal: bCondition=%d TimeoutSeconds=%ld TimeoutNanoSeconds=%ld\n",bCondition, TimeoutSeconds, TimeoutNanoSeconds));
286 if (bLock) {
287 dmsg(7,("Condition::WaitIfInternal() -> LOCK()\n"));
288 Lock();
289 dmsg(7,("Condition::WaitIfInternal() -> LOCK() passed\n"));
290 }
291 int res = 0;
292 #ifndef WIN32
293 pthread_cleanup_push(condition_cleanup, this);
294 #endif
295 if (this->bCondition == bCondition) {
296 if (bCondition) { // wait until condition turned 'false'
297 #if defined(WIN32)
298 win32_timespec timeout;
299 timeout.tv_sec = TimeoutSeconds;
300 timeout.tv_nsec = TimeoutNanoSeconds;
301 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'false' condition with timeout\n"));
302 res = ConditionInternal::win32thread_cond_timedwait(&__win32_false_condition, &hMutex, &timeout);
303 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'false' condition waiting\n"));
304 #else
305 if (TimeoutSeconds || TimeoutNanoSeconds) { // wait with timeout
306 struct timeval now;
307 gettimeofday(&now, 0);
308 timespec timeout;
309 timeout.tv_sec = now.tv_sec + TimeoutSeconds;
310 timeout.tv_nsec = now.tv_usec * 1000 + TimeoutNanoSeconds;
311 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'false' condition with timeout\n"));
312 res = pthread_cond_timedwait(&__posix_false_condition, &__posix_mutex, &timeout);
313 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'false' condition waiting\n"));
314 }
315 else { // wait without timeout
316 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'false' condition\n"));
317 pthread_cond_wait(&__posix_false_condition, &__posix_mutex);
318 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'false' condition waiting\n"));
319 }
320 #endif
321 }
322 else { // wait until condition turned 'true'
323 #if defined(WIN32)
324 win32_timespec timeout;
325 timeout.tv_sec = TimeoutSeconds;
326 timeout.tv_nsec = TimeoutNanoSeconds;
327 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'true' condition with timeout\n"));
328 res = ConditionInternal::win32thread_cond_timedwait(&__win32_true_condition, &hMutex, &timeout);
329 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'true' condition waiting\n"));
330 #else
331 if (TimeoutSeconds || TimeoutNanoSeconds) { // wait with timeout
332 struct timeval now;
333 gettimeofday(&now, 0);
334 timespec timeout;
335 timeout.tv_sec = now.tv_sec + TimeoutSeconds;
336 timeout.tv_nsec = now.tv_usec * 1000 + TimeoutNanoSeconds;
337 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'true' condition with timeout\n"));
338 res = pthread_cond_timedwait(&__posix_true_condition, &__posix_mutex, &timeout);
339 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'true' condition waiting\n"));
340 }
341 else { // wait without timeout
342 dmsg(7,("Condition::WaitIfInternal() -> waiting for 'true' condition\n"));
343 pthread_cond_wait(&__posix_true_condition, &__posix_mutex);
344 dmsg(7,("Condition::WaitIfInternal() -> awakened from 'true' condition waiting\n"));
345 }
346 #endif
347 }
348 }
349 #ifndef WIN32
350 pthread_cleanup_pop(0);
351 #endif
352 return res;
353 }
354
355 int Condition::WaitIf(bool bCondition, long TimeoutSeconds, long TimeoutNanoSeconds) {
356 return WaitIfInternal(true/*do lock*/, bCondition, TimeoutSeconds, TimeoutNanoSeconds);
357 }
358
359 int Condition::PreLockedWaitIf(bool bCondition, long TimeoutSeconds, long TimeoutNanoSeconds) {
360 return WaitIfInternal(false/*don't lock*/, bCondition, TimeoutSeconds, TimeoutNanoSeconds);
361 }
362
363 int Condition::WaitAndUnlockIf(bool bCondition, long TimeoutSeconds, long TimeoutNanoSeconds) {
364 int res = WaitIfInternal(true/*do lock*/, bCondition, TimeoutSeconds, TimeoutNanoSeconds);
365 dmsg(7,("Condition::WaitAndUnlockIf() -> UNLOCK()\n"));
366 Unlock();
367 dmsg(7,("Condition::WaitAndUnlockIf() -> UNLOCK() passed\n"));
368 return res;
369 }
370
371 int Condition::PreLockedWaitAndUnlockIf(bool bCondition, long TimeoutSeconds, long TimeoutNanoSeconds) {
372 int res = WaitIfInternal(false/*don't lock*/, bCondition, TimeoutSeconds, TimeoutNanoSeconds);
373 dmsg(7,("Condition::PreLockedWaitAndUnlockIf() -> UNLOCK()\n"));
374 Unlock();
375 dmsg(7,("Condition::PreLockedWaitAndUnlockIf() -> UNLOCK() passed\n"));
376 return res;
377 }
378
379 void Condition::SetInternal(bool bLock, bool bCondition) {
380 dmsg(7,("Condition::Set() -> LOCK()\n"));
381 LockGuard lock = (bLock) ? LockGuard(*this) : LockGuard();
382 dmsg(7,("Condition::Set() -> LOCK() passed\n"));
383 if (this->bCondition != bCondition) {
384 this->bCondition = bCondition;
385 if (bCondition) {
386 dmsg(7,("Condition::Set() -> broadcasting 'true' condition\n"));
387 #if defined(WIN32)
388 ConditionInternal::win32thread_cond_broadcast(&__win32_true_condition);
389 #else
390 pthread_cond_broadcast(&__posix_true_condition);
391 #endif
392 }
393 else {
394 dmsg(7,("Condition::Set() -> broadcasting 'false' condition\n"));
395 #if defined(WIN32)
396 ConditionInternal::win32thread_cond_broadcast(&__win32_false_condition);
397 #else
398 pthread_cond_broadcast(&__posix_false_condition);
399 #endif
400 }
401 }
402 }
403
404 void Condition::Set(bool bCondition) {
405 SetInternal(true/*do lock*/, bCondition);
406 }
407
408 void Condition::PreLockedSet(bool bCondition) {
409 SetInternal(false/*don't lock*/, bCondition);
410 }
411
412 bool Condition::GetUnsafe() {
413 return bCondition;
414 }
415
416 #ifdef WIN32
417 void Condition::Reset() {
418 __win32_true_condition.waiters_count_ = 0;
419 __win32_true_condition.was_broadcast_ = 0;
420 __win32_false_condition.waiters_count_ = 0;
421 __win32_false_condition.was_broadcast_ = 0;
422 }
423 #endif
424
425 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC