Added a threading wrapper with basic functionality that needs real testing

This commit is contained in:
dean11 2013-11-26 21:08:34 +01:00
parent 1f27336b3f
commit e4904870b3
14 changed files with 674 additions and 33015 deletions

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -150,12 +150,18 @@
<ClCompile Include="Resource\Loaders\CustomLoader.cpp" />
<ClCompile Include="Resource\OResourceHandler.cpp" />
<ClCompile Include="Resource\OResource.cpp" />
<ClCompile Include="Thread\OysterMutex.cpp" />
<ClCompile Include="Thread\OysterThread_Impl.cpp" />
<ClCompile Include="Utilities.cpp" />
<ClCompile Include="WinTimer.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Resource\OysterResource.h" />
<ClInclude Include="Resource\OResource.h" />
<ClInclude Include="Thread\IThreadObject.h" />
<ClInclude Include="Thread\OysterMutex.h" />
<ClInclude Include="Thread\OysterThread.h" />
<ClInclude Include="Thread\OysterThread_Impl.h" />
<ClInclude Include="Utilities-InlineImpl.h" />
<ClInclude Include="Utilities.h" />
<ClInclude Include="WinTimer.h" />

View File

@ -33,6 +33,12 @@
<ClCompile Include="Resource\Loaders\CustomLoader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Thread\OysterMutex.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Thread\OysterThread_Impl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Utilities.h">
@ -50,5 +56,17 @@
<ClInclude Include="Resource\OResource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Thread\IThreadObject.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Thread\OysterMutex.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Thread\OysterThread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Thread\OysterThread_Impl.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -47,7 +47,7 @@ namespace Oyster
static OResource* Reload (OResource* resource);
static bool Release (OResource* resource);
Utility::DynamicMemory::RefCount resourceRef;
Utility::DynamicMemory::ReferenceCount resourceRef;
private:
static OResource* ByteLoader (const wchar_t filename[], ResourceType type, OResource* old = 0);

View File

@ -0,0 +1,33 @@
#ifndef MISC_I_THREAD_OBJECT_H
#define MISC_I_THREAD_OBJECT_H
namespace Oyster
{
namespace Thread
{
/**
* Inherit this class to get threading compatibility.
*/
class IThreadObject
{
public:
/**
* Override this to get notified when the thread is started.
*/
virtual void ThreadEntry() { }
/**
* Override this to get notified when the thread is about to exit.
*/
virtual void ThreadExit() { }
/**
* This function is required to get threading working.
*/
virtual bool DoWork ( ) = 0;
};
}
}
#endif // !MISC_I_WORKER_THREAD_H

View File

@ -0,0 +1,67 @@
#include "OysterMutex.h"
#include <chrono>
#include <thread>
#include <future>
OysterMutex::OysterMutex()
{}
OysterMutex::OysterMutex(bool initialOwnership)
{
if(initialOwnership)
{
this->mutex.lock();
this->id = std::this_thread::get_id();
}
}
OysterMutex::~OysterMutex()
{
}
void OysterMutex::LockMutex()
{
if(std::this_thread::get_id() == this->id) return;
this->mutex.lock();
this->id = std::this_thread::get_id();
}
void OysterMutex::LockMutex(unsigned int msec)
{
if(std::this_thread::get_id() == this->id) return;
auto start = std::chrono::high_resolution_clock::now();
auto end = start + std::chrono::milliseconds(msec);
do
{
if(this->mutex.try_lock())
{
this->mutex.lock();
this->id = std::this_thread::get_id();
return;
}
} while (std::chrono::high_resolution_clock::now() < end);
this->mutex.lock();
}
void OysterMutex::UnlockMutex()
{
//Let the owner unlock
if(std::this_thread::get_id() != this->id) return;
this->mutex.unlock();
this->id = std::thread::id();
}
bool OysterMutex::IsTaken()
{
return !this->mutex.try_lock();
}
void OysterMutex::Reset()
{
if(!this->mutex.try_lock())
this->mutex.unlock();
}

View File

@ -0,0 +1,29 @@
#ifndef MISC_OYSTER_MUTEX_H
#define MISC_OYSTER_MUTEX_H
#include <mutex>
#include <thread>
#include <atomic>
class OysterMutex
{
public:
OysterMutex();
OysterMutex(bool initialOwnership);
virtual~OysterMutex();
void LockMutex();
void LockMutex(unsigned int timeSpan);
void UnlockMutex();
/** Returns true if mutex is taken */
bool IsTaken();
/** This function resets resource locking */
void Reset();
private:
std::mutex mutex;
std::thread::id id;
OysterMutex(const OysterMutex&);
};
#endif // !MISC_OYSTER_MUTEX_H

View File

@ -0,0 +1,45 @@
#ifndef MISC_OYSTER_THREAD_H
#define MISC_OYSTER_THREAD_H
#include "IThreadObject.h"
namespace Oyster
{
namespace Thread
{
enum OYSTER_THREAD_ERROR
{
OYSTER_THREAD_ERROR_FAILED,
OYSTER_THREAD_ERROR_SUCCESS,
};
class OysterThread
{
private:
struct PrivateData;
PrivateData *privateData;
OysterThread(const OysterThread& original);
const OysterThread& operator=(const OysterThread& original);
public:
OysterThread();
virtual~OysterThread();
OYSTER_THREAD_ERROR Create(IThreadObject* worker, bool start);
OYSTER_THREAD_ERROR Start();
void Stop();
void Pause();
void Pause(int mSec);
void Resume();
OYSTER_THREAD_ERROR Reset(IThreadObject* worker = 0);
void Terminate();
void Wait();
void Wait(int mSec);
OYSTER_THREAD_ERROR Swap(const OysterThread* other);
bool IsActive();
};
}
}
#endif // !MISC_I_OYSTER_THREAD_H

View File

@ -0,0 +1,274 @@
#include "OysterThread.h"
#include <thread>
#include "OysterMutex.h"
#include <assert.h>
#include "..\Utilities.h"
using namespace Oyster::Thread;
using namespace Utility::DynamicMemory::SmartPointer;
#pragma region Declerations
struct ThreadData;
/** A typical Oyster thread function */
typedef void (*ThreadFunction)(StdSmartPointer<ThreadData>&);
enum OYSTER_THREAD_STATE
{
OYSTER_THREAD_STATE_RESET,
OYSTER_THREAD_STATE_RUNNING,
OYSTER_THREAD_STATE_PAUSED,
OYSTER_THREAD_STATE_STOPED,
OYSTER_THREAD_STATE_TERMINATED,
OYSTER_THREAD_STATE_DEAD,
};
//TODO: Add a threadStartPackage struct that contains all the necasary data to fire of a thread
struct ThreadData
{
OYSTER_THREAD_STATE state; //<! The current thread state.
StdSmartPointer<std::thread> workerThread; //<! The worker thread.
std::thread::id callingThread; //<! The owner thread.
IThreadObject *owner; //<! The owner of the thread as IThread.
int msec; //<! A timer in miliseconds.
OysterMutex mutexLock; //<! The lock, locking the member variabls.
ThreadData() {}
private:
ThreadData(const ThreadData&){};
};
struct OysterThread::PrivateData
{
StdSmartPointer<ThreadData> threadData;
PrivateData()
:threadData(new ThreadData())
{
threadData->owner = 0;
threadData->workerThread = 0;
threadData->callingThread;
threadData->state = OYSTER_THREAD_STATE_STOPED;
}
~PrivateData()
{
//@todo TODO: Make detatch avalible.
//this->threadData->workerThread->detach();
this->threadData->owner = 0;
this->threadData->state = OYSTER_THREAD_STATE_DEAD;
}
};
#pragma endregion
static void ThreadingFunction(StdSmartPointer<ThreadData> &origin)
{
bool shouldContinue;
StdSmartPointer<ThreadData> w = origin;
theBegining:
while(w->state == OYSTER_THREAD_STATE_STOPED);
w->mutexLock.LockMutex();
w->owner->ThreadEntry();
w->mutexLock.UnlockMutex();
while (w->state != OYSTER_THREAD_STATE_STOPED && w->state != OYSTER_THREAD_STATE_DEAD)
{
w->mutexLock.LockMutex();
{
shouldContinue = w->owner->DoWork();
}
w->mutexLock.UnlockMutex();
if(!shouldContinue)
{
goto theEnd;
}
if(w->state == OYSTER_THREAD_STATE_TERMINATED)
{
return;
}
else if(w->state == OYSTER_THREAD_STATE_RESET)
{
goto theBegining;
}
else if(w->msec > 0)
{
std::this_thread::sleep_for(std::chrono::milliseconds(w->msec));
}
while (w->state == OYSTER_THREAD_STATE_PAUSED);
}
if(w->state == OYSTER_THREAD_STATE_DEAD)
{
return;
}
theEnd:
w->mutexLock.LockMutex();
w->owner->ThreadExit();
w->mutexLock.UnlockMutex();
w->state = OYSTER_THREAD_STATE_DEAD;
}
OysterThread::OysterThread()
{
this->privateData = new PrivateData();
}
OysterThread::~OysterThread()
{
delete this->privateData;
this->privateData = 0;
}
OYSTER_THREAD_ERROR OysterThread::Create(IThreadObject* worker, bool start)
{
if(!this->privateData) return OYSTER_THREAD_ERROR_FAILED;
if(this->privateData->threadData->workerThread) return OYSTER_THREAD_ERROR_FAILED;
this->privateData->threadData->owner = worker;
ThreadFunction fnc = ThreadingFunction;
/*
//Create a lambda function with current worker instance to fire of the thread
#if defined(DEBUG) || defined (_DEBUG)
ThreadFunction fnc = [](ThreadData* w) -> void
{
while(w->state == OYSTER_THREAD_STATE_STOPED);
w->owner->ThreadEntry();
while (true)
{
w->mutexLock.LockMutex();
w->owner->DoWork();
w->mutexLock.UnlockMutex();
}
w->mutexLock.LockMutex();
w->owner->ThreadExit();
w->mutexLock.UnlockMutex();
};
#else
ThreadFunction fnc = ThreadingFunction;
//ThreadFunction fnc = THREAD_LAMBDA_FUNCTION_CREATION;
#endif
*/
//Maby move this thread creation to a seperate Start() function because std::thread fires the thread when it is created. :(
this->privateData->threadData->workerThread = new std::thread(fnc, this->privateData->threadData);
if(!this->privateData->threadData->workerThread)
return OYSTER_THREAD_ERROR_FAILED;
if(start)
{
//@todo TODO: No need to lock since the other thread end is only reading this value. Worst case scenario is n lost cycles.
this->privateData->threadData->state = OYSTER_THREAD_STATE_RUNNING;
}
return OYSTER_THREAD_ERROR_SUCCESS;
}
OYSTER_THREAD_ERROR OysterThread::Start()
{
if(!this->privateData->threadData->workerThread)
return OYSTER_THREAD_ERROR_FAILED;
this->privateData->threadData->state = OYSTER_THREAD_STATE_RUNNING;
return OYSTER_THREAD_ERROR_SUCCESS;
}
void OysterThread::Stop()
{
this->privateData->threadData->mutexLock.LockMutex();
this->privateData->threadData->state = OYSTER_THREAD_STATE_STOPED;
this->privateData->threadData->mutexLock.UnlockMutex();
}
void OysterThread::Pause()
{
this->privateData->threadData->mutexLock.LockMutex();
this->privateData->threadData->state = OYSTER_THREAD_STATE_PAUSED;
this->privateData->threadData->mutexLock.UnlockMutex();
}
void OysterThread::Pause(int msec)
{
if(std::this_thread::get_id() == this->privateData->threadData->workerThread->get_id())
{
this->privateData->threadData->msec = msec;
}
else
{
this->privateData->threadData->mutexLock.LockMutex();
this->privateData->threadData->state = OYSTER_THREAD_STATE_PAUSED;
this->privateData->threadData->msec = msec;
this->privateData->threadData->mutexLock.UnlockMutex();
}
}
void OysterThread::Resume()
{
this->privateData->threadData->mutexLock.LockMutex();
this->privateData->threadData->state = OYSTER_THREAD_STATE_RUNNING;
this->privateData->threadData->mutexLock.UnlockMutex();
}
OYSTER_THREAD_ERROR OysterThread::Reset(IThreadObject* worker)
{
this->privateData->threadData->mutexLock.LockMutex();
if(worker)
{
this->privateData->threadData->owner = worker;
}
this->privateData->threadData->callingThread = std::this_thread::get_id();
this->privateData->threadData->msec = 0;
this->privateData->threadData->mutexLock.UnlockMutex();
return OYSTER_THREAD_ERROR_SUCCESS;
}
void OysterThread::Terminate()
{
delete this->privateData->threadData->workerThread;
this->privateData->threadData->mutexLock.Reset();
this->privateData->threadData->workerThread = 0;
this->privateData->threadData->callingThread = std::thread::id();
this->privateData->threadData->msec = 0;
this->privateData->threadData->state = OYSTER_THREAD_STATE_STOPED;
}
void OysterThread::Wait()
{
if(this->privateData->threadData->state == OYSTER_THREAD_STATE_DEAD)
{
return;
}
if(this->privateData->threadData->workerThread->get_id() == std::this_thread::get_id()) return;
this->privateData->threadData->workerThread->join();
}
void OysterThread::Wait(int msec)
{
if(this->privateData->threadData->workerThread->get_id() == std::this_thread::get_id()) return;
std::this_thread::sleep_for(std::chrono::milliseconds(msec));
}
OYSTER_THREAD_ERROR OysterThread::Swap(const OysterThread* other)
{
this->privateData->threadData->workerThread->swap(*other->privateData->threadData->workerThread);
return OYSTER_THREAD_ERROR_SUCCESS;
}
bool OysterThread::IsActive()
{
if (this->privateData->threadData->state == OYSTER_THREAD_STATE_RUNNING)
return true;
return false;
}

View File

@ -0,0 +1,41 @@
#ifndef INCLUDE_GUARD_OYSTER_THREAD_H
#define INCLUDE_GUARD_OYSTER_THREAD_H
#include "IOysterThread.h"
#include <thread>
namespace Oyster
{
namespace Thread
{
//class OysterThread :public IOysterThread
//{
//private:
// std::thread thread; //<! The worker thread.
// IWorkerThread *owner; //<! The owner of the thread as IThread
//
//public:
// bool Wait();
// bool Suspend();
// void Resume();
// bool Kill();
// bool IsActive();
// bool Reset();
// bool StartThread()
// {
// this->owner->ThreadEntry();
//
// while (true)
// {
// this->owner->DoWork();
// }
//
// this->owner->ThreadExit();
// }
//};
}
}
#endif // !INCLUDE_GUARD_OYSTER_THREAD_H

View File

@ -2,6 +2,7 @@
// Inline and template implementations for
// the Utility Collection of Miscellanious Handy Functions
// © Dan Andersson 2013
// © Dennis Andersen 2013 TODO: Is this correct?
/////////////////////////////////////////////////////////////////////
#ifndef UTILITIES_INLINE_IMPL_H
@ -149,8 +150,7 @@ namespace Utility
return this->ownedArray != NULL;
}
template<typename Type>
Type* UniqueArray<Type>::Release()
template<typename Type>Type* UniqueArray<Type>::Release()
{
Type *copy = this->ownedArray;
this->ownedArray = NULL;
@ -162,6 +162,113 @@ namespace Utility
{
return this->operator bool();
}
namespace SmartPointer
{
template<typename T>
void StdSmartPointer<T>::Destroy()
{
delete this->_rc;
this->_rc = NULL;
delete this->_ptr;
this->_ptr = NULL;
}
template<typename T> StdSmartPointer<T>::StdSmartPointer()
:_rc(0), _ptr(0)
{ }
template<typename T> StdSmartPointer<T>::StdSmartPointer(T* p)
:_ptr(p)
{
this->_rc = new ReferenceCount();
this->_rc->Incref();
}
template<typename T> StdSmartPointer<T>::StdSmartPointer(const StdSmartPointer& d)
:_ptr(d._ptr), _rc(d._rc)
{
if(this->_rc)
this->_rc->Incref();
}
template<typename T> StdSmartPointer<T>::~StdSmartPointer()
{
if (this->_rc && this->_rc->Decref() == 0)
{
Destroy();
}
}
template<typename T> StdSmartPointer<T>& StdSmartPointer<T>::operator= (const StdSmartPointer<T>& p)
{
if (this != &p)
{
//Last to go?
if(this->_rc && this->_rc->Release() == 0)
{
//Call child specific
Destroy();
}
this->_ptr = p._ptr;
this->_rc = p._rc;
this->_rc->Add();
}
return *this;
}
template<typename T> StdSmartPointer<T>& StdSmartPointer<T>::operator= (T* p)
{
if (this->_ptr != p)
{
//Last to go?
if(this->_rc)
{
if(this->_rc->Decref() == 0)
{
//Call child specific
Destroy();
this->_rc = new ReferenceCount();
}
}
else
this->_rc = new ReferenceCount();
this->_ptr = p;
this->_rc->Incref();
}
return *this;
}
template<typename T> inline bool StdSmartPointer<T>::operator== (const StdSmartPointer<T>& d)
{
return d._ptr == this->_ptr;
}
template<typename T> inline bool StdSmartPointer<T>::operator== (const T& p)
{
return &p == this->_ptr;
}
template<typename T> inline T& StdSmartPointer<T>::operator* ()
{
return *this->_ptr;
}
template<typename T> inline T* StdSmartPointer<T>::operator-> ()
{
return this->_ptr;
}
template<typename T> inline StdSmartPointer<T>::operator T* ()
{
return this->_ptr;
}
/**
* Returns the connected pointer */
template<typename T> inline T* StdSmartPointer<T>::Get()
{
return this->_ptr;
}
/** Checks if the pointer is valid (not NULL)
Returns true for valid, else false. */
template<typename T> inline bool StdSmartPointer<T>::IsValid()
{
return (this->_ptr != NULL) ? true : false;
}
}
}
}

View File

@ -1,6 +1,7 @@
/////////////////////////////////////////////////////////////////////
// Utility Collection of Miscellanious Handy Functions
// © Dan Andersson 2013
// © Dennis Andersen 2013
/////////////////////////////////////////////////////////////////////
#ifndef UTILITIES_H
@ -106,20 +107,62 @@ namespace Utility
mutable Type *ownedArray;
};
struct RefCount
struct ReferenceCount
{
private:
int count;
public:
RefCount() :count(0) { }
RefCount(const RefCount& o) { count = o.count; }
const RefCount& operator=(const RefCount& o) { count = o.count; return *this;}
void Incref() { this->count++; }
void Incref(int c) { this->count += c; }
int Decref() { return --this->count;}
void Reset() { this->count = 0; }
ReferenceCount() :count(0) { }
ReferenceCount(const ReferenceCount& o) { count = o.count; }
inline const ReferenceCount& operator=(const ReferenceCount& o) { count = o.count; return *this;}
inline void Incref() { this->count++; }
inline void Incref(int c) { this->count += c; }
inline int Decref() { return --this->count;}
inline void Reset() { this->count = 0; }
};
namespace SmartPointer
{
//! Smart pointer for a regular object.
/**
* Regular objects, objects that is deleted normaly (ie not COM objects, or array pointers)
* can use this class to easy the use of dynamic memory
*/
template<typename T>
struct StdSmartPointer
{
private:
ReferenceCount *_rc;
T *_ptr;
/** Destroys the pointer and returns the memory allocated. */
void Destroy();
public:
StdSmartPointer();
StdSmartPointer(T* p);
StdSmartPointer(const StdSmartPointer& d);
virtual~StdSmartPointer();
StdSmartPointer<T>& operator= (const StdSmartPointer<T>& p);
StdSmartPointer<T>& operator= (T* p);
bool operator== (const StdSmartPointer<T>& d);
bool operator== (const T& p);
T& operator* ();
T* operator-> ();
operator T* ();
/**
* Returns the connected pointer */
T* Get();
/** Checks if the pointer is valid (not NULL)
Returns true for valid, else false. */
bool IsValid();
};
}
}
namespace String