X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/169935ad4ed842421ef24470a06d1aa298f90fbe..919312f76764a6fee2422664d2251c4e6b6be993:/src/mac/thread.cpp diff --git a/src/mac/thread.cpp b/src/mac/thread.cpp index 8dd23bdce9..3634a69dde 100644 --- a/src/mac/thread.cpp +++ b/src/mac/thread.cpp @@ -1,264 +1,911 @@ ///////////////////////////////////////////////////////////////////////////// // Name: thread.cpp -// Purpose: wxThread Implementation. For Unix ports, see e.g. src/gtk -// Author: Original from Wolfram Gloger/Guilhem Lavaux -// Modified by: +// Purpose: wxThread Implementation +// Author: Original from Wolfram Gloger/Guilhem Lavaux/Vadim Zeitlin +// Modified by: Stefan Csomor // Created: 04/22/98 // RCS-ID: $Id$ -// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998) +// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998), +// Vadim Zeitlin (1999) , Stefan Csomor (2000) // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// #ifdef __GNUG__ -#pragma implementation "thread.h" + #pragma implementation "thread.h" #endif +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if defined(__BORLANDC__) + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#if wxUSE_THREADS + #include "wx/module.h" #include "wx/thread.h" -#include "wx/utils.h" -enum thread_state { - STATE_IDLE = 0, - STATE_RUNNING, - STATE_CANCELED, - STATE_EXITED +#ifdef __WXMAC__ +#include +#include "wx/mac/uma.h" +#include "wx/mac/macnotfy.h" +#endif + +#define INFINITE 0xFFFFFFFF + + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +// the possible states of the thread ("=>" shows all possible transitions from +// this state) +enum wxThreadState +{ + STATE_NEW, // didn't start execution yet (=> RUNNING) + STATE_RUNNING, // thread is running (=> PAUSED, CANCELED) + STATE_PAUSED, // thread is temporarily suspended (=> RUNNING) + STATE_CANCELED, // thread should terminate a.s.a.p. (=> EXITED) + STATE_EXITED // thread is terminating }; -#if wxUSE_THREADS +// ---------------------------------------------------------------------------- +// this module globals +// ---------------------------------------------------------------------------- -///////////////////////////////////////////////////////////////////////////// -// Static variables -///////////////////////////////////////////////////////////////////////////// +static ThreadID gs_idMainThread = kNoThreadID ; +static bool gs_waitingForThread = FALSE ; +size_t g_numberOfThreads = 0; -wxMutex *wxMainMutex; // controls access to all GUI functions +// ============================================================================ +// MacOS implementation of thread classes +// ============================================================================ -///////////////////////////////////////////////////////////////////////////// -// Windows implementation -///////////////////////////////////////////////////////////////////////////// +class wxMacStCritical +{ +public : + wxMacStCritical() + { + if ( UMASystemIsInitialized() ) + ThreadBeginCritical() ; + } + ~wxMacStCritical() + { + if ( UMASystemIsInitialized() ) + ThreadEndCritical() ; + } +}; + +// ---------------------------------------------------------------------------- +// wxMutex implementation +// ---------------------------------------------------------------------------- -class wxMutexInternal { +class wxMutexInternal +{ +public: + wxMutexInternal(wxMutexType WXUNUSED(mutexType)) + { + m_owner = kNoThreadID ; + m_locked = 0; + } + + ~wxMutexInternal() + { + if ( m_locked > 0 ) + { + wxLogDebug(_T("Warning: freeing a locked mutex (%ld locks)."), m_locked); + } + } + + bool IsOk() const { return true; } + + wxMutexError Lock() ; + wxMutexError TryLock() ; + wxMutexError Unlock(); public: - // TODO: internal mutex handle + ThreadID m_owner ; + wxArrayLong m_waiters ; + long m_locked ; }; -wxMutex::wxMutex() +wxMutexError wxMutexInternal::Lock() { - p_internal = new wxMutexInternal; - // TODO: create internal mutext handle - m_locked = 0; + wxMacStCritical critical ; + if ( UMASystemIsInitialized() ) + { + OSErr err ; + ThreadID current = kNoThreadID; + err = ::MacGetCurrentThread(¤t); + // if we are not the owner, add this thread to the list of waiting threads, stop this thread + // and invoke the scheduler to continue executing the owner's thread + while ( m_owner != kNoThreadID && m_owner != current) + { + m_waiters.Add(current); + err = ::SetThreadStateEndCritical(kCurrentThreadID, kStoppedThreadState, m_owner); + err = ::ThreadBeginCritical(); + } + m_owner = current; + } + m_locked++; + + return wxMUTEX_NO_ERROR; } -wxMutex::~wxMutex() +wxMutexError wxMutexInternal::TryLock() { - if (m_locked > 0) - wxDebugMsg("wxMutex warning: freeing a locked mutex (%d locks)\n", m_locked); - // TODO: free internal mutext handle + wxMacStCritical critical ; + if ( UMASystemIsInitialized() ) + { + ThreadID current = kNoThreadID; + ::MacGetCurrentThread(¤t); + // if we are not the owner, give an error back + if ( m_owner != kNoThreadID && m_owner != current ) + return wxMUTEX_BUSY; + + m_owner = current; + } + m_locked++; + + return wxMUTEX_NO_ERROR; } -wxMutexError wxMutex::Lock() +wxMutexError wxMutexInternal::Unlock() { - // TODO - m_locked++; + if ( UMASystemIsInitialized() ) + { + OSErr err; + err = ::ThreadBeginCritical(); + + if (m_locked > 0) + m_locked--; + + // this mutex is not owned by anybody anmore + m_owner = kNoThreadID; + + // now pass on to the first waiting thread + ThreadID firstWaiting = kNoThreadID; + bool found = false; + while (!m_waiters.IsEmpty() && !found) + { + firstWaiting = m_waiters[0]; + err = ::SetThreadState(firstWaiting, kReadyThreadState, kNoThreadID); + // in case this was not successful (dead thread), we just loop on and reset the id + found = (err != threadNotFoundErr); + if ( !found ) + firstWaiting = kNoThreadID ; + m_waiters.RemoveAt(0) ; + } + // now we have a valid firstWaiting thread, which has been scheduled to run next, just end the + // critical section and invoke the scheduler + err = ::SetThreadStateEndCritical(kCurrentThreadID, kReadyThreadState, firstWaiting); + } + else + { + if (m_locked > 0) + m_locked--; + } return wxMUTEX_NO_ERROR; } -wxMutexError wxMutex::TryLock() +// -------------------------------------------------------------------------- +// wxSemaphore +// -------------------------------------------------------------------------- + +// TODO not yet implemented + +class wxSemaphoreInternal +{ +public: + wxSemaphoreInternal(int initialcount, int maxcount); + ~wxSemaphoreInternal(); + + bool IsOk() const { return true ; } + + wxSemaError Wait() { return WaitTimeout(INFINITE); } + wxSemaError TryWait() { return WaitTimeout(0); } + wxSemaError WaitTimeout(unsigned long milliseconds); + + wxSemaError Post(); + +private: +}; + +wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount) +{ + if ( maxcount == 0 ) + { + // make it practically infinite + maxcount = INT_MAX; + } +} + +wxSemaphoreInternal::~wxSemaphoreInternal() { - // TODO - m_locked++; - return wxMUTEX_NO_ERROR; } -wxMutexError wxMutex::Unlock() +wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds) { - if (m_locked > 0) - m_locked--; + return wxSEMA_MISC_ERROR; +} - // TODO - return wxMUTEX_NO_ERROR; +wxSemaError wxSemaphoreInternal::Post() +{ + return wxSEMA_MISC_ERROR; } -class wxConditionInternal { +// ---------------------------------------------------------------------------- +// wxCondition implementation +// ---------------------------------------------------------------------------- + +// TODO this is not yet completed + +class wxConditionInternal +{ +public: + wxConditionInternal(wxMutex& mutex) : m_mutex(mutex) + { + m_excessSignals = 0 ; + } + ~wxConditionInternal() + { + } + + bool IsOk() const { return m_mutex.IsOk() ; } + + wxCondError Wait() + { + return WaitTimeout(0xFFFFFFFF ); + } + + wxCondError WaitTimeout(unsigned long msectimeout) + { + wxMacStCritical critical ; + if ( m_excessSignals > 0 ) + { + --m_excessSignals ; + return wxCOND_NO_ERROR ; + } + else if ( msectimeout == 0 ) + { + return wxCOND_MISC_ERROR ; + } + else + { + } + /* + waiters++; + + // FIXME this should be MsgWaitForMultipleObjects() as well probably + DWORD rc = ::WaitForSingleObject(event, timeout); + + waiters--; + + return rc != WAIT_TIMEOUT; + */ + return wxCOND_NO_ERROR ; + } + wxCondError Signal() + { + wxMacStCritical critical ; + return wxCOND_NO_ERROR; + } + + wxCondError Broadcast() + { + wxMacStCritical critical ; + return wxCOND_NO_ERROR; + } + + wxArrayLong m_waiters ; + wxInt32 m_excessSignals ; + wxMutex& m_mutex; +}; + +// ---------------------------------------------------------------------------- +// wxCriticalSection implementation +// ---------------------------------------------------------------------------- + +// it's implemented as a mutex on mac os, so it is defined in the headers + +// ---------------------------------------------------------------------------- +// wxThread implementation +// ---------------------------------------------------------------------------- + +// wxThreadInternal class +// ---------------------- + +class wxThreadInternal +{ public: - // TODO: internal handle - int waiters; + wxThreadInternal() + { + m_tid = kNoThreadID ; + m_state = STATE_NEW; + m_priority = WXTHREAD_DEFAULT_PRIORITY; + } + + ~wxThreadInternal() + { + } + + void Free() + { + } + + // create a new (suspended) thread (for the given thread object) + bool Create(wxThread *thread, unsigned int stackSize); + + // suspend/resume/terminate + bool Suspend(); + bool Resume(); + void Cancel() { m_state = STATE_CANCELED; } + + // thread state + void SetState(wxThreadState state) { m_state = state; } + wxThreadState GetState() const { return m_state; } + + // thread priority + void SetPriority(unsigned int priority); + unsigned int GetPriority() const { return m_priority; } + + void SetResult( void *res ) { m_result = res ; } + void *GetResult() { return m_result ; } + + // thread handle and id + ThreadID GetId() const { return m_tid; } + + // thread function + static pascal void* MacThreadStart(wxThread* arg); + +private: + wxThreadState m_state; // state, see wxThreadState enum + unsigned int m_priority; // thread priority in "wx" units + ThreadID m_tid; // thread id + void* m_result; + static ThreadEntryUPP s_threadEntry ; }; -wxCondition::wxCondition() +static wxArrayPtrVoid s_threads ; + +ThreadEntryUPP wxThreadInternal::s_threadEntry = NULL ; +pascal void* wxThreadInternal::MacThreadStart(wxThread *thread) { - p_internal = new wxConditionInternal; - // TODO: create internal handle - p_internal->waiters = 0; + // first of all, check whether we hadn't been cancelled already + if ( thread->m_internal->GetState() == STATE_EXITED ) + { + return (void*)-1; + } + + void* rc = thread->Entry(); + + // enter m_critsect before changing the thread state + thread->m_critsect.Enter(); + bool wasCancelled = thread->m_internal->GetState() == STATE_CANCELED; + thread->m_internal->SetState(STATE_EXITED); + thread->m_critsect.Leave(); + + thread->OnExit(); + + // if the thread was cancelled (from Delete()), then it the handle is still + // needed there + if ( thread->IsDetached() && !wasCancelled ) + { + // auto delete + delete thread; + } + //else: the joinable threads handle will be closed when Wait() is done + + return rc; +} +void wxThreadInternal::SetPriority(unsigned int priority) +{ + // Priorities don't exist on Mac } -wxCondition::~wxCondition() +bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize) { - // TODO: destroy internal handle + if ( s_threadEntry == NULL ) + { + s_threadEntry = NewThreadEntryUPP( (ThreadEntryProcPtr) MacThreadStart ) ; + } + OSErr err = NewThread( kCooperativeThread, + s_threadEntry, + (void*) thread, + stackSize, + kNewSuspend, + &m_result, + &m_tid ); + + if ( err != noErr ) + { + wxLogSysError(_("Can't create thread")); + return FALSE; + } + + if ( m_priority != WXTHREAD_DEFAULT_PRIORITY ) + { + SetPriority(m_priority); + } + + m_state = STATE_NEW; + + return TRUE; } -void wxCondition::Wait(wxMutex& mutex) +bool wxThreadInternal::Suspend() { - mutex.Unlock(); - p_internal->waiters++; - // TODO wait here - p_internal->waiters--; - mutex.Lock(); + OSErr err ; + + ::ThreadBeginCritical(); + + if ( m_state != STATE_RUNNING ) + { + ::ThreadEndCritical() ; + wxLogSysError(_("Can not suspend thread %x"), m_tid); + return FALSE; + } + + m_state = STATE_PAUSED; + + err = ::SetThreadStateEndCritical(m_tid, kStoppedThreadState, kNoThreadID); + + return TRUE; } -bool wxCondition::Wait(wxMutex& mutex, unsigned long sec, - unsigned long nsec) +bool wxThreadInternal::Resume() { - mutex.Unlock(); - p_internal->waiters++; + ThreadID current ; + OSErr err ; + err = MacGetCurrentThread( ¤t ) ; + + wxASSERT( err == noErr ) ; + wxASSERT( current != m_tid ) ; + + ::ThreadBeginCritical(); + if ( m_state != STATE_PAUSED && m_state != STATE_NEW ) + { + ::ThreadEndCritical() ; + wxLogSysError(_("Can not resume thread %x"), m_tid); + return FALSE; + + } + err = ::SetThreadStateEndCritical(m_tid, kReadyThreadState, kNoThreadID); + wxASSERT( err == noErr ) ; + + m_state = STATE_RUNNING; + ::ThreadEndCritical() ; + ::YieldToAnyThread() ; + return TRUE; +} + +// static functions +// ---------------- +wxThread *wxThread::This() +{ + wxMacStCritical critical ; + + ThreadID current ; + OSErr err ; + + err = MacGetCurrentThread( ¤t ) ; - // TODO wait here - p_internal->waiters--; - mutex.Lock(); + for ( size_t i = 0 ; i < s_threads.Count() ; ++i ) + { + if ( ( (wxThread*) s_threads[i] )->GetId() == current ) + return (wxThread*) s_threads[i] ; + } - return FALSE; + wxLogSysError(_("Couldn't get the current thread pointer")); + return NULL; } -void wxCondition::Signal() +bool wxThread::IsMain() { - // TODO + ThreadID current ; + OSErr err ; + + err = MacGetCurrentThread( ¤t ) ; + return current == gs_idMainThread; } -void wxCondition::Broadcast() +#ifdef Yield +#undef Yield +#endif + +void wxThread::Yield() { - // TODO + ::YieldToAnyThread() ; } -class wxThreadInternal { -public: - // TODO -}; +void wxThread::Sleep(unsigned long milliseconds) +{ + clock_t start = clock(); + do + { + YieldToAnyThread(); + } while( clock() - start < milliseconds * CLOCKS_PER_SEC / 1000.0 ) ; +} -wxThreadError wxThread::Create() +int wxThread::GetCPUCount() { - // TODO - return wxTHREAD_NO_ERROR; + // we will use whatever MP API will be used for the new MP Macs + return 1; } -wxThreadError wxThread::Destroy() +unsigned long wxThread::GetCurrentId() { - // TODO - return wxTHREAD_NO_ERROR; + ThreadID current ; + MacGetCurrentThread( ¤t ) ; + return (unsigned long)current; } -wxThreadError wxThread::Pause() +bool wxThread::SetConcurrency(size_t level) { - // TODO - return wxTHREAD_NO_ERROR; + wxASSERT_MSG( IsMain(), _T("should only be called from the main thread") ); + + // ok only for the default one + if ( level == 0 ) + return 0; + + // how many CPUs have we got? + if ( GetCPUCount() == 1 ) + { + // don't bother with all this complicated stuff - on a single + // processor system it doesn't make much sense anyhow + return level == 1; + } + + return TRUE ; } -wxThreadError wxThread::Resume() +// ctor and dtor +// ------------- + +wxThread::wxThread(wxThreadKind kind) { - // TODO - return wxTHREAD_NO_ERROR; + g_numberOfThreads++; + m_internal = new wxThreadInternal(); + + m_isDetached = kind == wxTHREAD_DETACHED; + s_threads.Add( (void*) this ) ; } -void wxThread::Exit(void *status) +wxThread::~wxThread() { - // TODO + if (g_numberOfThreads>0) + { + g_numberOfThreads--; + } +#ifdef __WXDEBUG__ + else + { + wxFAIL_MSG(wxT("More threads deleted than created.")); + } +#endif + + s_threads.Remove( (void*) this ) ; + if (m_internal != NULL) { + delete m_internal; + m_internal = NULL; + } } -void wxThread::SetPriority(int prio) +// create/start thread +// ------------------- + +wxThreadError wxThread::Create(unsigned int stackSize) { - // TODO + wxCriticalSectionLocker lock(m_critsect); + + if ( !m_internal->Create(this, stackSize) ) + return wxTHREAD_NO_RESOURCE; + + return wxTHREAD_NO_ERROR; } -int wxThread::GetPriority() const +wxThreadError wxThread::Run() { - // TODO - return 0; + wxCriticalSectionLocker lock(m_critsect); + + if ( m_internal->GetState() != STATE_NEW ) + { + // actually, it may be almost any state at all, not only STATE_RUNNING + return wxTHREAD_RUNNING; + } + + // the thread has just been created and is still suspended - let it run + return Resume(); } -void wxThread::DeferDestroy(bool on) +// suspend/resume thread +// --------------------- + +wxThreadError wxThread::Pause() { - // TODO + wxCriticalSectionLocker lock(m_critsect); + + return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR; } -void wxThread::TestDestroy() +wxThreadError wxThread::Resume() { - // TODO + wxCriticalSectionLocker lock(m_critsect); + + return m_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR; } -void *wxThread::Join() +// stopping thread +// --------------- + +wxThread::ExitCode wxThread::Wait() { - // TODO - return (void*) NULL; + // although under MacOS we can wait for any thread, it's an error to + // wait for a detached one in wxWin API + wxCHECK_MSG( !IsDetached(), (ExitCode)-1, + _T("can't wait for detached thread") ); + + ExitCode rc = (ExitCode)-1; + + (void)Delete(&rc); + + m_internal->Free(); + + return rc; } -unsigned long wxThread::GetID() const +wxThreadError wxThread::Delete(ExitCode *pRc) { - // TODO - return 0; + ExitCode rc = 0; + + // Delete() is always safe to call, so consider all possible states + + // has the thread started to run? + bool shouldResume = FALSE; + + { + wxCriticalSectionLocker lock(m_critsect); + + if ( m_internal->GetState() == STATE_NEW ) + { + // WinThreadStart() will see it and terminate immediately + m_internal->SetState(STATE_EXITED); + + shouldResume = TRUE; + } + } + + // is the thread paused? + if ( shouldResume || IsPaused() ) + Resume(); + + // does is still run? + if ( IsRunning() ) + { + if ( IsMain() ) + { + // set flag for wxIsWaitingForThread() + gs_waitingForThread = TRUE; + +#if wxUSE_GUI + wxBeginBusyCursor(); +#endif // wxUSE_GUI + } + + // ask the thread to terminate + { + wxCriticalSectionLocker lock(m_critsect); + + m_internal->Cancel(); + } + +#if wxUSE_GUI + // simply wait for the thread to terminate + while( TestDestroy() ) + { + ::YieldToAnyThread() ; + } +#else // !wxUSE_GUI + // simply wait for the thread to terminate + while( TestDestroy() ) + { + ::YieldToAnyThread() ; + } +#endif // wxUSE_GUI/!wxUSE_GUI + + if ( IsMain() ) + { + gs_waitingForThread = FALSE; + +#if wxUSE_GUI + wxEndBusyCursor(); +#endif // wxUSE_GUI + } + } + + if ( IsDetached() ) + { + // if the thread exits normally, this is done in WinThreadStart, but in + // this case it would have been too early because + // MsgWaitForMultipleObject() would fail if the therad handle was + // closed while we were waiting on it, so we must do it here + delete this; + } + + if ( pRc ) + *pRc = rc; + + return rc == (ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR; } -/* is this needed somewhere ? -wxThread *wxThread::GetThreadFromID(unsigned long id) +wxThreadError wxThread::Kill() { - // TODO - return NULL; + if ( !IsRunning() ) + return wxTHREAD_NOT_RUNNING; + +// if ( !::TerminateThread(m_internal->GetHandle(), (DWORD)-1) ) + { + wxLogSysError(_("Couldn't terminate thread")); + + return wxTHREAD_MISC_ERROR; + } + + m_internal->Free(); + + if ( IsDetached() ) + { + delete this; + } + + return wxTHREAD_NO_ERROR; } + +void wxThread::Exit(ExitCode status) +{ + m_internal->Free(); + + if ( IsDetached() ) + { + delete this; + } + + m_internal->SetResult( status ) ; + +/* +#if defined(__VISUALC__) || (defined(__BORLANDC__) && (__BORLANDC__ >= 0x500)) + _endthreadex((unsigned)status); +#else // !VC++ + ::ExitThread((DWORD)status); +#endif // VC++/!VC++ */ + wxFAIL_MSG(wxT("Couldn't return from ExitThread()!")); +} -bool wxThread::IsAlive() const +// priority setting +// ---------------- + +// since all these calls are execute cooperatively we don't have to use the critical section + +void wxThread::SetPriority(unsigned int prio) { - // TODO - return FALSE; + m_internal->SetPriority(prio); } -bool wxThread::IsRunning() const +unsigned int wxThread::GetPriority() const { - // TODO - return FALSE; + return m_internal->GetPriority(); } -bool wxThread::IsMain() +unsigned long wxThread::GetId() const { - // TODO - return FALSE; + return (unsigned long)m_internal->GetId(); } -wxThread::wxThread() +bool wxThread::IsRunning() const { - p_internal = new wxThreadInternal(); + return m_internal->GetState() == STATE_RUNNING; +} - // TODO +bool wxThread::IsAlive() const +{ + return (m_internal->GetState() == STATE_RUNNING) || + (m_internal->GetState() == STATE_PAUSED); } -wxThread::~wxThread() +bool wxThread::IsPaused() const { - Destroy(); - Join(); - delete p_internal; + return m_internal->GetState() == STATE_PAUSED; } -// The default callback just joins the thread and throws away the result. -void wxThread::OnExit() +bool wxThread::TestDestroy() { - Join(); + return m_internal->GetState() == STATE_CANCELED; } -// Automatic initialization -class wxThreadModule : public wxModule { - DECLARE_DYNAMIC_CLASS(wxThreadModule) +// ---------------------------------------------------------------------------- +// Automatic initialization for thread module +// ---------------------------------------------------------------------------- + +class wxThreadModule : public wxModule +{ public: - virtual bool OnInit() { - /* TODO p_mainid = GetCurrentThread(); */ - wxMainMutex = new wxMutex(); - wxMainMutex->Lock(); - return TRUE; - } + virtual bool OnInit(); + virtual void OnExit(); - // Global cleanup - virtual void OnExit() { - wxMainMutex->Unlock(); - delete wxMainMutex; - } +private: + DECLARE_DYNAMIC_CLASS(wxThreadModule) }; IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule) +bool wxThreadModule::OnInit() +{ + long response; + bool hasThreadManager ; + hasThreadManager = Gestalt( gestaltThreadMgrAttr, &response) == noErr && response & 1; +#if !TARGET_CARBON +#if GENERATINGCFM + // verify presence of shared library + hasThreadManager = hasThreadManager && ((Ptr)NewThread != (Ptr)kUnresolvedCFragSymbolAddress); +#endif #endif + if ( !hasThreadManager ) + { + wxLogSysError( wxT("Thread Support is not available on this System") ); + return FALSE ; + } + + // no error return for GetCurrentThreadId() + MacGetCurrentThread( &gs_idMainThread ) ; + + return TRUE; +} + +void wxThreadModule::OnExit() +{ +} + +// ---------------------------------------------------------------------------- +// under MacOS we don't have currently preemptive threads, so any thread may access +// the GUI at any time +// ---------------------------------------------------------------------------- + +void WXDLLEXPORT wxMutexGuiEnter() +{ +} + +void WXDLLEXPORT wxMutexGuiLeave() +{ +} + +void WXDLLEXPORT wxMutexGuiLeaveOrEnter() +{ +} + +bool WXDLLEXPORT wxGuiOwnedByMainThread() +{ + return false ; +} + +// wake up the main thread +void WXDLLEXPORT wxWakeUpMainThread() +{ + wxMacWakeUp() ; +} + +bool WXDLLEXPORT wxIsWaitingForThread() +{ + return false ; +} + +#include "wx/thrimpl.cpp" + +#endif // wxUSE_THREADS