The source files for this module are listed below. You can also download the module C_Thread as a zip archive; this archive contains all the source files and documentation.
TThread provides easy multi-threading support to your application, where each thread is maintained by a separate object.
To start using multithreading, derive your class from TThread. Then overload the Action routine; all processing that has to be done in your worker thread should be placed in this routine. You can return the result of your Action as a void*, so passing everything up to 4-byte sized return values (the size of a void*) is effortless. When you however want to return a pointer to something, make sure that someone will deallocate the structure referenced to. The result of the action can be called from each thread via the Result method.
If you need to do some intitialization or destruction in your thread, overload the ThreadInit and/or the ThreadEnd routines. These will be guaranteed to run before and after your Action code.
To actually use the threading class, create an object of your TThread derived class and call it's Start method. The Start method will return immediately, and soon after the thread object's Action routine will start running on a separate thread.
Since the thread object belongs in two threads (the thread controlling part and the worker part), the object may not destruct the originating thread e.g. when it goes out of scope, since the thread object will be needed in the worker as well.
The option of where to destroy the thread object can be specified as a parameter to the Start method. If you choose to let the object self destruct, the object will be deleted as a last step in the thread's lifetime (after the ThreadEnd method has run).
Three indicators are available to get the current state of the thread.
When the thread is done running it's worker specific code, the State will reflect this. You can then re-start the thread if you didn't specify that the thread should auto-destruct.
To get thread-specific details about your newly created worker thread object, you can use the GetID and GetHandle methods. The handle that GetHandle returns will be closed when the thread object destructs.
Each file belonging to this source code module is listed below.
/*******************************************************************************
Version: 6
Author: Carl Colijn, TwoLogs
Contact: c.colijn@twologs.com
Source: https://www.twologs.com/sourcecode
This code is freely distributable, as long as this comment remains intact.
If you find this source useful, you may use this code in your own projects
free of charge, but some acknowledgement to the author of this code is always
appreciated :)
The source is however distributed 'as is' without waranty and/or support, and
may not be fit for each and every application. Use it at your own discretion
and at your own risk.
The source already has undergone testing. This doesn't mean however that all
bugs are removed from this piece of code. If you find one of them, please
contact me about it. I can however not guarantee when and if the bug will be
fixed.
More information about this module can be found in the accompanying HTML file.
*******************************************************************************/
#ifndef INCLUDE_TWOLOGS_COMMON_THREAD_H
#define INCLUDE_TWOLOGS_COMMON_THREAD_H
#include <windows.h>
// Phase of the thread execution
enum class TThreadState {
idle,
starting,
busy,
finishing,
error
};
class TThread {
public:
// Con- & destructor
TThread();
virtual ~TThread();
// Starts up the thread. If you specify the object should auto-
// destruct, it will be deleted at the end of the thread's work
bool Start(bool autoDestruct);
// Returns the state the thread is in
TThreadState State() const;
// Returns whether the thread is still busy
bool IsBusy() const;
// Returns the result
void* Result() const;
// Gets the thread's ID
unsigned int GetID() const;
// Gets the thread's handle
HANDLE GetHandle() const;
// Kills the thread
void Kill(DWORD exitCode);
// Waits for the thread to stop, possibly only waiting the given time (in mSec)
// Returns if the thread stopped
bool WaitTillDone(DWORD waitTime = INFINITE);
protected:
// Handle to the thread
HANDLE m_thread;
// The thread's ID
unsigned int m_threadID;
// The result of the thread
void* m_resultPtr;
// Thread initialization and destruction
virtual bool ThreadInit();
virtual void ThreadEnd();
// Action to perform
virtual void* Action();
private:
// Our current state
TThreadState m_state;
// Whether we should auto-destruct
bool m_autoDestruct;
// Thread startpoint helper function
static unsigned int __stdcall StartThread(void* voidThreadPtr);
};
#endif // INCLUDE_TWOLOGS_COMMON_THREAD_H
/*******************************************************************************
Version: 6
Author: Carl Colijn, TwoLogs
Contact: c.colijn@twologs.com
Source: https://www.twologs.com/sourcecode
This code is freely distributable, as long as this comment remains intact.
If you find this source useful, you may use this code in your own projects
free of charge, but some acknowledgement to the author of this code is always
appreciated :)
The source is however distributed 'as is' without waranty and/or support, and
may not be fit for each and every application. Use it at your own discretion
and at your own risk.
The source already has undergone testing. This doesn't mean however that all
bugs are removed from this piece of code. If you find one of them, please
contact me about it. I can however not guarantee when and if the bug will be
fixed.
More information about this module can be found in the accompanying HTML file.
*******************************************************************************/
#include "Thread.h"
#include <process.h>
// Constructor
TThread::TThread():
m_thread(NULL),
m_threadID(0),
m_resultPtr(nullptr),
m_state(TThreadState::idle),
m_autoDestruct(false) {
}
// Destructor
TThread::~TThread() {
// Look if we still have a thread handle lying around
if (m_thread != NULL) {
// Yes -> close the handle
CloseHandle(m_thread);
}
}
// Starts up the thread. If you specify the object should auto-
// destruct, it will be deleted at the end of the thread's work
bool TThread::Start(bool autoDestruct) {
// Look if we have already started
bool allOK = false;
if (!IsBusy()) {
// No -> close any old thread handle
if (m_thread != NULL) {
CloseHandle(m_thread);
}
// And try to start the new thread
m_autoDestruct = autoDestruct;
m_state = TThreadState::starting;
m_thread = (HANDLE)_beginthreadex(nullptr, 0, StartThread, this, 0, &m_threadID);
allOK = m_thread != NULL;
if (!allOK) {
m_state = TThreadState::error;
}
}
return allOK;
}
// Returns the state the thread is in
TThreadState TThread::State() const {
return m_state;
}
// Returns whether the thread is still busy
bool TThread::IsBusy() const {
return m_state != TThreadState::idle &&
m_state != TThreadState::error;
}
// Returns the result
void* TThread::Result() const {
return m_resultPtr;
}
// Gets the thread's ID
unsigned int TThread::GetID() const {
return m_threadID;
}
// Gets the thread's handle
HANDLE TThread::GetHandle() const {
return m_thread;
}
// Kills the thread
void TThread::Kill(DWORD exitCode) {
// Look if the thread is actually busy
if (IsBusy()) {
// Yes -> terminate it forcefully
if (m_thread != NULL) {
TerminateThread(m_thread, exitCode);
// Note: must use _endthreadex according to docu, but can't
// pass the thread handle to it?! How lame is that...
}
// And correctly set our state
m_state = TThreadState::error;
}
}
// Waits for the thread to stop, possibly only waiting the given time (in mSec)
// Returns if the thread stopped
bool TThread::WaitTillDone(DWORD waitTime) {
// Look if it has already stopped
bool threadStopped = !IsBusy();
if (!threadStopped) {
// No -> wait for it to stop
if (m_thread != NULL) {
WaitForSingleObject(m_thread, waitTime);
}
// And look if it has stopped now
threadStopped = !IsBusy();
}
// And return the verdict
return threadStopped;
}
// Thread initialization
bool TThread::ThreadInit() {
return true;
}
// Thread destruction
void TThread::ThreadEnd() {
}
// Action to perform
void* TThread::Action() {
return nullptr;
}
// Thread startpoint helper function
unsigned int __stdcall TThread::StartThread(void* voidThreadPtr) {
// Easy try-run-and-catch-on-error for simple statements
#define TRYTOPERFORM(what, newErrorCode) \
try { \
what; \
} \
catch (...) { \
errorCode = newErrorCode; \
}
// Get the thread object to use
TThread* threadPtr = (TThread*)voidThreadPtr;
DWORD errorCode = 0;
// Do thread initialization
bool proceedAfterInit = false;
TRYTOPERFORM(proceedAfterInit = threadPtr->ThreadInit(), 1);
if (proceedAfterInit) {
// Init OK -> note we've started
TRYTOPERFORM(threadPtr->m_state = TThreadState::busy, 2);
// Perform the action
TRYTOPERFORM(threadPtr->m_resultPtr = threadPtr->Action(), 3);
// Note we're done
TRYTOPERFORM(threadPtr->m_state = TThreadState::finishing, 4);
// And do thread destruct code
TRYTOPERFORM(threadPtr->ThreadEnd(), 5);
}
// Note the thread is done, and is safe to delete if needed
TRYTOPERFORM(threadPtr->m_state = TThreadState::idle, 6);
// Look if any errors occured up till now
if (errorCode != 0) {
// Yes -> report this as well
TRYTOPERFORM(threadPtr->m_state = TThreadState::error, 7);
}
// Look if the thread should auto-destruct
bool selfDestruct = false;
TRYTOPERFORM(selfDestruct = threadPtr->m_autoDestruct, 8);
if (selfDestruct) {
// Yes -> kill it now
TRYTOPERFORM(delete threadPtr, 9);
}
// And return what happened
return errorCode;
}