]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/thread.cpp
no message
[wxWidgets.git] / src / mac / carbon / thread.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: thread.cpp
3 // Purpose: wxThread Implementation
4 // Author: Original from Wolfram Gloger/Guilhem Lavaux/Vadim Zeitlin
5 // Modified by: Stefan Csomor
6 // Created: 04/22/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998),
9 // Vadim Zeitlin (1999) , Stefan Csomor (2000)
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 "wx/module.h"
35 #include "wx/thread.h"
36
37 #ifdef __WXMAC__
38 #include <Threads.h>
39 #include "wx/mac/uma.h"
40 #endif
41
42 #define INFINITE 0xFFFFFFFF
43
44 // ----------------------------------------------------------------------------
45 // constants
46 // ----------------------------------------------------------------------------
47
48 // the possible states of the thread ("=>" shows all possible transitions from
49 // this state)
50 enum wxThreadState
51 {
52 STATE_NEW, // didn't start execution yet (=> RUNNING)
53 STATE_RUNNING, // thread is running (=> PAUSED, CANCELED)
54 STATE_PAUSED, // thread is temporarily suspended (=> RUNNING)
55 STATE_CANCELED, // thread should terminate a.s.a.p. (=> EXITED)
56 STATE_EXITED // thread is terminating
57 };
58
59 // ----------------------------------------------------------------------------
60 // this module globals
61 // ----------------------------------------------------------------------------
62
63 static ThreadID gs_idMainThread = kNoThreadID ;
64 static bool gs_waitingForThread = FALSE ;
65
66 // ============================================================================
67 // MacOS implementation of thread classes
68 // ============================================================================
69
70 class wxMacStCritical
71 {
72 public :
73 wxMacStCritical()
74 {
75 if ( UMASystemIsInitialized() )
76 ThreadBeginCritical() ;
77 }
78 ~wxMacStCritical()
79 {
80 if ( UMASystemIsInitialized() )
81 ThreadEndCritical() ;
82 }
83 };
84
85 // ----------------------------------------------------------------------------
86 // wxMutex implementation
87 // ----------------------------------------------------------------------------
88
89 class wxMutexInternal
90 {
91 public:
92 wxMutexInternal(wxMutexType WXUNUSED(mutexType))
93 {
94 m_owner = kNoThreadID ;
95 m_locked = 0;
96 }
97
98 ~wxMutexInternal()
99 {
100 if ( m_locked > 0 )
101 {
102 wxLogDebug(_T("Warning: freeing a locked mutex (%ld locks)."), m_locked);
103 }
104 }
105
106 bool IsOk() const { return true; }
107
108 wxMutexError Lock() ;
109 wxMutexError TryLock() ;
110 wxMutexError Unlock();
111 public:
112 ThreadID m_owner ;
113 wxArrayLong m_waiters ;
114 long m_locked ;
115 };
116
117 wxMutexError wxMutexInternal::Lock()
118 {
119 wxMacStCritical critical ;
120 if ( UMASystemIsInitialized() )
121 {
122 OSErr err ;
123 ThreadID current = kNoThreadID;
124 err = ::MacGetCurrentThread(&current);
125 // if we are not the owner, add this thread to the list of waiting threads, stop this thread
126 // and invoke the scheduler to continue executing the owner's thread
127 while ( m_owner != kNoThreadID && m_owner != current)
128 {
129 m_waiters.Add(current);
130 err = ::SetThreadStateEndCritical(kCurrentThreadID, kStoppedThreadState, m_owner);
131 err = ::ThreadBeginCritical();
132 }
133 m_owner = current;
134 }
135 m_locked++;
136
137 return wxMUTEX_NO_ERROR;
138 }
139
140 wxMutexError wxMutexInternal::TryLock()
141 {
142 wxMacStCritical critical ;
143 if ( UMASystemIsInitialized() )
144 {
145 ThreadID current = kNoThreadID;
146 ::MacGetCurrentThread(&current);
147 // if we are not the owner, give an error back
148 if ( m_owner != kNoThreadID && m_owner != current )
149 return wxMUTEX_BUSY;
150
151 m_owner = current;
152 }
153 m_locked++;
154
155 return wxMUTEX_NO_ERROR;
156 }
157
158 wxMutexError wxMutexInternal::Unlock()
159 {
160 if ( UMASystemIsInitialized() )
161 {
162 OSErr err;
163 err = ::ThreadBeginCritical();
164
165 if (m_locked > 0)
166 m_locked--;
167
168 // this mutex is not owned by anybody anmore
169 m_owner = kNoThreadID;
170
171 // now pass on to the first waiting thread
172 ThreadID firstWaiting = kNoThreadID;
173 bool found = false;
174 while (!m_waiters.IsEmpty() && !found)
175 {
176 firstWaiting = m_waiters[0];
177 err = ::SetThreadState(firstWaiting, kReadyThreadState, kNoThreadID);
178 // in case this was not successful (dead thread), we just loop on and reset the id
179 found = (err != threadNotFoundErr);
180 if ( !found )
181 firstWaiting = kNoThreadID ;
182 m_waiters.RemoveAt(0) ;
183 }
184 // now we have a valid firstWaiting thread, which has been scheduled to run next, just end the
185 // critical section and invoke the scheduler
186 err = ::SetThreadStateEndCritical(kCurrentThreadID, kReadyThreadState, firstWaiting);
187 }
188 else
189 {
190 if (m_locked > 0)
191 m_locked--;
192 }
193 return wxMUTEX_NO_ERROR;
194 }
195
196 // --------------------------------------------------------------------------
197 // wxSemaphore
198 // --------------------------------------------------------------------------
199
200 // TODO not yet implemented
201
202 class wxSemaphoreInternal
203 {
204 public:
205 wxSemaphoreInternal(int initialcount, int maxcount);
206 ~wxSemaphoreInternal();
207
208 bool IsOk() const { return true ; }
209
210 wxSemaError Wait() { return WaitTimeout(INFINITE); }
211 wxSemaError TryWait() { return WaitTimeout(0); }
212 wxSemaError WaitTimeout(unsigned long milliseconds);
213
214 wxSemaError Post();
215
216 private:
217 };
218
219 wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount)
220 {
221 if ( maxcount == 0 )
222 {
223 // make it practically infinite
224 maxcount = INT_MAX;
225 }
226 }
227
228 wxSemaphoreInternal::~wxSemaphoreInternal()
229 {
230 }
231
232 wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds)
233 {
234 return wxSEMA_MISC_ERROR;
235 }
236
237 wxSemaError wxSemaphoreInternal::Post()
238 {
239 return wxSEMA_MISC_ERROR;
240 }
241
242 // ----------------------------------------------------------------------------
243 // wxCondition implementation
244 // ----------------------------------------------------------------------------
245
246 // TODO this is not yet completed
247
248 class wxConditionInternal
249 {
250 public:
251 wxConditionInternal(wxMutex& mutex) : m_mutex(mutex)
252 {
253 m_excessSignals = 0 ;
254 }
255 ~wxConditionInternal()
256 {
257 }
258
259 bool IsOk() const { return m_mutex.IsOk() ; }
260
261 wxCondError Wait()
262 {
263 return WaitTimeout(0xFFFFFFFF );
264 }
265
266 wxCondError WaitTimeout(unsigned long msectimeout)
267 {
268 wxMacStCritical critical ;
269 if ( m_excessSignals > 0 )
270 {
271 --m_excessSignals ;
272 return wxCOND_NO_ERROR ;
273 }
274 else if ( msectimeout == 0 )
275 {
276 return wxCOND_MISC_ERROR ;
277 }
278 else
279 {
280 }
281 /*
282 waiters++;
283
284 // FIXME this should be MsgWaitForMultipleObjects() as well probably
285 DWORD rc = ::WaitForSingleObject(event, timeout);
286
287 waiters--;
288
289 return rc != WAIT_TIMEOUT;
290 */
291 return wxCOND_NO_ERROR ;
292 }
293 wxCondError Signal()
294 {
295 wxMacStCritical critical ;
296 return wxCOND_NO_ERROR;
297 }
298
299 wxCondError Broadcast()
300 {
301 wxMacStCritical critical ;
302 return wxCOND_NO_ERROR;
303 }
304
305 wxArrayLong m_waiters ;
306 wxInt32 m_excessSignals ;
307 wxMutex& m_mutex;
308 };
309
310 // ----------------------------------------------------------------------------
311 // wxCriticalSection implementation
312 // ----------------------------------------------------------------------------
313
314 // it's implemented as a mutex on mac os, so it is defined in the headers
315
316 // ----------------------------------------------------------------------------
317 // wxThread implementation
318 // ----------------------------------------------------------------------------
319
320 // wxThreadInternal class
321 // ----------------------
322
323 class wxThreadInternal
324 {
325 public:
326 wxThreadInternal()
327 {
328 m_tid = kNoThreadID ;
329 m_state = STATE_NEW;
330 m_priority = WXTHREAD_DEFAULT_PRIORITY;
331 }
332
333 ~wxThreadInternal()
334 {
335 }
336
337 void Free()
338 {
339 }
340
341 // create a new (suspended) thread (for the given thread object)
342 bool Create(wxThread *thread, unsigned int stackSize);
343
344 // suspend/resume/terminate
345 bool Suspend();
346 bool Resume();
347 void Cancel() { m_state = STATE_CANCELED; }
348
349 // thread state
350 void SetState(wxThreadState state) { m_state = state; }
351 wxThreadState GetState() const { return m_state; }
352
353 // thread priority
354 void SetPriority(unsigned int priority);
355 unsigned int GetPriority() const { return m_priority; }
356
357 void SetResult( void *res ) { m_result = res ; }
358 void *GetResult() { return m_result ; }
359
360 // thread handle and id
361 ThreadID GetId() const { return m_tid; }
362
363 // thread function
364 static pascal void* MacThreadStart(wxThread* arg);
365
366 private:
367 wxThreadState m_state; // state, see wxThreadState enum
368 unsigned int m_priority; // thread priority in "wx" units
369 ThreadID m_tid; // thread id
370 void* m_result;
371 static ThreadEntryUPP s_threadEntry ;
372 };
373
374 static wxArrayPtrVoid s_threads ;
375
376 ThreadEntryUPP wxThreadInternal::s_threadEntry = NULL ;
377 pascal void* wxThreadInternal::MacThreadStart(wxThread *thread)
378 {
379 // first of all, check whether we hadn't been cancelled already
380 if ( thread->m_internal->GetState() == STATE_EXITED )
381 {
382 return (void*)-1;
383 }
384
385 void* rc = thread->Entry();
386
387 // enter m_critsect before changing the thread state
388 thread->m_critsect.Enter();
389 bool wasCancelled = thread->m_internal->GetState() == STATE_CANCELED;
390 thread->m_internal->SetState(STATE_EXITED);
391 thread->m_critsect.Leave();
392
393 thread->OnExit();
394
395 // if the thread was cancelled (from Delete()), then it the handle is still
396 // needed there
397 if ( thread->IsDetached() && !wasCancelled )
398 {
399 // auto delete
400 delete thread;
401 }
402 //else: the joinable threads handle will be closed when Wait() is done
403
404 return rc;
405 }
406 void wxThreadInternal::SetPriority(unsigned int priority)
407 {
408 // Priorities don't exist on Mac
409 }
410
411 bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize)
412 {
413 if ( s_threadEntry == NULL )
414 {
415 s_threadEntry = NewThreadEntryUPP( (ThreadEntryProcPtr) MacThreadStart ) ;
416 }
417 OSErr err = NewThread( kCooperativeThread,
418 s_threadEntry,
419 (void*) thread,
420 stackSize,
421 kNewSuspend,
422 &m_result,
423 &m_tid );
424
425 if ( err != noErr )
426 {
427 wxLogSysError(_("Can't create thread"));
428 return FALSE;
429 }
430
431 if ( m_priority != WXTHREAD_DEFAULT_PRIORITY )
432 {
433 SetPriority(m_priority);
434 }
435
436 return TRUE;
437 }
438
439 bool wxThreadInternal::Suspend()
440 {
441 OSErr err ;
442
443 ::ThreadBeginCritical();
444
445 if ( m_state != STATE_RUNNING )
446 {
447 ::ThreadEndCritical() ;
448 wxLogSysError(_("Can not suspend thread %x"), m_tid);
449 return FALSE;
450 }
451
452 m_state = STATE_PAUSED;
453
454 err = ::SetThreadStateEndCritical(m_tid, kStoppedThreadState, kNoThreadID);
455
456 return TRUE;
457 }
458
459 bool wxThreadInternal::Resume()
460 {
461 ThreadID current ;
462 OSErr err ;
463 err = MacGetCurrentThread( &current ) ;
464
465 wxASSERT( err == noErr ) ;
466 wxASSERT( current != m_tid ) ;
467
468 ::ThreadBeginCritical();
469 if ( m_state != STATE_PAUSED && m_state != STATE_NEW )
470 {
471 ::ThreadEndCritical() ;
472 wxLogSysError(_("Can not resume thread %x"), m_tid);
473 return FALSE;
474
475 }
476 err = ::SetThreadStateEndCritical(m_tid, kReadyThreadState, kNoThreadID);
477 wxASSERT( err == noErr ) ;
478
479 m_state = STATE_RUNNING;
480 ::ThreadEndCritical() ;
481 ::YieldToAnyThread() ;
482 return TRUE;
483 }
484
485 // static functions
486 // ----------------
487 wxThread *wxThread::This()
488 {
489 wxMacStCritical critical ;
490
491 ThreadID current ;
492 OSErr err ;
493
494 err = MacGetCurrentThread( &current ) ;
495
496 for ( size_t i = 0 ; i < s_threads.Count() ; ++i )
497 {
498 if ( ( (wxThread*) s_threads[i] )->GetId() == current )
499 return (wxThread*) s_threads[i] ;
500 }
501
502 wxLogSysError(_("Couldn't get the current thread pointer"));
503 return NULL;
504 }
505
506 bool wxThread::IsMain()
507 {
508 ThreadID current ;
509 OSErr err ;
510
511 err = MacGetCurrentThread( &current ) ;
512 return current == gs_idMainThread;
513 }
514
515 #ifdef Yield
516 #undef Yield
517 #endif
518
519 void wxThread::Yield()
520 {
521 ::YieldToAnyThread() ;
522 }
523
524 void wxThread::Sleep(unsigned long milliseconds)
525 {
526 clock_t start = clock();
527 do
528 {
529 YieldToAnyThread();
530 } while( clock() - start < milliseconds / 1000.0 * CLOCKS_PER_SEC ) ;
531 }
532
533 int wxThread::GetCPUCount()
534 {
535 // we will use whatever MP API will be used for the new MP Macs
536 return 1;
537 }
538
539 unsigned long wxThread::GetCurrentId()
540 {
541 ThreadID current ;
542 MacGetCurrentThread( &current ) ;
543 return (unsigned long)current;
544 }
545
546 bool wxThread::SetConcurrency(size_t level)
547 {
548 wxASSERT_MSG( IsMain(), _T("should only be called from the main thread") );
549
550 // ok only for the default one
551 if ( level == 0 )
552 return 0;
553
554 // how many CPUs have we got?
555 if ( GetCPUCount() == 1 )
556 {
557 // don't bother with all this complicated stuff - on a single
558 // processor system it doesn't make much sense anyhow
559 return level == 1;
560 }
561
562 return TRUE ;
563 }
564
565 // ctor and dtor
566 // -------------
567
568 wxThread::wxThread(wxThreadKind kind)
569 {
570 m_internal = new wxThreadInternal();
571
572 m_isDetached = kind == wxTHREAD_DETACHED;
573 s_threads.Add( (void*) this ) ;
574 }
575
576 wxThread::~wxThread()
577 {
578 s_threads.Remove( (void*) this ) ;
579 if (m_internal != NULL) {
580 delete m_internal;
581 m_internal = NULL;
582 }
583 }
584
585 // create/start thread
586 // -------------------
587
588 wxThreadError wxThread::Create(unsigned int stackSize)
589 {
590 wxCriticalSectionLocker lock(m_critsect);
591
592 if ( !m_internal->Create(this, stackSize) )
593 return wxTHREAD_NO_RESOURCE;
594
595 return wxTHREAD_NO_ERROR;
596 }
597
598 wxThreadError wxThread::Run()
599 {
600 wxCriticalSectionLocker lock(m_critsect);
601
602 if ( m_internal->GetState() != STATE_NEW )
603 {
604 // actually, it may be almost any state at all, not only STATE_RUNNING
605 return wxTHREAD_RUNNING;
606 }
607
608 // the thread has just been created and is still suspended - let it run
609 return Resume();
610 }
611
612 // suspend/resume thread
613 // ---------------------
614
615 wxThreadError wxThread::Pause()
616 {
617 wxCriticalSectionLocker lock(m_critsect);
618
619 return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
620 }
621
622 wxThreadError wxThread::Resume()
623 {
624 wxCriticalSectionLocker lock(m_critsect);
625
626 return m_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
627 }
628
629 // stopping thread
630 // ---------------
631
632 wxThread::ExitCode wxThread::Wait()
633 {
634 // although under MacOS we can wait for any thread, it's an error to
635 // wait for a detached one in wxWin API
636 wxCHECK_MSG( !IsDetached(), (ExitCode)-1,
637 _T("can't wait for detached thread") );
638
639 ExitCode rc = (ExitCode)-1;
640
641 (void)Delete(&rc);
642
643 m_internal->Free();
644
645 return rc;
646 }
647
648 wxThreadError wxThread::Delete(ExitCode *pRc)
649 {
650 ExitCode rc = 0;
651
652 // Delete() is always safe to call, so consider all possible states
653
654 // has the thread started to run?
655 bool shouldResume = FALSE;
656
657 {
658 wxCriticalSectionLocker lock(m_critsect);
659
660 if ( m_internal->GetState() == STATE_NEW )
661 {
662 // WinThreadStart() will see it and terminate immediately
663 m_internal->SetState(STATE_EXITED);
664
665 shouldResume = TRUE;
666 }
667 }
668
669 // is the thread paused?
670 if ( shouldResume || IsPaused() )
671 Resume();
672
673 // does is still run?
674 if ( IsRunning() )
675 {
676 if ( IsMain() )
677 {
678 // set flag for wxIsWaitingForThread()
679 gs_waitingForThread = TRUE;
680
681 #if wxUSE_GUI
682 wxBeginBusyCursor();
683 #endif // wxUSE_GUI
684 }
685
686 // ask the thread to terminate
687 {
688 wxCriticalSectionLocker lock(m_critsect);
689
690 m_internal->Cancel();
691 }
692
693 #if wxUSE_GUI
694 // simply wait for the thread to terminate
695 while( TestDestroy() )
696 {
697 ::YieldToAnyThread() ;
698 }
699 #else // !wxUSE_GUI
700 // simply wait for the thread to terminate
701 while( TestDestroy() )
702 {
703 ::YieldToAnyThread() ;
704 }
705 #endif // wxUSE_GUI/!wxUSE_GUI
706
707 if ( IsMain() )
708 {
709 gs_waitingForThread = FALSE;
710
711 #if wxUSE_GUI
712 wxEndBusyCursor();
713 #endif // wxUSE_GUI
714 }
715 }
716
717 // if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) )
718 {
719 wxLogLastError("GetExitCodeThread");
720
721 rc = (ExitCode)-1;
722 }
723
724 if ( IsDetached() )
725 {
726 // if the thread exits normally, this is done in WinThreadStart, but in
727 // this case it would have been too early because
728 // MsgWaitForMultipleObject() would fail if the therad handle was
729 // closed while we were waiting on it, so we must do it here
730 delete this;
731 }
732
733 // wxASSERT_MSG( (DWORD)rc != STILL_ACTIVE,
734 // wxT("thread must be already terminated.") );
735
736 if ( pRc )
737 *pRc = rc;
738
739 return rc == (ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR;
740 }
741
742 wxThreadError wxThread::Kill()
743 {
744 if ( !IsRunning() )
745 return wxTHREAD_NOT_RUNNING;
746
747 // if ( !::TerminateThread(m_internal->GetHandle(), (DWORD)-1) )
748 {
749 wxLogSysError(_("Couldn't terminate thread"));
750
751 return wxTHREAD_MISC_ERROR;
752 }
753
754 m_internal->Free();
755
756 if ( IsDetached() )
757 {
758 delete this;
759 }
760
761 return wxTHREAD_NO_ERROR;
762 }
763
764 void wxThread::Exit(ExitCode status)
765 {
766 m_internal->Free();
767
768 if ( IsDetached() )
769 {
770 delete this;
771 }
772
773 m_internal->SetResult( status ) ;
774
775 /*
776 #if defined(__VISUALC__) || (defined(__BORLANDC__) && (__BORLANDC__ >= 0x500))
777 _endthreadex((unsigned)status);
778 #else // !VC++
779 ::ExitThread((DWORD)status);
780 #endif // VC++/!VC++
781 */
782 wxFAIL_MSG(wxT("Couldn't return from ExitThread()!"));
783 }
784
785 // priority setting
786 // ----------------
787
788 // since all these calls are execute cooperatively we don't have to use the critical section
789
790 void wxThread::SetPriority(unsigned int prio)
791 {
792 m_internal->SetPriority(prio);
793 }
794
795 unsigned int wxThread::GetPriority() const
796 {
797 return m_internal->GetPriority();
798 }
799
800 unsigned long wxThread::GetId() const
801 {
802 return (unsigned long)m_internal->GetId();
803 }
804
805 bool wxThread::IsRunning() const
806 {
807 return m_internal->GetState() == STATE_RUNNING;
808 }
809
810 bool wxThread::IsAlive() const
811 {
812 return (m_internal->GetState() == STATE_RUNNING) ||
813 (m_internal->GetState() == STATE_PAUSED);
814 }
815
816 bool wxThread::IsPaused() const
817 {
818 return m_internal->GetState() == STATE_PAUSED;
819 }
820
821 bool wxThread::TestDestroy()
822 {
823 return m_internal->GetState() == STATE_CANCELED;
824 }
825
826 // ----------------------------------------------------------------------------
827 // Automatic initialization for thread module
828 // ----------------------------------------------------------------------------
829
830 class wxThreadModule : public wxModule
831 {
832 public:
833 virtual bool OnInit();
834 virtual void OnExit();
835
836 private:
837 DECLARE_DYNAMIC_CLASS(wxThreadModule)
838 };
839
840 IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
841
842 bool wxThreadModule::OnInit()
843 {
844 long response;
845 bool hasThreadManager ;
846 hasThreadManager = Gestalt( gestaltThreadMgrAttr, &response) == noErr && response & 1;
847 #if !TARGET_CARBON
848 #if GENERATINGCFM
849 // verify presence of shared library
850 hasThreadManager = hasThreadManager && ((Ptr)NewThread != (Ptr)kUnresolvedCFragSymbolAddress);
851 #endif
852 #endif
853 if ( !hasThreadManager )
854 {
855 wxMessageBox( wxT("Error") , wxT("Thread Support is not available on this System") , wxOK ) ;
856 return FALSE ;
857 }
858
859 // no error return for GetCurrentThreadId()
860 MacGetCurrentThread( &gs_idMainThread ) ;
861
862 return TRUE;
863 }
864
865 void wxThreadModule::OnExit()
866 {
867 }
868
869 // ----------------------------------------------------------------------------
870 // under MacOS we don't have currently preemptive threads, so any thread may access
871 // the GUI at any time
872 // ----------------------------------------------------------------------------
873
874 void WXDLLEXPORT wxMutexGuiEnter()
875 {
876 }
877
878 void WXDLLEXPORT wxMutexGuiLeave()
879 {
880 }
881
882 void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
883 {
884 }
885
886 bool WXDLLEXPORT wxGuiOwnedByMainThread()
887 {
888 return false ;
889 }
890
891 // wake up the main thread
892 void WXDLLEXPORT wxWakeUpMainThread()
893 {
894 wxMacWakeUp() ;
895 }
896
897 bool WXDLLEXPORT wxIsWaitingForThread()
898 {
899 return false ;
900 }
901
902 #include "wx/thrimpl.cpp"
903
904 #endif // wxUSE_THREADS