X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/d90895ac118ad4546eed7ee4c358a3fe644a1ad7..095d49f2faa766ad00159fe9d75c7661039fece1:/src/os2/thread.cpp diff --git a/src/os2/thread.cpp b/src/os2/thread.cpp index c636e6e9ae..738a99a758 100644 --- a/src/os2/thread.cpp +++ b/src/os2/thread.cpp @@ -1,14 +1,19 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: thread.cpp -// Purpose: wxThread Implementation. For Unix ports, see e.g. src/gtk -// Author: Original from Wolfram Gloger/Guilhem Lavaux -// Modified by: David Webster +// Name: src/os2/thread.cpp +// Purpose: wxThread Implementation +// Author: Original from Wolfram Gloger/Guilhem Lavaux/David Webster +// Modified by: Stefan Neis // Created: 04/22/98 // RCS-ID: $Id$ -// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998) +// Copyright: (c) Stefan Neis (2003) +// // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// +#ifdef __GNUG__ + #pragma implementation "thread.h" +#endif + // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- @@ -20,12 +25,22 @@ #include -#define INCL_DOS -#include - +#include "wx/app.h" +#include "wx/apptrait.h" #include "wx/module.h" +#include "wx/intl.h" +#include "wx/utils.h" +#include "wx/log.h" #include "wx/thread.h" +#define INCL_DOSSEMAPHORES +#define INCL_DOSPROCESS +#define INCL_DOSMISC +#define INCL_ERRORS +#include +#ifndef __EMX__ +#include +#endif // the possible states of the thread ("=>" shows all possible transitions from // this state) enum wxThreadState @@ -38,35 +53,36 @@ enum wxThreadState }; // ---------------------------------------------------------------------------- -// static variables +// this module's globals // ---------------------------------------------------------------------------- -// TLS index of the slot where we store the pointer to the current thread -static DWORD s_tlsThisThread = 0xFFFFFFFF; - // id of the main thread - the one which can call GUI functions without first // calling wxMutexGuiEnter() -static DWORD s_idMainThread = 0; +static ULONG s_ulIdMainThread = 1; +wxMutex* p_wxMainMutex; + +// OS2 substitute for Tls pointer the current parent thread object +wxThread* m_pThread; // pointer to the wxWindows thread object // if it's FALSE, some secondary thread is holding the GUI lock -static bool s_bGuiOwnedByMainThread = TRUE; +static bool gs_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; +static wxCriticalSection *gs_pCritsectGui = NULL; // critical section which protects s_nWaitingForGui variable -static wxCriticalSection *s_critsectWaitingForGui = NULL; +static wxCriticalSection *gs_pCritsectWaitingForGui = NULL; // number of threads waiting for GUI in wxMutexGuiEnter() -static size_t s_nWaitingForGui = 0; +static size_t gs_nWaitingForGui = 0; // are we waiting for a thread termination? -static bool s_waitingForThread = FALSE; +static bool gs_bWaitingForThread = FALSE; // ============================================================================ -// Windows implementation of thread classes +// OS/2 implementation of thread and related classes // ============================================================================ // ---------------------------------------------------------------------------- @@ -75,206 +91,246 @@ static bool s_waitingForThread = FALSE; class wxMutexInternal { public: - HANDLE p_mutex; + wxMutexInternal(wxMutexType mutexType); + ~wxMutexInternal(); + + bool IsOk() const { return m_vMutex != NULL; } + + wxMutexError Lock() { return LockTimeout(SEM_INDEFINITE_WAIT); } + wxMutexError TryLock() { return LockTimeout(SEM_IMMEDIATE_RETURN); } + wxMutexError Unlock(); + +private: + wxMutexError LockTimeout(ULONG ulMilliseconds); + HMTX m_vMutex; }; -wxMutex::wxMutex() +// all mutexes are "pseudo-"recursive under OS2 so we don't use mutexType +// (Calls to DosRequestMutexSem and DosReleaseMutexSem can be nested, but +// the request count for a semaphore cannot exceed 65535. If an attempt is +// made to exceed this number, ERROR_TOO_MANY_SEM_REQUESTS is returned.) +wxMutexInternal::wxMutexInternal( + wxMutexType WXUNUSED(eMutexType) +) { - p_internal = new wxMutexInternal; -// p_internal->p_mutex = CreateMutex(NULL, FALSE, NULL); - if ( !p_internal->p_mutex ) + APIRET ulrc; + + ulrc = ::DosCreateMutexSem(NULL, &m_vMutex, 0L, FALSE); + if (ulrc != 0) { wxLogSysError(_("Can not create mutex.")); + m_vMutex = NULL; } - - m_locked = 0; } -wxMutex::~wxMutex() +wxMutexInternal::~wxMutexInternal() { - if (m_locked > 0) - wxLogDebug(wxT("Warning: freeing a locked mutex (%d locks)."), m_locked); -// CloseHandle(p_internal->p_mutex); + if (m_vMutex) + { + if (::DosCloseMutexSem(m_vMutex)) + wxLogLastError(_T("DosCloseMutexSem(mutex)")); + } } -wxMutexError wxMutex::Lock() +wxMutexError wxMutexInternal::LockTimeout(ULONG ulMilliseconds) { - DWORD ret; + APIRET ulrc; -// TODO: -/* - ret = WaitForSingleObject(p_internal->p_mutex, INFINITE); - switch ( ret ) + ulrc = ::DosRequestMutexSem(m_vMutex, ulMilliseconds); + + switch (ulrc) { - case WAIT_ABANDONED: + case ERROR_TIMEOUT: + case ERROR_TOO_MANY_SEM_REQUESTS: return wxMUTEX_BUSY; - case WAIT_OBJECT_0: + case NO_ERROR: // ok break; - case WAIT_FAILED: + case ERROR_INVALID_HANDLE: + case ERROR_INTERRUPT: + case ERROR_SEM_OWNER_DIED: 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_locked++; -*/ - return wxMUTEX_NO_ERROR; -} - -wxMutexError wxMutex::TryLock() -{ - DWORD ret; - -// TODO: -/* - ret = WaitForSingleObject(p_internal->p_mutex, 0); - if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED) - return wxMUTEX_BUSY; - - m_locked++; -*/ + return wxMUTEX_MISC_ERROR; + } return wxMUTEX_NO_ERROR; } -wxMutexError wxMutex::Unlock() +wxMutexError wxMutexInternal::Unlock() { - if (m_locked > 0) - m_locked--; + APIRET ulrc; - BOOL ret = 0; // TODO: ReleaseMutex(p_internal->p_mutex); - if ( ret == 0 ) + ulrc = ::DosReleaseMutexSem(m_vMutex); + if (ulrc != 0) { wxLogSysError(_("Couldn't release a mutex")); return wxMUTEX_MISC_ERROR; } - return wxMUTEX_NO_ERROR; } -// ---------------------------------------------------------------------------- -// wxCondition implementation -// ---------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +// wxSemaphore +// -------------------------------------------------------------------------- -class wxConditionInternal +// a trivial wrapper around OS2 event semaphore +class wxSemaphoreInternal { public: - HANDLE event; - int waiters; -}; + wxSemaphoreInternal(int initialcount, int maxcount); + ~wxSemaphoreInternal(); -wxCondition::wxCondition() -{ - p_internal = new wxConditionInternal; -// TODO: -/* - p_internal->event = CreateEvent(NULL, FALSE, FALSE, NULL); - if ( !p_internal->event ) - { - wxLogSysError(_("Can not create event object.")); - } -*/ - p_internal->waiters = 0; -} + bool IsOk() const { return m_vEvent != NULL; } -wxCondition::~wxCondition() -{ -// CloseHandle(p_internal->event); -} + wxSemaError Wait() { return WaitTimeout(SEM_INDEFINITE_WAIT); } + wxSemaError TryWait() { return WaitTimeout(SEM_IMMEDIATE_RETURN); } + wxSemaError WaitTimeout(unsigned long milliseconds); -void wxCondition::Wait(wxMutex& mutex) -{ - mutex.Unlock(); - p_internal->waiters++; -// WaitForSingleObject(p_internal->event, INFINITE); - p_internal->waiters--; - mutex.Lock(); -} + wxSemaError Post(); -bool wxCondition::Wait(wxMutex& mutex, - 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 (ret != WAIT_TIMEOUT); - return FALSE; -} +private: + HEV m_vEvent; + HMTX m_vMutex; + int m_count; + int m_maxcount; +}; -void wxCondition::Signal() +wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount) { -// SetEvent(p_internal->event); + APIRET ulrc; + if ( maxcount == 0 ) + { + // make it practically infinite + maxcount = INT_MAX; + } + + m_count = initialcount; + m_maxcount = maxcount; + ulrc = ::DosCreateMutexSem(NULL, &m_vMutex, 0L, FALSE); + if (ulrc != 0) + { + wxLogLastError(_T("DosCreateMutexSem()")); + m_vMutex = NULL; + m_vEvent = NULL; + return; + } + ulrc = ::DosCreateEventSem(NULL, &m_vEvent, 0L, FALSE); + if ( ulrc != 0) + { + wxLogLastError(_T("DosCreateEventSem()")); + ::DosCloseMutexSem(m_vMutex); + m_vMutex = NULL; + m_vEvent = NULL; + } + if (initialcount) + ::DosPostEventSem(m_vEvent); } -void wxCondition::Broadcast() +wxSemaphoreInternal::~wxSemaphoreInternal() { - int i; - -// TODO: -/* - for (i=0;iwaiters;i++) + if ( m_vEvent ) { - if ( SetEvent(p_internal->event) == 0 ) + if ( ::DosCloseEventSem(m_vEvent) ) { - wxLogSysError(_("Couldn't change the state of event object.")); + wxLogLastError(_T("DosCloseEventSem(semaphore)")); } + if ( ::DosCloseMutexSem(m_vMutex) ) + { + wxLogLastError(_T("DosCloseMutexSem(semaphore)")); + } + else + m_vEvent = NULL; } -*/ } -// ---------------------------------------------------------------------------- -// wxCriticalSection implementation -// ---------------------------------------------------------------------------- -#define CRITICAL_SECTION ULONG -class wxCriticalSectionInternal +wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long ulMilliseconds) { -public: - // init the critical section object - wxCriticalSectionInternal() - { //::InitializeCriticalSection(&m_data); + APIRET ulrc; + do { + ulrc = ::DosWaitEventSem(m_vEvent, ulMilliseconds ); + switch ( ulrc ) + { + case NO_ERROR: + break; + + case ERROR_TIMEOUT: + if (ulMilliseconds == SEM_IMMEDIATE_RETURN) + return wxSEMA_BUSY; + else + return wxSEMA_TIMEOUT; + + default: + wxLogLastError(_T("DosWaitEventSem(semaphore)")); + return wxSEMA_MISC_ERROR; } - - // implicit cast to the associated data - operator CRITICAL_SECTION *() { return &m_data; } - - // free the associated ressources - ~wxCriticalSectionInternal() - { //::DeleteCriticalSection(&m_data); + ulrc = :: DosRequestMutexSem(m_vMutex, ulMilliseconds); + switch ( ulrc ) + { + case NO_ERROR: + // ok + break; + + case ERROR_TIMEOUT: + case ERROR_TOO_MANY_SEM_REQUESTS: + if (ulMilliseconds == SEM_IMMEDIATE_RETURN) + return wxSEMA_BUSY; + else + return wxSEMA_TIMEOUT; + + default: + wxFAIL_MSG(wxT("DosRequestMutexSem(mutex)")); + return wxSEMA_MISC_ERROR; } - -private: - CRITICAL_SECTION m_data; -}; - -wxCriticalSection::wxCriticalSection() -{ - m_critsect = new wxCriticalSectionInternal; + bool OK = false; + if (m_count > 0) + { + m_count--; + OK = true; + } + else + { + ULONG ulPostCount; + ::DosResetEventSem(m_vEvent, &ulPostCount); + } + ::DosReleaseMutexSem(m_vMutex); + if (OK) + return wxSEMA_NO_ERROR; + } while (ulMilliseconds == SEM_INDEFINITE_WAIT); + + if (ulMilliseconds == SEM_IMMEDIATE_RETURN) + return wxSEMA_BUSY; + return wxSEMA_TIMEOUT; } -wxCriticalSection::~wxCriticalSection() +wxSemaError wxSemaphoreInternal::Post() { - delete m_critsect; -} + APIRET ulrc; + ulrc = ::DosRequestMutexSem(m_vMutex, SEM_INDEFINITE_WAIT); + if (ulrc != NO_ERROR) + return wxSEMA_MISC_ERROR; + bool OK = false; + if (m_count < m_maxcount) + { + m_count++; + ulrc = ::DosPostEventSem(m_vEvent); + OK = true; + } + ::DosReleaseMutexSem(m_vMutex); + if (!OK) + return wxSEMA_OVERFLOW; + if ( ulrc != NO_ERROR && ulrc != ERROR_ALREADY_POSTED ) + { + wxLogLastError(_T("DosPostEventSem(semaphore)")); -void wxCriticalSection::Enter() -{ -//TODO: ::EnterCriticalSection(*m_critsect); -} + return wxSEMA_MISC_ERROR; + } -void wxCriticalSection::Leave() -{ -// TODO: ::LeaveCriticalSection(*m_critsect); + return wxSEMA_NO_ERROR; } // ---------------------------------------------------------------------------- @@ -287,144 +343,196 @@ void wxCriticalSection::Leave() class wxThreadInternal { public: - wxThreadInternal() + inline wxThreadInternal() + { + m_hThread = 0; + m_eState = STATE_NEW; + m_nPriority = WXTHREAD_DEFAULT_PRIORITY; + } + + ~wxThreadInternal() { m_hThread = 0; - m_state = STATE_NEW; - m_priority = 0; // TODO: WXTHREAD_DEFAULT_PRIORITY; } // create a new (suspended) thread (for the given thread object) - bool Create(wxThread *thread); + bool Create( wxThread* pThread + ,unsigned int uStackSize + ); // suspend/resume/terminate bool Suspend(); bool Resume(); - void Cancel() { m_state = STATE_CANCELED; } + inline void Cancel() { m_eState = STATE_CANCELED; } // thread state - void SetState(wxThreadState state) { m_state = state; } - wxThreadState GetState() const { return m_state; } + inline void SetState(wxThreadState eState) { m_eState = eState; } + inline wxThreadState GetState() const { return m_eState; } // thread priority - void SetPriority(unsigned int priority) { m_priority = priority; } - unsigned int GetPriority() const { return m_priority; } + void SetPriority(unsigned int nPriority); + inline unsigned int GetPriority() const { return m_nPriority; } // thread handle and id - HANDLE GetHandle() const { return m_hThread; } - DWORD GetId() const { return m_tid; } + inline TID GetHandle() const { return m_hThread; } + TID GetId() const { return m_hThread; } // thread function - static DWORD OS2ThreadStart(wxThread *thread); + static void OS2ThreadStart(void* pParam); private: - HANDLE m_hThread; // handle of the thread - wxThreadState m_state; // state, see wxThreadState enum - unsigned int m_priority; // thread priority in "wx" units - DWORD m_tid; // thread id + // Threads in OS/2 have only an ID, so m_hThread is both it's handle and ID + // PM also has no real Tls mechanism to index pointers by so we'll just + // keep track of the wxWindows parent object here. + TID m_hThread; // handle and ID of the thread + wxThreadState m_eState; // state, see wxThreadState enum + unsigned int m_nPriority; // thread priority in "wx" units }; -DWORD wxThreadInternal::OS2ThreadStart(wxThread *thread) +void wxThreadInternal::OS2ThreadStart( + void * pParam +) { - // store the thread object in the TLS -// TODO: -/* - if ( !::TlsSetValue(s_tlsThisThread, thread) ) - { - wxLogSysError(_("Can not start thread: error writing TLS.")); + DWORD dwRet; + bool bWasCancelled; - return (DWORD)-1; + wxThread *pThread = (wxThread *)pParam; + + // first of all, wait for the thread to be started. + pThread->m_critsect.Enter(); + pThread->m_critsect.Leave(); + // Now check whether we hadn't been cancelled already and don't + // start the user code at all in this case. + if ( pThread->m_internal->GetState() == STATE_EXITED ) + { + dwRet = (DWORD)-1; + bWasCancelled = TRUE; } -*/ - DWORD ret = (DWORD)thread->Entry(); - thread->p_internal->SetState(STATE_EXITED); - thread->OnExit(); + else // do run thread + { + wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; + unsigned long ulHab; + if (traits) + traits->InitializeGui(ulHab); + dwRet = (DWORD)pThread->Entry(); + if (traits) + traits->TerminateGui(ulHab); - delete thread; + // enter m_critsect before changing the thread state + pThread->m_critsect.Enter(); - return ret; -} + bWasCancelled = pThread->m_internal->GetState() == STATE_CANCELED; -bool wxThreadInternal::Create(wxThread *thread) -{ -// TODO: -/* - m_hThread = ::CreateThread - ( - NULL, // default security - 0, // default stack size - (LPTHREAD_START_ROUTINE) // thread entry point - wxThreadInternal::OS2ThreadStart, // - (LPVOID)thread, // parameter - CREATE_SUSPENDED, // flags - &m_tid // [out] thread id - ); + pThread->m_internal->SetState(STATE_EXITED); + pThread->m_critsect.Leave(); + } + pThread->OnExit(); - if ( m_hThread == NULL ) + // if the thread was cancelled (from Delete()), then it the handle is still + // needed there + if (pThread->IsDetached() && !bWasCancelled) { - wxLogSysError(_("Can't create thread")); - - return FALSE; + // auto delete + delete pThread; } + //else: the joinable threads handle will be closed when Wait() is done + return; +} - // 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; +void wxThreadInternal::SetPriority( + unsigned int nPriority +) +{ + // translate wxWindows priority to the PM one + ULONG ulOS2_PriorityClass; + ULONG ulOS2_SubPriority; + ULONG ulrc; + + m_nPriority = nPriority; + if (m_nPriority <= 25) + ulOS2_PriorityClass = PRTYC_IDLETIME; + else if (m_nPriority <= 50) + ulOS2_PriorityClass = PRTYC_REGULAR; + else if (m_nPriority <= 75) + ulOS2_PriorityClass = PRTYC_TIMECRITICAL; + else if (m_nPriority <= 100) + ulOS2_PriorityClass = PRTYC_FOREGROUNDSERVER; else { wxFAIL_MSG(wxT("invalid value of thread priority parameter")); - win_priority = THREAD_PRIORITY_NORMAL; + ulOS2_PriorityClass = PRTYC_REGULAR; } - - if ( ::SetThreadPriority(m_hThread, win_priority) == 0 ) + ulOS2_SubPriority = (ULONG) (((m_nPriority - 1) % 25 + 1) * 31.0 / 25); + ulrc = ::DosSetPriority( PRTYS_THREAD + ,ulOS2_PriorityClass + ,ulOS2_SubPriority + ,(ULONG)m_hThread + ); + if (ulrc != 0) { wxLogSysError(_("Can't set thread priority")); } -*/ - return FALSE; } -bool wxThreadInternal::Suspend() +bool wxThreadInternal::Create( + wxThread* pThread +, unsigned int uStackSize +) { -// TODO: -/* - DWORD nSuspendCount = ::SuspendThread(m_hThread); - if ( nSuspendCount == (DWORD)-1 ) + int tid; + + if (!uStackSize) + uStackSize = 131072; + pThread->m_critsect.Enter(); + tid = _beginthread(wxThreadInternal::OS2ThreadStart, + NULL, uStackSize, pThread); + if(tid == -1) { - wxLogSysError(_("Can not suspend thread %x"), m_hThread); + wxLogSysError(_("Can't create thread")); return FALSE; } + m_hThread = tid; + if (m_nPriority != WXTHREAD_DEFAULT_PRIORITY) + { + SetPriority(m_nPriority); + } - m_state = STATE_PAUSED; -*/ - return FALSE; + return(TRUE); } -bool wxThreadInternal::Resume() +bool wxThreadInternal::Suspend() { -// TODO: -/* - DWORD nSuspendCount = ::ResumeThread(m_hThread); - if ( nSuspendCount == (DWORD)-1 ) + ULONG ulrc = ::DosSuspendThread(m_hThread); + + if (ulrc != 0) { - wxLogSysError(_("Can not resume thread %x"), m_hThread); + wxLogSysError(_("Can not suspend thread %lu"), m_hThread); + return FALSE; + } + m_eState = STATE_PAUSED; + return TRUE; +} + +bool wxThreadInternal::Resume() +{ + ULONG ulrc = ::DosResumeThread(m_hThread); + if (ulrc != 0) + { + wxLogSysError(_("Can not resume thread %lu"), m_hThread); return FALSE; } - m_state = STATE_RUNNING; -*/ - return FALSE; + // don't change the state from STATE_EXITED because it's special and means + // we are going to terminate without running any user code - if we did it, + // the codei n Delete() wouldn't work + if ( m_eState != STATE_EXITED ) + { + m_eState = STATE_RUNNING; + } + + return TRUE; } // static functions @@ -432,24 +540,19 @@ bool wxThreadInternal::Resume() wxThread *wxThread::This() { - wxThread *thread = NULL; // TODO (wxThread *)::TlsGetValue(s_tlsThisThread); - -// TODO: -/* - // be careful, 0 may be a valid return value as well - if ( !thread && (::GetLastError() != NO_ERROR) ) - { - wxLogSysError(_("Couldn't get the current thread pointer")); - - // return NULL... - } -*/ - return thread; + wxThread* pThread = m_pThread; + return pThread; } bool wxThread::IsMain() { -// TODO: return ::GetCurrentThreadId() == s_idMainThread; + PTIB ptib; + PPIB ppib; + + ::DosGetInfoBlocks(&ptib, &ppib); + + if (ptib->tib_ptib2->tib2_ultid == s_ulIdMainThread) + return TRUE; return FALSE; } @@ -459,21 +562,75 @@ bool wxThread::IsMain() void wxThread::Yield() { - // 0 argument to Sleep() is special ::DosSleep(0); } -void wxThread::Sleep(unsigned long milliseconds) +void wxThread::Sleep( + unsigned long ulMilliseconds +) +{ + ::DosSleep(ulMilliseconds); +} + +int wxThread::GetCPUCount() +{ + ULONG CPUCount; + APIRET ulrc; + ulrc = ::DosQuerySysInfo(26, 26, (void *)&CPUCount, sizeof(ULONG)); + // QSV_NUMPROCESSORS(26) is typically not defined in header files + + if (ulrc != 0) + CPUCount = 1; + + return CPUCount; +} + +unsigned long wxThread::GetCurrentId() +{ + PTIB ptib; + PPIB ppib; + + ::DosGetInfoBlocks(&ptib, &ppib); + return (unsigned long) ptib->tib_ptib2->tib2_ultid; +} + +bool wxThread::SetConcurrency(size_t level) { - ::DosSleep(milliseconds); + wxASSERT_MSG( IsMain(), _T("should only be called from the main thread") ); + + // ok only for the default one + if ( level == 0 ) + return 0; + + // Don't know how to realize this on OS/2. + return level == 1; +} + +// ctor and dtor +// ------------- + +wxThread::wxThread(wxThreadKind kind) +{ + m_internal = new wxThreadInternal(); + + m_isDetached = kind == wxTHREAD_DETACHED; +} + +wxThread::~wxThread() +{ + delete m_internal; } // create/start thread // ------------------- -wxThreadError wxThread::Create() +wxThreadError wxThread::Create( + unsigned int uStackSize +) { - if ( !p_internal->Create(this) ) + wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); + + if ( !m_internal->Create(this, uStackSize) ) return wxTHREAD_NO_RESOURCE; return wxTHREAD_NO_ERROR; @@ -481,14 +638,13 @@ wxThreadError wxThread::Create() wxThreadError wxThread::Run() { - wxCriticalSectionLocker lock(m_critsect); + wxCriticalSectionLocker lock((wxCriticalSection &)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; } - return Resume(); } @@ -497,215 +653,270 @@ wxThreadError wxThread::Run() wxThreadError wxThread::Pause() { - wxCriticalSectionLocker lock(m_critsect); + wxCriticalSectionLocker lock((wxCriticalSection &)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); + if (m_internal->GetState() == STATE_NEW) + { + m_internal->SetState(STATE_RUNNING); + m_critsect.Leave(); + return wxTHREAD_NO_ERROR; + } + + wxCriticalSectionLocker lock((wxCriticalSection &)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 Windows 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); + return(rc); +} + +wxThreadError wxThread::Delete(ExitCode *pRc) { ExitCode rc = 0; // Delete() is always safe to call, so consider all possible states - if ( IsPaused() ) + + // we might need to resume the thread, but we might also not need to cancel + // it if it doesn't run yet + bool shouldResume = FALSE, + shouldCancel = TRUE, + isRunning = FALSE; + + // check if the thread already started to run + { + wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); + + if ( m_internal->GetState() == STATE_NEW ) + { + // WinThreadStart() will see it and terminate immediately, no need + // to cancel the thread - but we still need to resume it to let it + // run + m_internal->SetState(STATE_EXITED); + + Resume(); // it knows about STATE_EXITED special case + + shouldCancel = FALSE; + isRunning = TRUE; + + // shouldResume is correctly set to FALSE here + } + else + { + shouldResume = IsPaused(); + } + } + + // resume the thread if it is paused + if ( shouldResume ) Resume(); - if ( IsRunning() ) + TID hThread = m_internal->GetHandle(); + + if ( isRunning || IsRunning()) { - if ( IsMain() ) + if (IsMain()) { // set flag for wxIsWaitingForThread() - s_waitingForThread = TRUE; - - wxBeginBusyCursor(); + gs_bWaitingForThread = TRUE; } - HANDLE hThread; + // ask the thread to terminate + if ( shouldCancel ) { wxCriticalSectionLocker lock(m_critsect); - p_internal->Cancel(); - hThread = p_internal->GetHandle(); + m_internal->Cancel(); } +#if 0 // 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; -// TODO: -/* + DWORD result = 0; // suppress warnings from broken compilers 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 - ); + if ( IsMain() ) + { + // give the thread we're waiting for chance to do the GUI call + // it might be in + if ( (gs_nWaitingForGui > 0) && wxGuiOwnedByMainThread() ) + { + wxMutexGuiLeave(); + } + } + + result = ::DosWaitThread(&hThread, DCWW_NOWAIT); + // FIXME: We ought to have a message processing loop here!! 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 + case ERROR_INTERRUPT: + case ERROR_THREAD_NOT_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(); - } - } - + case ERROR_INVALID_THREADID: + case NO_ERROR: + // thread we're waiting for just terminated + // or even does not exist any more. + result = NO_ERROR; break; - default: - wxFAIL_MSG(wxT("unexpected result of MsgWaitForMultipleObject")); + wxFAIL_MSG(wxT("unexpected result of DosWaitThread")); } - } while ( result != WAIT_OBJECT_0 ); -*/ - if ( IsMain() ) + if ( IsMain() ) + { + // event processing - needed if we are the main thread + // to give other threads a chance to do remaining GUI + // processing and terminate cleanly. + wxTheApp->HandleSockets(); + if (wxTheApp->Pending()) + if ( !wxTheApp->DoMessage() ) + { + // WM_QUIT received: kill the thread + Kill(); + + return wxTHREAD_KILLED; + } + else + wxUsleep(10); + } + else + wxUsleep(10); + } while ( result != NO_ERROR ); +#else // !wxUSE_GUI + // simply wait for the thread to terminate + // + // OTOH, even console apps create windows (in wxExecute, for WinSock + // &c), so may be use MsgWaitForMultipleObject() too here? + if ( ::DosWaitThread(&hThread, DCWW_WAIT) != NO_ERROR ) { - s_waitingForThread = FALSE; + wxFAIL_MSG(wxT("unexpected result of DosWaitThread")); + } +#endif // wxUSE_GUI/!wxUSE_GUI - wxEndBusyCursor(); + if ( IsMain() ) + { + gs_bWaitingForThread = FALSE; } + } -// TODO: -/* +#if 0 + // although the thread might be already in the EXITED state it might not + // have terminated yet and so we are not sure that it has actually + // terminated if the "if" above hadn't been taken + do + { if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) ) { - wxLogLastError("GetExitCodeThread"); + wxLogLastError(wxT("GetExitCodeThread")); rc = (ExitCode)-1; } + } while ( (DWORD)rc == STILL_ACTIVE ); +#endif - 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 thread 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() { - if ( !IsRunning() ) + if (!IsRunning()) return wxTHREAD_NOT_RUNNING; -// TODO: -/* - if ( !::TerminateThread(p_internal->GetHandle(), (DWORD)-1) ) + ::DosKillThread(m_internal->GetHandle()); + if (IsDetached()) { - wxLogSysError(_("Couldn't terminate thread")); - - return wxTHREAD_MISC_ERROR; + delete this; } -*/ - delete this; - return wxTHREAD_NO_ERROR; } -void wxThread::Exit(void *status) +void wxThread::Exit( + ExitCode pStatus +) { delete this; - -// TODO: ::ExitThread((DWORD)status); - - wxFAIL_MSG(wxT("Couldn't return from ExitThread()!")); + _endthread(); + wxFAIL_MSG(wxT("Couldn't return from DosExit()!")); } -void wxThread::SetPriority(unsigned int prio) +void wxThread::SetPriority( + unsigned int nPrio +) { - wxCriticalSectionLocker lock(m_critsect); + wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); - p_internal->SetPriority(prio); + m_internal->SetPriority(nPrio); } unsigned int wxThread::GetPriority() const { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); + 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); + wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast - return (unsigned long)p_internal->GetId(); + return (unsigned long)m_internal->GetId(); } bool wxThread::IsRunning() const { - wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); + 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); + 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); + 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; -} + wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); -wxThread::wxThread() -{ - p_internal = new wxThreadInternal(); -} - -wxThread::~wxThread() -{ - delete p_internal; + return m_internal->GetState() == STATE_CANCELED; } // ---------------------------------------------------------------------------- @@ -726,73 +937,52 @@ IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule) bool wxThreadModule::OnInit() { - // allocate TLS index for storing the pointer to the current thread -// TODO: -/* - 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 - -// TODO: -/* - 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")); + gs_pCritsectWaitingForGui = new wxCriticalSection(); - return FALSE; - } -*/ - s_critsectWaitingForGui = new wxCriticalSection(); + gs_pCritsectGui = new wxCriticalSection(); + gs_pCritsectGui->Enter(); - s_critsectGui = new wxCriticalSection(); - s_critsectGui->Enter(); + PTIB ptib; + PPIB ppib; - // no error return for GetCurrentThreadId() -// s_idMainThread = ::GetCurrentThreadId(); + ::DosGetInfoBlocks(&ptib, &ppib); + s_ulIdMainThread = ptib->tib_ptib2->tib2_ultid; return TRUE; } void wxThreadModule::OnExit() { -// TODO: -/* - if ( !::TlsFree(s_tlsThisThread) ) - { - wxLogLastError("TlsFree failed."); - } -*/ - if ( s_critsectGui ) + if (gs_pCritsectGui) { - s_critsectGui->Leave(); - delete s_critsectGui; - s_critsectGui = NULL; + gs_pCritsectGui->Leave(); +#if (!(defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 ))) + delete gs_pCritsectGui; +#endif + gs_pCritsectGui = NULL; } - wxDELETE(s_critsectWaitingForGui); +#if (!(defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 ))) + wxDELETE(gs_pCritsectWaitingForGui); +#endif } // ---------------------------------------------------------------------------- -// under Windows, these functions are implemented usign a critical section and -// not a mutex, so the names are a bit confusing +// Helper functions // ---------------------------------------------------------------------------- +// wake up the main thread if it's in ::GetMessage() +void WXDLLEXPORT wxWakeUpMainThread() +{ +#if 0 + if ( !::WinPostQueueMsg(wxTheApp->m_hMq, WM_NULL, 0, 0) ) + { + // should never happen + wxLogLastError(wxT("WinPostMessage(WM_NULL)")); + } +#endif +} + void WXDLLEXPORT wxMutexGuiEnter() { // this would dead lock everything... @@ -803,38 +993,38 @@ void WXDLLEXPORT wxMutexGuiEnter() // set the flag telling to the main thread that we want to do some GUI { - wxCriticalSectionLocker enter(*s_critsectWaitingForGui); + wxCriticalSectionLocker enter(*gs_pCritsectWaitingForGui); - s_nWaitingForGui++; + gs_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(); + gs_pCritsectGui->Enter(); } void WXDLLEXPORT wxMutexGuiLeave() { - wxCriticalSectionLocker enter(*s_critsectWaitingForGui); + wxCriticalSectionLocker enter(*gs_pCritsectWaitingForGui); if ( wxThread::IsMain() ) { - s_bGuiOwnedByMainThread = FALSE; + gs_bGuiOwnedByMainThread = FALSE; } else { // decrement the number of waiters now - wxASSERT_MSG( s_nWaitingForGui > 0, + wxASSERT_MSG(gs_nWaitingForGui > 0, wxT("calling wxMutexGuiLeave() without entering it first?") ); - s_nWaitingForGui--; + gs_nWaitingForGui--; wxWakeUpMainThread(); } - s_critsectGui->Leave(); + gs_pCritsectGui->Leave(); } void WXDLLEXPORT wxMutexGuiLeaveOrEnter() @@ -842,24 +1032,24 @@ void WXDLLEXPORT wxMutexGuiLeaveOrEnter() wxASSERT_MSG( wxThread::IsMain(), wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") ); - wxCriticalSectionLocker enter(*s_critsectWaitingForGui); + wxCriticalSectionLocker enter(*gs_pCritsectWaitingForGui); - if ( s_nWaitingForGui == 0 ) + if (gs_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() ) + if (!wxGuiOwnedByMainThread()) { - s_critsectGui->Enter(); + gs_pCritsectGui->Enter(); - s_bGuiOwnedByMainThread = TRUE; + gs_bGuiOwnedByMainThread = TRUE; } //else: already have it, nothing to do } else { // some threads are waiting, release the GUI lock if we have it - if ( wxGuiOwnedByMainThread() ) + if (wxGuiOwnedByMainThread()) { wxMutexGuiLeave(); } @@ -869,28 +1059,19 @@ void WXDLLEXPORT wxMutexGuiLeaveOrEnter() bool WXDLLEXPORT wxGuiOwnedByMainThread() { - return s_bGuiOwnedByMainThread; -} - -// wake up the main thread if it's in ::GetMessage() -void WXDLLEXPORT wxWakeUpMainThread() -{ - // sending any message would do - hopefully WM_NULL is harmless enough -// TODO: -/* - if ( !::PostThreadMessage(s_idMainThread, WM_NULL, 0, 0) ) - { - // should never happen - wxLogLastError("PostThreadMessage(WM_NULL)"); - } -*/ + return gs_bGuiOwnedByMainThread; } bool WXDLLEXPORT wxIsWaitingForThread() { - return s_waitingForThread; + return gs_bWaitingForThread; } +// ---------------------------------------------------------------------------- +// include common implementation code +// ---------------------------------------------------------------------------- + +#include "wx/thrimpl.cpp" #endif // wxUSE_THREADS