Files
wxWidgets/src/mac/thread.cpp
Stefan Csomor 427ff66291 wxMac Unicode support
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@19841 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2003-03-27 20:33:32 +00:00

905 lines
21 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: thread.cpp
// Purpose: wxThread Implementation
// Author: Original from Wolfram Gloger/Guilhem Lavaux/Vadim Zeitlin
// Modified by: Stefan Csomor
// Created: 04/22/98
// RCS-ID: $Id$
// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998),
// Vadim Zeitlin (1999) , Stefan Csomor (2000)
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#ifdef __GNUG__
#pragma implementation "thread.h"
#endif
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if defined(__BORLANDC__)
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#if wxUSE_THREADS
#include "wx/module.h"
#include "wx/thread.h"
#ifdef __WXMAC__
#include <Threads.h>
#include "wx/mac/uma.h"
#endif
#define INFINITE 0xFFFFFFFF
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
// the possible states of the thread ("=>" shows all possible transitions from
// this state)
enum wxThreadState
{
STATE_NEW, // didn't start execution yet (=> RUNNING)
STATE_RUNNING, // thread is running (=> PAUSED, CANCELED)
STATE_PAUSED, // thread is temporarily suspended (=> RUNNING)
STATE_CANCELED, // thread should terminate a.s.a.p. (=> EXITED)
STATE_EXITED // thread is terminating
};
// ----------------------------------------------------------------------------
// this module globals
// ----------------------------------------------------------------------------
static ThreadID gs_idMainThread = kNoThreadID ;
static bool gs_waitingForThread = FALSE ;
// ============================================================================
// MacOS implementation of thread classes
// ============================================================================
class wxMacStCritical
{
public :
wxMacStCritical()
{
if ( UMASystemIsInitialized() )
ThreadBeginCritical() ;
}
~wxMacStCritical()
{
if ( UMASystemIsInitialized() )
ThreadEndCritical() ;
}
};
// ----------------------------------------------------------------------------
// wxMutex implementation
// ----------------------------------------------------------------------------
class wxMutexInternal
{
public:
wxMutexInternal(wxMutexType WXUNUSED(mutexType))
{
m_owner = kNoThreadID ;
m_locked = 0;
}
~wxMutexInternal()
{
if ( m_locked > 0 )
{
wxLogDebug(_T("Warning: freeing a locked mutex (%ld locks)."), m_locked);
}
}
bool IsOk() const { return true; }
wxMutexError Lock() ;
wxMutexError TryLock() ;
wxMutexError Unlock();
public:
ThreadID m_owner ;
wxArrayLong m_waiters ;
long m_locked ;
};
wxMutexError wxMutexInternal::Lock()
{
wxMacStCritical critical ;
if ( UMASystemIsInitialized() )
{
OSErr err ;
ThreadID current = kNoThreadID;
err = ::MacGetCurrentThread(&current);
// 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 wxMutexInternal::TryLock()
{
wxMacStCritical critical ;
if ( UMASystemIsInitialized() )
{
ThreadID current = kNoThreadID;
::MacGetCurrentThread(&current);
// if we are not the owner, give an error back
if ( m_owner != kNoThreadID && m_owner != current )
return wxMUTEX_BUSY;
m_owner = current;
}
m_locked++;
return wxMUTEX_NO_ERROR;
}
wxMutexError wxMutexInternal::Unlock()
{
if ( UMASystemIsInitialized() )
{
OSErr err;
err = ::ThreadBeginCritical();
if (m_locked > 0)
m_locked--;
// this mutex is not owned by anybody anmore
m_owner = kNoThreadID;
// now pass on to the first waiting thread
ThreadID firstWaiting = kNoThreadID;
bool found = false;
while (!m_waiters.IsEmpty() && !found)
{
firstWaiting = m_waiters[0];
err = ::SetThreadState(firstWaiting, kReadyThreadState, kNoThreadID);
// in case this was not successful (dead thread), we just loop on and reset the id
found = (err != threadNotFoundErr);
if ( !found )
firstWaiting = kNoThreadID ;
m_waiters.RemoveAt(0) ;
}
// now we have a valid firstWaiting thread, which has been scheduled to run next, just end the
// critical section and invoke the scheduler
err = ::SetThreadStateEndCritical(kCurrentThreadID, kReadyThreadState, firstWaiting);
}
else
{
if (m_locked > 0)
m_locked--;
}
return wxMUTEX_NO_ERROR;
}
// --------------------------------------------------------------------------
// wxSemaphore
// --------------------------------------------------------------------------
// TODO not yet implemented
class wxSemaphoreInternal
{
public:
wxSemaphoreInternal(int initialcount, int maxcount);
~wxSemaphoreInternal();
bool IsOk() const { return true ; }
wxSemaError Wait() { return WaitTimeout(INFINITE); }
wxSemaError TryWait() { return WaitTimeout(0); }
wxSemaError WaitTimeout(unsigned long milliseconds);
wxSemaError Post();
private:
};
wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount)
{
if ( maxcount == 0 )
{
// make it practically infinite
maxcount = INT_MAX;
}
}
wxSemaphoreInternal::~wxSemaphoreInternal()
{
}
wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds)
{
return wxSEMA_MISC_ERROR;
}
wxSemaError wxSemaphoreInternal::Post()
{
return wxSEMA_MISC_ERROR;
}
// ----------------------------------------------------------------------------
// wxCondition implementation
// ----------------------------------------------------------------------------
// TODO this is not yet completed
class wxConditionInternal
{
public:
wxConditionInternal(wxMutex& mutex) : m_mutex(mutex)
{
m_excessSignals = 0 ;
}
~wxConditionInternal()
{
}
bool IsOk() const { return m_mutex.IsOk() ; }
wxCondError Wait()
{
return WaitTimeout(0xFFFFFFFF );
}
wxCondError WaitTimeout(unsigned long msectimeout)
{
wxMacStCritical critical ;
if ( m_excessSignals > 0 )
{
--m_excessSignals ;
return wxCOND_NO_ERROR ;
}
else if ( msectimeout == 0 )
{
return wxCOND_MISC_ERROR ;
}
else
{
}
/*
waiters++;
// FIXME this should be MsgWaitForMultipleObjects() as well probably
DWORD rc = ::WaitForSingleObject(event, timeout);
waiters--;
return rc != WAIT_TIMEOUT;
*/
return wxCOND_NO_ERROR ;
}
wxCondError Signal()
{
wxMacStCritical critical ;
return wxCOND_NO_ERROR;
}
wxCondError Broadcast()
{
wxMacStCritical critical ;
return wxCOND_NO_ERROR;
}
wxArrayLong m_waiters ;
wxInt32 m_excessSignals ;
wxMutex& m_mutex;
};
// ----------------------------------------------------------------------------
// wxCriticalSection implementation
// ----------------------------------------------------------------------------
// it's implemented as a mutex on mac os, so it is defined in the headers
// ----------------------------------------------------------------------------
// wxThread implementation
// ----------------------------------------------------------------------------
// wxThreadInternal class
// ----------------------
class wxThreadInternal
{
public:
wxThreadInternal()
{
m_tid = kNoThreadID ;
m_state = STATE_NEW;
m_priority = WXTHREAD_DEFAULT_PRIORITY;
}
~wxThreadInternal()
{
}
void Free()
{
}
// create a new (suspended) thread (for the given thread object)
bool Create(wxThread *thread, unsigned int stackSize);
// suspend/resume/terminate
bool Suspend();
bool Resume();
void Cancel() { m_state = STATE_CANCELED; }
// thread state
void SetState(wxThreadState state) { m_state = state; }
wxThreadState GetState() const { return m_state; }
// thread priority
void SetPriority(unsigned int priority);
unsigned int GetPriority() const { return m_priority; }
void SetResult( void *res ) { m_result = res ; }
void *GetResult() { return m_result ; }
// thread handle and id
ThreadID GetId() const { return m_tid; }
// thread function
static pascal void* MacThreadStart(wxThread* arg);
private:
wxThreadState m_state; // state, see wxThreadState enum
unsigned int m_priority; // thread priority in "wx" units
ThreadID m_tid; // thread id
void* m_result;
static ThreadEntryUPP s_threadEntry ;
};
static wxArrayPtrVoid s_threads ;
ThreadEntryUPP wxThreadInternal::s_threadEntry = NULL ;
pascal void* wxThreadInternal::MacThreadStart(wxThread *thread)
{
// first of all, check whether we hadn't been cancelled already
if ( thread->m_internal->GetState() == STATE_EXITED )
{
return (void*)-1;
}
void* rc = thread->Entry();
// enter m_critsect before changing the thread state
thread->m_critsect.Enter();
bool wasCancelled = thread->m_internal->GetState() == STATE_CANCELED;
thread->m_internal->SetState(STATE_EXITED);
thread->m_critsect.Leave();
thread->OnExit();
// if the thread was cancelled (from Delete()), then it the handle is still
// needed there
if ( thread->IsDetached() && !wasCancelled )
{
// auto delete
delete thread;
}
//else: the joinable threads handle will be closed when Wait() is done
return rc;
}
void wxThreadInternal::SetPriority(unsigned int priority)
{
// Priorities don't exist on Mac
}
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;
}
if ( m_priority != WXTHREAD_DEFAULT_PRIORITY )
{
SetPriority(m_priority);
}
return TRUE;
}
bool wxThreadInternal::Suspend()
{
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()
{
ThreadID current ;
OSErr err ;
err = MacGetCurrentThread( &current ) ;
wxASSERT( err == noErr ) ;
wxASSERT( current != m_tid ) ;
::ThreadBeginCritical();
if ( m_state != STATE_PAUSED && m_state != STATE_NEW )
{
::ThreadEndCritical() ;
wxLogSysError(_("Can not resume thread %x"), m_tid);
return FALSE;
}
err = ::SetThreadStateEndCritical(m_tid, kReadyThreadState, kNoThreadID);
wxASSERT( err == noErr ) ;
m_state = STATE_RUNNING;
::ThreadEndCritical() ;
::YieldToAnyThread() ;
return TRUE;
}
// static functions
// ----------------
wxThread *wxThread::This()
{
wxMacStCritical critical ;
ThreadID current ;
OSErr err ;
err = MacGetCurrentThread( &current ) ;
for ( size_t i = 0 ; i < s_threads.Count() ; ++i )
{
if ( ( (wxThread*) s_threads[i] )->GetId() == current )
return (wxThread*) s_threads[i] ;
}
wxLogSysError(_("Couldn't get the current thread pointer"));
return NULL;
}
bool wxThread::IsMain()
{
ThreadID current ;
OSErr err ;
err = MacGetCurrentThread( &current ) ;
return current == gs_idMainThread;
}
#ifdef Yield
#undef Yield
#endif
void wxThread::Yield()
{
::YieldToAnyThread() ;
}
void wxThread::Sleep(unsigned long milliseconds)
{
clock_t start = clock();
do
{
YieldToAnyThread();
} while( clock() - start < milliseconds / 1000.0 * CLOCKS_PER_SEC ) ;
}
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( &current ) ;
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)
{
m_internal = new wxThreadInternal();
m_isDetached = kind == wxTHREAD_DETACHED;
s_threads.Add( (void*) this ) ;
}
wxThread::~wxThread()
{
s_threads.Remove( (void*) this ) ;
if (m_internal != NULL) {
delete m_internal;
m_internal = NULL;
}
}
// create/start thread
// -------------------
wxThreadError wxThread::Create(unsigned int stackSize)
{
wxCriticalSectionLocker lock(m_critsect);
if ( !m_internal->Create(this, stackSize) )
return wxTHREAD_NO_RESOURCE;
return wxTHREAD_NO_ERROR;
}
wxThreadError wxThread::Run()
{
wxCriticalSectionLocker lock(m_critsect);
if ( m_internal->GetState() != STATE_NEW )
{
// actually, it may be almost any state at all, not only STATE_RUNNING
return wxTHREAD_RUNNING;
}
// the thread has just been created and is still suspended - let it run
return Resume();
}
// suspend/resume thread
// ---------------------
wxThreadError wxThread::Pause()
{
wxCriticalSectionLocker lock(m_critsect);
return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
}
wxThreadError wxThread::Resume()
{
wxCriticalSectionLocker lock(m_critsect);
return m_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
}
// stopping thread
// ---------------
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
// has the thread started to run?
bool shouldResume = FALSE;
{
wxCriticalSectionLocker lock(m_critsect);
if ( m_internal->GetState() == STATE_NEW )
{
// WinThreadStart() will see it and terminate immediately
m_internal->SetState(STATE_EXITED);
shouldResume = TRUE;
}
}
// is the thread paused?
if ( shouldResume || IsPaused() )
Resume();
// does is still run?
if ( IsRunning() )
{
if ( IsMain() )
{
// set flag for wxIsWaitingForThread()
gs_waitingForThread = TRUE;
#if wxUSE_GUI
wxBeginBusyCursor();
#endif // wxUSE_GUI
}
// ask the thread to terminate
{
wxCriticalSectionLocker lock(m_critsect);
m_internal->Cancel();
}
#if wxUSE_GUI
// simply wait for the thread to terminate
while( TestDestroy() )
{
::YieldToAnyThread() ;
}
#else // !wxUSE_GUI
// simply wait for the thread to terminate
while( TestDestroy() )
{
::YieldToAnyThread() ;
}
#endif // wxUSE_GUI/!wxUSE_GUI
if ( IsMain() )
{
gs_waitingForThread = FALSE;
#if wxUSE_GUI
wxEndBusyCursor();
#endif // wxUSE_GUI
}
}
// if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) )
{
wxLogLastError("GetExitCodeThread");
rc = (ExitCode)-1;
}
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;
}
// 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()
{
if ( !IsRunning() )
return wxTHREAD_NOT_RUNNING;
// if ( !::TerminateThread(m_internal->GetHandle(), (DWORD)-1) )
{
wxLogSysError(_("Couldn't terminate thread"));
return wxTHREAD_MISC_ERROR;
}
m_internal->Free();
if ( IsDetached() )
{
delete this;
}
return wxTHREAD_NO_ERROR;
}
void wxThread::Exit(ExitCode status)
{
m_internal->Free();
if ( IsDetached() )
{
delete this;
}
m_internal->SetResult( status ) ;
/*
#if defined(__VISUALC__) || (defined(__BORLANDC__) && (__BORLANDC__ >= 0x500))
_endthreadex((unsigned)status);
#else // !VC++
::ExitThread((DWORD)status);
#endif // VC++/!VC++
*/
wxFAIL_MSG(wxT("Couldn't return from ExitThread()!"));
}
// priority setting
// ----------------
// since all these calls are execute cooperatively we don't have to use the critical section
void wxThread::SetPriority(unsigned int prio)
{
m_internal->SetPriority(prio);
}
unsigned int wxThread::GetPriority() const
{
return m_internal->GetPriority();
}
unsigned long wxThread::GetId() const
{
return (unsigned long)m_internal->GetId();
}
bool wxThread::IsRunning() const
{
return m_internal->GetState() == STATE_RUNNING;
}
bool wxThread::IsAlive() const
{
return (m_internal->GetState() == STATE_RUNNING) ||
(m_internal->GetState() == STATE_PAUSED);
}
bool wxThread::IsPaused() const
{
return m_internal->GetState() == STATE_PAUSED;
}
bool wxThread::TestDestroy()
{
return m_internal->GetState() == STATE_CANCELED;
}
// ----------------------------------------------------------------------------
// Automatic initialization for thread module
// ----------------------------------------------------------------------------
class wxThreadModule : public wxModule
{
public:
virtual bool OnInit();
virtual void OnExit();
private:
DECLARE_DYNAMIC_CLASS(wxThreadModule)
};
IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
bool wxThreadModule::OnInit()
{
long response;
bool hasThreadManager ;
hasThreadManager = Gestalt( gestaltThreadMgrAttr, &response) == noErr && response & 1;
#if !TARGET_CARBON
#if GENERATINGCFM
// verify presence of shared library
hasThreadManager = hasThreadManager && ((Ptr)NewThread != (Ptr)kUnresolvedCFragSymbolAddress);
#endif
#endif
if ( !hasThreadManager )
{
wxMessageBox( wxT("Error") , wxT("Thread Support is not available on this System") , wxOK ) ;
return FALSE ;
}
// no error return for GetCurrentThreadId()
MacGetCurrentThread( &gs_idMainThread ) ;
return TRUE;
}
void wxThreadModule::OnExit()
{
}
// ----------------------------------------------------------------------------
// under MacOS we don't have currently preemptive threads, so any thread may access
// the GUI at any time
// ----------------------------------------------------------------------------
void WXDLLEXPORT wxMutexGuiEnter()
{
}
void WXDLLEXPORT wxMutexGuiLeave()
{
}
void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
{
}
bool WXDLLEXPORT wxGuiOwnedByMainThread()
{
return false ;
}
// wake up the main thread
void WXDLLEXPORT wxWakeUpMainThread()
{
wxMacWakeUp() ;
}
bool WXDLLEXPORT wxIsWaitingForThread()
{
return false ;
}
#include "wx/thrimpl.cpp"
#endif // wxUSE_THREADS