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