X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e75491071dbefcada61175e3eb89ce4edf335983..b77b87881bae2e6306366d79e7fe160334b3d4a2:/src/mac/carbon/thread.cpp diff --git a/src/mac/carbon/thread.cpp b/src/mac/carbon/thread.cpp index 3dca96a6fa..3634a69dde 100644 --- a/src/mac/carbon/thread.cpp +++ b/src/mac/carbon/thread.cpp @@ -1,12 +1,12 @@ ///////////////////////////////////////////////////////////////////////////// // Name: thread.cpp // Purpose: wxThread Implementation -// Author: Original from Wolfram Gloger/Guilhem Lavaux -// Modified by: Vadim Zeitlin to make it work :-) +// 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), -// Vadim Zeitlin (1999) +// Vadim Zeitlin (1999) , Stefan Csomor (2000) // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -31,13 +31,22 @@ #if wxUSE_THREADS -#include - -#include - #include "wx/module.h" #include "wx/thread.h" +#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 @@ -50,226 +59,262 @@ enum wxThreadState }; // ---------------------------------------------------------------------------- -// static variables +// this module globals // ---------------------------------------------------------------------------- -// if it's FALSE, some secondary thread is holding the GUI lock -static bool s_bGuiOwnedByMainThread = TRUE; - -// critical section which controls access to all GUI functions: any secondary -// thread (i.e. except the main one) must enter this crit section before doing -// any GUI calls -static wxCriticalSection *s_critsectGui = NULL; - -// critical section which protects s_nWaitingForGui variable -static wxCriticalSection *s_critsectWaitingForGui = NULL; - -// number of threads waiting for GUI in wxMutexGuiEnter() -static size_t s_nWaitingForGui = 0; - -// are we waiting for a thread termination? -static bool s_waitingForThread = FALSE; +static ThreadID gs_idMainThread = kNoThreadID ; +static bool gs_waitingForThread = FALSE ; +size_t g_numberOfThreads = 0; // ============================================================================ -// Windows implementation of thread classes +// MacOS implementation of thread classes // ============================================================================ +class wxMacStCritical +{ +public : + wxMacStCritical() + { + if ( UMASystemIsInitialized() ) + ThreadBeginCritical() ; + } + ~wxMacStCritical() + { + if ( UMASystemIsInitialized() ) + ThreadEndCritical() ; + } +}; + // ---------------------------------------------------------------------------- // wxMutex implementation // ---------------------------------------------------------------------------- + class wxMutexInternal { public: - Handle p_mutex; -}; + wxMutexInternal(wxMutexType WXUNUSED(mutexType)) + { + m_owner = kNoThreadID ; + m_locked = 0; + } -wxMutex::wxMutex() -{ - p_internal = new wxMutexInternal; -// p_internal->p_mutex = CreateMutex(NULL, FALSE, NULL); - if ( !p_internal->p_mutex ) + ~wxMutexInternal() { - wxLogSysError(_("Can not create mutex.")); + if ( m_locked > 0 ) + { + wxLogDebug(_T("Warning: freeing a locked mutex (%ld locks)."), m_locked); + } } - m_locked = 0; -} + bool IsOk() const { return true; } -wxMutex::~wxMutex() + wxMutexError Lock() ; + wxMutexError TryLock() ; + wxMutexError Unlock(); +public: + ThreadID m_owner ; + wxArrayLong m_waiters ; + long m_locked ; +}; + +wxMutexError wxMutexInternal::Lock() { - if (m_locked > 0) - wxLogDebug(wxT("Warning: freeing a locked mutex (%d locks)."), m_locked); -// CloseHandle(p_internal->p_mutex); + 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; } -wxMutexError wxMutex::Lock() +wxMutexError wxMutexInternal::TryLock() { -/* - DWORD ret; - - ret = WaitForSingleObject(p_internal->p_mutex, INFINITE); - switch ( ret ) + wxMacStCritical critical ; + if ( UMASystemIsInitialized() ) { - case WAIT_ABANDONED: + 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; - case WAIT_OBJECT_0: - // ok - break; - - case WAIT_FAILED: - wxLogSysError(_("Couldn't acquire a mutex lock")); - return wxMUTEX_MISC_ERROR; - - case WAIT_TIMEOUT: - default: - wxFAIL_MSG(wxT("impossible return value in wxMutex::Lock")); + m_owner = current; } -*/ m_locked++; - return wxMUTEX_NO_ERROR; + + return wxMUTEX_NO_ERROR; } -wxMutexError wxMutex::TryLock() +wxMutexError wxMutexInternal::Unlock() { -/* - DWORD ret; + if ( UMASystemIsInitialized() ) + { + OSErr err; + err = ::ThreadBeginCritical(); - ret = WaitForSingleObject(p_internal->p_mutex, 0); - if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED) - return wxMUTEX_BUSY; + if (m_locked > 0) + m_locked--; - m_locked++; -*/ - return wxMUTEX_NO_ERROR; -} + // this mutex is not owned by anybody anmore + m_owner = kNoThreadID; -wxMutexError wxMutex::Unlock() -{ - if (m_locked > 0) - m_locked--; -/* - BOOL ret = ReleaseMutex(p_internal->p_mutex); - if ( ret == 0 ) + // 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 { - wxLogSysError(_("Couldn't release a mutex")); - return wxMUTEX_MISC_ERROR; + if (m_locked > 0) + m_locked--; } -*/ return wxMUTEX_NO_ERROR; } -// ---------------------------------------------------------------------------- -// wxCondition implementation -// ---------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +// wxSemaphore +// -------------------------------------------------------------------------- -class wxConditionInternal +// TODO not yet implemented + +class wxSemaphoreInternal { public: - Handle event; - int waiters; + 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: }; -wxCondition::wxCondition() +wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount) { - p_internal = new wxConditionInternal; -// p_internal->event = CreateEvent(NULL, FALSE, FALSE, NULL); - if ( !p_internal->event ) + if ( maxcount == 0 ) { - wxLogSysError(_("Can not create event object.")); + // make it practically infinite + maxcount = INT_MAX; } - - p_internal->waiters = 0; } -wxCondition::~wxCondition() +wxSemaphoreInternal::~wxSemaphoreInternal() { -// CloseHandle(p_internal->event); } -void wxCondition::Wait(wxMutex& mutex) +wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds) { - mutex.Unlock(); - p_internal->waiters++; -// WaitForSingleObject(p_internal->event, INFINITE); - p_internal->waiters--; - mutex.Lock(); + return wxSEMA_MISC_ERROR; } -bool wxCondition::Wait(wxMutex& mutex, - unsigned long sec, - unsigned long nsec) +wxSemaError wxSemaphoreInternal::Post() { -// DWORD ret; + return wxSEMA_MISC_ERROR; +} - mutex.Unlock(); - p_internal->waiters++; - // ret = WaitForSingleObject(p_internal->event, (sec*1000)+(nsec/1000000)); - p_internal->waiters--; - mutex.Lock(); +// ---------------------------------------------------------------------------- +// wxCondition implementation +// ---------------------------------------------------------------------------- - return TRUE; // false for timeout -} +// TODO this is not yet completed -void wxCondition::Signal() +class wxConditionInternal { -// SetEvent(p_internal->event); -} +public: + wxConditionInternal(wxMutex& mutex) : m_mutex(mutex) + { + m_excessSignals = 0 ; + } + ~wxConditionInternal() + { + } -void wxCondition::Broadcast() -{ - int i; + bool IsOk() const { return m_mutex.IsOk() ; } - for (i=0;iwaiters;i++) + wxCondError Wait() { -// if ( SetEvent(p_internal->event) == 0 ) + return WaitTimeout(0xFFFFFFFF ); + } + + wxCondError WaitTimeout(unsigned long msectimeout) + { + wxMacStCritical critical ; + if ( m_excessSignals > 0 ) { - wxLogSysError(_("Couldn't change the state of event object.")); + --m_excessSignals ; + return wxCOND_NO_ERROR ; } - } -} + else if ( msectimeout == 0 ) + { + return wxCOND_MISC_ERROR ; + } + else + { + } + /* + waiters++; -// ---------------------------------------------------------------------------- -// wxCriticalSection implementation -// ---------------------------------------------------------------------------- + // FIXME this should be MsgWaitForMultipleObjects() as well probably + DWORD rc = ::WaitForSingleObject(event, timeout); -class wxCriticalSectionInternal -{ -public: - // init the critical section object - wxCriticalSectionInternal() - { /* ::InitializeCriticalSection(&m_data);*/ } + waiters--; - // implicit cast to the associated data - operator Handle *() { return &m_data; } + return rc != WAIT_TIMEOUT; + */ + return wxCOND_NO_ERROR ; + } + wxCondError Signal() + { + wxMacStCritical critical ; + return wxCOND_NO_ERROR; + } - // free the associated ressources - ~wxCriticalSectionInternal() - { /* ::DeleteCriticalSection(&m_data); */} + wxCondError Broadcast() + { + wxMacStCritical critical ; + return wxCOND_NO_ERROR; + } -private: - Handle m_data; + wxArrayLong m_waiters ; + wxInt32 m_excessSignals ; + wxMutex& m_mutex; }; -wxCriticalSection::wxCriticalSection() -{ - m_critsect = new wxCriticalSectionInternal; -} - -wxCriticalSection::~wxCriticalSection() -{ - delete m_critsect; -} - -void wxCriticalSection::Enter() -{ -// ::EnterCriticalSection(*m_critsect); -} +// ---------------------------------------------------------------------------- +// wxCriticalSection implementation +// ---------------------------------------------------------------------------- -void wxCriticalSection::Leave() -{ -// ::LeaveCriticalSection(*m_critsect); -} +// it's implemented as a mutex on mac os, so it is defined in the headers // ---------------------------------------------------------------------------- // wxThread implementation @@ -278,19 +323,26 @@ void wxCriticalSection::Leave() // wxThreadInternal class // ---------------------- -/* class wxThreadInternal { public: wxThreadInternal() { - m_hThread = 0; + 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); + bool Create(wxThread *thread, unsigned int stackSize); // suspend/resume/terminate bool Suspend(); @@ -302,162 +354,259 @@ public: wxThreadState GetState() const { return m_state; } // thread priority - void SetPriority(unsigned int priority) { m_priority = 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 - HANDLE GetHandle() const { return m_hThread; } - DWORD GetId() const { return m_tid; } + ThreadID GetId() const { return m_tid; } // thread function - static DWORD WinThreadStart(wxThread *thread); + static pascal void* MacThreadStart(wxThread* arg); private: - Handle m_hThread; // handle of the thread - wxThreadState m_state; // state, see wxThreadState enum - unsigned int m_priority; // thread priority in "wx" units - ThreadId m_tid; // thread id + 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 ; }; -DWORD wxThreadInternal::WinThreadStart(wxThread *thread) +static wxArrayPtrVoid s_threads ; + +ThreadEntryUPP wxThreadInternal::s_threadEntry = NULL ; +pascal void* wxThreadInternal::MacThreadStart(wxThread *thread) { - // store the thread object in the TLS - if ( !::TlsSetValue(s_tlsThisThread, thread) ) + // first of all, check whether we hadn't been cancelled already + if ( thread->m_internal->GetState() == STATE_EXITED ) { - wxLogSysError(_("Can not start thread: error writing TLS.")); - - return (DWORD)-1; + return (void*)-1; } - DWORD ret = (DWORD)thread->Entry(); - thread->p_internal->SetState(STATE_EXITED); + 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(); - delete thread; + // 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 ret; + return rc; } - -bool wxThreadInternal::Create(wxThread *thread) +void wxThreadInternal::SetPriority(unsigned int priority) { - m_hThread = ::CreateThread - ( - NULL, // default security - 0, // default stack size - (LPTHREAD_START_ROUTINE) // thread entry point - wxThreadInternal::WinThreadStart, // - (LPVOID)thread, // parameter - CREATE_SUSPENDED, // flags - &m_tid // [out] thread id - ); + // Priorities don't exist on Mac +} - if ( m_hThread == NULL ) +bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize) +{ + 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; } - // translate wxWindows priority to the Windows one - int win_priority; - if (m_priority <= 20) - win_priority = THREAD_PRIORITY_LOWEST; - else if (m_priority <= 40) - win_priority = THREAD_PRIORITY_BELOW_NORMAL; - else if (m_priority <= 60) - win_priority = THREAD_PRIORITY_NORMAL; - else if (m_priority <= 80) - win_priority = THREAD_PRIORITY_ABOVE_NORMAL; - else if (m_priority <= 100) - win_priority = THREAD_PRIORITY_HIGHEST; - else + if ( m_priority != WXTHREAD_DEFAULT_PRIORITY ) { - wxFAIL_MSG(wxT("invalid value of thread priority parameter")); - win_priority = THREAD_PRIORITY_NORMAL; + SetPriority(m_priority); } - if ( ::SetThreadPriority(m_hThread, win_priority) == 0 ) - { - wxLogSysError(_("Can't set thread priority")); - } + m_state = STATE_NEW; return TRUE; } bool wxThreadInternal::Suspend() { - DWORD nSuspendCount = ::SuspendThread(m_hThread); - if ( nSuspendCount == (DWORD)-1 ) - { - wxLogSysError(_("Can not suspend thread %x"), m_hThread); + 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 wxThreadInternal::Resume() { - DWORD nSuspendCount = ::ResumeThread(m_hThread); - if ( nSuspendCount == (DWORD)-1 ) - { - wxLogSysError(_("Can not resume thread %x"), m_hThread); + 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() { - wxThread *thread = (wxThread *)::TlsGetValue(s_tlsThisThread); + wxMacStCritical critical ; - // be careful, 0 may be a valid return value as well - if ( !thread && (::GetLastError() != NO_ERROR) ) - { - wxLogSysError(_("Couldn't get the current thread pointer")); + ThreadID current ; + OSErr err ; - // return NULL... + err = MacGetCurrentThread( ¤t ) ; + + for ( size_t i = 0 ; i < s_threads.Count() ; ++i ) + { + if ( ( (wxThread*) s_threads[i] )->GetId() == current ) + return (wxThread*) s_threads[i] ; } - return thread; + wxLogSysError(_("Couldn't get the current thread pointer")); + return NULL; } bool wxThread::IsMain() { - return ::GetCurrentThreadId() == s_idMainThread; + ThreadID current ; + OSErr err ; + + err = MacGetCurrentThread( ¤t ) ; + return current == gs_idMainThread; } #ifdef Yield - #undef Yield +#undef Yield #endif void wxThread::Yield() { - // 0 argument to Sleep() is special - ::Sleep(0); + ::YieldToAnyThread() ; } void wxThread::Sleep(unsigned long milliseconds) { - ::Sleep(milliseconds); + clock_t start = clock(); + do + { + YieldToAnyThread(); + } while( clock() - start < milliseconds * CLOCKS_PER_SEC / 1000.0 ) ; +} + +int wxThread::GetCPUCount() +{ + // we will use whatever MP API will be used for the new MP Macs + return 1; +} + +unsigned long wxThread::GetCurrentId() +{ + ThreadID current ; + MacGetCurrentThread( ¤t ) ; + return (unsigned long)current; +} + +bool wxThread::SetConcurrency(size_t level) +{ + 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 ; +} + +// ctor and dtor +// ------------- + +wxThread::wxThread(wxThreadKind kind) +{ + g_numberOfThreads++; + m_internal = new wxThreadInternal(); + + m_isDetached = kind == wxTHREAD_DETACHED; + s_threads.Add( (void*) this ) ; +} + +wxThread::~wxThread() +{ + 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; + } } // create/start thread // ------------------- -wxThreadError wxThread::Create() +wxThreadError wxThread::Create(unsigned int stackSize) { - if ( !p_internal->Create(this) ) + wxCriticalSectionLocker lock(m_critsect); + + if ( !m_internal->Create(this, stackSize) ) return wxTHREAD_NO_RESOURCE; return wxTHREAD_NO_ERROR; @@ -467,12 +616,13 @@ wxThreadError wxThread::Run() { wxCriticalSectionLocker lock(m_critsect); - if ( p_internal->GetState() != STATE_NEW ) + 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(); } @@ -483,120 +633,117 @@ wxThreadError wxThread::Pause() { wxCriticalSectionLocker lock(m_critsect); - return p_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR; + return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR; } wxThreadError wxThread::Resume() { wxCriticalSectionLocker lock(m_critsect); - return p_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR; + return m_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR; } // stopping thread // --------------- -wxThread::ExitCode wxThread::Delete() +wxThread::ExitCode wxThread::Wait() +{ + // 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; +} + +wxThreadError wxThread::Delete(ExitCode *pRc) { ExitCode rc = 0; // Delete() is always safe to call, so consider all possible states - if ( IsPaused() ) + + // 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() - s_waitingForThread = TRUE; + gs_waitingForThread = TRUE; +#if wxUSE_GUI wxBeginBusyCursor(); +#endif // wxUSE_GUI } - HANDLE hThread; + // ask the thread to terminate { wxCriticalSectionLocker lock(m_critsect); - p_internal->Cancel(); - hThread = p_internal->GetHandle(); + m_internal->Cancel(); } - // we can't just wait for the thread to terminate because it might be - // calling some GUI functions and so it will never terminate before we - // process the Windows messages that result from these functions - DWORD result; - do +#if wxUSE_GUI + // simply wait for the thread to terminate + while( TestDestroy() ) { - result = ::MsgWaitForMultipleObjects - ( - 1, // number of objects to wait for - &hThread, // the objects - FALSE, // don't wait for all objects - INFINITE, // no timeout - QS_ALLEVENTS // return as soon as there are any events - ); - - switch ( result ) - { - case 0xFFFFFFFF: - // error - wxLogSysError(_("Can not wait for thread termination")); - Kill(); - return (ExitCode)-1; - - case WAIT_OBJECT_0: - // thread we're waiting for terminated - break; - - case WAIT_OBJECT_0 + 1: - // new message arrived, process it - if ( !wxTheApp->DoMessage() ) - { - // WM_QUIT received: kill the thread - Kill(); - - return (ExitCode)-1; - } - - if ( IsMain() ) - { - // give the thread we're waiting for chance to exit - // from the GUI call it might have been in - if ( (s_nWaitingForGui > 0) && wxGuiOwnedByMainThread() ) - { - wxMutexGuiLeave(); - } - } - - break; - - default: - wxFAIL_MSG(wxT("unexpected result of MsgWaitForMultipleObject")); - } - } while ( result != WAIT_OBJECT_0 ); - - if ( IsMain() ) + ::YieldToAnyThread() ; + } +#else // !wxUSE_GUI + // simply wait for the thread to terminate + while( TestDestroy() ) { - s_waitingForThread = FALSE; - - wxEndBusyCursor(); + ::YieldToAnyThread() ; } +#endif // wxUSE_GUI/!wxUSE_GUI - if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) ) + if ( IsMain() ) { - wxLogLastError("GetExitCodeThread"); + gs_waitingForThread = FALSE; - rc = (ExitCode)-1; +#if wxUSE_GUI + wxEndBusyCursor(); +#endif // wxUSE_GUI } + } - wxASSERT_MSG( (LPVOID)rc != (LPVOID)STILL_ACTIVE, - wxT("thread must be already terminated.") ); - - ::CloseHandle(hThread); + 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; } - return rc; + if ( pRc ) + *pRc = rc; + + return rc == (ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR; } wxThreadError wxThread::Kill() @@ -604,85 +751,83 @@ wxThreadError wxThread::Kill() if ( !IsRunning() ) return wxTHREAD_NOT_RUNNING; - if ( !::TerminateThread(p_internal->GetHandle(), (DWORD)-1) ) +// if ( !::TerminateThread(m_internal->GetHandle(), (DWORD)-1) ) { wxLogSysError(_("Couldn't terminate thread")); return wxTHREAD_MISC_ERROR; } - delete this; + m_internal->Free(); + + if ( IsDetached() ) + { + delete this; + } return wxTHREAD_NO_ERROR; } -void wxThread::Exit(void *status) +void wxThread::Exit(ExitCode status) { - delete this; + m_internal->Free(); - ::ExitThread((DWORD)status); + 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()!")); } +// priority setting +// ---------------- + +// since all these calls are execute cooperatively we don't have to use the critical section + void wxThread::SetPriority(unsigned int prio) { - wxCriticalSectionLocker lock(m_critsect); - - p_internal->SetPriority(prio); + m_internal->SetPriority(prio); } unsigned int wxThread::GetPriority() const { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); - - return p_internal->GetPriority(); + return m_internal->GetPriority(); } -unsigned long wxThread::GetID() const +unsigned long wxThread::GetId() const { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); - - return (unsigned long)p_internal->GetId(); + return (unsigned long)m_internal->GetId(); } bool wxThread::IsRunning() const { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); - - return p_internal->GetState() == STATE_RUNNING; + return m_internal->GetState() == STATE_RUNNING; } bool wxThread::IsAlive() const { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); - - return (p_internal->GetState() == STATE_RUNNING) || - (p_internal->GetState() == STATE_PAUSED); + return (m_internal->GetState() == STATE_RUNNING) || + (m_internal->GetState() == STATE_PAUSED); } bool wxThread::IsPaused() const { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); - - return (p_internal->GetState() == STATE_PAUSED); + return m_internal->GetState() == STATE_PAUSED; } bool wxThread::TestDestroy() { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); - - return p_internal->GetState() == STATE_CANCELED; -} - -wxThread::wxThread() -{ - p_internal = new wxThreadInternal(); -} - -wxThread::~wxThread() -{ - delete p_internal; + return m_internal->GetState() == STATE_CANCELED; } // ---------------------------------------------------------------------------- @@ -703,160 +848,64 @@ IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule) bool wxThreadModule::OnInit() { - // allocate TLS index for storing the pointer to the current thread - s_tlsThisThread = ::TlsAlloc(); - if ( s_tlsThisThread == 0xFFFFFFFF ) - { - // in normal circumstances it will only happen if all other - // TLS_MINIMUM_AVAILABLE (>= 64) indices are already taken - in other - // words, this should never happen - wxLogSysError(_("Thread module initialization failed: " - "impossible to allocate index in thread " - "local storage")); - - return FALSE; - } - - // main thread doesn't have associated wxThread object, so store 0 in the - // TLS instead - if ( !::TlsSetValue(s_tlsThisThread, (LPVOID)0) ) + 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 ) { - ::TlsFree(s_tlsThisThread); - s_tlsThisThread = 0xFFFFFFFF; - - wxLogSysError(_("Thread module initialization failed: " - "can not store value in thread local storage")); - - return FALSE; + wxLogSysError( wxT("Thread Support is not available on this System") ); + return FALSE ; } - s_critsectWaitingForGui = new wxCriticalSection(); - - s_critsectGui = new wxCriticalSection(); - s_critsectGui->Enter(); - // no error return for GetCurrentThreadId() - s_idMainThread = ::GetCurrentThreadId(); + MacGetCurrentThread( &gs_idMainThread ) ; return TRUE; } void wxThreadModule::OnExit() { - if ( !::TlsFree(s_tlsThisThread) ) - { - wxLogLastError("TlsFree failed."); - } - - if ( s_critsectGui ) - { - s_critsectGui->Leave(); - delete s_critsectGui; - s_critsectGui = NULL; - } - - wxDELETE(s_critsectWaitingForGui); } // ---------------------------------------------------------------------------- -// under Windows, these functions are implemented usign a critical section and -// not a mutex, so the names are a bit confusing +// under MacOS we don't have currently preemptive threads, so any thread may access +// the GUI at any time // ---------------------------------------------------------------------------- void WXDLLEXPORT wxMutexGuiEnter() { - // this would dead lock everything... - wxASSERT_MSG( !wxThread::IsMain(), - wxT("main thread doesn't want to block in wxMutexGuiEnter()!") ); - - // the order in which we enter the critical sections here is crucial!! - - // set the flag telling to the main thread that we want to do some GUI - { - wxCriticalSectionLocker enter(*s_critsectWaitingForGui); - - s_nWaitingForGui++; - } - - wxWakeUpMainThread(); - - // now we may block here because the main thread will soon let us in - // (during the next iteration of OnIdle()) - s_critsectGui->Enter(); } void WXDLLEXPORT wxMutexGuiLeave() { - wxCriticalSectionLocker enter(*s_critsectWaitingForGui); - - if ( wxThread::IsMain() ) - { - s_bGuiOwnedByMainThread = FALSE; - } - else - { - // decrement the number of waiters now - wxASSERT_MSG( s_nWaitingForGui > 0, - wxT("calling wxMutexGuiLeave() without entering it first?") ); - - s_nWaitingForGui--; - - wxWakeUpMainThread(); - } - - s_critsectGui->Leave(); } void WXDLLEXPORT wxMutexGuiLeaveOrEnter() { - wxASSERT_MSG( wxThread::IsMain(), - wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") ); - - wxCriticalSectionLocker enter(*s_critsectWaitingForGui); - - if ( s_nWaitingForGui == 0 ) - { - // no threads are waiting for GUI - so we may acquire the lock without - // any danger (but only if we don't already have it) - if ( !wxGuiOwnedByMainThread() ) - { - s_critsectGui->Enter(); - - s_bGuiOwnedByMainThread = TRUE; - } - //else: already have it, nothing to do - } - else - { - // some threads are waiting, release the GUI lock if we have it - if ( wxGuiOwnedByMainThread() ) - { - wxMutexGuiLeave(); - } - //else: some other worker thread is doing GUI - } } bool WXDLLEXPORT wxGuiOwnedByMainThread() { - return s_bGuiOwnedByMainThread; + return false ; } -// wake up the main thread if it's in ::GetMessage() +// wake up the main thread void WXDLLEXPORT wxWakeUpMainThread() { - // sending any message would do - hopefully WM_NULL is harmless enough - if ( !::PostThreadMessage(s_idMainThread, WM_NULL, 0, 0) ) - { - // should never happen - wxLogLastError("PostThreadMessage(WM_NULL)"); - } + wxMacWakeUp() ; } bool WXDLLEXPORT wxIsWaitingForThread() { - return s_waitingForThread; + return false ; } -*/ + +#include "wx/thrimpl.cpp" #endif // wxUSE_THREADS