X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e75491071dbefcada61175e3eb89ce4edf335983..de0439842539961ed4a41781a7729227cfb6953e:/src/mac/carbon/thread.cpp diff --git a/src/mac/carbon/thread.cpp b/src/mac/carbon/thread.cpp index 3dca96a6fa..a37865ee7a 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,13 @@ #if wxUSE_THREADS -#include - -#include - #include "wx/module.h" #include "wx/thread.h" +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + // the possible states of the thread ("=>" shows all possible transitions from // this state) enum wxThreadState @@ -50,112 +50,133 @@ 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 ; // ============================================================================ -// Windows implementation of thread classes +// MacOS implementation of thread classes // ============================================================================ +class wxMacStCritical +{ +public : + wxMacStCritical() + { + ThreadBeginCritical() ; + } + ~wxMacStCritical() + { + ThreadEndCritical() ; + } +} ; + // ---------------------------------------------------------------------------- // wxMutex implementation // ---------------------------------------------------------------------------- + class wxMutexInternal { public: - Handle p_mutex; + wxMutexInternal() + { + m_owner = kNoThreadID ; + } + + ~wxMutexInternal() + { + } + +public: + ThreadID m_owner ; + wxArrayLong m_waiters ; }; wxMutex::wxMutex() { - p_internal = new wxMutexInternal; -// p_internal->p_mutex = CreateMutex(NULL, FALSE, NULL); - if ( !p_internal->p_mutex ) - { - wxLogSysError(_("Can not create mutex.")); - } + m_internal = new wxMutexInternal; m_locked = 0; } wxMutex::~wxMutex() { - if (m_locked > 0) - wxLogDebug(wxT("Warning: freeing a locked mutex (%d locks)."), m_locked); -// CloseHandle(p_internal->p_mutex); + if ( m_locked > 0 ) + { + wxLogDebug(_T("Warning: freeing a locked mutex (%d locks)."), m_locked); + } + + delete m_internal; } wxMutexError wxMutex::Lock() { -/* - DWORD ret; - - ret = WaitForSingleObject(p_internal->p_mutex, INFINITE); - switch ( ret ) - { - case WAIT_ABANDONED: - 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")); - } -*/ + wxMacStCritical critical ; + + 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_internal->m_owner != kNoThreadID && m_internal->m_owner != current) + { + m_internal->m_waiters.Add(current); + err = ::SetThreadStateEndCritical(kCurrentThreadID, kStoppedThreadState, m_internal->m_owner); + err = ::ThreadBeginCritical(); + } + m_internal->m_owner = current; m_locked++; + return wxMUTEX_NO_ERROR; } wxMutexError wxMutex::TryLock() { -/* - DWORD ret; - - ret = WaitForSingleObject(p_internal->p_mutex, 0); - if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED) + wxMacStCritical critical ; + + OSErr err ; + ThreadID current = kNoThreadID; + ::MacGetCurrentThread(¤t); + // if we are not the owner, give an error back + if ( m_internal->m_owner != kNoThreadID && m_internal->m_owner != current ) return wxMUTEX_BUSY; - + + m_internal->m_owner = current; m_locked++; -*/ - return wxMUTEX_NO_ERROR; + + return wxMUTEX_NO_ERROR; } wxMutexError wxMutex::Unlock() { + OSErr err; + err = ::ThreadBeginCritical(); + if (m_locked > 0) m_locked--; -/* - BOOL ret = ReleaseMutex(p_internal->p_mutex); - if ( ret == 0 ) - { - wxLogSysError(_("Couldn't release a mutex")); - return wxMUTEX_MISC_ERROR; - } -*/ + + // this mutex is not owned by anybody anmore + m_internal->m_owner = kNoThreadID; + + // now pass on to the first waiting thread + ThreadID firstWaiting = kNoThreadID; + bool found = false; + while (!m_internal->m_waiters.IsEmpty() && !found) + { + firstWaiting = m_internal->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_internal->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); + return wxMUTEX_NO_ERROR; } @@ -166,66 +187,89 @@ wxMutexError wxMutex::Unlock() class wxConditionInternal { public: - Handle event; - int waiters; -}; + wxConditionInternal() + { + m_excessSignals = 0 ; + } + ~wxConditionInternal() + { + } -wxCondition::wxCondition() -{ - p_internal = new wxConditionInternal; -// p_internal->event = CreateEvent(NULL, FALSE, FALSE, NULL); - if ( !p_internal->event ) + bool Wait(unsigned long msectimeout) { - wxLogSysError(_("Can not create event object.")); + wxMacStCritical critical ; + if ( m_excessSignals > 0 ) + { + --m_excessSignals ; + return TRUE ; + } + else if ( msectimeout == 0 ) + { + return FALSE ; + } + else + { + } + /* + waiters++; + + // FIXME this should be MsgWaitForMultipleObjects() as well probably + DWORD rc = ::WaitForSingleObject(event, timeout); + + waiters--; + + return rc != WAIT_TIMEOUT; + */ + return TRUE ; } + void Signal() + { + wxMacStCritical critical ; + } + + wxArrayLong m_waiters ; + wxInt32 m_excessSignals ; +}; - p_internal->waiters = 0; +wxCondition::wxCondition() +{ + m_internal = new wxConditionInternal; } wxCondition::~wxCondition() { -// CloseHandle(p_internal->event); + delete m_internal; } -void wxCondition::Wait(wxMutex& mutex) +void wxCondition::Wait() { - mutex.Unlock(); - p_internal->waiters++; -// WaitForSingleObject(p_internal->event, INFINITE); - p_internal->waiters--; - mutex.Lock(); + (void)m_internal->Wait(0xFFFFFFFFL ); } -bool wxCondition::Wait(wxMutex& mutex, - unsigned long sec, +bool wxCondition::Wait(unsigned long sec, unsigned long nsec) { -// DWORD ret; - - mutex.Unlock(); - p_internal->waiters++; - // ret = WaitForSingleObject(p_internal->event, (sec*1000)+(nsec/1000000)); - p_internal->waiters--; - mutex.Lock(); - - return TRUE; // false for timeout + return m_internal->Wait(sec*1000 + nsec/1000000); } void wxCondition::Signal() { -// SetEvent(p_internal->event); + // set the event to signaled: if a thread is already waiting on it, it will + // be woken up, otherwise the event will remain in the signaled state until + // someone waits on it. In any case, the system will return it to a non + // signalled state afterwards. If multiple threads are waiting, only one + // will be woken up. + m_internal->Signal() ; } void wxCondition::Broadcast() { - int i; - - for (i=0;iwaiters;i++) + // this works because all these threads are already waiting and so each + // SetEvent() inside Signal() is really a PulseEvent() because the event + // state is immediately returned to non-signaled + for ( int i = 0; i < m_internal->m_waiters.Count(); i++ ) { -// if ( SetEvent(p_internal->event) == 0 ) - { - wxLogSysError(_("Couldn't change the state of event object.")); - } + Signal(); } } @@ -233,43 +277,7 @@ void wxCondition::Broadcast() // wxCriticalSection implementation // ---------------------------------------------------------------------------- -class wxCriticalSectionInternal -{ -public: - // init the critical section object - wxCriticalSectionInternal() - { /* ::InitializeCriticalSection(&m_data);*/ } - - // implicit cast to the associated data - operator Handle *() { return &m_data; } - - // free the associated ressources - ~wxCriticalSectionInternal() - { /* ::DeleteCriticalSection(&m_data); */} - -private: - Handle m_data; -}; - -wxCriticalSection::wxCriticalSection() -{ - m_critsect = new wxCriticalSectionInternal; -} - -wxCriticalSection::~wxCriticalSection() -{ - delete m_critsect; -} - -void wxCriticalSection::Enter() -{ -// ::EnterCriticalSection(*m_critsect); -} - -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,17 +286,24 @@ 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); @@ -302,83 +317,87 @@ 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 ; +public : }; -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; +} +void wxThreadInternal::SetPriority(unsigned int priority) +{ + // Priorities don't exist on Mac } bool wxThreadInternal::Create(wxThread *thread) { - 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 - ); - - if ( m_hThread == NULL ) + if ( s_threadEntry == NULL ) + { + s_threadEntry = NewThreadEntryUPP( (ThreadEntryProcPtr) MacThreadStart ) ; + } + OSErr err = NewThread(kCooperativeThread, + s_threadEntry, + (void*) thread , + 0 , + 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 - { - wxFAIL_MSG(wxT("invalid value of thread priority parameter")); - win_priority = THREAD_PRIORITY_NORMAL; - } - - if ( ::SetThreadPriority(m_hThread, win_priority) == 0 ) + if ( m_priority != WXTHREAD_DEFAULT_PRIORITY ) { - wxLogSysError(_("Can't set thread priority")); + SetPriority(m_priority); } return TRUE; @@ -386,70 +405,138 @@ bool wxThreadInternal::Create(wxThread *thread) 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); - - // be careful, 0 may be a valid return value as well - if ( !thread && (::GetLastError() != NO_ERROR) ) - { - wxLogSysError(_("Couldn't get the current thread pointer")); + wxMacStCritical critical ; + + ThreadID current ; + OSErr err ; + + err = MacGetCurrentThread( ¤t ) ; + + for ( int i = 0 ; i < s_threads.Count() ; ++i ) + { + if ( ( (wxThread*) s_threads[i] )->GetId() == current ) + return (wxThread*) s_threads[i] ; + } - // return NULL... - } - - 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 ) ; +} + +int wxThread::GetCPUCount() +{ + // we will use whatever MP API will be used for the new MP Macs + return 1; +} + +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) +{ + m_internal = new wxThreadInternal(); + + m_isDetached = kind == wxTHREAD_DETACHED; + s_threads.Add( (void*) this ) ; +} + +wxThread::~wxThread() +{ + s_threads.Remove( (void*) this ) ; + delete m_internal; } // create/start thread @@ -457,7 +544,9 @@ void wxThread::Sleep(unsigned long milliseconds) wxThreadError wxThread::Create() { - if ( !p_internal->Create(this) ) + wxCriticalSectionLocker lock(m_critsect); + + if ( !m_internal->Create(this) ) return wxTHREAD_NO_RESOURCE; return wxTHREAD_NO_ERROR; @@ -467,12 +556,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 +573,127 @@ 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 - { - 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 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() ) { - s_waitingForThread = FALSE; + gs_waitingForThread = FALSE; +#if wxUSE_GUI wxEndBusyCursor(); +#endif // wxUSE_GUI } + } - if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) ) - { - wxLogLastError("GetExitCodeThread"); - - rc = (ExitCode)-1; - } + // if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) ) + { + wxLogLastError("GetExitCodeThread"); - wxASSERT_MSG( (LPVOID)rc != (LPVOID)STILL_ACTIVE, - wxT("thread must be already terminated.") ); + rc = (ExitCode)-1; + } - ::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; + // wxASSERT_MSG( (DWORD)rc != STILL_ACTIVE, + // wxT("thread must be already terminated.") ); + + if ( pRc ) + *pRc = rc; + + return rc == (ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR; } wxThreadError wxThread::Kill() @@ -604,85 +701,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 +798,63 @@ 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) ) - { - ::TlsFree(s_tlsThisThread); - s_tlsThisThread = 0xFFFFFFFF; - - wxLogSysError(_("Thread module initialization failed: " - "can not store value in thread local storage")); - - return FALSE; - } - - s_critsectWaitingForGui = new wxCriticalSection(); - - s_critsectGui = new wxCriticalSection(); - s_critsectGui->Enter(); + 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 ) + { + wxMessageBox( "Error" , "Thread Support is not available on this System" , wxOK ) ; + return FALSE ; + } // 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 ; } -*/ #endif // wxUSE_THREADS +