📄 threads_w32.cpp
字号:
/************************************************************/
/* */
/* Copley Motion Libraries */
/* */
/* Author: Stephen Glow */
/* */
/* Copyright (c) 2002-2005 Copley Controls Corp. */
/* http://www.copleycontrols.com */
/* */
/************************************************************/
#include <windows.h>
#include <process.h>
#include "CML.h"
CML_NAMESPACE_USE();
/* local data */
DWORD tlsIndex = TLS_OUT_OF_INDEXES;
Mutex globalThreadMutex;
/* local classes */
struct WinThreadData
{
// Pointer to the thread object
Thread *thread;
// Mutex used to synchronize access to the thread specific data
Mutex mtx;
// True if the thread is running.
bool running;
// This event is signaled when the thread is destroyed
HANDLE killEvent;
// The thread signals this event when it exits.
HANDLE exitEvent;
// The thread's handle
HANDLE threadHandle;
WinThreadData( Thread *t )
{
thread = t;
running = false;
// Create events used to manage the thread
killEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
exitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
}
~WinThreadData(){
if( killEvent ) CloseHandle( killEvent );
if( exitEvent ) CloseHandle( exitEvent );
}
};
/**************************************************
* This class is used to terminate the running thread.
* It's thrown as an exception, and caught in the
* thread starter stub. This allows my stack to
* unwind properly in a thread that is stopped.
**************************************************/
class ThreadExitException
{
};
/* local functions */
static void __cdecl ThreadStarter( void *arg );
static WinThreadData *GetThreadData( void );
static void KillThread( WinThreadData *tData );
/* global functions */
const Error *WaitOnWindowsObject( HANDLE hndl, int32 timeout );
/***************************************************************************/
/**
Default constructor for a new thread object. For the pthreads version of
this class the default constructor doesn't do anything.
*/
/***************************************************************************/
Thread::Thread( void )
{
priority = 5;
WinThreadData *tData = new WinThreadData( this );
// Make sure the events were created successfully
if( !tData->killEvent || !tData->exitEvent )
{
delete tData;
data = 0;
}
else
data = tData;
}
/***************************************************************************/
/**
Destructor for a thread. Causes the thread to be cancelled and waits for
it to finish.
*/
/***************************************************************************/
Thread::~Thread( void )
{
// Stop the thread
const Error *err = stop(500);
// If this fails I just return without deleting the thread data.
// This really shouldn't happen!
if( err )
{
cml.Error( "Error stopping thread in destructor: %s\n", err->toString() );
return;
}
// Lock the mutex to make sure the thread really stopped
WinThreadData *tData = (WinThreadData*)data;
tData->mtx.Lock();
delete data;
}
/***************************************************************************/
/**
Set the threads priority. The priority must be specified in the range 0
(lowest priority) to 9 (highest priority). The default thread priority
is 5 and this will be used if the priority is not explicitely set.
This funciton must be called before the thread is started. Calling it
after the thread has already started will have no effect.
@param pri The thread's priority, in the range 0 to 9.
@return A valid error object.
*/
/***************************************************************************/
const Error *Thread::setPriority( int pri )
{
if( pri < 0 || pri > 9 )
return &ThreadError::BadParam;
priority = pri;
return 0;
}
/***************************************************************************/
/**
Start this thread. The thread's virtual run() function will be called when
the thread has started.
*/
/***************************************************************************/
const Error *Thread::start( void )
{
HANDLE h;
const Error *err = 0;
// Make sure my local data was properly allocated by the
// constructor.
if( !data ) return &ThreadError::Alloc;
// Allocate a single slot of thread local storage if not already done.
globalThreadMutex.Lock();
if( tlsIndex == TLS_OUT_OF_INDEXES )
tlsIndex = TlsAlloc();
if( tlsIndex == TLS_OUT_OF_INDEXES )
err = &ThreadError::Alloc;
globalThreadMutex.Unlock();
if( err ) return err;
WinThreadData *tData = (WinThreadData*)data;
tData->mtx.Lock();
// Make sure the thread isn't already started
if( tData->running )
err = &ThreadError::Running;
else if( (h = (HANDLE)_beginthread( ThreadStarter, 0, tData )) == (HANDLE)-1 )
err = &ThreadError::Start;
else
{
int pri;
switch( priority )
{
case 0: pri = THREAD_PRIORITY_IDLE; break;
case 1: pri = THREAD_PRIORITY_LOWEST; break;
case 2: pri = THREAD_PRIORITY_LOWEST; break;
case 3: pri = THREAD_PRIORITY_BELOW_NORMAL; break;
case 4: pri = THREAD_PRIORITY_BELOW_NORMAL; break;
case 5: pri = THREAD_PRIORITY_NORMAL; break;
case 6: pri = THREAD_PRIORITY_ABOVE_NORMAL; break;
case 7: pri = THREAD_PRIORITY_ABOVE_NORMAL; break;
case 8: pri = THREAD_PRIORITY_HIGHEST; break;
case 9: pri = THREAD_PRIORITY_TIME_CRITICAL; break;
default: pri = THREAD_PRIORITY_NORMAL; break;
}
SetThreadPriority( h, pri );
tData->running = true;
}
tData->mtx.Unlock();
return err;
}
/***************************************************************************/
/**
Stop the thread. The threads stack should unwind properly when stopped.
Note, I believe that there are race conditions if the thread returns and
another thread trys to stop it at the same time. Also make sure that
things are properly cleaned up when the thread returns.
I may need to add a mutex to the thread object to help manage this.
*/
/***************************************************************************/
const Error *Thread::stop( int32 timeout )
{
cml.Debug( "Stopping thread %p\n", this );
if( !data ) return &ThreadError::Alloc;
WinThreadData *tData = (WinThreadData *)data;
tData->mtx.Lock();
// Just return if the thread wasn't running
if( !tData->running )
{
tData->mtx.Unlock();
return 0;
}
tData->mtx.Unlock();
// If this happens to by the running thread, then throw an exception.
// This will cause my stack to unwind until the exception is caught
// by the starter function.
if( tData == GetThreadData() )
{
cml.Debug( "Thread %p is running thread, exiting\n", this );
throw ThreadExitException();
}
// Otherwise, signal the thread to kill itself
SetEvent( tData->killEvent );
if( !timeout ) return 0;
// Wait for the thread to exit
DWORD ret = WaitForSingleObject( tData->exitEvent, timeout );
switch( ret )
{
case WAIT_OBJECT_0:
cml.Debug( "Thread %p successfully stopped\n", this );
break;
case WAIT_TIMEOUT:
cml.Warn( "Thread %p, timeout waiting for exit\n", this );
return &ThreadError::Timeout;
default:
cml.Warn( "Thread %p, error %d waiting for exit\n", this, ret );
break;
}
return 0;
}
/***************************************************************************/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -