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