Files
wxWidgets/src/msw/thread.cpp
Vadim Zeitlin 6af5329710 Fix waiting for threads in MSW in presence of events
We need to handle WAIT_OBJECT_0+2 value which can be returned by
WaitForThread() since 6c40531fb7 as it now waits
for two objects and not just one, failure to do this resulted in an endless
stream of asserts if any Windows messages were pending when we started waiting
for the thread.
2015-11-02 18:17:03 +01:00

1391 lines
37 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/msw/thread.cpp
// Purpose: wxThread Implementation
// Author: Original from Wolfram Gloger/Guilhem Lavaux
// Modified by: Vadim Zeitlin to make it work :-)
// Created: 04/22/98
// Copyright: (c) Wolfram Gloger (1996, 1997), Guilhem Lavaux (1998);
// Vadim Zeitlin (1999-2002)
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if defined(__BORLANDC__)
#pragma hdrstop
#endif
#if wxUSE_THREADS
#include "wx/thread.h"
#ifndef WX_PRECOMP
#include "wx/intl.h"
#include "wx/app.h"
#include "wx/log.h"
#include "wx/module.h"
#include "wx/msgout.h"
#endif
#include "wx/apptrait.h"
#include "wx/scopeguard.h"
#include "wx/msw/private.h"
#include "wx/msw/missing.h"
#include "wx/msw/seh.h"
#include "wx/except.h"
// must have this symbol defined to get _beginthread/_endthread declarations
#ifndef _MT
#define _MT
#endif
#if defined(__BORLANDC__)
#if !defined(__MT__)
// I can't set -tWM in the IDE (anyone?) so have to do this
#define __MT__
#endif
#if !defined(__MFC_COMPAT__)
// Needed to know about _beginthreadex etc..
#define __MFC_COMPAT__
#endif
#endif // BC++
// define wxUSE_BEGIN_THREAD if the compiler has _beginthreadex() function
// which should be used instead of Win32 ::CreateThread() if possible
#if defined(__VISUALC__) || \
(defined(__BORLANDC__) && (__BORLANDC__ >= 0x500)) || \
(defined(__GNUG__) && defined(__MSVCRT__))
#undef wxUSE_BEGIN_THREAD
#define wxUSE_BEGIN_THREAD
#endif
#ifdef wxUSE_BEGIN_THREAD
// this is where _beginthreadex() is declared
#include <process.h>
// the return type of the thread function entry point: notice that this
// type can't hold a pointer under Win64
typedef unsigned THREAD_RETVAL;
// the calling convention of the thread function entry point
#define THREAD_CALLCONV __stdcall
#else
// the settings for CreateThread()
typedef DWORD THREAD_RETVAL;
#define THREAD_CALLCONV WINAPI
#endif
static const THREAD_RETVAL THREAD_ERROR_EXIT = (THREAD_RETVAL)-1;
// ----------------------------------------------------------------------------
// 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
// ----------------------------------------------------------------------------
// TLS index of the slot where we store the pointer to the current thread
static DWORD gs_tlsThisThread = 0xFFFFFFFF;
// id of the main thread - the one which can call GUI functions without first
// calling wxMutexGuiEnter()
wxThreadIdType wxThread::ms_idMainThread = 0;
// if it's false, some secondary thread is holding the GUI lock
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 *gs_critsectGui = NULL;
// critical section which protects gs_nWaitingForGui variable
static wxCriticalSection *gs_critsectWaitingForGui = NULL;
// critical section which serializes WinThreadStart() and WaitForTerminate()
// (this is a potential bottleneck, we use a single crit sect for all threads
// in the system, but normally time spent inside it should be quite short)
static wxCriticalSection *gs_critsectThreadDelete = NULL;
// number of threads waiting for GUI in wxMutexGuiEnter()
static size_t gs_nWaitingForGui = 0;
// are we waiting for a thread termination?
static bool gs_waitingForThread = false;
// ============================================================================
// Windows implementation of thread and related classes
// ============================================================================
// ----------------------------------------------------------------------------
// wxCriticalSection
// ----------------------------------------------------------------------------
wxCriticalSection::wxCriticalSection( wxCriticalSectionType WXUNUSED(critSecType) )
{
wxCOMPILE_TIME_ASSERT( sizeof(CRITICAL_SECTION) <= sizeof(wxCritSectBuffer),
wxCriticalSectionBufferTooSmall );
::InitializeCriticalSection((CRITICAL_SECTION *)m_buffer);
}
wxCriticalSection::~wxCriticalSection()
{
::DeleteCriticalSection((CRITICAL_SECTION *)m_buffer);
}
void wxCriticalSection::Enter()
{
::EnterCriticalSection((CRITICAL_SECTION *)m_buffer);
}
bool wxCriticalSection::TryEnter()
{
return ::TryEnterCriticalSection((CRITICAL_SECTION *)m_buffer) != 0;
}
void wxCriticalSection::Leave()
{
::LeaveCriticalSection((CRITICAL_SECTION *)m_buffer);
}
// ----------------------------------------------------------------------------
// wxMutex
// ----------------------------------------------------------------------------
class wxMutexInternal
{
public:
wxMutexInternal(wxMutexType mutexType);
~wxMutexInternal();
bool IsOk() const { return m_mutex != NULL; }
wxMutexError Lock() { return LockTimeout(INFINITE); }
wxMutexError Lock(unsigned long ms) { return LockTimeout(ms); }
wxMutexError TryLock();
wxMutexError Unlock();
private:
wxMutexError LockTimeout(DWORD milliseconds);
HANDLE m_mutex;
unsigned long m_owningThread;
wxMutexType m_type;
wxDECLARE_NO_COPY_CLASS(wxMutexInternal);
};
// all mutexes are recursive under Win32 so we don't use mutexType
wxMutexInternal::wxMutexInternal(wxMutexType mutexType)
{
// create a nameless (hence intra process and always private) mutex
m_mutex = ::CreateMutex
(
NULL, // default secutiry attributes
FALSE, // not initially locked
NULL // no name
);
m_type = mutexType;
m_owningThread = 0;
if ( !m_mutex )
{
wxLogLastError(wxT("CreateMutex()"));
}
}
wxMutexInternal::~wxMutexInternal()
{
if ( m_mutex )
{
if ( !::CloseHandle(m_mutex) )
{
wxLogLastError(wxT("CloseHandle(mutex)"));
}
}
}
wxMutexError wxMutexInternal::TryLock()
{
const wxMutexError rc = LockTimeout(0);
// we have a special return code for timeout in this case
return rc == wxMUTEX_TIMEOUT ? wxMUTEX_BUSY : rc;
}
wxMutexError wxMutexInternal::LockTimeout(DWORD milliseconds)
{
if (m_type == wxMUTEX_DEFAULT)
{
// Don't allow recursive
if (m_owningThread != 0)
{
if (m_owningThread == wxThread::GetCurrentId())
return wxMUTEX_DEAD_LOCK;
}
}
DWORD rc = ::WaitForSingleObject(m_mutex, milliseconds);
switch ( rc )
{
case WAIT_ABANDONED:
// the previous caller died without releasing the mutex, so even
// though we did get it, log a message about this
wxLogDebug(wxT("WaitForSingleObject() returned WAIT_ABANDONED"));
// fall through
case WAIT_OBJECT_0:
// ok
break;
case WAIT_TIMEOUT:
return wxMUTEX_TIMEOUT;
default:
wxFAIL_MSG(wxT("impossible return value in wxMutex::Lock"));
// fall through
case WAIT_FAILED:
wxLogLastError(wxT("WaitForSingleObject(mutex)"));
return wxMUTEX_MISC_ERROR;
}
if (m_type == wxMUTEX_DEFAULT)
{
// required for checking recursiveness
m_owningThread = wxThread::GetCurrentId();
}
return wxMUTEX_NO_ERROR;
}
wxMutexError wxMutexInternal::Unlock()
{
// required for checking recursiveness
m_owningThread = 0;
if ( !::ReleaseMutex(m_mutex) )
{
wxLogLastError(wxT("ReleaseMutex()"));
return wxMUTEX_MISC_ERROR;
}
return wxMUTEX_NO_ERROR;
}
// --------------------------------------------------------------------------
// wxSemaphore
// --------------------------------------------------------------------------
// a trivial wrapper around Win32 semaphore
class wxSemaphoreInternal
{
public:
wxSemaphoreInternal(int initialcount, int maxcount);
~wxSemaphoreInternal();
bool IsOk() const { return m_semaphore != NULL; }
wxSemaError Wait() { return WaitTimeout(INFINITE); }
wxSemaError TryWait()
{
wxSemaError rc = WaitTimeout(0);
if ( rc == wxSEMA_TIMEOUT )
rc = wxSEMA_BUSY;
return rc;
}
wxSemaError WaitTimeout(unsigned long milliseconds);
wxSemaError Post();
private:
HANDLE m_semaphore;
wxDECLARE_NO_COPY_CLASS(wxSemaphoreInternal);
};
wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount)
{
if ( maxcount == 0 )
{
// make it practically infinite
maxcount = INT_MAX;
}
m_semaphore = ::CreateSemaphore
(
NULL, // default security attributes
initialcount,
maxcount,
NULL // no name
);
if ( !m_semaphore )
{
wxLogLastError(wxT("CreateSemaphore()"));
}
}
wxSemaphoreInternal::~wxSemaphoreInternal()
{
if ( m_semaphore )
{
if ( !::CloseHandle(m_semaphore) )
{
wxLogLastError(wxT("CloseHandle(semaphore)"));
}
}
}
wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds)
{
DWORD rc = ::WaitForSingleObject( m_semaphore, milliseconds );
switch ( rc )
{
case WAIT_OBJECT_0:
return wxSEMA_NO_ERROR;
case WAIT_TIMEOUT:
return wxSEMA_TIMEOUT;
default:
wxLogLastError(wxT("WaitForSingleObject(semaphore)"));
}
return wxSEMA_MISC_ERROR;
}
wxSemaError wxSemaphoreInternal::Post()
{
if ( !::ReleaseSemaphore(m_semaphore, 1, NULL /* ptr to previous count */) )
{
if ( GetLastError() == ERROR_TOO_MANY_POSTS )
{
return wxSEMA_OVERFLOW;
}
else
{
wxLogLastError(wxT("ReleaseSemaphore"));
return wxSEMA_MISC_ERROR;
}
}
return wxSEMA_NO_ERROR;
}
// ----------------------------------------------------------------------------
// wxThread implementation
// ----------------------------------------------------------------------------
// wxThreadInternal class
// ----------------------
class wxThreadInternal
{
public:
wxThreadInternal(wxThread *thread)
{
m_thread = thread;
m_hThread = 0;
m_state = STATE_NEW;
m_priority = wxPRIORITY_DEFAULT;
m_nRef = 1;
}
~wxThreadInternal()
{
Free();
}
void Free()
{
if ( m_hThread )
{
if ( !::CloseHandle(m_hThread) )
{
wxLogLastError(wxT("CloseHandle(thread)"));
}
m_hThread = 0;
}
}
// create a new (suspended) thread (for the given thread object)
bool Create(wxThread *thread, unsigned int stackSize);
// wait for the thread to terminate, either by itself, or by asking it
// (politely, this is not Kill()!) to do it
wxThreadError WaitForTerminate(wxCriticalSection& cs,
wxThread::ExitCode *pRc,
wxThreadWait waitMode,
wxThread *threadToDelete = NULL);
// kill the thread unconditionally
wxThreadError Kill();
// 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; }
// thread handle and id
HANDLE GetHandle() const { return m_hThread; }
DWORD GetId() const { return m_tid; }
// the thread function forwarding to DoThreadStart
static THREAD_RETVAL THREAD_CALLCONV WinThreadStart(void *thread);
// really start the thread (if it's not already dead)
static THREAD_RETVAL DoThreadStart(wxThread *thread);
// call OnExit() on the thread
static void DoThreadOnExit(wxThread *thread);
void KeepAlive()
{
if ( m_thread->IsDetached() )
::InterlockedIncrement(&m_nRef);
}
void LetDie()
{
if ( m_thread->IsDetached() && !::InterlockedDecrement(&m_nRef) )
delete m_thread;
}
private:
// the thread we're associated with
wxThread *m_thread;
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
// number of threads which need this thread to remain alive, when the count
// reaches 0 we kill the owning wxThread -- and die ourselves with it
LONG m_nRef;
wxDECLARE_NO_COPY_CLASS(wxThreadInternal);
};
// small class which keeps a thread alive during its lifetime
class wxThreadKeepAlive
{
public:
wxThreadKeepAlive(wxThreadInternal& thrImpl) : m_thrImpl(thrImpl)
{ m_thrImpl.KeepAlive(); }
~wxThreadKeepAlive()
{ m_thrImpl.LetDie(); }
private:
wxThreadInternal& m_thrImpl;
};
/* static */
void wxThreadInternal::DoThreadOnExit(wxThread *thread)
{
wxTRY
{
thread->OnExit();
}
wxCATCH_ALL( wxTheApp->OnUnhandledException(); )
}
/* static */
THREAD_RETVAL wxThreadInternal::DoThreadStart(wxThread *thread)
{
wxON_BLOCK_EXIT1(DoThreadOnExit, thread);
THREAD_RETVAL rc = THREAD_ERROR_EXIT;
wxTRY
{
// store the thread object in the TLS
if ( !::TlsSetValue(gs_tlsThisThread, thread) )
{
wxLogSysError(_("Cannot start thread: error writing TLS."));
return THREAD_ERROR_EXIT;
}
rc = wxPtrToUInt(thread->CallEntry());
}
wxCATCH_ALL( wxTheApp->OnUnhandledException(); )
return rc;
}
/* static */
THREAD_RETVAL THREAD_CALLCONV wxThreadInternal::WinThreadStart(void *param)
{
THREAD_RETVAL rc = THREAD_ERROR_EXIT;
wxThread * const thread = (wxThread *)param;
// each thread has its own SEH translator so install our own a.s.a.p.
DisableAutomaticSETranslator();
// NB: Notice that we can't use wxCriticalSectionLocker in this function as
// we use SEH and it's incompatible with C++ object dtors.
// first of all, check whether we hadn't been cancelled already and don't
// start the user code at all then
thread->m_critsect.Enter();
const bool hasExited = thread->m_internal->GetState() == STATE_EXITED;
thread->m_critsect.Leave();
// run the thread function itself inside a SEH try/except block
wxSEH_TRY
{
if ( hasExited )
DoThreadOnExit(thread);
else
rc = DoThreadStart(thread);
}
wxSEH_HANDLE(THREAD_ERROR_EXIT)
// save IsDetached because thread object can be deleted by joinable
// threads after state is changed to STATE_EXITED.
const bool isDetached = thread->IsDetached();
if ( !hasExited )
{
thread->m_critsect.Enter();
thread->m_internal->SetState(STATE_EXITED);
thread->m_critsect.Leave();
}
// the thread may delete itself now if it wants, we don't need it any more
if ( isDetached )
thread->m_internal->LetDie();
return rc;
}
void wxThreadInternal::SetPriority(unsigned int priority)
{
m_priority = priority;
// translate wxWidgets 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) )
{
wxLogSysError(_("Can't set thread priority"));
}
}
bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize)
{
wxASSERT_MSG( m_state == STATE_NEW && !m_hThread,
wxT("Create()ing thread twice?") );
// for compilers which have it, we should use C RTL function for thread
// creation instead of Win32 API one because otherwise we will have memory
// leaks if the thread uses C RTL (and most threads do)
#ifdef wxUSE_BEGIN_THREAD
m_hThread = (HANDLE)_beginthreadex
(
NULL, // default security
stackSize,
wxThreadInternal::WinThreadStart, // entry point
thread,
CREATE_SUSPENDED,
(unsigned int *)&m_tid
);
#else // compiler doesn't have _beginthreadex
m_hThread = ::CreateThread
(
NULL, // default security
stackSize, // stack size
wxThreadInternal::WinThreadStart, // thread entry point
(LPVOID)thread, // parameter
CREATE_SUSPENDED, // flags
&m_tid // [out] thread id
);
#endif // _beginthreadex/CreateThread
if ( m_hThread == NULL )
{
wxLogSysError(_("Can't create thread"));
return false;
}
if ( m_priority != wxPRIORITY_DEFAULT )
{
SetPriority(m_priority);
}
return true;
}
wxThreadError wxThreadInternal::Kill()
{
m_thread->OnKill();
if ( !::TerminateThread(m_hThread, THREAD_ERROR_EXIT) )
{
wxLogSysError(_("Couldn't terminate thread"));
return wxTHREAD_MISC_ERROR;
}
Free();
return wxTHREAD_NO_ERROR;
}
wxThreadError
wxThreadInternal::WaitForTerminate(wxCriticalSection& cs,
wxThread::ExitCode *pRc,
wxThreadWait waitMode,
wxThread *threadToDelete)
{
// prevent the thread C++ object from disappearing as long as we are using
// it here
wxThreadKeepAlive keepAlive(*this);
// we may either wait passively for the thread to terminate (when called
// from Wait()) or ask it to terminate (when called from Delete())
bool shouldDelete = threadToDelete != NULL;
DWORD rc = 0;
// we might need to resume the thread if it's currently stopped
bool shouldResume = false;
// as Delete() (which calls us) is always safe to call we need to consider
// all possible states
{
wxCriticalSectionLocker lock(cs);
if ( m_state == STATE_NEW )
{
if ( shouldDelete )
{
// 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_state = STATE_EXITED;
// we must call Resume() as the thread hasn't been initially
// resumed yet (and as Resume() it knows about STATE_EXITED
// special case, it won't touch it and WinThreadStart() will
// just exit immediately)
shouldResume = true;
shouldDelete = false;
}
//else: shouldResume is correctly set to false here, wait until
// someone else runs the thread and it finishes
}
else // running, paused, cancelled or even exited
{
shouldResume = m_state == STATE_PAUSED;
}
}
// resume the thread if it is paused
if ( shouldResume )
Resume();
// ask the thread to terminate
if ( shouldDelete )
{
wxCriticalSectionLocker lock(cs);
Cancel();
}
if ( threadToDelete )
threadToDelete->OnDelete();
// now wait for thread to finish
if ( wxThread::IsMain() )
{
// set flag for wxIsWaitingForThread()
gs_waitingForThread = true;
}
wxAppTraits& traits = wxApp::GetValidTraits();
// 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
// (note that even in console applications we might have to process
// messages if we use wxExecute() or timers or ...)
DWORD result wxDUMMY_INITIALIZE(0);
do
{
if ( wxThread::IsMain() )
{
// give the thread we're waiting for chance to do the GUI call
// it might be in
if ( (gs_nWaitingForGui > 0) && wxGuiOwnedByMainThread() )
{
wxMutexGuiLeave();
}
}
// Wait for the thread while still processing events in the GUI apps or
// just simply wait for it in the console ones.
result = traits.WaitForThread(m_hThread, waitMode);
switch ( result )
{
case 0xFFFFFFFF:
// error
wxLogSysError(_("Cannot wait for thread termination"));
Kill();
return wxTHREAD_KILLED;
case WAIT_OBJECT_0:
// thread we're waiting for terminated
break;
case WAIT_OBJECT_0 + 1:
case WAIT_OBJECT_0 + 2:
// Wake up has been signaled or a new message arrived, process
// it -- but only if we're the main thread as we don't support
// processing messages in the other ones
//
// NB: we still must include QS_ALLINPUT even when waiting
// in a secondary thread because if it had created some
// window somehow (possible not even using wxWidgets)
// the system might dead lock then
if ( wxThread::IsMain() )
{
if ( !traits.DoMessageFromThreadWait() )
{
// WM_QUIT received: kill the thread
Kill();
return wxTHREAD_KILLED;
}
}
break;
default:
wxFAIL_MSG(wxT("unexpected result of MsgWaitForMultipleObject"));
}
} while ( result != WAIT_OBJECT_0 );
if ( wxThread::IsMain() )
{
gs_waitingForThread = false;
}
// 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
for ( ;; )
{
if ( !::GetExitCodeThread(m_hThread, &rc) )
{
wxLogLastError(wxT("GetExitCodeThread"));
rc = THREAD_ERROR_EXIT;
break;
}
if ( rc != STILL_ACTIVE )
break;
// give the other thread some time to terminate, otherwise we may be
// starving it
::Sleep(1);
}
if ( pRc )
*pRc = wxUIntToPtr(rc);
// we don't need the thread handle any more in any case
Free();
return rc == THREAD_ERROR_EXIT ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR;
}
bool wxThreadInternal::Suspend()
{
DWORD nSuspendCount = ::SuspendThread(m_hThread);
if ( nSuspendCount == (DWORD)-1 )
{
wxLogSysError(_("Cannot suspend thread %lx"),
static_cast<unsigned long>(wxPtrToUInt(m_hThread)));
return false;
}
m_state = STATE_PAUSED;
return true;
}
bool wxThreadInternal::Resume()
{
DWORD nSuspendCount = ::ResumeThread(m_hThread);
if ( nSuspendCount == (DWORD)-1 )
{
wxLogSysError(_("Cannot resume thread %lx"),
static_cast<unsigned long>(wxPtrToUInt(m_hThread)));
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 code in WaitForTerminate() wouldn't work
if ( m_state != STATE_EXITED )
{
m_state = STATE_RUNNING;
}
return true;
}
// static functions
// ----------------
wxThread *wxThread::This()
{
wxThread *thread = (wxThread *)::TlsGetValue(gs_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"));
// return NULL...
}
return thread;
}
void wxThread::Yield()
{
// 0 argument to Sleep() is special and means to just give away the rest of
// our timeslice
::Sleep(0);
}
int wxThread::GetCPUCount()
{
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwNumberOfProcessors;
}
unsigned long wxThread::GetCurrentId()
{
return (unsigned long)::GetCurrentThreadId();
}
bool wxThread::SetConcurrency(size_t level)
{
wxASSERT_MSG( IsMain(), wxT("should only be called from the main thread") );
// ok only for the default one
if ( level == 0 )
return 0;
// get system affinity mask first
HANDLE hProcess = ::GetCurrentProcess();
DWORD_PTR dwProcMask, dwSysMask;
if ( ::GetProcessAffinityMask(hProcess, &dwProcMask, &dwSysMask) == 0 )
{
wxLogLastError(wxT("GetProcessAffinityMask"));
return false;
}
// how many CPUs have we got?
if ( dwSysMask == 1 )
{
// don't bother with all this complicated stuff - on a single
// processor system it doesn't make much sense anyhow
return level == 1;
}
// calculate the process mask: it's a bit vector with one bit per
// processor; we want to schedule the process to run on first level
// CPUs
DWORD bit = 1;
while ( bit )
{
if ( dwSysMask & bit )
{
// ok, we can set this bit
dwProcMask |= bit;
// another process added
if ( --level == 0 )
{
// and that's enough
break;
}
}
// next bit
bit <<= 1;
}
// could we set all bits?
if ( level != 0 )
{
wxLogDebug(wxT("bad level %u in wxThread::SetConcurrency()"), level);
return false;
}
if ( ::SetProcessAffinityMask(hProcess, dwProcMask) == 0 )
{
wxLogLastError(wxT("SetProcessAffinityMask"));
return false;
}
return true;
}
// ctor and dtor
// -------------
wxThread::wxThread(wxThreadKind kind)
{
m_internal = new wxThreadInternal(this);
m_isDetached = kind == wxTHREAD_DETACHED;
}
wxThread::~wxThread()
{
delete m_internal;
}
// 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);
// Create the thread if it wasn't created yet with an explicit
// Create() call:
if ( !m_internal->GetHandle() )
{
if ( !m_internal->Create(this, 0) )
return wxTHREAD_NO_RESOURCE;
}
wxCHECK_MSG( m_internal->GetState() == STATE_NEW, wxTHREAD_RUNNING,
wxT("thread may only be started once after Create()") );
// 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(wxThreadWait waitMode)
{
ExitCode rc = wxUIntToPtr(THREAD_ERROR_EXIT);
// 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(), rc,
wxT("wxThread::Wait(): can't wait for detached thread") );
(void)m_internal->WaitForTerminate(m_critsect, &rc, waitMode);
return rc;
}
wxThreadError wxThread::Delete(ExitCode *pRc, wxThreadWait waitMode)
{
return m_internal->WaitForTerminate(m_critsect, pRc, waitMode, this);
}
wxThreadError wxThread::Kill()
{
if ( !IsRunning() )
return wxTHREAD_NOT_RUNNING;
wxThreadError rc = m_internal->Kill();
if ( IsDetached() )
{
delete this;
}
else // joinable
{
// update the status of the joinable thread
wxCriticalSectionLocker lock(m_critsect);
m_internal->SetState(STATE_EXITED);
}
return rc;
}
void wxThread::Exit(ExitCode status)
{
wxThreadInternal::DoThreadOnExit(this);
m_internal->Free();
if ( IsDetached() )
{
delete this;
}
else // joinable
{
// update the status of the joinable thread
wxCriticalSectionLocker lock(m_critsect);
m_internal->SetState(STATE_EXITED);
}
#ifdef wxUSE_BEGIN_THREAD
_endthreadex(wxPtrToUInt(status));
#else // !VC++
::ExitThread(wxPtrToUInt(status));
#endif // VC++/!VC++
wxFAIL_MSG(wxT("Couldn't return from ExitThread()!"));
}
// priority setting
// ----------------
void wxThread::SetPriority(unsigned int prio)
{
wxCriticalSectionLocker lock(m_critsect);
m_internal->SetPriority(prio);
}
unsigned int wxThread::GetPriority() const
{
wxCriticalSectionLocker lock(const_cast<wxCriticalSection &>(m_critsect));
return m_internal->GetPriority();
}
unsigned long wxThread::GetId() const
{
wxCriticalSectionLocker lock(const_cast<wxCriticalSection &>(m_critsect));
return (unsigned long)m_internal->GetId();
}
WXHANDLE wxThread::MSWGetHandle() const
{
wxCriticalSectionLocker lock(const_cast<wxCriticalSection &>(m_critsect));
return m_internal->GetHandle();
}
bool wxThread::IsRunning() const
{
wxCriticalSectionLocker lock(const_cast<wxCriticalSection &>(m_critsect));
return m_internal->GetState() == STATE_RUNNING;
}
bool wxThread::IsAlive() const
{
wxCriticalSectionLocker lock(const_cast<wxCriticalSection &>(m_critsect));
return (m_internal->GetState() == STATE_RUNNING) ||
(m_internal->GetState() == STATE_PAUSED);
}
bool wxThread::IsPaused() const
{
wxCriticalSectionLocker lock(const_cast<wxCriticalSection &>(m_critsect));
return m_internal->GetState() == STATE_PAUSED;
}
bool wxThread::TestDestroy()
{
wxCriticalSectionLocker lock(const_cast<wxCriticalSection &>(m_critsect));
return m_internal->GetState() == STATE_CANCELED;
}
// ----------------------------------------------------------------------------
// Automatic initialization for thread module
// ----------------------------------------------------------------------------
class wxThreadModule : public wxModule
{
public:
virtual bool OnInit();
virtual void OnExit();
private:
wxDECLARE_DYNAMIC_CLASS(wxThreadModule);
};
wxIMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule);
bool wxThreadModule::OnInit()
{
// allocate TLS index for storing the pointer to the current thread
gs_tlsThisThread = ::TlsAlloc();
if ( gs_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(gs_tlsThisThread, (LPVOID)0) )
{
::TlsFree(gs_tlsThisThread);
gs_tlsThisThread = 0xFFFFFFFF;
wxLogSysError(_("Thread module initialization failed: cannot store value in thread local storage"));
return false;
}
gs_critsectWaitingForGui = new wxCriticalSection();
gs_critsectGui = new wxCriticalSection();
gs_critsectGui->Enter();
gs_critsectThreadDelete = new wxCriticalSection;
wxThread::ms_idMainThread = wxThread::GetCurrentId();
return true;
}
void wxThreadModule::OnExit()
{
if ( !::TlsFree(gs_tlsThisThread) )
{
wxLogLastError(wxT("TlsFree failed."));
}
wxDELETE(gs_critsectThreadDelete);
if ( gs_critsectGui )
{
gs_critsectGui->Leave();
wxDELETE(gs_critsectGui);
}
wxDELETE(gs_critsectWaitingForGui);
}
// ----------------------------------------------------------------------------
// under Windows, these functions are implemented using a critical section and
// not a mutex, so the names are a bit confusing
// ----------------------------------------------------------------------------
void wxMutexGuiEnterImpl()
{
// 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(*gs_critsectWaitingForGui);
gs_nWaitingForGui++;
}
wxWakeUpMainThread();
// now we may block here because the main thread will soon let us in
// (during the next iteration of OnIdle())
gs_critsectGui->Enter();
}
void wxMutexGuiLeaveImpl()
{
wxCriticalSectionLocker enter(*gs_critsectWaitingForGui);
if ( wxThread::IsMain() )
{
gs_bGuiOwnedByMainThread = false;
}
else
{
// decrement the number of threads waiting for GUI access now
wxASSERT_MSG( gs_nWaitingForGui > 0,
wxT("calling wxMutexGuiLeave() without entering it first?") );
gs_nWaitingForGui--;
wxWakeUpMainThread();
}
gs_critsectGui->Leave();
}
void WXDLLIMPEXP_BASE wxMutexGuiLeaveOrEnter()
{
wxASSERT_MSG( wxThread::IsMain(),
wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") );
wxCriticalSectionLocker enter(*gs_critsectWaitingForGui);
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() )
{
gs_critsectGui->Enter();
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() )
{
wxMutexGuiLeave();
}
//else: some other worker thread is doing GUI
}
}
bool WXDLLIMPEXP_BASE wxGuiOwnedByMainThread()
{
return gs_bGuiOwnedByMainThread;
}
// wake up the main thread if it's in ::GetMessage()
void WXDLLIMPEXP_BASE wxWakeUpMainThread()
{
// sending any message would do - hopefully WM_NULL is harmless enough
if ( !::PostThreadMessage(wxThread::GetMainId(), WM_NULL, 0, 0) )
{
// should never happen, but log an error if it does, however do not use
// wxLog here as it would result in reentrancy because logging from a
// thread calls wxWakeUpIdle() which calls this function itself again
const unsigned long ec = wxSysErrorCode();
wxMessageOutputDebug().Printf
(
wxS("Failed to wake up main thread: PostThreadMessage(WM_NULL) ")
wxS("failed with error 0x%08lx (%s)."),
ec,
wxSysErrorMsg(ec)
);
}
}
bool WXDLLIMPEXP_BASE wxIsWaitingForThread()
{
return gs_waitingForThread;
}
// ----------------------------------------------------------------------------
// include common implementation code
// ----------------------------------------------------------------------------
#include "wx/thrimpl.cpp"
#endif // wxUSE_THREADS