*** empty log message ***
[wxWidgets.git] / src / os2 / thread.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: thread.cpp
3 // Purpose: wxThread Implementation. For Unix ports, see e.g. src/gtk
4 // Author: Original from Wolfram Gloger/Guilhem Lavaux
5 // Modified by: David Webster
6 // Created: 04/22/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ----------------------------------------------------------------------------
13 // headers
14 // ----------------------------------------------------------------------------
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #if wxUSE_THREADS
20
21 #include <stdio.h>
22
23 #define INCL_DOS
24 #include <os2.h>
25
26 #include "wx/module.h"
27 #include "wx/thread.h"
28
29 // the possible states of the thread ("=>" shows all possible transitions from
30 // this state)
31 enum wxThreadState
32 {
33 STATE_NEW, // didn't start execution yet (=> RUNNING)
34 STATE_RUNNING, // thread is running (=> PAUSED, CANCELED)
35 STATE_PAUSED, // thread is temporarily suspended (=> RUNNING)
36 STATE_CANCELED, // thread should terminate a.s.a.p. (=> EXITED)
37 STATE_EXITED // thread is terminating
38 };
39
40 // ----------------------------------------------------------------------------
41 // static variables
42 // ----------------------------------------------------------------------------
43
44 // TLS index of the slot where we store the pointer to the current thread
45 static DWORD s_tlsThisThread = 0xFFFFFFFF;
46
47 // id of the main thread - the one which can call GUI functions without first
48 // calling wxMutexGuiEnter()
49 static DWORD s_idMainThread = 0;
50
51 // if it's FALSE, some secondary thread is holding the GUI lock
52 static bool s_bGuiOwnedByMainThread = TRUE;
53
54 // critical section which controls access to all GUI functions: any secondary
55 // thread (i.e. except the main one) must enter this crit section before doing
56 // any GUI calls
57 static wxCriticalSection *s_critsectGui = NULL;
58
59 // critical section which protects s_nWaitingForGui variable
60 static wxCriticalSection *s_critsectWaitingForGui = NULL;
61
62 // number of threads waiting for GUI in wxMutexGuiEnter()
63 static size_t s_nWaitingForGui = 0;
64
65 // are we waiting for a thread termination?
66 static bool s_waitingForThread = FALSE;
67
68 // ============================================================================
69 // Windows implementation of thread classes
70 // ============================================================================
71
72 // ----------------------------------------------------------------------------
73 // wxMutex implementation
74 // ----------------------------------------------------------------------------
75 class wxMutexInternal
76 {
77 public:
78 HANDLE p_mutex;
79 };
80
81 wxMutex::wxMutex()
82 {
83 p_internal = new wxMutexInternal;
84 // p_internal->p_mutex = CreateMutex(NULL, FALSE, NULL);
85 if ( !p_internal->p_mutex )
86 {
87 wxLogSysError(_("Can not create mutex."));
88 }
89
90 m_locked = 0;
91 }
92
93 wxMutex::~wxMutex()
94 {
95 if (m_locked > 0)
96 wxLogDebug(wxT("Warning: freeing a locked mutex (%d locks)."), m_locked);
97 // CloseHandle(p_internal->p_mutex);
98 }
99
100 wxMutexError wxMutex::Lock()
101 {
102 DWORD ret;
103
104 // TODO:
105 /*
106 ret = WaitForSingleObject(p_internal->p_mutex, INFINITE);
107 switch ( ret )
108 {
109 case WAIT_ABANDONED:
110 return wxMUTEX_BUSY;
111
112 case WAIT_OBJECT_0:
113 // ok
114 break;
115
116 case WAIT_FAILED:
117 wxLogSysError(_("Couldn't acquire a mutex lock"));
118 return wxMUTEX_MISC_ERROR;
119
120 case WAIT_TIMEOUT:
121 default:
122 wxFAIL_MSG(wxT("impossible return value in wxMutex::Lock"));
123 }
124
125 m_locked++;
126 */
127 return wxMUTEX_NO_ERROR;
128 }
129
130 wxMutexError wxMutex::TryLock()
131 {
132 DWORD ret;
133
134 // TODO:
135 /*
136 ret = WaitForSingleObject(p_internal->p_mutex, 0);
137 if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED)
138 return wxMUTEX_BUSY;
139
140 m_locked++;
141 */
142 return wxMUTEX_NO_ERROR;
143 }
144
145 wxMutexError wxMutex::Unlock()
146 {
147 if (m_locked > 0)
148 m_locked--;
149
150 BOOL ret = 0; // TODO: ReleaseMutex(p_internal->p_mutex);
151 if ( ret == 0 )
152 {
153 wxLogSysError(_("Couldn't release a mutex"));
154 return wxMUTEX_MISC_ERROR;
155 }
156
157 return wxMUTEX_NO_ERROR;
158 }
159
160 // ----------------------------------------------------------------------------
161 // wxCondition implementation
162 // ----------------------------------------------------------------------------
163
164 class wxConditionInternal
165 {
166 public:
167 HANDLE event;
168 int waiters;
169 };
170
171 wxCondition::wxCondition()
172 {
173 p_internal = new wxConditionInternal;
174 // TODO:
175 /*
176 p_internal->event = CreateEvent(NULL, FALSE, FALSE, NULL);
177 if ( !p_internal->event )
178 {
179 wxLogSysError(_("Can not create event object."));
180 }
181 */
182 p_internal->waiters = 0;
183 }
184
185 wxCondition::~wxCondition()
186 {
187 // CloseHandle(p_internal->event);
188 }
189
190 void wxCondition::Wait(wxMutex& mutex)
191 {
192 mutex.Unlock();
193 p_internal->waiters++;
194 // WaitForSingleObject(p_internal->event, INFINITE);
195 p_internal->waiters--;
196 mutex.Lock();
197 }
198
199 bool wxCondition::Wait(wxMutex& mutex,
200 unsigned long sec,
201 unsigned long nsec)
202 {
203 DWORD ret;
204
205 mutex.Unlock();
206 p_internal->waiters++;
207 // ret = WaitForSingleObject(p_internal->event, (sec*1000)+(nsec/1000000));
208 p_internal->waiters--;
209 mutex.Lock();
210
211 // return (ret != WAIT_TIMEOUT);
212 return FALSE;
213 }
214
215 void wxCondition::Signal()
216 {
217 // SetEvent(p_internal->event);
218 }
219
220 void wxCondition::Broadcast()
221 {
222 int i;
223
224 // TODO:
225 /*
226 for (i=0;i<p_internal->waiters;i++)
227 {
228 if ( SetEvent(p_internal->event) == 0 )
229 {
230 wxLogSysError(_("Couldn't change the state of event object."));
231 }
232 }
233 */
234 }
235
236 // ----------------------------------------------------------------------------
237 // wxCriticalSection implementation
238 // ----------------------------------------------------------------------------
239 #define CRITICAL_SECTION ULONG
240 class wxCriticalSectionInternal
241 {
242 public:
243 // init the critical section object
244 wxCriticalSectionInternal()
245 { //::InitializeCriticalSection(&m_data);
246 }
247
248 // implicit cast to the associated data
249 operator CRITICAL_SECTION *() { return &m_data; }
250
251 // free the associated ressources
252 ~wxCriticalSectionInternal()
253 { //::DeleteCriticalSection(&m_data);
254 }
255
256 private:
257 CRITICAL_SECTION m_data;
258 };
259
260 wxCriticalSection::wxCriticalSection()
261 {
262 m_critsect = new wxCriticalSectionInternal;
263 }
264
265 wxCriticalSection::~wxCriticalSection()
266 {
267 delete m_critsect;
268 }
269
270 void wxCriticalSection::Enter()
271 {
272 //TODO: ::EnterCriticalSection(*m_critsect);
273 }
274
275 void wxCriticalSection::Leave()
276 {
277 // TODO: ::LeaveCriticalSection(*m_critsect);
278 }
279
280 // ----------------------------------------------------------------------------
281 // wxThread implementation
282 // ----------------------------------------------------------------------------
283
284 // wxThreadInternal class
285 // ----------------------
286
287 class wxThreadInternal
288 {
289 public:
290 wxThreadInternal()
291 {
292 m_hThread = 0;
293 m_state = STATE_NEW;
294 m_priority = 0; // TODO: WXTHREAD_DEFAULT_PRIORITY;
295 }
296
297 // create a new (suspended) thread (for the given thread object)
298 bool Create(wxThread *thread);
299
300 // suspend/resume/terminate
301 bool Suspend();
302 bool Resume();
303 void Cancel() { m_state = STATE_CANCELED; }
304
305 // thread state
306 void SetState(wxThreadState state) { m_state = state; }
307 wxThreadState GetState() const { return m_state; }
308
309 // thread priority
310 void SetPriority(unsigned int priority) { m_priority = priority; }
311 unsigned int GetPriority() const { return m_priority; }
312
313 // thread handle and id
314 HANDLE GetHandle() const { return m_hThread; }
315 DWORD GetId() const { return m_tid; }
316
317 // thread function
318 static DWORD OS2ThreadStart(wxThread *thread);
319
320 private:
321 HANDLE m_hThread; // handle of the thread
322 wxThreadState m_state; // state, see wxThreadState enum
323 unsigned int m_priority; // thread priority in "wx" units
324 DWORD m_tid; // thread id
325 };
326
327 DWORD wxThreadInternal::OS2ThreadStart(wxThread *thread)
328 {
329 // store the thread object in the TLS
330 // TODO:
331 /*
332 if ( !::TlsSetValue(s_tlsThisThread, thread) )
333 {
334 wxLogSysError(_("Can not start thread: error writing TLS."));
335
336 return (DWORD)-1;
337 }
338 */
339 DWORD ret = (DWORD)thread->Entry();
340 thread->p_internal->SetState(STATE_EXITED);
341 thread->OnExit();
342
343 delete thread;
344
345 return ret;
346 }
347
348 bool wxThreadInternal::Create(wxThread *thread)
349 {
350 // TODO:
351 /*
352 m_hThread = ::CreateThread
353 (
354 NULL, // default security
355 0, // default stack size
356 (LPTHREAD_START_ROUTINE) // thread entry point
357 wxThreadInternal::OS2ThreadStart, //
358 (LPVOID)thread, // parameter
359 CREATE_SUSPENDED, // flags
360 &m_tid // [out] thread id
361 );
362
363 if ( m_hThread == NULL )
364 {
365 wxLogSysError(_("Can't create thread"));
366
367 return FALSE;
368 }
369
370 // translate wxWindows priority to the Windows one
371 int win_priority;
372 if (m_priority <= 20)
373 win_priority = THREAD_PRIORITY_LOWEST;
374 else if (m_priority <= 40)
375 win_priority = THREAD_PRIORITY_BELOW_NORMAL;
376 else if (m_priority <= 60)
377 win_priority = THREAD_PRIORITY_NORMAL;
378 else if (m_priority <= 80)
379 win_priority = THREAD_PRIORITY_ABOVE_NORMAL;
380 else if (m_priority <= 100)
381 win_priority = THREAD_PRIORITY_HIGHEST;
382 else
383 {
384 wxFAIL_MSG(wxT("invalid value of thread priority parameter"));
385 win_priority = THREAD_PRIORITY_NORMAL;
386 }
387
388 if ( ::SetThreadPriority(m_hThread, win_priority) == 0 )
389 {
390 wxLogSysError(_("Can't set thread priority"));
391 }
392 */
393 return FALSE;
394 }
395
396 bool wxThreadInternal::Suspend()
397 {
398 // TODO:
399 /*
400 DWORD nSuspendCount = ::SuspendThread(m_hThread);
401 if ( nSuspendCount == (DWORD)-1 )
402 {
403 wxLogSysError(_("Can not suspend thread %x"), m_hThread);
404
405 return FALSE;
406 }
407
408 m_state = STATE_PAUSED;
409 */
410 return FALSE;
411 }
412
413 bool wxThreadInternal::Resume()
414 {
415 // TODO:
416 /*
417 DWORD nSuspendCount = ::ResumeThread(m_hThread);
418 if ( nSuspendCount == (DWORD)-1 )
419 {
420 wxLogSysError(_("Can not resume thread %x"), m_hThread);
421
422 return FALSE;
423 }
424
425 m_state = STATE_RUNNING;
426 */
427 return FALSE;
428 }
429
430 // static functions
431 // ----------------
432
433 wxThread *wxThread::This()
434 {
435 wxThread *thread = NULL; // TODO (wxThread *)::TlsGetValue(s_tlsThisThread);
436
437 // TODO:
438 /*
439 // be careful, 0 may be a valid return value as well
440 if ( !thread && (::GetLastError() != NO_ERROR) )
441 {
442 wxLogSysError(_("Couldn't get the current thread pointer"));
443
444 // return NULL...
445 }
446 */
447 return thread;
448 }
449
450 bool wxThread::IsMain()
451 {
452 // TODO: return ::GetCurrentThreadId() == s_idMainThread;
453 return FALSE;
454 }
455
456 #ifdef Yield
457 #undef Yield
458 #endif
459
460 void wxThread::Yield()
461 {
462 // 0 argument to Sleep() is special
463 ::DosSleep(0);
464 }
465
466 void wxThread::Sleep(unsigned long milliseconds)
467 {
468 ::DosSleep(milliseconds);
469 }
470
471 // create/start thread
472 // -------------------
473
474 wxThreadError wxThread::Create()
475 {
476 if ( !p_internal->Create(this) )
477 return wxTHREAD_NO_RESOURCE;
478
479 return wxTHREAD_NO_ERROR;
480 }
481
482 wxThreadError wxThread::Run()
483 {
484 wxCriticalSectionLocker lock(m_critsect);
485
486 if ( p_internal->GetState() != STATE_NEW )
487 {
488 // actually, it may be almost any state at all, not only STATE_RUNNING
489 return wxTHREAD_RUNNING;
490 }
491
492 return Resume();
493 }
494
495 // suspend/resume thread
496 // ---------------------
497
498 wxThreadError wxThread::Pause()
499 {
500 wxCriticalSectionLocker lock(m_critsect);
501
502 return p_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
503 }
504
505 wxThreadError wxThread::Resume()
506 {
507 wxCriticalSectionLocker lock(m_critsect);
508
509 return p_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
510 }
511
512 // stopping thread
513 // ---------------
514
515 wxThread::ExitCode wxThread::Delete()
516 {
517 ExitCode rc = 0;
518
519 // Delete() is always safe to call, so consider all possible states
520 if ( IsPaused() )
521 Resume();
522
523 if ( IsRunning() )
524 {
525 if ( IsMain() )
526 {
527 // set flag for wxIsWaitingForThread()
528 s_waitingForThread = TRUE;
529
530 wxBeginBusyCursor();
531 }
532
533 HANDLE hThread;
534 {
535 wxCriticalSectionLocker lock(m_critsect);
536
537 p_internal->Cancel();
538 hThread = p_internal->GetHandle();
539 }
540
541 // we can't just wait for the thread to terminate because it might be
542 // calling some GUI functions and so it will never terminate before we
543 // process the Windows messages that result from these functions
544 DWORD result;
545 // TODO:
546 /*
547 do
548 {
549 result = ::MsgWaitForMultipleObjects
550 (
551 1, // number of objects to wait for
552 &hThread, // the objects
553 FALSE, // don't wait for all objects
554 INFINITE, // no timeout
555 QS_ALLEVENTS // return as soon as there are any events
556 );
557
558 switch ( result )
559 {
560 case 0xFFFFFFFF:
561 // error
562 wxLogSysError(_("Can not wait for thread termination"));
563 Kill();
564 return (ExitCode)-1;
565
566 case WAIT_OBJECT_0:
567 // thread we're waiting for terminated
568 break;
569
570 case WAIT_OBJECT_0 + 1:
571 // new message arrived, process it
572 if ( !wxTheApp->DoMessage() )
573 {
574 // WM_QUIT received: kill the thread
575 Kill();
576
577 return (ExitCode)-1;
578 }
579
580 if ( IsMain() )
581 {
582 // give the thread we're waiting for chance to exit
583 // from the GUI call it might have been in
584 if ( (s_nWaitingForGui > 0) && wxGuiOwnedByMainThread() )
585 {
586 wxMutexGuiLeave();
587 }
588 }
589
590 break;
591
592 default:
593 wxFAIL_MSG(wxT("unexpected result of MsgWaitForMultipleObject"));
594 }
595 } while ( result != WAIT_OBJECT_0 );
596 */
597 if ( IsMain() )
598 {
599 s_waitingForThread = FALSE;
600
601 wxEndBusyCursor();
602 }
603
604 // TODO:
605 /*
606 if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) )
607 {
608 wxLogLastError("GetExitCodeThread");
609
610 rc = (ExitCode)-1;
611 }
612
613 wxASSERT_MSG( (LPVOID)rc != (LPVOID)STILL_ACTIVE,
614 wxT("thread must be already terminated.") );
615
616 ::CloseHandle(hThread);
617 */
618 }
619
620 return rc;
621 }
622
623 wxThreadError wxThread::Kill()
624 {
625 if ( !IsRunning() )
626 return wxTHREAD_NOT_RUNNING;
627
628 // TODO:
629 /*
630 if ( !::TerminateThread(p_internal->GetHandle(), (DWORD)-1) )
631 {
632 wxLogSysError(_("Couldn't terminate thread"));
633
634 return wxTHREAD_MISC_ERROR;
635 }
636 */
637 delete this;
638
639 return wxTHREAD_NO_ERROR;
640 }
641
642 void wxThread::Exit(void *status)
643 {
644 delete this;
645
646 // TODO: ::ExitThread((DWORD)status);
647
648 wxFAIL_MSG(wxT("Couldn't return from ExitThread()!"));
649 }
650
651 void wxThread::SetPriority(unsigned int prio)
652 {
653 wxCriticalSectionLocker lock(m_critsect);
654
655 p_internal->SetPriority(prio);
656 }
657
658 unsigned int wxThread::GetPriority() const
659 {
660 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
661
662 return p_internal->GetPriority();
663 }
664
665 unsigned long wxThread::GetID() const
666 {
667 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
668
669 return (unsigned long)p_internal->GetId();
670 }
671
672 bool wxThread::IsRunning() const
673 {
674 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
675
676 return p_internal->GetState() == STATE_RUNNING;
677 }
678
679 bool wxThread::IsAlive() const
680 {
681 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
682
683 return (p_internal->GetState() == STATE_RUNNING) ||
684 (p_internal->GetState() == STATE_PAUSED);
685 }
686
687 bool wxThread::IsPaused() const
688 {
689 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
690
691 return (p_internal->GetState() == STATE_PAUSED);
692 }
693
694 bool wxThread::TestDestroy()
695 {
696 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
697
698 return p_internal->GetState() == STATE_CANCELED;
699 }
700
701 wxThread::wxThread()
702 {
703 p_internal = new wxThreadInternal();
704 }
705
706 wxThread::~wxThread()
707 {
708 delete p_internal;
709 }
710
711 // ----------------------------------------------------------------------------
712 // Automatic initialization for thread module
713 // ----------------------------------------------------------------------------
714
715 class wxThreadModule : public wxModule
716 {
717 public:
718 virtual bool OnInit();
719 virtual void OnExit();
720
721 private:
722 DECLARE_DYNAMIC_CLASS(wxThreadModule)
723 };
724
725 IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
726
727 bool wxThreadModule::OnInit()
728 {
729 // allocate TLS index for storing the pointer to the current thread
730 // TODO:
731 /*
732 s_tlsThisThread = ::TlsAlloc();
733 if ( s_tlsThisThread == 0xFFFFFFFF )
734 {
735 // in normal circumstances it will only happen if all other
736 // TLS_MINIMUM_AVAILABLE (>= 64) indices are already taken - in other
737 // words, this should never happen
738 wxLogSysError(_("Thread module initialization failed: "
739 "impossible to allocate index in thread "
740 "local storage"));
741
742 return FALSE;
743 }
744 */
745 // main thread doesn't have associated wxThread object, so store 0 in the
746 // TLS instead
747
748 // TODO:
749 /*
750 if ( !::TlsSetValue(s_tlsThisThread, (LPVOID)0) )
751 {
752 ::TlsFree(s_tlsThisThread);
753 s_tlsThisThread = 0xFFFFFFFF;
754
755 wxLogSysError(_("Thread module initialization failed: "
756 "can not store value in thread local storage"));
757
758 return FALSE;
759 }
760 */
761 s_critsectWaitingForGui = new wxCriticalSection();
762
763 s_critsectGui = new wxCriticalSection();
764 s_critsectGui->Enter();
765
766 // no error return for GetCurrentThreadId()
767 // s_idMainThread = ::GetCurrentThreadId();
768
769 return TRUE;
770 }
771
772 void wxThreadModule::OnExit()
773 {
774 // TODO:
775 /*
776 if ( !::TlsFree(s_tlsThisThread) )
777 {
778 wxLogLastError("TlsFree failed.");
779 }
780 */
781 if ( s_critsectGui )
782 {
783 s_critsectGui->Leave();
784 delete s_critsectGui;
785 s_critsectGui = NULL;
786 }
787
788 wxDELETE(s_critsectWaitingForGui);
789 }
790
791 // ----------------------------------------------------------------------------
792 // under Windows, these functions are implemented usign a critical section and
793 // not a mutex, so the names are a bit confusing
794 // ----------------------------------------------------------------------------
795
796 void WXDLLEXPORT wxMutexGuiEnter()
797 {
798 // this would dead lock everything...
799 wxASSERT_MSG( !wxThread::IsMain(),
800 wxT("main thread doesn't want to block in wxMutexGuiEnter()!") );
801
802 // the order in which we enter the critical sections here is crucial!!
803
804 // set the flag telling to the main thread that we want to do some GUI
805 {
806 wxCriticalSectionLocker enter(*s_critsectWaitingForGui);
807
808 s_nWaitingForGui++;
809 }
810
811 wxWakeUpMainThread();
812
813 // now we may block here because the main thread will soon let us in
814 // (during the next iteration of OnIdle())
815 s_critsectGui->Enter();
816 }
817
818 void WXDLLEXPORT wxMutexGuiLeave()
819 {
820 wxCriticalSectionLocker enter(*s_critsectWaitingForGui);
821
822 if ( wxThread::IsMain() )
823 {
824 s_bGuiOwnedByMainThread = FALSE;
825 }
826 else
827 {
828 // decrement the number of waiters now
829 wxASSERT_MSG( s_nWaitingForGui > 0,
830 wxT("calling wxMutexGuiLeave() without entering it first?") );
831
832 s_nWaitingForGui--;
833
834 wxWakeUpMainThread();
835 }
836
837 s_critsectGui->Leave();
838 }
839
840 void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
841 {
842 wxASSERT_MSG( wxThread::IsMain(),
843 wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") );
844
845 wxCriticalSectionLocker enter(*s_critsectWaitingForGui);
846
847 if ( s_nWaitingForGui == 0 )
848 {
849 // no threads are waiting for GUI - so we may acquire the lock without
850 // any danger (but only if we don't already have it)
851 if ( !wxGuiOwnedByMainThread() )
852 {
853 s_critsectGui->Enter();
854
855 s_bGuiOwnedByMainThread = TRUE;
856 }
857 //else: already have it, nothing to do
858 }
859 else
860 {
861 // some threads are waiting, release the GUI lock if we have it
862 if ( wxGuiOwnedByMainThread() )
863 {
864 wxMutexGuiLeave();
865 }
866 //else: some other worker thread is doing GUI
867 }
868 }
869
870 bool WXDLLEXPORT wxGuiOwnedByMainThread()
871 {
872 return s_bGuiOwnedByMainThread;
873 }
874
875 // wake up the main thread if it's in ::GetMessage()
876 void WXDLLEXPORT wxWakeUpMainThread()
877 {
878 // sending any message would do - hopefully WM_NULL is harmless enough
879 // TODO:
880 /*
881 if ( !::PostThreadMessage(s_idMainThread, WM_NULL, 0, 0) )
882 {
883 // should never happen
884 wxLogLastError("PostThreadMessage(WM_NULL)");
885 }
886 */
887 }
888
889 bool WXDLLEXPORT wxIsWaitingForThread()
890 {
891 return s_waitingForThread;
892 }
893
894
895 #endif
896 // wxUSE_THREADS