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