fix handling of errors in wxConditionInternal::Wait() and WaitTimeout() (#10111)
[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::LockTimeout(unsigned long ms)
48 {
49 wxCHECK_MSG( m_internal, wxMUTEX_INVALID,
50 _T("wxMutex::Lock(): not initialized") );
51
52 return m_internal->Lock(ms);
53 }
54
55 wxMutexError wxMutex::TryLock()
56 {
57 wxCHECK_MSG( m_internal, wxMUTEX_INVALID,
58 _T("wxMutex::TryLock(): not initialized") );
59
60 return m_internal->TryLock();
61 }
62
63 wxMutexError wxMutex::Unlock()
64 {
65 wxCHECK_MSG( m_internal, wxMUTEX_INVALID,
66 _T("wxMutex::Unlock(): not initialized") );
67
68 return m_internal->Unlock();
69 }
70
71 // --------------------------------------------------------------------------
72 // wxConditionInternal
73 // --------------------------------------------------------------------------
74
75 // Win32 and OS/2 don't have explicit support for the POSIX condition
76 // variables and their events/event semaphores have quite different semantics,
77 // so we reimplement the conditions from scratch using the mutexes and
78 // semaphores
79 #if defined(__WXMSW__) || defined(__OS2__) || defined(__EMX__)
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 {
119 wxCriticalSectionLocker lock(m_csWaiters);
120 m_numWaiters++;
121 }
122
123 m_mutex.Unlock();
124
125 // after unlocking the mutex other threads may Signal() us, but it is ok
126 // now as we had already incremented m_numWaiters so Signal() will post the
127 // semaphore and decrement m_numWaiters back even if it is called before we
128 // start to Wait()
129 const wxSemaError err = m_semaphore.Wait();
130
131 m_mutex.Lock();
132
133 if ( err == wxSEMA_NO_ERROR )
134 {
135 // m_numWaiters was decremented by Signal()
136 return wxCOND_NO_ERROR;
137 }
138
139 // but in case of an error we need to do it manually
140 {
141 wxCriticalSectionLocker lock(m_csWaiters);
142 m_numWaiters--;
143 }
144
145 return err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT : wxCOND_MISC_ERROR;
146 }
147
148 wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds)
149 {
150 {
151 wxCriticalSectionLocker lock(m_csWaiters);
152 m_numWaiters++;
153 }
154
155 m_mutex.Unlock();
156
157 wxSemaError err = m_semaphore.WaitTimeout(milliseconds);
158
159 m_mutex.Lock();
160
161 if ( err == wxSEMA_NO_ERROR )
162 return wxCOND_NO_ERROR;
163
164 if ( err == wxSEMA_TIMEOUT )
165 {
166 // a potential race condition exists here: it happens when a waiting
167 // thread times out but doesn't have time to decrement m_numWaiters yet
168 // before Signal() is called in another thread
169 //
170 // to handle this particular case, check the semaphore again after
171 // acquiring m_csWaiters lock -- this will catch the signals missed
172 // during this window
173 wxCriticalSectionLocker lock(m_csWaiters);
174
175 err = m_semaphore.WaitTimeout(0);
176 if ( err == wxSEMA_NO_ERROR )
177 return wxCOND_NO_ERROR;
178
179 // we need to decrement m_numWaiters ourselves as it wasn't done by
180 // Signal()
181 m_numWaiters--;
182
183 return err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT : wxCOND_MISC_ERROR;
184 }
185
186 // undo m_numWaiters++ above in case of an error
187 {
188 wxCriticalSectionLocker lock(m_csWaiters);
189 m_numWaiters--;
190 }
191
192 return wxCOND_MISC_ERROR;
193 }
194
195 wxCondError wxConditionInternal::Signal()
196 {
197 wxCriticalSectionLocker lock(m_csWaiters);
198
199 if ( m_numWaiters > 0 )
200 {
201 // increment the semaphore by 1
202 if ( m_semaphore.Post() != wxSEMA_NO_ERROR )
203 return wxCOND_MISC_ERROR;
204
205 m_numWaiters--;
206 }
207
208 return wxCOND_NO_ERROR;
209 }
210
211 wxCondError wxConditionInternal::Broadcast()
212 {
213 wxCriticalSectionLocker lock(m_csWaiters);
214
215 while ( m_numWaiters > 0 )
216 {
217 if ( m_semaphore.Post() != wxSEMA_NO_ERROR )
218 return wxCOND_MISC_ERROR;
219
220 m_numWaiters--;
221 }
222
223 return wxCOND_NO_ERROR;
224 }
225
226 #endif // MSW or OS2
227
228 // ----------------------------------------------------------------------------
229 // wxCondition
230 // ----------------------------------------------------------------------------
231
232 wxCondition::wxCondition(wxMutex& mutex)
233 {
234 m_internal = new wxConditionInternal(mutex);
235
236 if ( !m_internal->IsOk() )
237 {
238 delete m_internal;
239 m_internal = NULL;
240 }
241 }
242
243 wxCondition::~wxCondition()
244 {
245 delete m_internal;
246 }
247
248 bool wxCondition::IsOk() const
249 {
250 return m_internal != NULL;
251 }
252
253 wxCondError wxCondition::Wait()
254 {
255 wxCHECK_MSG( m_internal, wxCOND_INVALID,
256 _T("wxCondition::Wait(): not initialized") );
257
258 return m_internal->Wait();
259 }
260
261 wxCondError wxCondition::WaitTimeout(unsigned long milliseconds)
262 {
263 wxCHECK_MSG( m_internal, wxCOND_INVALID,
264 _T("wxCondition::Wait(): not initialized") );
265
266 return m_internal->WaitTimeout(milliseconds);
267 }
268
269 wxCondError wxCondition::Signal()
270 {
271 wxCHECK_MSG( m_internal, wxCOND_INVALID,
272 _T("wxCondition::Signal(): not initialized") );
273
274 return m_internal->Signal();
275 }
276
277 wxCondError wxCondition::Broadcast()
278 {
279 wxCHECK_MSG( m_internal, wxCOND_INVALID,
280 _T("wxCondition::Broadcast(): not initialized") );
281
282 return m_internal->Broadcast();
283 }
284
285 // --------------------------------------------------------------------------
286 // wxSemaphore
287 // --------------------------------------------------------------------------
288
289 wxSemaphore::wxSemaphore(int initialcount, int maxcount)
290 {
291 m_internal = new wxSemaphoreInternal( initialcount, maxcount );
292 if ( !m_internal->IsOk() )
293 {
294 delete m_internal;
295 m_internal = NULL;
296 }
297 }
298
299 wxSemaphore::~wxSemaphore()
300 {
301 delete m_internal;
302 }
303
304 bool wxSemaphore::IsOk() const
305 {
306 return m_internal != NULL;
307 }
308
309 wxSemaError wxSemaphore::Wait()
310 {
311 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
312 _T("wxSemaphore::Wait(): not initialized") );
313
314 return m_internal->Wait();
315 }
316
317 wxSemaError wxSemaphore::TryWait()
318 {
319 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
320 _T("wxSemaphore::TryWait(): not initialized") );
321
322 return m_internal->TryWait();
323 }
324
325 wxSemaError wxSemaphore::WaitTimeout(unsigned long milliseconds)
326 {
327 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
328 _T("wxSemaphore::WaitTimeout(): not initialized") );
329
330 return m_internal->WaitTimeout(milliseconds);
331 }
332
333 wxSemaError wxSemaphore::Post()
334 {
335 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
336 _T("wxSemaphore::Post(): not initialized") );
337
338 return m_internal->Post();
339 }
340
341 // ----------------------------------------------------------------------------
342 // wxThread
343 // ----------------------------------------------------------------------------
344
345 #include "wx/utils.h"
346
347 void wxThread::Sleep(unsigned long milliseconds)
348 {
349 wxMilliSleep(milliseconds);
350 }