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