]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/thread.cpp
VC++ compilation correction; doc file corrections
[wxWidgets.git] / src / msw / thread.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: thread.cpp
3// Purpose: wxThread Implementation
4// Author: Original from Wolfram Gloger/Guilhem Lavaux
5// Modified by: Vadim Zeitlin to make it work :-)
6// Created: 04/22/98
7// RCS-ID: $Id$
8// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998),
9// Vadim Zeitlin (1999)
10// Licence: wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13#ifdef __GNUG__
14 #pragma implementation "thread.h"
15#endif
16
17// ----------------------------------------------------------------------------
18// headers
19// ----------------------------------------------------------------------------
20
21// For compilers that support precompilation, includes "wx.h".
22#include "wx/wxprec.h"
23
24#if defined(__BORLANDC__)
25 #pragma hdrstop
26#endif
27
28#ifndef WX_PRECOMP
29 #include "wx/wx.h"
30#endif
31
32#if wxUSE_THREADS
33
34#include <stdio.h>
35
36#include <windows.h>
37
38#include "wx/module.h"
39#include "wx/thread.h"
40
41enum thread_state
42{
43 STATE_IDLE = 0,
44 STATE_RUNNING,
45 STATE_PAUSED,
46 STATE_CANCELED,
47 STATE_EXITED
48};
49
50// ----------------------------------------------------------------------------
51// static variables
52// ----------------------------------------------------------------------------
53
54// id of the main thread - the one which can call GUI functions without first
55// calling wxMutexGuiEnter()
56static DWORD s_idMainThread = 0;
57
58// if it's FALSE, some secondary thread is holding the GUI lock
59static bool s_bGuiOwnedByMainThread = TRUE;
60
61// critical section which controls access to all GUI functions: any secondary
62// thread (i.e. except the main one) must enter this crit section before doing
63// any GUI calls
64static wxCriticalSection *s_critsectGui = NULL;
65
66// critical section which protects s_nWaitingForGui variable
67static wxCriticalSection *s_critsectWaitingForGui = NULL;
68
69// number of threads waiting for GUI in wxMutexGuiEnter()
70static size_t s_nWaitingForGui = 0;
71
72// ============================================================================
73// Windows implementation of thread classes
74// ============================================================================
75
76// ----------------------------------------------------------------------------
77// wxMutex implementation
78// ----------------------------------------------------------------------------
79class wxMutexInternal
80{
81public:
82 HANDLE p_mutex;
83};
84
85wxMutex::wxMutex()
86{
87 p_internal = new wxMutexInternal;
88 p_internal->p_mutex = CreateMutex(NULL, FALSE, NULL);
89 if ( !p_internal->p_mutex )
90 {
91 wxLogSysError(_("Can not create mutex."));
92 }
93
94 m_locked = 0;
95}
96
97wxMutex::~wxMutex()
98{
99 if (m_locked > 0)
100 wxLogDebug("Warning: freeing a locked mutex (%d locks).", m_locked);
101 CloseHandle(p_internal->p_mutex);
102}
103
104wxMutexError wxMutex::Lock()
105{
106 DWORD ret;
107
108 ret = WaitForSingleObject(p_internal->p_mutex, INFINITE);
109 switch ( ret )
110 {
111 case WAIT_ABANDONED:
112 return wxMUTEX_BUSY;
113
114 case WAIT_OBJECT_0:
115 // ok
116 break;
117
118 case WAIT_FAILED:
119 wxLogSysError(_("Couldn't acquire a mutex lock"));
120 return wxMUTEX_MISC_ERROR;
121
122 case WAIT_TIMEOUT:
123 default:
124 wxFAIL_MSG("impossible return value in wxMutex::Lock");
125 }
126
127 m_locked++;
128 return wxMUTEX_NO_ERROR;
129}
130
131wxMutexError wxMutex::TryLock()
132{
133 DWORD ret;
134
135 ret = WaitForSingleObject(p_internal->p_mutex, 0);
136 if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED)
137 return wxMUTEX_BUSY;
138
139 m_locked++;
140 return wxMUTEX_NO_ERROR;
141}
142
143wxMutexError wxMutex::Unlock()
144{
145 if (m_locked > 0)
146 m_locked--;
147
148 BOOL ret = ReleaseMutex(p_internal->p_mutex);
149 if ( ret == 0 )
150 {
151 wxLogSysError(_("Couldn't release a mutex"));
152 return wxMUTEX_MISC_ERROR;
153 }
154
155 return wxMUTEX_NO_ERROR;
156}
157
158// ----------------------------------------------------------------------------
159// wxCondition implementation
160// ----------------------------------------------------------------------------
161
162class wxConditionInternal
163{
164public:
165 HANDLE event;
166 int waiters;
167};
168
169wxCondition::wxCondition()
170{
171 p_internal = new wxConditionInternal;
172 p_internal->event = CreateEvent(NULL, FALSE, FALSE, NULL);
173 if ( !p_internal->event )
174 {
175 wxLogSysError(_("Can not create event object."));
176 }
177
178 p_internal->waiters = 0;
179}
180
181wxCondition::~wxCondition()
182{
183 CloseHandle(p_internal->event);
184}
185
186void wxCondition::Wait(wxMutex& mutex)
187{
188 mutex.Unlock();
189 p_internal->waiters++;
190 WaitForSingleObject(p_internal->event, INFINITE);
191 p_internal->waiters--;
192 mutex.Lock();
193}
194
195bool wxCondition::Wait(wxMutex& mutex,
196 unsigned long sec,
197 unsigned long nsec)
198{
199 DWORD ret;
200
201 mutex.Unlock();
202 p_internal->waiters++;
203 ret = WaitForSingleObject(p_internal->event, (sec*1000)+(nsec/1000000));
204 p_internal->waiters--;
205 mutex.Lock();
206
207 return (ret != WAIT_TIMEOUT);
208}
209
210void wxCondition::Signal()
211{
212 SetEvent(p_internal->event);
213}
214
215void wxCondition::Broadcast()
216{
217 int i;
218
219 for (i=0;i<p_internal->waiters;i++)
220 {
221 if ( SetEvent(p_internal->event) == 0 )
222 {
223 wxLogSysError(_("Couldn't change the state of event object."));
224 }
225 }
226}
227
228// ----------------------------------------------------------------------------
229// wxCriticalSection implementation
230// ----------------------------------------------------------------------------
231
232class wxCriticalSectionInternal
233{
234public:
235 // init the critical section object
236 wxCriticalSectionInternal()
237 { ::InitializeCriticalSection(&m_data); }
238
239 // implicit cast to the associated data
240 operator CRITICAL_SECTION *() { return &m_data; }
241
242 // free the associated ressources
243 ~wxCriticalSectionInternal()
244 { ::DeleteCriticalSection(&m_data); }
245
246private:
247 CRITICAL_SECTION m_data;
248};
249
250wxCriticalSection::wxCriticalSection()
251{
252 m_critsect = new wxCriticalSectionInternal;
253}
254
255wxCriticalSection::~wxCriticalSection()
256{
257 delete m_critsect;
258}
259
260void wxCriticalSection::Enter()
261{
262 ::EnterCriticalSection(*m_critsect);
263}
264
265void wxCriticalSection::Leave()
266{
267 ::LeaveCriticalSection(*m_critsect);
268}
269
270// ----------------------------------------------------------------------------
271// wxThread implementation
272// ----------------------------------------------------------------------------
273
274class wxThreadInternal
275{
276public:
277 static DWORD WinThreadStart(wxThread *thread);
278
279 HANDLE hThread;
280 thread_state state;
281 int prio, defer;
282 DWORD tid;
283};
284
285DWORD wxThreadInternal::WinThreadStart(wxThread *thread)
286{
287 DWORD ret = (DWORD)thread->Entry();
288 thread->p_internal->state = STATE_EXITED;
289 thread->OnExit();
290
291 return ret;
292}
293
294wxThreadError wxThread::Create()
295{
296 p_internal->hThread = ::CreateThread
297 (
298 NULL, // default security
299 0, // default stack size
300 (LPTHREAD_START_ROUTINE)
301 wxThreadInternal::WinThreadStart, // entry point
302 (void *)this, // parameter
303 CREATE_SUSPENDED, // flags
304 &p_internal->tid // [out] thread id
305 );
306
307 if ( p_internal->hThread == NULL )
308 {
309 wxLogSysError(_("Can't create thread"));
310 return wxTHREAD_NO_RESOURCE;
311 }
312
313 int win_prio, prio = p_internal->prio;
314 if (prio <= 20)
315 win_prio = THREAD_PRIORITY_LOWEST;
316 else if (prio <= 40)
317 win_prio = THREAD_PRIORITY_BELOW_NORMAL;
318 else if (prio <= 60)
319 win_prio = THREAD_PRIORITY_NORMAL;
320 else if (prio <= 80)
321 win_prio = THREAD_PRIORITY_ABOVE_NORMAL;
322 else if (prio <= 100)
323 win_prio = THREAD_PRIORITY_HIGHEST;
324 else
325 {
326 wxFAIL_MSG("invalid value of thread priority parameter");
327 win_prio = THREAD_PRIORITY_NORMAL;
328 }
329
330 if ( SetThreadPriority(p_internal->hThread, win_prio) == 0 )
331 {
332 wxLogSysError(_("Can't set thread priority"));
333 }
334
335 return Resume();
336}
337
338wxThreadError wxThread::Destroy()
339{
340 if ( p_internal->state != STATE_RUNNING )
341 return wxTHREAD_NOT_RUNNING;
342
343 if ( p_internal->defer )
344 {
345 // soft termination: just set the flag and wait until the thread
346 // auto terminates
347 p_internal->state = STATE_CANCELED;
348 }
349 else
350 {
351 // kill the thread
352 OnExit();
353 if ( ::TerminateThread(p_internal->hThread, 0) == 0 )
354 {
355 wxLogLastError("TerminateThread");
356 }
357 }
358
359 return wxTHREAD_NO_ERROR;
360}
361
362wxThreadError wxThread::Pause()
363{
364 DWORD nSuspendCount = ::SuspendThread(p_internal->hThread);
365 if ( nSuspendCount == (DWORD)-1 )
366 {
367 wxLogSysError(_("Can not suspend thread %x"), p_internal->hThread);
368
369 return wxTHREAD_MISC_ERROR; // no idea what might provoke this error...
370 }
371
372 p_internal->state = STATE_PAUSED;
373
374 return wxTHREAD_NO_ERROR;
375}
376
377wxThreadError wxThread::Resume()
378{
379 DWORD nSuspendCount = ::ResumeThread(p_internal->hThread);
380 if ( nSuspendCount == (DWORD)-1 )
381 {
382 wxLogSysError(_("Can not resume thread %x"), p_internal->hThread);
383
384 return wxTHREAD_MISC_ERROR; // no idea what might provoke this error...
385 }
386
387 p_internal->state = STATE_RUNNING;
388
389 return wxTHREAD_NO_ERROR;
390}
391
392void wxThread::Exit(void *status)
393{
394 p_internal->state = STATE_EXITED;
395 ExitThread((DWORD)status);
396}
397
398void wxThread::SetPriority(int prio)
399{
400 p_internal->prio = prio;
401}
402
403int wxThread::GetPriority() const
404{
405 return p_internal->prio;
406}
407
408void wxThread::DeferDestroy(bool on)
409{
410 p_internal->defer = on;
411}
412
413bool wxThread::TestDestroy()
414{
415 return p_internal->state == STATE_CANCELED;
416}
417
418void *wxThread::Join()
419{
420 DWORD exit_code;
421
422 if (p_internal->state == STATE_IDLE)
423 return NULL;
424
425 // FIXME this dead locks... wxThread class design must be changed
426#if 0
427 WaitForSingleObject(p_internal->hThread, INFINITE);
428#else
429 ::TerminateThread(p_internal->hThread, 0);
430#endif // 0
431
432 GetExitCodeThread(p_internal->hThread, &exit_code);
433 CloseHandle(p_internal->hThread);
434
435 p_internal->state = STATE_IDLE;
436
437 return (void *)exit_code;
438}
439
440unsigned long wxThread::GetID() const
441{
442 return (unsigned long)p_internal->tid;
443}
444
445bool wxThread::IsRunning() const
446{
447 return (p_internal->state == STATE_RUNNING);
448}
449
450bool wxThread::IsAlive() const
451{
452 return (p_internal->state == STATE_RUNNING);
453}
454
455bool wxThread::IsMain()
456{
457 return ::GetCurrentThreadId() == s_idMainThread;
458}
459
460wxThread::wxThread()
461{
462 p_internal = new wxThreadInternal();
463
464 p_internal->defer = FALSE;
465 p_internal->prio = WXTHREAD_DEFAULT_PRIORITY;
466 p_internal->state = STATE_IDLE;
467}
468
469wxThread::~wxThread()
470{
471 Destroy();
472 Join();
473 delete p_internal;
474}
475
476void wxThread::OnExit()
477{
478}
479
480// ----------------------------------------------------------------------------
481// Automatic initialization for thread module
482// ----------------------------------------------------------------------------
483
484class wxThreadModule : public wxModule
485{
486public:
487 virtual bool OnInit();
488 virtual void OnExit();
489
490private:
491 DECLARE_DYNAMIC_CLASS(wxThreadModule)
492};
493
494IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
495
496bool wxThreadModule::OnInit()
497{
498 s_critsectWaitingForGui = new wxCriticalSection();
499
500 s_critsectGui = new wxCriticalSection();
501 s_critsectGui->Enter();
502
503 s_idMainThread = ::GetCurrentThreadId();
504
505 return TRUE;
506}
507
508void wxThreadModule::OnExit()
509{
510 if ( s_critsectGui )
511 {
512 s_critsectGui->Leave();
513 delete s_critsectGui;
514 s_critsectGui = NULL;
515 }
516
517 wxDELETE(s_critsectWaitingForGui);
518}
519
520// ----------------------------------------------------------------------------
521// under Windows, these functions are implemented usign a critical section and
522// not a mutex, so the names are a bit confusing
523// ----------------------------------------------------------------------------
524
525void WXDLLEXPORT wxMutexGuiEnter()
526{
527 // this would dead lock everything...
528 wxASSERT_MSG( !wxThread::IsMain(),
529 "main thread doesn't want to block in wxMutexGuiEnter()!" );
530
531 // the order in which we enter the critical sections here is crucial!!
532
533 // set the flag telling to the main thread that we want to do some GUI
534 {
535 wxCriticalSectionLocker enter(*s_critsectWaitingForGui);
536
537 s_nWaitingForGui++;
538 }
539
540 wxWakeUpMainThread();
541
542 // now we may block here because the main thread will soon let us in
543 // (during the next iteration of OnIdle())
544 s_critsectGui->Enter();
545}
546
547void WXDLLEXPORT wxMutexGuiLeave()
548{
549 wxCriticalSectionLocker enter(*s_critsectWaitingForGui);
550
551 if ( wxThread::IsMain() )
552 {
553 s_bGuiOwnedByMainThread = FALSE;
554 }
555 else
556 {
557 // decrement the number of waiters now
558 wxASSERT_MSG( s_nWaitingForGui > 0,
559 "calling wxMutexGuiLeave() without entering it first?" );
560
561 s_nWaitingForGui--;
562
563 wxWakeUpMainThread();
564 }
565
566 s_critsectGui->Leave();
567}
568
569void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
570{
571 wxASSERT_MSG( wxThread::IsMain(),
572 "only main thread may call wxMutexGuiLeaveOrEnter()!" );
573
574 wxCriticalSectionLocker enter(*s_critsectWaitingForGui);
575
576 if ( s_nWaitingForGui == 0 )
577 {
578 // no threads are waiting for GUI - so we may acquire the lock without
579 // any danger (but only if we don't already have it)
580 if ( !wxGuiOwnedByMainThread() )
581 {
582 s_critsectGui->Enter();
583
584 s_bGuiOwnedByMainThread = TRUE;
585 }
586 //else: already have it, nothing to do
587 }
588 else
589 {
590 // some threads are waiting, release the GUI lock if we have it
591 if ( wxGuiOwnedByMainThread() )
592 {
593 wxMutexGuiLeave();
594 }
595 //else: some other worker thread is doing GUI
596 }
597}
598
599bool WXDLLEXPORT wxGuiOwnedByMainThread()
600{
601 return s_bGuiOwnedByMainThread;
602}
603
604// wake up the main thread if it's in ::GetMessage()
605void WXDLLEXPORT wxWakeUpMainThread()
606{
607 // sending any message would do - hopefully WM_NULL is harmless enough
608 if ( !::PostThreadMessage(s_idMainThread, WM_NULL, 0, 0) )
609 {
610 // should never happen
611 wxLogLastError("PostThreadMessage(WM_NULL)");
612 }
613}
614
615#endif // wxUSE_THREADS