dolphin-emulator/Externals/wxWidgets3/src/unix/threadpsx.cpp
EmptyChaos 822326eea9 Update wxWidgets to 3.1.0
From wxWidgets master 81570ae070b35c9d52de47b1f14897f3ff1a66c7.

include/wx/defs.h -- __w64 warning disable patch by comex brought forward.

include/wx/msw/window.h -- added GetContentScaleFactor() which was not implemented on Windows but is necessary for wxBitmap scaling on Mac OS X so it needs to work to avoid #ifdef-ing the code.

src/gtk/window.cpp -- Modified DoSetClientSize() to direct call wxWindowGTK::DoSetSize() instead of using public wxWindowBase::SetSize() which now prevents derived classes (like wxAuiToolbar) intercepting the call and breaking it. This matches Windows which does NOT need to call DoSetSize internally. End result is this fixes Dolphin's debug tools toolbars on Linux.

src/osx/window_osx.cpp -- Same fix as for GTK since it has the same issue.

src/msw/radiobox.cpp -- Hacked to fix display in HiDPI (was clipping off end of text).

Updated CMakeLists for Linux and Mac OS X. Small code changes to Dolphin to fix debug error boxes, deprecation warnings, and retain previous UI behavior on Windows.
2016-06-26 15:25:29 +10:00

1935 lines
54 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/unix/threadpsx.cpp
// Purpose: wxThread (Posix) Implementation
// Author: Original from Wolfram Gloger/Guilhem Lavaux
// Modified by: K. S. Sreeram (2002): POSIXified wxCondition, added wxSemaphore
// Created: 04/22/98
// Copyright: (c) Wolfram Gloger (1996, 1997)
// Guilhem Lavaux (1998)
// Vadim Zeitlin (1999-2002)
// Robert Roebling (1999)
// K. S. Sreeram (2002)
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declaration
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if wxUSE_THREADS
#include "wx/thread.h"
#include "wx/except.h"
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/dynarray.h"
#include "wx/intl.h"
#include "wx/log.h"
#include "wx/utils.h"
#include "wx/timer.h"
#include "wx/stopwatch.h"
#include "wx/module.h"
#endif
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h> // needed for at least __QNX__
#ifdef HAVE_SCHED_H
#include <sched.h>
#endif
#ifdef HAVE_THR_SETCONCURRENCY
#include <thread.h>
#endif
#ifdef HAVE_ABI_FORCEDUNWIND
#include <cxxabi.h>
#endif
#ifdef HAVE_SETPRIORITY
#include <sys/resource.h> // for setpriority()
#endif
// we use wxFFile under Linux in GetCPUCount()
#ifdef __LINUX__
#include "wx/ffile.h"
#endif
#define THR_ID_CAST(id) (reinterpret_cast<void*>(id))
#define THR_ID(thr) THR_ID_CAST((thr)->GetId())
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
// the possible states of the thread and transitions from them
enum wxThreadState
{
STATE_NEW, // didn't start execution yet (=> RUNNING)
STATE_RUNNING, // running (=> PAUSED or EXITED)
STATE_PAUSED, // suspended (=> RUNNING or EXITED)
STATE_EXITED // thread doesn't exist any more
};
// the exit value of a thread which has been cancelled
static const wxThread::ExitCode EXITCODE_CANCELLED = (wxThread::ExitCode)-1;
// trace mask for wxThread operations
#define TRACE_THREADS wxT("thread")
// you can get additional debugging messages for the semaphore operations
#define TRACE_SEMA wxT("semaphore")
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
static void ScheduleThreadForDeletion();
static void DeleteThread(wxThread *This);
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
// an (non owning) array of pointers to threads
WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);
// an entry for a thread we can wait for
// -----------------------------------------------------------------------------
// global data
// -----------------------------------------------------------------------------
// we keep the list of all threads created by the application to be able to
// terminate them on exit if there are some left - otherwise the process would
// be left in memory
static wxArrayThread gs_allThreads;
// a mutex to protect gs_allThreads
static wxMutex *gs_mutexAllThreads = NULL;
// the id of the main thread
//
// we suppose that 0 is not a valid pthread_t value but in principle this might
// be false (e.g. if it's a selector-like value), wxThread::IsMain() would need
// to be updated in such case
wxThreadIdType wxThread::ms_idMainThread = 0;
// the key for the pointer to the associated wxThread object
static pthread_key_t gs_keySelf;
// the number of threads which are being deleted - the program won't exit
// until there are any left
static size_t gs_nThreadsBeingDeleted = 0;
// a mutex to protect gs_nThreadsBeingDeleted
static wxMutex *gs_mutexDeleteThread = NULL;
// and a condition variable which will be signaled when all
// gs_nThreadsBeingDeleted will have been deleted
static wxCondition *gs_condAllDeleted = NULL;
#ifndef __WXOSX__
// this mutex must be acquired before any call to a GUI function
// (it's not inside #if wxUSE_GUI because this file is compiled as part
// of wxBase)
static wxMutex *gs_mutexGui = NULL;
#endif
// when we wait for a thread to exit, we're blocking on a condition which the
// thread signals in its SignalExit() method -- but this condition can't be a
// member of the thread itself as a detached thread may delete itself at any
// moment and accessing the condition member of the thread after this would
// result in a disaster
//
// so instead we maintain a global list of the structs below for the threads
// we're interested in waiting on
// ============================================================================
// wxMutex implementation
// ============================================================================
// ----------------------------------------------------------------------------
// wxMutexInternal
// ----------------------------------------------------------------------------
// this is a simple wrapper around pthread_mutex_t which provides error
// checking
class wxMutexInternal
{
public:
wxMutexInternal(wxMutexType mutexType);
~wxMutexInternal();
wxMutexError Lock();
wxMutexError Lock(unsigned long ms);
wxMutexError TryLock();
wxMutexError Unlock();
bool IsOk() const { return m_isOk; }
private:
// convert the result of pthread_mutex_[timed]lock() call to wx return code
wxMutexError HandleLockResult(int err);
private:
pthread_mutex_t m_mutex;
bool m_isOk;
wxMutexType m_type;
unsigned long m_owningThread;
// wxConditionInternal uses our m_mutex
friend class wxConditionInternal;
};
#if defined(HAVE_PTHREAD_MUTEXATTR_T) && \
wxUSE_UNIX && !defined(HAVE_PTHREAD_MUTEXATTR_SETTYPE_DECL)
// on some systems pthread_mutexattr_settype() is not in the headers (but it is
// in the library, otherwise we wouldn't compile this code at all)
extern "C" int pthread_mutexattr_settype(pthread_mutexattr_t *, int);
#endif
wxMutexInternal::wxMutexInternal(wxMutexType mutexType)
{
m_type = mutexType;
m_owningThread = 0;
int err;
switch ( mutexType )
{
case wxMUTEX_RECURSIVE:
// support recursive locks like Win32, i.e. a thread can lock a
// mutex which it had itself already locked
//
// unfortunately initialization of recursive mutexes is non
// portable, so try several methods
#ifdef HAVE_PTHREAD_MUTEXATTR_T
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
err = pthread_mutex_init(&m_mutex, &attr);
}
#elif defined(HAVE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER)
// we can use this only as initializer so we have to assign it
// first to a temp var - assigning directly to m_mutex wouldn't
// even compile
{
pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
m_mutex = mutex;
}
#else // no recursive mutexes
err = EINVAL;
#endif // HAVE_PTHREAD_MUTEXATTR_T/...
break;
default:
wxFAIL_MSG( wxT("unknown mutex type") );
wxFALLTHROUGH;
case wxMUTEX_DEFAULT:
err = pthread_mutex_init(&m_mutex, NULL);
break;
}
m_isOk = err == 0;
if ( !m_isOk )
{
wxLogApiError( wxT("pthread_mutex_init()"), err);
}
}
wxMutexInternal::~wxMutexInternal()
{
if ( m_isOk )
{
int err = pthread_mutex_destroy(&m_mutex);
if ( err != 0 )
{
wxLogApiError( wxT("pthread_mutex_destroy()"), err);
}
}
}
wxMutexError wxMutexInternal::Lock()
{
if ((m_type == wxMUTEX_DEFAULT) && (m_owningThread != 0))
{
if (m_owningThread == wxThread::GetCurrentId())
return wxMUTEX_DEAD_LOCK;
}
return HandleLockResult(pthread_mutex_lock(&m_mutex));
}
wxMutexError wxMutexInternal::Lock(unsigned long ms)
{
#ifdef HAVE_PTHREAD_MUTEX_TIMEDLOCK
static const long MSEC_IN_SEC = 1000;
static const long NSEC_IN_MSEC = 1000000;
static const long NSEC_IN_USEC = 1000;
static const long NSEC_IN_SEC = MSEC_IN_SEC * NSEC_IN_MSEC;
time_t seconds = ms/MSEC_IN_SEC;
long nanoseconds = (ms % MSEC_IN_SEC) * NSEC_IN_MSEC;
timespec ts = { 0, 0 };
// normally we should use clock_gettime(CLOCK_REALTIME) here but this
// function is in librt and we don't link with it currently, so use
// gettimeofday() instead -- if it turns out that this is really too
// imprecise, we should modify configure to check if clock_gettime() is
// available and whether it requires -lrt and use it instead
#if 0
if ( clock_gettime(CLOCK_REALTIME, &ts) == 0 )
{
}
#else
struct timeval tv;
if ( wxGetTimeOfDay(&tv) != -1 )
{
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec*NSEC_IN_USEC;
}
#endif
else // fall back on system timer
{
ts.tv_sec = time(NULL);
}
ts.tv_sec += seconds;
ts.tv_nsec += nanoseconds;
if ( ts.tv_nsec > NSEC_IN_SEC )
{
ts.tv_sec += 1;
ts.tv_nsec -= NSEC_IN_SEC;
}
return HandleLockResult(pthread_mutex_timedlock(&m_mutex, &ts));
#else // !HAVE_PTHREAD_MUTEX_TIMEDLOCK
wxUnusedVar(ms);
return wxMUTEX_MISC_ERROR;
#endif // HAVE_PTHREAD_MUTEX_TIMEDLOCK/!HAVE_PTHREAD_MUTEX_TIMEDLOCK
}
wxMutexError wxMutexInternal::HandleLockResult(int err)
{
// wxPrintf( "err %d\n", err );
switch ( err )
{
case EDEADLK:
// only error checking mutexes return this value and so it's an
// unexpected situation -- hence use assert, not wxLogDebug
wxFAIL_MSG( wxT("mutex deadlock prevented") );
return wxMUTEX_DEAD_LOCK;
case EINVAL:
wxLogDebug(wxT("pthread_mutex_[timed]lock(): mutex not initialized"));
break;
case ETIMEDOUT:
return wxMUTEX_TIMEOUT;
case 0:
if (m_type == wxMUTEX_DEFAULT)
m_owningThread = wxThread::GetCurrentId();
return wxMUTEX_NO_ERROR;
default:
wxLogApiError(wxT("pthread_mutex_[timed]lock()"), err);
}
return wxMUTEX_MISC_ERROR;
}
wxMutexError wxMutexInternal::TryLock()
{
int err = pthread_mutex_trylock(&m_mutex);
switch ( err )
{
case EBUSY:
// not an error: mutex is already locked, but we're prepared for
// this
return wxMUTEX_BUSY;
case EINVAL:
wxLogDebug(wxT("pthread_mutex_trylock(): mutex not initialized."));
break;
case 0:
if (m_type == wxMUTEX_DEFAULT)
m_owningThread = wxThread::GetCurrentId();
return wxMUTEX_NO_ERROR;
default:
wxLogApiError(wxT("pthread_mutex_trylock()"), err);
}
return wxMUTEX_MISC_ERROR;
}
wxMutexError wxMutexInternal::Unlock()
{
m_owningThread = 0;
int err = pthread_mutex_unlock(&m_mutex);
switch ( err )
{
case EPERM:
// we don't own the mutex
return wxMUTEX_UNLOCKED;
case EINVAL:
wxLogDebug(wxT("pthread_mutex_unlock(): mutex not initialized."));
break;
case 0:
return wxMUTEX_NO_ERROR;
default:
wxLogApiError(wxT("pthread_mutex_unlock()"), err);
}
return wxMUTEX_MISC_ERROR;
}
// ===========================================================================
// wxCondition implementation
// ===========================================================================
// ---------------------------------------------------------------------------
// wxConditionInternal
// ---------------------------------------------------------------------------
// this is a wrapper around pthread_cond_t associated with a wxMutex (and hence
// with a pthread_mutex_t)
class wxConditionInternal
{
public:
wxConditionInternal(wxMutex& mutex);
~wxConditionInternal();
bool IsOk() const { return m_isOk && m_mutex.IsOk(); }
wxCondError Wait();
wxCondError WaitTimeout(unsigned long milliseconds);
wxCondError Signal();
wxCondError Broadcast();
private:
// get the POSIX mutex associated with us
pthread_mutex_t *GetPMutex() const { return &m_mutex.m_internal->m_mutex; }
wxMutex& m_mutex;
pthread_cond_t m_cond;
bool m_isOk;
};
wxConditionInternal::wxConditionInternal(wxMutex& mutex)
: m_mutex(mutex)
{
int err = pthread_cond_init(&m_cond, NULL /* default attributes */);
m_isOk = err == 0;
if ( !m_isOk )
{
wxLogApiError(wxT("pthread_cond_init()"), err);
}
}
wxConditionInternal::~wxConditionInternal()
{
if ( m_isOk )
{
int err = pthread_cond_destroy(&m_cond);
if ( err != 0 )
{
wxLogApiError(wxT("pthread_cond_destroy()"), err);
}
}
}
wxCondError wxConditionInternal::Wait()
{
int err = pthread_cond_wait(&m_cond, GetPMutex());
if ( err != 0 )
{
wxLogApiError(wxT("pthread_cond_wait()"), err);
return wxCOND_MISC_ERROR;
}
return wxCOND_NO_ERROR;
}
wxCondError wxConditionInternal::WaitTimeout(unsigned long milliseconds)
{
wxLongLong curtime = wxGetUTCTimeMillis();
curtime += milliseconds;
wxLongLong temp = curtime / 1000;
int sec = temp.GetLo();
temp *= 1000;
temp = curtime - temp;
int millis = temp.GetLo();
timespec tspec;
tspec.tv_sec = sec;
tspec.tv_nsec = millis * 1000L * 1000L;
int err = pthread_cond_timedwait( &m_cond, GetPMutex(), &tspec );
switch ( err )
{
case ETIMEDOUT:
return wxCOND_TIMEOUT;
case 0:
return wxCOND_NO_ERROR;
default:
wxLogApiError(wxT("pthread_cond_timedwait()"), err);
}
return wxCOND_MISC_ERROR;
}
wxCondError wxConditionInternal::Signal()
{
int err = pthread_cond_signal(&m_cond);
if ( err != 0 )
{
wxLogApiError(wxT("pthread_cond_signal()"), err);
return wxCOND_MISC_ERROR;
}
return wxCOND_NO_ERROR;
}
wxCondError wxConditionInternal::Broadcast()
{
int err = pthread_cond_broadcast(&m_cond);
if ( err != 0 )
{
wxLogApiError(wxT("pthread_cond_broadcast()"), err);
return wxCOND_MISC_ERROR;
}
return wxCOND_NO_ERROR;
}
// ===========================================================================
// wxSemaphore implementation
// ===========================================================================
// ---------------------------------------------------------------------------
// wxSemaphoreInternal
// ---------------------------------------------------------------------------
// we implement the semaphores using mutexes and conditions instead of using
// the sem_xxx() POSIX functions because they're not widely available and also
// because it's impossible to implement WaitTimeout() using them
class wxSemaphoreInternal
{
public:
wxSemaphoreInternal(int initialcount, int maxcount);
bool IsOk() const { return m_isOk; }
wxSemaError Wait();
wxSemaError TryWait();
wxSemaError WaitTimeout(unsigned long milliseconds);
wxSemaError Post();
private:
wxMutex m_mutex;
wxCondition m_cond;
size_t m_count,
m_maxcount;
bool m_isOk;
};
wxSemaphoreInternal::wxSemaphoreInternal(int initialcount, int maxcount)
: m_cond(m_mutex)
{
if ( (initialcount < 0 || maxcount < 0) ||
((maxcount > 0) && (initialcount > maxcount)) )
{
wxFAIL_MSG( wxT("wxSemaphore: invalid initial or maximal count") );
m_isOk = false;
}
else
{
m_maxcount = (size_t)maxcount;
m_count = (size_t)initialcount;
}
m_isOk = m_mutex.IsOk() && m_cond.IsOk();
}
wxSemaError wxSemaphoreInternal::Wait()
{
wxMutexLocker locker(m_mutex);
while ( m_count == 0 )
{
wxLogTrace(TRACE_SEMA,
wxT("Thread %p waiting for semaphore to become signalled"),
THR_ID_CAST(wxThread::GetCurrentId()));
if ( m_cond.Wait() != wxCOND_NO_ERROR )
return wxSEMA_MISC_ERROR;
wxLogTrace(TRACE_SEMA,
wxT("Thread %p finished waiting for semaphore, count = %lu"),
THR_ID_CAST(wxThread::GetCurrentId()), (unsigned long)m_count);
}
m_count--;
return wxSEMA_NO_ERROR;
}
wxSemaError wxSemaphoreInternal::TryWait()
{
wxMutexLocker locker(m_mutex);
if ( m_count == 0 )
return wxSEMA_BUSY;
m_count--;
return wxSEMA_NO_ERROR;
}
wxSemaError wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds)
{
wxMutexLocker locker(m_mutex);
wxLongLong startTime = wxGetLocalTimeMillis();
while ( m_count == 0 )
{
wxLongLong elapsed = wxGetLocalTimeMillis() - startTime;
long remainingTime = (long)milliseconds - (long)elapsed.GetLo();
if ( remainingTime <= 0 )
{
// timeout
return wxSEMA_TIMEOUT;
}
switch ( m_cond.WaitTimeout(remainingTime) )
{
case wxCOND_TIMEOUT:
return wxSEMA_TIMEOUT;
default:
return wxSEMA_MISC_ERROR;
case wxCOND_NO_ERROR:
;
}
}
m_count--;
return wxSEMA_NO_ERROR;
}
wxSemaError wxSemaphoreInternal::Post()
{
wxMutexLocker locker(m_mutex);
if ( m_maxcount > 0 && m_count == m_maxcount )
{
return wxSEMA_OVERFLOW;
}
m_count++;
wxLogTrace(TRACE_SEMA,
wxT("Thread %p about to signal semaphore, count = %lu"),
THR_ID_CAST(wxThread::GetCurrentId()), (unsigned long)m_count);
return m_cond.Signal() == wxCOND_NO_ERROR ? wxSEMA_NO_ERROR
: wxSEMA_MISC_ERROR;
}
// ===========================================================================
// wxThread implementation
// ===========================================================================
// the thread callback functions must have the C linkage
extern "C"
{
#ifdef wxHAVE_PTHREAD_CLEANUP
// thread exit function
void wxPthreadCleanup(void *ptr);
#endif // wxHAVE_PTHREAD_CLEANUP
void *wxPthreadStart(void *ptr);
} // extern "C"
// ----------------------------------------------------------------------------
// wxThreadInternal
// ----------------------------------------------------------------------------
class wxThreadInternal
{
public:
wxThreadInternal();
~wxThreadInternal();
// thread entry function
static void *PthreadStart(wxThread *thread);
// thread actions
// create the thread
wxThreadError Create(wxThread *thread, unsigned int stackSize);
// start the thread
wxThreadError Run();
// unblock the thread allowing it to run
void SignalRun() { m_semRun.Post(); }
// ask the thread to terminate
void Wait();
// go to sleep until Resume() is called
void Pause();
// resume the thread
void Resume();
// accessors
// priority
int GetPriority() const { return m_prio; }
void SetPriority(int prio) { m_prio = prio; }
// state
wxThreadState GetState() const { return m_state; }
void SetState(wxThreadState state)
{
#if wxUSE_LOG_TRACE
static const wxChar *const stateNames[] =
{
wxT("NEW"),
wxT("RUNNING"),
wxT("PAUSED"),
wxT("EXITED"),
};
wxLogTrace(TRACE_THREADS, wxT("Thread %p: %s => %s."),
THR_ID(this), stateNames[m_state], stateNames[state]);
#endif // wxUSE_LOG_TRACE
m_state = state;
}
// id
pthread_t GetId() const { return m_threadId; }
pthread_t *GetIdPtr() { return &m_threadId; }
// "created" flag
bool WasCreated() const { return m_created; }
// "cancelled" flag
void SetCancelFlag() { m_cancelled = true; }
bool WasCancelled() const { return m_cancelled; }
// exit code
void SetExitCode(wxThread::ExitCode exitcode) { m_exitcode = exitcode; }
wxThread::ExitCode GetExitCode() const { return m_exitcode; }
// the pause flag
void SetReallyPaused(bool paused) { m_isPaused = paused; }
bool IsReallyPaused() const { return m_isPaused; }
// tell the thread that it is a detached one
void Detach()
{
wxCriticalSectionLocker lock(m_csJoinFlag);
m_shouldBeJoined = false;
m_isDetached = true;
}
#ifdef wxHAVE_PTHREAD_CLEANUP
// this is used by wxPthreadCleanup() only
static void Cleanup(wxThread *thread);
#endif // wxHAVE_PTHREAD_CLEANUP
private:
pthread_t m_threadId; // id of the thread
wxThreadState m_state; // see wxThreadState enum
int m_prio; // in wxWidgets units: from 0 to 100
// this flag is set when the thread was successfully created
bool m_created;
// this flag is set when the thread should terminate
bool m_cancelled;
// this flag is set when the thread is blocking on m_semSuspend
bool m_isPaused;
// the thread exit code - only used for joinable (!detached) threads and
// is only valid after the thread termination
wxThread::ExitCode m_exitcode;
// many threads may call Wait(), but only one of them should call
// pthread_join(), so we have to keep track of this
wxCriticalSection m_csJoinFlag;
bool m_shouldBeJoined;
bool m_isDetached;
// this semaphore is posted by Run() and the threads Entry() is not
// called before it is done
wxSemaphore m_semRun;
// this one is signaled when the thread should resume after having been
// Pause()d
wxSemaphore m_semSuspend;
};
// ----------------------------------------------------------------------------
// thread startup and exit functions
// ----------------------------------------------------------------------------
void *wxPthreadStart(void *ptr)
{
return wxThreadInternal::PthreadStart((wxThread *)ptr);
}
void *wxThreadInternal::PthreadStart(wxThread *thread)
{
wxThreadInternal *pthread = thread->m_internal;
wxLogTrace(TRACE_THREADS, wxT("Thread %p started."), THR_ID(pthread));
// associate the thread pointer with the newly created thread so that
// wxThread::This() will work
int rc = pthread_setspecific(gs_keySelf, thread);
if ( rc != 0 )
{
wxLogSysError(rc, _("Cannot start thread: error writing TLS."));
return (void *)-1;
}
// have to declare this before pthread_cleanup_push() which defines a
// block!
bool dontRunAtAll;
#ifdef wxHAVE_PTHREAD_CLEANUP
// install the cleanup handler which will be called if the thread is
// cancelled
pthread_cleanup_push(wxPthreadCleanup, thread);
#endif // wxHAVE_PTHREAD_CLEANUP
// wait for the semaphore to be posted from Run()
pthread->m_semRun.Wait();
// test whether we should run the run at all - may be it was deleted
// before it started to Run()?
{
wxCriticalSectionLocker lock(thread->m_critsect);
dontRunAtAll = pthread->GetState() == STATE_NEW &&
pthread->WasCancelled();
}
if ( !dontRunAtAll )
{
// call the main entry
wxLogTrace(TRACE_THREADS,
wxT("Thread %p about to enter its Entry()."),
THR_ID(pthread));
wxTRY
{
pthread->m_exitcode = thread->CallEntry();
wxLogTrace(TRACE_THREADS,
wxT("Thread %p Entry() returned %lu."),
THR_ID(pthread), wxPtrToUInt(pthread->m_exitcode));
}
#ifndef wxNO_EXCEPTIONS
#ifdef HAVE_ABI_FORCEDUNWIND
// When using common C++ ABI under Linux we must always rethrow this
// special exception used to unwind the stack when the thread was
// cancelled, otherwise the thread library would simply terminate the
// program, see http://udrepper.livejournal.com/21541.html
catch ( abi::__forced_unwind& )
{
wxCriticalSectionLocker lock(thread->m_critsect);
pthread->SetState(STATE_EXITED);
throw;
}
#endif // HAVE_ABI_FORCEDUNWIND
catch ( ... )
{
wxTheApp->OnUnhandledException();
}
#endif // !wxNO_EXCEPTIONS
{
wxCriticalSectionLocker lock(thread->m_critsect);
// change the state of the thread to "exited" so that
// wxPthreadCleanup handler won't do anything from now (if it's
// called before we do pthread_cleanup_pop below)
pthread->SetState(STATE_EXITED);
}
}
// NB: pthread_cleanup_push/pop() are macros and pop contains the matching
// '}' for the '{' in push, so they must be used in the same block!
#ifdef wxHAVE_PTHREAD_CLEANUP
#ifdef __DECCXX
// under Tru64 we get a warning from macro expansion
#pragma message save
#pragma message disable(declbutnotref)
#endif
// remove the cleanup handler without executing it
pthread_cleanup_pop(FALSE);
#ifdef __DECCXX
#pragma message restore
#endif
#endif // wxHAVE_PTHREAD_CLEANUP
if ( dontRunAtAll )
{
// FIXME: deleting a possibly joinable thread here???
delete thread;
return EXITCODE_CANCELLED;
}
else
{
// terminate the thread
thread->Exit(pthread->m_exitcode);
wxFAIL_MSG(wxT("wxThread::Exit() can't return."));
return NULL;
}
}
#ifdef wxHAVE_PTHREAD_CLEANUP
// this handler is called when the thread is cancelled
extern "C" void wxPthreadCleanup(void *ptr)
{
wxThreadInternal::Cleanup((wxThread *)ptr);
}
void wxThreadInternal::Cleanup(wxThread *thread)
{
if (pthread_getspecific(gs_keySelf) == 0) return;
{
wxCriticalSectionLocker lock(thread->m_critsect);
if ( thread->m_internal->GetState() == STATE_EXITED )
{
// thread is already considered as finished.
return;
}
}
// exit the thread gracefully
thread->Exit(EXITCODE_CANCELLED);
}
#endif // wxHAVE_PTHREAD_CLEANUP
// ----------------------------------------------------------------------------
// wxThreadInternal
// ----------------------------------------------------------------------------
wxThreadInternal::wxThreadInternal()
{
m_state = STATE_NEW;
m_created = false;
m_cancelled = false;
m_prio = wxPRIORITY_DEFAULT;
m_threadId = 0;
m_exitcode = 0;
// set to true only when the thread starts waiting on m_semSuspend
m_isPaused = false;
// defaults for joinable threads
m_shouldBeJoined = true;
m_isDetached = false;
}
wxThreadInternal::~wxThreadInternal()
{
}
#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
#define WXUNUSED_STACKSIZE(identifier) identifier
#else
#define WXUNUSED_STACKSIZE(identifier) WXUNUSED(identifier)
#endif
wxThreadError wxThreadInternal::Create(wxThread *thread,
unsigned int WXUNUSED_STACKSIZE(stackSize))
{
if ( GetState() != STATE_NEW )
{
// don't recreate thread
return wxTHREAD_RUNNING;
}
// set up the thread attribute: right now, we only set thread priority
pthread_attr_t attr;
pthread_attr_init(&attr);
#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
if (stackSize)
pthread_attr_setstacksize(&attr, stackSize);
#endif
#ifdef HAVE_THREAD_PRIORITY_FUNCTIONS
int policy;
if ( pthread_attr_getschedpolicy(&attr, &policy) != 0 )
{
wxLogError(_("Cannot retrieve thread scheduling policy."));
}
#ifdef __VMS__
/* the pthread.h contains too many spaces. This is a work-around */
# undef sched_get_priority_max
#undef sched_get_priority_min
#define sched_get_priority_max(_pol_) \
(_pol_ == SCHED_OTHER ? PRI_FG_MAX_NP : PRI_FIFO_MAX)
#define sched_get_priority_min(_pol_) \
(_pol_ == SCHED_OTHER ? PRI_FG_MIN_NP : PRI_FIFO_MIN)
#endif
int max_prio = sched_get_priority_max(policy),
min_prio = sched_get_priority_min(policy),
prio = GetPriority();
if ( min_prio == -1 || max_prio == -1 )
{
wxLogError(_("Cannot get priority range for scheduling policy %d."),
policy);
}
else if ( max_prio == min_prio )
{
if ( prio != wxPRIORITY_DEFAULT )
{
// notify the programmer that this doesn't work here
wxLogWarning(_("Thread priority setting is ignored."));
}
//else: we have default priority, so don't complain
// anyhow, don't do anything because priority is just ignored
}
else
{
struct sched_param sp;
if ( pthread_attr_getschedparam(&attr, &sp) != 0 )
{
wxFAIL_MSG(wxT("pthread_attr_getschedparam() failed"));
}
sp.sched_priority = min_prio + (prio*(max_prio - min_prio))/100;
if ( pthread_attr_setschedparam(&attr, &sp) != 0 )
{
wxFAIL_MSG(wxT("pthread_attr_setschedparam(priority) failed"));
}
}
#endif // HAVE_THREAD_PRIORITY_FUNCTIONS
#ifdef HAVE_PTHREAD_ATTR_SETSCOPE
// this will make the threads created by this process really concurrent
if ( pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) != 0 )
{
wxFAIL_MSG(wxT("pthread_attr_setscope(PTHREAD_SCOPE_SYSTEM) failed"));
}
#endif // HAVE_PTHREAD_ATTR_SETSCOPE
// VZ: assume that this one is always available (it's rather fundamental),
// if this function is ever missing we should try to use
// pthread_detach() instead (after thread creation)
if ( thread->IsDetached() )
{
if ( pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0 )
{
wxFAIL_MSG(wxT("pthread_attr_setdetachstate(DETACHED) failed"));
}
// never try to join detached threads
Detach();
}
//else: threads are created joinable by default, it's ok
// create the new OS thread object
int rc = pthread_create
(
GetIdPtr(),
&attr,
wxPthreadStart,
(void *)thread
);
if ( pthread_attr_destroy(&attr) != 0 )
{
wxFAIL_MSG(wxT("pthread_attr_destroy() failed"));
}
if ( rc != 0 )
{
SetState(STATE_EXITED);
return wxTHREAD_NO_RESOURCE;
}
m_created = true;
return wxTHREAD_NO_ERROR;
}
wxThreadError wxThreadInternal::Run()
{
wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING,
wxT("thread may only be started once after Create()") );
SetState(STATE_RUNNING);
// wake up threads waiting for our start
SignalRun();
return wxTHREAD_NO_ERROR;
}
void wxThreadInternal::Wait()
{
wxCHECK_RET( !m_isDetached, wxT("can't wait for a detached thread") );
// if the thread we're waiting for is waiting for the GUI mutex, we will
// deadlock so make sure we release it temporarily
if ( wxThread::IsMain() )
{
#ifdef __WXOSX__
// give the thread we're waiting for chance to do the GUI call
// it might be in, we don't do this conditionally as the to be waited on
// thread might have to acquire the mutex later but before terminating
if ( wxGuiOwnedByMainThread() )
wxMutexGuiLeave();
#else
wxMutexGuiLeave();
#endif
}
wxLogTrace(TRACE_THREADS,
wxT("Starting to wait for thread %p to exit."),
THR_ID(this));
// to avoid memory leaks we should call pthread_join(), but it must only be
// done once so use a critical section to serialize the code below
{
wxCriticalSectionLocker lock(m_csJoinFlag);
if ( m_shouldBeJoined )
{
// FIXME shouldn't we set cancellation type to DISABLED here? If
// we're cancelled inside pthread_join(), things will almost
// certainly break - but if we disable the cancellation, we
// might deadlock
if ( pthread_join(GetId(), &m_exitcode) != 0 )
{
// this is a serious problem, so use wxLogError and not
// wxLogDebug: it is possible to bring the system to its knees
// by creating too many threads and not joining them quite
// easily
wxLogError(_("Failed to join a thread, potential memory leak detected - please restart the program"));
}
m_shouldBeJoined = false;
}
}
#ifndef __WXOSX__
// reacquire GUI mutex
if ( wxThread::IsMain() )
wxMutexGuiEnter();
#endif
}
void wxThreadInternal::Pause()
{
// the state is set from the thread which pauses us first, this function
// is called later so the state should have been already set
wxCHECK_RET( m_state == STATE_PAUSED,
wxT("thread must first be paused with wxThread::Pause().") );
wxLogTrace(TRACE_THREADS,
wxT("Thread %p goes to sleep."), THR_ID(this));
// wait until the semaphore is Post()ed from Resume()
m_semSuspend.Wait();
}
void wxThreadInternal::Resume()
{
wxCHECK_RET( m_state == STATE_PAUSED,
wxT("can't resume thread which is not suspended.") );
// the thread might be not actually paused yet - if there were no call to
// TestDestroy() since the last call to Pause() for example
if ( IsReallyPaused() )
{
wxLogTrace(TRACE_THREADS,
wxT("Waking up thread %p"), THR_ID(this));
// wake up Pause()
m_semSuspend.Post();
// reset the flag
SetReallyPaused(false);
}
else
{
wxLogTrace(TRACE_THREADS,
wxT("Thread %p is not yet really paused"), THR_ID(this));
}
SetState(STATE_RUNNING);
}
// -----------------------------------------------------------------------------
// wxThread static functions
// -----------------------------------------------------------------------------
wxThread *wxThread::This()
{
return (wxThread *)pthread_getspecific(gs_keySelf);
}
void wxThread::Yield()
{
#ifdef HAVE_SCHED_YIELD
sched_yield();
#endif
}
int wxThread::GetCPUCount()
{
#if defined(_SC_NPROCESSORS_ONLN)
// this works for Solaris and Linux 2.6
int rc = sysconf(_SC_NPROCESSORS_ONLN);
if ( rc != -1 )
{
return rc;
}
#elif defined(__LINUX__) && wxUSE_FFILE
// read from proc (can't use wxTextFile here because it's a special file:
// it has 0 size but still can be read from)
wxLogNull nolog;
wxFFile file(wxT("/proc/cpuinfo"));
if ( file.IsOpened() )
{
// slurp the whole file
wxString s;
if ( file.ReadAll(&s) )
{
// (ab)use Replace() to find the number of "processor: num" strings
size_t count = s.Replace(wxT("processor\t:"), wxT(""));
if ( count > 0 )
{
return count;
}
wxLogDebug(wxT("failed to parse /proc/cpuinfo"));
}
else
{
wxLogDebug(wxT("failed to read /proc/cpuinfo"));
}
}
#endif // different ways to get number of CPUs
// unknown
return -1;
}
wxThreadIdType wxThread::GetCurrentId()
{
return (wxThreadIdType)pthread_self();
}
bool wxThread::SetConcurrency(size_t level)
{
#ifdef HAVE_PTHREAD_SET_CONCURRENCY
int rc = pthread_setconcurrency( level );
#elif defined(HAVE_THR_SETCONCURRENCY)
int rc = thr_setconcurrency(level);
#else // !HAVE_THR_SETCONCURRENCY
// ok only for the default value
int rc = level == 0 ? 0 : -1;
#endif // HAVE_THR_SETCONCURRENCY/!HAVE_THR_SETCONCURRENCY
if ( rc != 0 )
{
wxLogSysError(rc, _("Failed to set thread concurrency level to %lu"),
static_cast<unsigned long>(level));
return false;
}
return true;
}
// -----------------------------------------------------------------------------
// creating thread
// -----------------------------------------------------------------------------
wxThread::wxThread(wxThreadKind kind)
{
// add this thread to the global list of all threads
{
wxMutexLocker lock(*gs_mutexAllThreads);
gs_allThreads.Add(this);
}
m_internal = new wxThreadInternal();
m_isDetached = kind == wxTHREAD_DETACHED;
}
wxThreadError wxThread::Create(unsigned int stackSize)
{
wxCriticalSectionLocker lock(m_critsect);
return m_internal->Create(this, stackSize);
}
wxThreadError wxThread::Run()
{
wxCriticalSectionLocker lock(m_critsect);
// Create the thread if it wasn't created yet with an explicit
// Create() call:
if ( !m_internal->WasCreated() )
{
wxThreadError rv = m_internal->Create(this, 0);
if ( rv != wxTHREAD_NO_ERROR )
return rv;
}
return m_internal->Run();
}
// -----------------------------------------------------------------------------
// misc accessors
// -----------------------------------------------------------------------------
void wxThread::SetPriority(unsigned int prio)
{
wxCHECK_RET( wxPRIORITY_MIN <= prio && prio <= wxPRIORITY_MAX,
wxT("invalid thread priority") );
wxCriticalSectionLocker lock(m_critsect);
switch ( m_internal->GetState() )
{
case STATE_NEW:
// thread not yet started, priority will be set when it is
m_internal->SetPriority(prio);
break;
case STATE_RUNNING:
case STATE_PAUSED:
{
#ifdef HAVE_THREAD_PRIORITY_FUNCTIONS
// We map our priority values to pthreads scheduling params as
// follows:
// 0..20 to SCHED_IDLE
// 21..40 to SCHED_BATCH
// 41..60 to SCHED_OTHER
// 61..80 to SCHED_RR
// 81..100 to SCHED_FIFO
//
// For the last two, we can also use the additional priority
// parameter which must be in 1..99 range under Linux (TODO:
// what should be used for the other systems?).
struct sched_param sparam = { 0 };
// The only scheduling policy guaranteed to be supported
// everywhere is this one.
int policy = SCHED_OTHER;
#ifdef SCHED_IDLE
if ( prio <= 20 )
policy = SCHED_IDLE;
#endif
#ifdef SCHED_BATCH
if ( 20 < prio && prio <= 40 )
policy = SCHED_BATCH;
#endif
#ifdef SCHED_RR
if ( 60 < prio && prio <= 80 )
policy = SCHED_RR;
#endif
#ifdef SCHED_FIFO
if ( 80 < prio )
policy = SCHED_FIFO;
#endif
// This test is not redundant as it takes care of the systems
// where neither SCHED_RR nor SCHED_FIFO are defined.
if ( prio > 60 && policy != SCHED_OTHER )
{
// There is no good way to map our 20 possible priorities
// (61..80 or 81..100) to the 99 pthread priorities, so we
// do the best that we can and ensure that the extremes of
// our range are mapped to the pthread extremes and all the
// rest fall in between.
// This gets us to 1..96 range.
sparam.sched_priority = ((prio - 61) % 20)*5 + 1;
// And we artificially increase our highest priority to the
// highest pthread one.
if ( sparam.sched_priority == 96 )
sparam.sched_priority = 99;
}
if ( pthread_setschedparam(m_internal->GetId(),
policy, &sparam) != 0 )
{
wxLogError(_("Failed to set thread priority %d."), prio);
}
#endif // HAVE_THREAD_PRIORITY_FUNCTIONS
}
break;
case STATE_EXITED:
default:
wxFAIL_MSG(wxT("impossible to set thread priority in this state"));
}
}
unsigned int wxThread::GetPriority() const
{
wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
return m_internal->GetPriority();
}
wxThreadIdType wxThread::GetId() const
{
return (wxThreadIdType) m_internal->GetId();
}
// -----------------------------------------------------------------------------
// pause/resume
// -----------------------------------------------------------------------------
wxThreadError wxThread::Pause()
{
wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR,
wxT("a thread can't pause itself") );
wxCriticalSectionLocker lock(m_critsect);
if ( m_internal->GetState() != STATE_RUNNING )
{
wxLogDebug(wxT("Can't pause thread which is not running."));
return wxTHREAD_NOT_RUNNING;
}
// just set a flag, the thread will be really paused only during the next
// call to TestDestroy()
m_internal->SetState(STATE_PAUSED);
return wxTHREAD_NO_ERROR;
}
wxThreadError wxThread::Resume()
{
wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR,
wxT("a thread can't resume itself") );
wxCriticalSectionLocker lock(m_critsect);
wxThreadState state = m_internal->GetState();
switch ( state )
{
case STATE_PAUSED:
wxLogTrace(TRACE_THREADS, wxT("Thread %p suspended, resuming."),
THR_ID(this));
m_internal->Resume();
return wxTHREAD_NO_ERROR;
case STATE_EXITED:
wxLogTrace(TRACE_THREADS, wxT("Thread %p exited, won't resume."),
THR_ID(this));
return wxTHREAD_NO_ERROR;
default:
wxLogDebug(wxT("Attempt to resume a thread which is not paused."));
return wxTHREAD_MISC_ERROR;
}
}
// -----------------------------------------------------------------------------
// exiting thread
// -----------------------------------------------------------------------------
wxThread::ExitCode wxThread::Wait(wxThreadWait WXUNUSED(waitMode))
{
wxCHECK_MSG( This() != this, (ExitCode)-1,
wxT("a thread can't wait for itself") );
wxCHECK_MSG( !m_isDetached, (ExitCode)-1,
wxT("can't wait for detached thread") );
m_internal->Wait();
return m_internal->GetExitCode();
}
wxThreadError wxThread::Delete(ExitCode *rc, wxThreadWait WXUNUSED(waitMode))
{
wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR,
wxT("a thread can't delete itself") );
bool isDetached = m_isDetached;
m_critsect.Enter();
wxThreadState state = m_internal->GetState();
// ask the thread to stop
m_internal->SetCancelFlag();
m_critsect.Leave();
OnDelete();
switch ( state )
{
case STATE_NEW:
// we need to wake up the thread so that PthreadStart() will
// terminate - right now it's blocking on run semaphore in
// PthreadStart()
m_internal->SignalRun();
wxFALLTHROUGH;
case STATE_EXITED:
// nothing to do
break;
case STATE_PAUSED:
// resume the thread first
m_internal->Resume();
wxFALLTHROUGH;
default:
if ( !isDetached )
{
// wait until the thread stops
m_internal->Wait();
if ( rc )
{
// return the exit code of the thread
*rc = m_internal->GetExitCode();
}
}
//else: can't wait for detached threads
}
if (state == STATE_NEW)
return wxTHREAD_MISC_ERROR;
// for coherency with the MSW implementation, signal the user that
// Delete() was called on a thread which didn't start to run yet.
return wxTHREAD_NO_ERROR;
}
wxThreadError wxThread::Kill()
{
wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR,
wxT("a thread can't kill itself") );
OnKill();
switch ( m_internal->GetState() )
{
case STATE_NEW:
case STATE_EXITED:
return wxTHREAD_NOT_RUNNING;
case STATE_PAUSED:
// resume the thread first
Resume();
wxFALLTHROUGH;
default:
#ifdef HAVE_PTHREAD_CANCEL
if ( pthread_cancel(m_internal->GetId()) != 0 )
#endif // HAVE_PTHREAD_CANCEL
{
wxLogError(_("Failed to terminate a thread."));
return wxTHREAD_MISC_ERROR;
}
#ifdef HAVE_PTHREAD_CANCEL
if ( m_isDetached )
{
// if we use cleanup function, this will be done from
// wxPthreadCleanup()
#ifndef wxHAVE_PTHREAD_CLEANUP
ScheduleThreadForDeletion();
// don't call OnExit() here, it can only be called in the
// threads context and we're in the context of another thread
DeleteThread(this);
#endif // wxHAVE_PTHREAD_CLEANUP
}
else
{
m_internal->SetExitCode(EXITCODE_CANCELLED);
}
return wxTHREAD_NO_ERROR;
#endif // HAVE_PTHREAD_CANCEL
}
}
void wxThread::Exit(ExitCode status)
{
wxASSERT_MSG( This() == this,
wxT("wxThread::Exit() can only be called in the context of the same thread") );
if ( m_isDetached )
{
// from the moment we call OnExit(), the main program may terminate at
// any moment, so mark this thread as being already in process of being
// deleted or wxThreadModule::OnExit() will try to delete it again
ScheduleThreadForDeletion();
}
// don't enter m_critsect before calling OnExit() because the user code
// might deadlock if, for example, it signals a condition in OnExit() (a
// common case) while the main thread calls any of functions entering
// m_critsect on us (almost all of them do)
wxTRY
{
OnExit();
}
wxCATCH_ALL( wxTheApp->OnUnhandledException(); )
// delete C++ thread object if this is a detached thread - user is
// responsible for doing this for joinable ones
if ( m_isDetached )
{
// FIXME I'm feeling bad about it - what if another thread function is
// called (in another thread context) now? It will try to access
// half destroyed object which will probably result in something
// very bad - but we can't protect this by a crit section unless
// we make it a global object, but this would mean that we can
// only call one thread function at a time :-(
DeleteThread(this);
pthread_setspecific(gs_keySelf, 0);
}
else
{
m_critsect.Enter();
m_internal->SetState(STATE_EXITED);
m_critsect.Leave();
}
// terminate the thread (pthread_exit() never returns)
pthread_exit(status);
wxFAIL_MSG(wxT("pthread_exit() failed"));
}
// also test whether we were paused
bool wxThread::TestDestroy()
{
wxASSERT_MSG( This() == this,
wxT("wxThread::TestDestroy() can only be called in the context of the same thread") );
m_critsect.Enter();
if ( m_internal->GetState() == STATE_PAUSED )
{
m_internal->SetReallyPaused(true);
// leave the crit section or the other threads will stop too if they
// try to call any of (seemingly harmless) IsXXX() functions while we
// sleep
m_critsect.Leave();
m_internal->Pause();
}
else
{
// thread wasn't requested to pause, nothing to do
m_critsect.Leave();
}
return m_internal->WasCancelled();
}
wxThread::~wxThread()
{
m_critsect.Enter();
// check that the thread either exited or couldn't be created
if ( m_internal->GetState() != STATE_EXITED &&
m_internal->GetState() != STATE_NEW )
{
wxLogDebug(wxT("The thread %p is being destroyed although it is still running! The application may crash."),
THR_ID(this));
}
m_critsect.Leave();
delete m_internal;
// remove this thread from the global array
{
wxMutexLocker lock(*gs_mutexAllThreads);
gs_allThreads.Remove(this);
}
}
// -----------------------------------------------------------------------------
// state tests
// -----------------------------------------------------------------------------
bool wxThread::IsRunning() const
{
wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
return m_internal->GetState() == STATE_RUNNING;
}
bool wxThread::IsAlive() const
{
wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect);
switch ( m_internal->GetState() )
{
case STATE_RUNNING:
case STATE_PAUSED:
return true;
default:
return false;
}
}
bool wxThread::IsPaused() const
{
wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect);
return (m_internal->GetState() == STATE_PAUSED);
}
//--------------------------------------------------------------------
// wxThreadModule
//--------------------------------------------------------------------
#ifdef __WXOSX__
void wxOSXThreadModuleOnInit();
void wxOSXThreadModuleOnExit();
#endif
class wxThreadModule : public wxModule
{
public:
virtual bool OnInit() wxOVERRIDE;
virtual void OnExit() wxOVERRIDE;
private:
wxDECLARE_DYNAMIC_CLASS(wxThreadModule);
};
wxIMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule);
bool wxThreadModule::OnInit()
{
int rc = pthread_key_create(&gs_keySelf, NULL /* dtor function */);
if ( rc != 0 )
{
wxLogSysError(rc, _("Thread module initialization failed: failed to create thread key"));
return false;
}
wxThread::ms_idMainThread = wxThread::GetCurrentId();
gs_mutexAllThreads = new wxMutex();
#ifdef __WXOSX__
wxOSXThreadModuleOnInit();
#else
gs_mutexGui = new wxMutex();
gs_mutexGui->Lock();
#endif
gs_mutexDeleteThread = new wxMutex();
gs_condAllDeleted = new wxCondition(*gs_mutexDeleteThread);
return true;
}
void wxThreadModule::OnExit()
{
wxASSERT_MSG( wxThread::IsMain(), wxT("only main thread can be here") );
// are there any threads left which are being deleted right now?
size_t nThreadsBeingDeleted;
{
wxMutexLocker lock( *gs_mutexDeleteThread );
nThreadsBeingDeleted = gs_nThreadsBeingDeleted;
if ( nThreadsBeingDeleted > 0 )
{
wxLogTrace(TRACE_THREADS,
wxT("Waiting for %lu threads to disappear"),
(unsigned long)nThreadsBeingDeleted);
// have to wait until all of them disappear
gs_condAllDeleted->Wait();
}
}
size_t count;
{
wxMutexLocker lock(*gs_mutexAllThreads);
// terminate any threads left
count = gs_allThreads.GetCount();
if ( count != 0u )
{
wxLogDebug(wxT("%lu threads were not terminated by the application."),
(unsigned long)count);
}
} // unlock mutex before deleting the threads as they lock it in their dtor
for ( size_t n = 0u; n < count; n++ )
{
// Delete calls the destructor which removes the current entry. We
// should only delete the first one each time.
gs_allThreads[0]->Delete();
}
delete gs_mutexAllThreads;
#ifdef __WXOSX__
wxOSXThreadModuleOnExit();
#else
// destroy GUI mutex
gs_mutexGui->Unlock();
delete gs_mutexGui;
#endif
// and free TLD slot
(void)pthread_key_delete(gs_keySelf);
delete gs_condAllDeleted;
delete gs_mutexDeleteThread;
}
// ----------------------------------------------------------------------------
// global functions
// ----------------------------------------------------------------------------
static void ScheduleThreadForDeletion()
{
wxMutexLocker lock( *gs_mutexDeleteThread );
gs_nThreadsBeingDeleted++;
wxLogTrace(TRACE_THREADS, wxT("%lu thread%s waiting to be deleted"),
(unsigned long)gs_nThreadsBeingDeleted,
gs_nThreadsBeingDeleted == 1 ? wxT("") : wxT("s"));
}
static void DeleteThread(wxThread *This)
{
wxLogTrace(TRACE_THREADS, wxT("Thread %p auto deletes."), THR_ID(This));
delete This;
// only lock gs_mutexDeleteThread after deleting the thread to avoid
// calling out into user code with it locked as this may result in
// deadlocks if the thread dtor deletes another thread (see #11501)
wxMutexLocker locker( *gs_mutexDeleteThread );
wxCHECK_RET( gs_nThreadsBeingDeleted > 0,
wxT("no threads scheduled for deletion, yet we delete one?") );
wxLogTrace(TRACE_THREADS, wxT("%lu threads remain scheduled for deletion."),
(unsigned long)gs_nThreadsBeingDeleted - 1);
if ( !--gs_nThreadsBeingDeleted )
{
// no more threads left, signal it
gs_condAllDeleted->Signal();
}
}
#ifndef __DARWIN__
void wxMutexGuiEnterImpl()
{
gs_mutexGui->Lock();
}
void wxMutexGuiLeaveImpl()
{
gs_mutexGui->Unlock();
}
#endif
// ----------------------------------------------------------------------------
// include common implementation code
// ----------------------------------------------------------------------------
#include "wx/thrimpl.cpp"
#endif // wxUSE_THREADS