thread.cpp
来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 1,353 行 · 第 1/3 页
CPP
1,353 行
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;
DECLARE_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;
};
THREAD_RETVAL THREAD_CALLCONV wxThreadInternal::WinThreadStart(void *param)
{
THREAD_RETVAL rc;
wxThread * const thread = (wxThread *)param;
// first of all, check whether we hadn't been cancelled already and don't
// start the user code at all then
const bool hasExited = thread->m_internal->GetState() == STATE_EXITED;
if ( hasExited )
{
rc = (THREAD_RETVAL)-1;
}
else // do run thread
{
// store the thread object in the TLS
if ( !::TlsSetValue(gs_tlsThisThread, thread) )
{
wxLogSysError(_("Can not start thread: error writing TLS."));
return (DWORD)-1;
}
rc = (THREAD_RETVAL)thread->Entry();
}
thread->OnExit();
// save IsDetached because thread object can be deleted by joinable
// threads after state is changed to STATE_EXITED.
bool isDetached = thread->IsDetached();
if ( !hasExited )
{
// enter m_critsect before changing the thread state
wxCriticalSectionLocker lock(thread->m_critsect);
thread->m_internal->SetState(STATE_EXITED);
}
// 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,
_T("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
// Watcom is reported to not like 0 stack size (which means "use default"
// for the other compilers and is also the default value for stackSize)
#ifdef __WATCOMC__
if ( !stackSize )
stackSize = 10240;
#endif // __WATCOMC__
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 != WXTHREAD_DEFAULT_PRIORITY )
{
SetPriority(m_priority);
}
return true;
}
wxThreadError wxThreadInternal::Kill()
{
if ( !::TerminateThread(m_hThread, (DWORD)-1) )
{
wxLogSysError(_("Couldn't terminate thread"));
return wxTHREAD_MISC_ERROR;
}
Free();
return wxTHREAD_NO_ERROR;
}
wxThreadError
wxThreadInternal::WaitForTerminate(wxCriticalSection& cs,
wxThread::ExitCode *pRc,
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;
wxThread::ExitCode 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();
}
// now wait for thread to finish
if ( wxThread::IsMain() )
{
// set flag for wxIsWaitingForThread()
gs_waitingForThread = true;
}
// 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();
}
}
#if !defined(QS_ALLPOSTMESSAGE)
#define QS_ALLPOSTMESSAGE 0
#endif
result = ::MsgWaitForMultipleObjects
(
1, // number of objects to wait for
&m_hThread, // the objects
false, // don't wait for all objects
INFINITE, // no timeout
QS_ALLINPUT|QS_ALLPOSTMESSAGE // return as soon as there are any events
);
switch ( result )
{
case 0xFFFFFFFF:
// error
wxLogSysError(_("Can not wait for thread termination"));
Kill();
return wxTHREAD_KILLED;
case WAIT_OBJECT_0:
// thread we're waiting for terminated
break;
case WAIT_OBJECT_0 + 1:
// 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() )
{
// it looks that sometimes WAIT_OBJECT_0 + 1 is
// returned but there are no messages in the thread
// queue -- prevent DoMessageFromThreadWait() from
// blocking inside ::GetMessage() forever in this case
::PostMessage(NULL, WM_NULL, 0, 0);
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits()
: NULL;
if ( traits && !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, (LPDWORD)&rc) )
{
wxLogLastError(wxT("GetExitCodeThread"));
rc = (wxThread::ExitCode)-1;
break;
}
if ( (DWORD)rc != STILL_ACTIVE )
break;
// give the other thread some time to terminate, otherwise we may be
// starving it
::Sleep(1);
}
if ( pRc )
*pRc = rc;
// we don't need the thread handle any more in any case
Free();
return rc == (wxThread::ExitCode)-1 ? wxTHREAD_MISC_ERROR
: wxTHREAD_NO_ERROR;
}
bool wxThreadInternal::Suspend()
{
DWORD nSuspendCount = ::SuspendThread(m_hThread);
if ( nSuspendCount == (DWORD)-1 )
{
wxLogSysError(_("Can not suspend thread %x"), m_hThread);
return false;
}
m_state = STATE_PAUSED;
return true;
}
bool wxThreadInternal::Resume()
{
DWORD nSuspendCount = ::ResumeThread(m_hThread);
if ( nSuspendCount == (DWORD)-1 )
{
wxLogSysError(_("Can not resume thread %x"), 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;
}
bool wxThread::IsMain()
{
return ::GetCurrentThreadId() == gs_idMainThread || gs_idMainThread == 0;
}
void wxThread::Yield()
{
// 0 argument to Sleep() is special and means to just give away the rest of
// our timeslice
::Sleep(0);
}
void wxThread::Sleep(unsigned long milliseconds)
{
::Sleep(milliseconds);
}
int wxThread::GetCPUCount()
{
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwNumberOfProcessors;
}
unsigned long wxThread::GetCurrentId()
{
return (unsigned long)::GetCurrentThreadId();
}
bool wxThread::SetConcurrency(size_t WXUNUSED_IN_WINCE(level))
{
#ifdef __WXWINCE__
return false;
#else
wxASSERT_MSG( IsMain(), _T("should only be called from the main thread") );
// ok only for the default one
if ( level == 0 )
return 0;
// get system affinity mask first
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?