1 /* 2 * Copyright (C) 2002 - David W. Durham 3 * 4 * This file is part of ReZound, an audio editing application. 5 * 6 * ReZound is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published 8 * by the Free Software Foundation; either version 2 of the License, 9 * or (at your option) any later version. 10 * 11 * ReZound is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 19 */ 20 #include "CConditionVariable.h" 21 22 #include <stdexcept> 23 #include <string> 24 #include <stdio.h> 25 using namespace std; 26 27 #if defined(__APPLE__) 28 # include "clocks.h" 29 #endif 30 31 #ifdef _WIN32 32 // *** WIN32 implementation *** 33 34 /* 35 This implementation is based on section 3.2 of http://www.cs.wustl.edu/~schmidt/win32-cv-1.html 36 */ 37 38 #include <windows.h> 39 CConditionVariable()40 CConditionVariable::CConditionVariable() : 41 waiters_count_lock_(new CRITICAL_SECTION) 42 { 43 waiters_count_=0; 44 InitializeCriticalSection(waiters_count_lock_); 45 46 // Create an auto-reset event. 47 events_[SIGNAL] = CreateEvent (NULL, // no security 48 FALSE, // auto-reset event 49 FALSE, // non-signaled initially 50 NULL); // unnamed 51 52 // Create a manual-reset event. 53 events_[BROADCAST] = CreateEvent (NULL, // no security 54 TRUE, // manual-reset 55 FALSE, // non-signaled initially 56 NULL); // unnamed 57 58 } 59 ~CConditionVariable()60 CConditionVariable::~CConditionVariable() throw() 61 { 62 CloseHandle(events_[SIGNAL]); 63 CloseHandle(events_[BROADCAST]); 64 DeleteCriticalSection(waiters_count_lock_); 65 66 delete waiters_count_lock_; 67 } 68 wait(CMutex & mutex,int timeout_ms)69 bool CConditionVariable::wait(CMutex &mutex,int timeout_ms) 70 { 71 // Avoid race conditions. 72 EnterCriticalSection (waiters_count_lock_); 73 waiters_count_++; 74 LeaveCriticalSection (waiters_count_lock_); 75 76 // It's ok to release the <external_mutex> here since Win32 77 // manual-reset events maintain state when used with 78 // <SetEvent>. This avoids the "lost wakeup" bug... 79 //LeaveCriticalSection (external_mutex); 80 mutex.unlock(); 81 82 // Wait for either event to become signaled due to <pthread_cond_signal> 83 // being called or <pthread_cond_broadcast> being called. 84 int result = WaitForMultipleObjects (2, events_, FALSE, (timeout_ms<0 ? INFINITE : timeout_ms) ); 85 86 EnterCriticalSection (waiters_count_lock_); 87 waiters_count_--; 88 int last_waiter = result == WAIT_OBJECT_0 + BROADCAST && waiters_count_ == 0; 89 LeaveCriticalSection (waiters_count_lock_); 90 91 // Some thread called <pthread_cond_broadcast>. 92 if (last_waiter) 93 { 94 // We're the last waiter to be notified or to stop waiting, so 95 // reset the manual event. 96 ResetEvent (events_[BROADCAST]); 97 } 98 99 // Reacquire the <external_mutex>. 100 //EnterCriticalSection (external_mutex, INFINITE); 101 mutex.lock(); 102 103 return result!=WAIT_TIMEOUT; 104 } 105 signal()106 void CConditionVariable::signal() 107 { 108 // Avoid race conditions. 109 EnterCriticalSection (waiters_count_lock_); 110 int have_waiters = waiters_count_ > 0; 111 LeaveCriticalSection (waiters_count_lock_); 112 113 if (have_waiters) 114 SetEvent (events_[SIGNAL]); 115 } 116 broadcast()117 void CConditionVariable::broadcast() 118 { 119 // Avoid race conditions. 120 EnterCriticalSection (waiters_count_lock_); 121 int have_waiters = waiters_count_ > 0; 122 LeaveCriticalSection (waiters_count_lock_); 123 124 if (have_waiters) 125 SetEvent (events_[BROADCAST]); 126 } 127 128 #else 129 // *** posix implementation *** 130 131 #include <errno.h> // for EBUSY 132 #include <string.h> // for strerror() 133 134 135 #ifdef __APPLE__ 136 #include <sys/types.h> 137 #include <sys/timeb.h> 138 #endif 139 CConditionVariable()140 CConditionVariable::CConditionVariable() 141 { 142 pthread_cond_init(&cond,NULL); 143 } 144 ~CConditionVariable()145 CConditionVariable::~CConditionVariable() throw() 146 { 147 const int ret=pthread_cond_destroy(&cond); 148 if(ret) 149 //throw runtime_error(string(__func__)+" -- error destroying conditional variable -- "+strerror(ret)); // may not care tho 150 fprintf(stderr, "%s -- error destroying conditional variable -- %s\n",__func__,strerror(ret)); 151 } 152 153 /* 154 void CConditionVariable::wait(CMutex &mutex) 155 { 156 const int ret=pthread_cond_wait(&cond,(pthread_mutex_t *)mutex.mutex); 157 if(ret) 158 throw runtime_error(string(__func__)+" -- error waiting -- "+strerror(ret)); 159 } 160 */ 161 wait(CMutex & mutex,int timeout)162 bool CConditionVariable::wait(CMutex &mutex,int timeout) 163 { 164 if(timeout<0) 165 { 166 const int ret=pthread_cond_wait(&cond,(pthread_mutex_t *)mutex.mutex); 167 if(ret) 168 throw runtime_error(string(__func__)+" -- error waiting -- "+strerror(ret)); 169 return true; 170 } 171 else 172 { 173 struct timespec abs_timeout; 174 175 #if !defined(__APPLE__) 176 /* getting the abs_timeout this way is the most precise, but requires linking against librt */ 177 clock_gettime(CLOCK_REALTIME,&abs_timeout); 178 #else 179 /* getting the time this way is less precise that above, but is more compatible */ 180 { 181 clocks::msec_t current=clocks::time_msec(); 182 abs_timeout.tv_sec=current/1000; 183 abs_timeout.tv_nsec=(current%1000)*1000*1000; 184 } 185 #endif 186 187 abs_timeout.tv_sec+=(timeout/1000); 188 abs_timeout.tv_nsec+=(timeout%1000)*1000*1000; 189 abs_timeout.tv_sec+=abs_timeout.tv_nsec/1000000000; 190 abs_timeout.tv_nsec%=1000000000; 191 const int ret=pthread_cond_timedwait(&cond,(pthread_mutex_t *)mutex.mutex,&abs_timeout); 192 if(ret && ret!=ETIMEDOUT) /* ??? also could get interrupted with a signal ??? EINTR */ 193 throw runtime_error(string(__func__)+" -- error waiting -- "+strerror(ret)); 194 return ret==0; 195 } 196 } 197 signal()198 void CConditionVariable::signal() 199 { 200 const int ret=pthread_cond_signal(&cond); 201 if(ret) 202 throw runtime_error(string(__func__)+" -- error signaling -- "+strerror(ret)); 203 } 204 broadcast()205 void CConditionVariable::broadcast() 206 { 207 const int ret=pthread_cond_broadcast(&cond); 208 if(ret) 209 throw runtime_error(string(__func__)+" -- error broadcasting -- "+strerror(ret)); 210 } 211 212 #endif 213