added wxMutex::LockTimeout() (modified patch 1671637)
[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 // a potential race condition can occur here
126 //
127 // after a thread increments m_numWaiters, and unlocks the mutex and before
128 // the semaphore.Wait() is called, if another thread can cause a signal to
129 // be generated
130 //
131 // this race condition is handled by using a semaphore and incrementing the
132 // semaphore only if m_numWaiters is greater that zero since the semaphore,
133 // can 'remember' signals the race condition will not occur
134
135 // wait ( if necessary ) and decrement semaphore
136 wxSemaError err = m_semaphore.Wait();
137 m_mutex.Lock();
138
139 if ( err == wxSEMA_NO_ERROR )
140 return wxCOND_NO_ERROR;
141 else if ( err == wxSEMA_TIMEOUT )
142 return wxCOND_TIMEOUT;
143 else
144 return wxCOND_MISC_ERROR;
145 }
146
147 wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds)
148 {
149 {
150 wxCriticalSectionLocker lock(m_csWaiters);
151 m_numWaiters++;
152 }
153
154 m_mutex.Unlock();
155
156 // a race condition can occur at this point in the code
157 //
158 // please see the comments in Wait(), for details
159
160 wxSemaError err = m_semaphore.WaitTimeout(milliseconds);
161
162 if ( err == wxSEMA_TIMEOUT )
163 {
164 // another potential race condition exists here it is caused when a
165 // 'waiting' thread times out, and returns from WaitForSingleObject,
166 // but has not yet decremented m_numWaiters
167 //
168 // at this point if another thread calls signal() then the semaphore
169 // will be incremented, but the waiting thread will miss it.
170 //
171 // to handle this particular case, the waiting thread calls
172 // WaitForSingleObject again with a timeout of 0, after locking
173 // m_csWaiters. This call does not block because of the zero
174 // timeout, but will allow the waiting thread to catch the missed
175 // signals.
176 wxCriticalSectionLocker lock(m_csWaiters);
177
178 wxSemaError err2 = m_semaphore.WaitTimeout(0);
179
180 if ( err2 != wxSEMA_NO_ERROR )
181 {
182 m_numWaiters--;
183 }
184 }
185
186 m_mutex.Lock();
187
188 return err == wxSEMA_NO_ERROR ? wxCOND_NO_ERROR
189 : err == wxSEMA_TIMEOUT ? wxCOND_TIMEOUT
190 : wxCOND_MISC_ERROR;
191 }
192
193 wxCondError wxConditionInternal::Signal()
194 {
195 wxCriticalSectionLocker lock(m_csWaiters);
196
197 if ( m_numWaiters > 0 )
198 {
199 // increment the semaphore by 1
200 if ( m_semaphore.Post() != wxSEMA_NO_ERROR )
201 return wxCOND_MISC_ERROR;
202
203 m_numWaiters--;
204 }
205
206 return wxCOND_NO_ERROR;
207 }
208
209 wxCondError wxConditionInternal::Broadcast()
210 {
211 wxCriticalSectionLocker lock(m_csWaiters);
212
213 while ( m_numWaiters > 0 )
214 {
215 if ( m_semaphore.Post() != wxSEMA_NO_ERROR )
216 return wxCOND_MISC_ERROR;
217
218 m_numWaiters--;
219 }
220
221 return wxCOND_NO_ERROR;
222 }
223
224 #endif // MSW or OS2
225
226 // ----------------------------------------------------------------------------
227 // wxCondition
228 // ----------------------------------------------------------------------------
229
230 wxCondition::wxCondition(wxMutex& mutex)
231 {
232 m_internal = new wxConditionInternal(mutex);
233
234 if ( !m_internal->IsOk() )
235 {
236 delete m_internal;
237 m_internal = NULL;
238 }
239 }
240
241 wxCondition::~wxCondition()
242 {
243 delete m_internal;
244 }
245
246 bool wxCondition::IsOk() const
247 {
248 return m_internal != NULL;
249 }
250
251 wxCondError wxCondition::Wait()
252 {
253 wxCHECK_MSG( m_internal, wxCOND_INVALID,
254 _T("wxCondition::Wait(): not initialized") );
255
256 return m_internal->Wait();
257 }
258
259 wxCondError wxCondition::WaitTimeout(unsigned long milliseconds)
260 {
261 wxCHECK_MSG( m_internal, wxCOND_INVALID,
262 _T("wxCondition::Wait(): not initialized") );
263
264 return m_internal->WaitTimeout(milliseconds);
265 }
266
267 wxCondError wxCondition::Signal()
268 {
269 wxCHECK_MSG( m_internal, wxCOND_INVALID,
270 _T("wxCondition::Signal(): not initialized") );
271
272 return m_internal->Signal();
273 }
274
275 wxCondError wxCondition::Broadcast()
276 {
277 wxCHECK_MSG( m_internal, wxCOND_INVALID,
278 _T("wxCondition::Broadcast(): not initialized") );
279
280 return m_internal->Broadcast();
281 }
282
283 // --------------------------------------------------------------------------
284 // wxSemaphore
285 // --------------------------------------------------------------------------
286
287 wxSemaphore::wxSemaphore(int initialcount, int maxcount)
288 {
289 m_internal = new wxSemaphoreInternal( initialcount, maxcount );
290 if ( !m_internal->IsOk() )
291 {
292 delete m_internal;
293 m_internal = NULL;
294 }
295 }
296
297 wxSemaphore::~wxSemaphore()
298 {
299 delete m_internal;
300 }
301
302 bool wxSemaphore::IsOk() const
303 {
304 return m_internal != NULL;
305 }
306
307 wxSemaError wxSemaphore::Wait()
308 {
309 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
310 _T("wxSemaphore::Wait(): not initialized") );
311
312 return m_internal->Wait();
313 }
314
315 wxSemaError wxSemaphore::TryWait()
316 {
317 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
318 _T("wxSemaphore::TryWait(): not initialized") );
319
320 return m_internal->TryWait();
321 }
322
323 wxSemaError wxSemaphore::WaitTimeout(unsigned long milliseconds)
324 {
325 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
326 _T("wxSemaphore::WaitTimeout(): not initialized") );
327
328 return m_internal->WaitTimeout(milliseconds);
329 }
330
331 wxSemaError wxSemaphore::Post()
332 {
333 wxCHECK_MSG( m_internal, wxSEMA_INVALID,
334 _T("wxSemaphore::Post(): not initialized") );
335
336 return m_internal->Post();
337 }
338