corrected handling of timeouts in wxConditionInternal::WaitTimeout(): check for wxSEM...
[wxWidgets.git] / include / wx / thrimpl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: include/wx/thrimpl.cpp
3 // Purpose: common part of wxThread Implementations
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 04.06.02 (extracted from src/*/thread.cpp files)
7 // RCS-ID: $Id$
8 // Copyright: (c) Vadim Zeitlin (2002)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // this file is supposed to be included only by the various thread.cpp
13
14 // ----------------------------------------------------------------------------
15 // wxMutex
16 // ----------------------------------------------------------------------------
17
18 wxMutex::wxMutex(wxMutexType mutexType)
19 {
20 m_internal = new wxMutexInternal(mutexType);
21
22 if ( !m_internal->IsOk() )
23 {
24 delete m_internal;
25 m_internal = NULL;
26 }
27 }
28
29 wxMutex::~wxMutex()
30 {
31 delete m_internal;
32 }
33
34 bool wxMutex::IsOk() const
35 {
36 return m_internal != NULL;
37 }
38
39 wxMutexError wxMutex::Lock()
40 {
41 wxCHECK_MSG( m_internal, wxMUTEX_INVALID,
42 _T("wxMutex::Lock(): not initialized") );
43
44 return m_internal->Lock();
45 }
46
47 wxMutexError wxMutex::TryLock()
48 {
49 wxCHECK_MSG( m_internal, wxMUTEX_INVALID,
50 _T("wxMutex::TryLock(): not initialized") );
51
52 return m_internal->TryLock();
53 }
54
55 wxMutexError wxMutex::Unlock()
56 {
57 wxCHECK_MSG( m_internal, wxMUTEX_INVALID,
58 _T("wxMutex::Unlock(): not initialized") );
59
60 return m_internal->Unlock();
61 }
62
63 // --------------------------------------------------------------------------
64 // wxConditionInternal
65 // --------------------------------------------------------------------------
66
67 #if defined(__WXMSW__) || defined(__OS2__) || defined(__EMX__)
68 // Win32 and OS/2 don't have explicit support for the POSIX condition
69 // variables and their events/event semaphores have quite different semantics,
70 // so we reimplement the conditions from scratch using the mutexes and
71 // semaphores
72 #if defined(__OS2__) || defined(__EMX__)
73 void InterlockedIncrement(LONG *num)
74 {
75 ::DosEnterCritSec();
76 (*num)++;
77 ::DosExitCritSec();
78 }
79 #endif
80
81 class wxConditionInternal
82 {
83 public:
84 wxConditionInternal(wxMutex& mutex);
85
86 bool IsOk() const { return m_mutex.IsOk() && m_semaphore.IsOk(); }
87
88 wxCondError Wait();
89 wxCondError WaitTimeout(unsigned long milliseconds);
90
91 wxCondError Signal();
92 wxCondError Broadcast();
93
94 private:
95 // the number of threads currently waiting for this condition
96 LONG m_numWaiters;
97
98 // the critical section protecting m_numWaiters
99 wxCriticalSection m_csWaiters;
100
101 wxMutex& m_mutex;
102 wxSemaphore m_semaphore;
103
104 DECLARE_NO_COPY_CLASS(wxConditionInternal)
105 };
106
107 wxConditionInternal::wxConditionInternal(wxMutex& mutex)
108 : m_mutex(mutex)
109 {
110 // another thread can't access it until we return from ctor, so no need to
111 // protect access to m_numWaiters here
112 m_numWaiters = 0;
113 }
114
115 wxCondError wxConditionInternal::Wait()
116 {
117 // increment the number of waiters
118 ::InterlockedIncrement(&m_numWaiters);
119
120 m_mutex.Unlock();
121
122 // a potential race condition can occur here
123 //
124 // after a thread increments nwaiters, and unlocks the mutex and before the
125 // semaphore.Wait() is called, if another thread can cause a signal to be
126 // generated
127 //
128 // this race condition is handled by using a semaphore and incrementing the
129 // semaphore only if 'nwaiters' is greater that zero since the semaphore,
130 // can 'remember' signals the race condition will not occur
131
132 // wait ( if necessary ) and decrement semaphore
133 wxSemaError err = m_semaphore.Wait();
134 m_mutex.Lock();
135
136 return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR : wxCOND_MISC_ERROR;
137 }
138
139 wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds)
140 {
141 ::InterlockedIncrement(&m_numWaiters);
142
143 m_mutex.Unlock();
144
145 // a race condition can occur at this point in the code
146 //
147 // please see the comments in Wait(), for details
148
149 wxSemaError err = m_semaphore.WaitTimeout(milliseconds);
150
151 if ( err == wxSEMA_TIMEOUT )
152 {
153 // another potential race condition exists here it is caused when a
154 // 'waiting' thread timesout, and returns from WaitForSingleObject, but
155 // has not yet decremented 'nwaiters'.
156 //
157 // at this point if another thread calls signal() then the semaphore
158 // will be incremented, but the waiting thread will miss it.
159 //
160 // to handle this particular case, the waiting thread calls
161 // WaitForSingleObject again with a timeout of 0, after locking
162 // 'nwaiters_mutex'. this call does not block because of the zero
163 // timeout, but will allow the waiting thread to catch the missed
164 // signals.
165 wxCriticalSectionLocker lock(m_csWaiters);
166
167 err = m_semaphore.WaitTimeout(0);
168
169 if ( err != wxSEMA_NO_ERROR )
170 {
171 m_numWaiters--;
172 }
173 }
174
175 m_mutex.Lock();
176
177 return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR
178 : err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT
179 : wxCOND_MISC_ERROR;
180 }
181
182 wxCondError wxConditionInternal::Signal()
183 {
184 wxCriticalSectionLocker lock(m_csWaiters);
185
186 if ( m_numWaiters > 0 )
187 {
188 // increment the semaphore by 1
189 if ( m_semaphore.Post() != wxSEMA_NO_ERROR )
190 return wxCOND_MISC_ERROR;
191
192 m_numWaiters--;
193 }
194
195 return wxCOND_NO_ERROR;
196 }
197
198 wxCondError wxConditionInternal::Broadcast()
199 {
200 wxCriticalSectionLocker lock(m_csWaiters);
201
202 while ( m_numWaiters > 0 )
203 {
204 if ( m_semaphore.Post() != wxSEMA_NO_ERROR )
205 return wxCOND_MISC_ERROR;
206
207 m_numWaiters--;
208 }
209
210 return wxCOND_NO_ERROR;
211 }
212 #endif
213
214 // ----------------------------------------------------------------------------
215 // wxCondition
216 // ----------------------------------------------------------------------------
217
218 wxCondition::wxCondition(wxMutex& mutex)
219 {
220 m_internal = new wxConditionInternal(mutex);
221
222 if ( !m_internal->IsOk() )
223 {
224 delete m_internal;
225 m_internal = NULL;
226 }
227 }
228
229 wxCondition::~wxCondition()
230 {
231 delete m_internal;
232 }
233
234 bool wxCondition::IsOk() const
235 {
236 return m_internal != NULL;
237 }
238
239 wxCondError wxCondition::Wait()
240 {
241 wxCHECK_MSG( m_internal, wxCOND_INVALID,
242 _T("wxCondition::Wait(): not initialized") );
243
244 return m_internal->Wait();
245 }
246
247 wxCondError wxCondition::WaitTimeout(unsigned long milliseconds)
248 {
249 wxCHECK_MSG( m_internal, wxCOND_INVALID,
250 _T("wxCondition::Wait(): not initialized") );
251
252 return m_internal->WaitTimeout(milliseconds);
253 }
254
255 wxCondError wxCondition::Signal()
256 {
257 wxCHECK_MSG( m_internal, wxCOND_INVALID,
258 _T("wxCondition::Signal(): not initialized") );
259
260 return m_internal->Signal();
261 }
262
263 wxCondError wxCondition::Broadcast()
264 {
265 wxCHECK_MSG( m_internal, wxCOND_INVALID,
266 _T("wxCondition::Broadcast(): not initialized") );
267
268 return m_internal->Broadcast();
269 }
270
271 // --------------------------------------------------------------------------
272 // wxSemaphore
273 // --------------------------------------------------------------------------
274
275 wxSemaphore::wxSemaphore(int initialcount, int maxcount)
276 {
277 m_internal = new wxSemaphoreInternal( initialcount, maxcount );
278 if ( !m_internal->IsOk() )
279 {
280 delete m_internal;
281 m_internal = NULL;
282 }
283 }
284
285 wxSemaphore::~wxSemaphore()
286 {
287 delete m_internal;
288 }
289
290 bool wxSemaphore::IsOk() const
291 {
292 return m_internal != NULL;
293 }
294
295 wxSemaError wxSemaphore::Wait()
296 {
297 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
298 _T("wxSemaphore::Wait(): not initialized") );
299
300 return m_internal->Wait();
301 }
302
303 wxSemaError wxSemaphore::TryWait()
304 {
305 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
306 _T("wxSemaphore::TryWait(): not initialized") );
307
308 return m_internal->TryWait();
309 }
310
311 wxSemaError wxSemaphore::WaitTimeout(unsigned long milliseconds)
312 {
313 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
314 _T("wxSemaphore::WaitTimeout(): not initialized") );
315
316 return m_internal->WaitTimeout(milliseconds);
317 }
318
319 wxSemaError wxSemaphore::Post()
320 {
321 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
322 _T("wxSemaphore::Post(): not initialized") );
323
324 return m_internal->Post();
325 }
326