diff --git a/core/ThreadSupport.cpp b/core/ThreadSupport.cpp new file mode 100644 index 00000000..5a8689d0 --- /dev/null +++ b/core/ThreadSupport.cpp @@ -0,0 +1,35 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#include "ThreadSupport.h" +#include "sm_globals.h" +#include "ShareSys.h" + +#if defined PLATFORM_POSIX +#include "thread/PosixThreads.h" +#elif defined PLATFORM_WINDOWS +#include "thread/WinThreads.h" +#endif + +MainThreader g_MainThreader; +IThreader *g_pThreader = &g_MainThreader; + +class RegThreadStuff : public SMGlobalClass +{ +public: + void OnSourceModAllInitialized() + { + g_ShareSys.AddInterface(NULL, g_pThreader); + } +}; diff --git a/core/ThreadSupport.h b/core/ThreadSupport.h new file mode 100644 index 00000000..b7277def --- /dev/null +++ b/core/ThreadSupport.h @@ -0,0 +1,33 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod Threader API + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_THREAD_SUPPORT_H +#define _INCLUDE_SOURCEMOD_THREAD_SUPPORT_H + +#include + +using namespace SourceMod; + +extern IThreader *g_pThreader; + +#endif //_INCLUDE_SOURCEMOD_THREAD_SUPPORT_H diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 66206180..7568de8d 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -41,7 +41,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\;..\systems;..\..\public;..\..\public\sourcepawn" - PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SOURCEMOD_MM_EXPORTS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;SOURCEMOD_BUILD" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SOURCEMOD_MM_EXPORTS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;SOURCEMOD_BUILD;SM_DEFAULT_THREADER" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="1" @@ -120,7 +120,7 @@ + + @@ -391,6 +395,10 @@ RelativePath="..\sm_autonatives.h" > + + @@ -407,6 +415,14 @@ RelativePath="..\..\public\sm_platform.h" > + + + + @@ -439,6 +455,10 @@ RelativePath="..\TextParsers.h" > + + @@ -913,6 +933,42 @@ > + + + + + + + + + + + + + + + + + + diff --git a/core/thread/BaseWorker.cpp b/core/thread/BaseWorker.cpp new file mode 100644 index 00000000..0179b788 --- /dev/null +++ b/core/thread/BaseWorker.cpp @@ -0,0 +1,263 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#include "BaseWorker.h" + +BaseWorker::BaseWorker() : + m_perFrame(SM_DEFAULT_THREADS_PER_FRAME), + m_state(Worker_Stopped) +{ +} + +BaseWorker::~BaseWorker() +{ + if (m_state != Worker_Stopped || m_state != Worker_Invalid) + Stop(true); + + if (m_ThreadQueue.size()) + Flush(true); +} + +void BaseWorker::MakeThread(IThread *pThread) +{ + ThreadParams pt; + + pt.flags = Thread_AutoRelease; + pt.prio = ThreadPrio_Normal; + + MakeThread(pThread, &pt); +} + +IThreadHandle *BaseWorker::MakeThread(IThread *pThread, ThreadFlags flags) +{ + ThreadParams pt; + + pt.flags = flags; + pt.prio = ThreadPrio_Normal; + + return MakeThread(pThread, &pt); +} + +IThreadHandle *BaseWorker::MakeThread(IThread *pThread, const ThreadParams *params) +{ + if (m_state != Worker_Running) + return NULL; + + SWThreadHandle *swt = new SWThreadHandle(this, params, pThread); + + AddThreadToQueue(swt); + + return swt; +} + +void BaseWorker::GetPriorityBounds(ThreadPriority &max, ThreadPriority &min) +{ + max = ThreadPrio_Normal; + min = ThreadPrio_Normal; +} + +unsigned int BaseWorker::Flush(bool flush_cancel) +{ + SWThreadHandle *swt; + unsigned int num = 0; + + while ((swt=PopThreadFromQueue()) != NULL) + { + swt->m_state = Thread_Done; + if (!flush_cancel) + swt->pThread->RunThread(swt); + swt->pThread->OnTerminate(swt, flush_cancel); + if (swt->m_params.flags & Thread_AutoRelease) + delete swt; + num++; + } + + return num; +} + +SWThreadHandle *BaseWorker::PopThreadFromQueue() +{ + if (!m_ThreadQueue.size()) + return NULL; + + SourceHook::List::iterator begin; + SWThreadHandle *swt; + + begin = m_ThreadQueue.begin(); + swt = (*begin); + m_ThreadQueue.erase(begin); + + return swt; +} + +void BaseWorker::AddThreadToQueue(SWThreadHandle *pHandle) +{ + m_ThreadQueue.push_back(pHandle); +} + +unsigned int BaseWorker::GetMaxThreadsPerFrame() +{ + return m_perFrame; +} + +WorkerState BaseWorker::GetStatus(unsigned int *threads) +{ + if (threads) + *threads = m_perFrame; + + return m_state; +} + +unsigned int BaseWorker::RunFrame() +{ + unsigned int done = 0; + unsigned int max = GetMaxThreadsPerFrame(); + SWThreadHandle *swt = NULL; + IThread *pThread = NULL; + + while (done < max) + { + if ((swt=PopThreadFromQueue()) == NULL) + break; + pThread = swt->pThread; + swt->m_state = Thread_Running; + pThread->RunThread(swt); + swt->m_state = Thread_Done; + pThread->OnTerminate(swt, false); + if (swt->m_params.flags & Thread_AutoRelease) + delete swt; + done++; + } + + return done; +} + +void BaseWorker::SetMaxThreadsPerFrame(unsigned int threads) +{ + m_perFrame = threads; +} + +bool BaseWorker::Start() +{ + if (m_state != Worker_Invalid && m_state != Worker_Stopped) + { + return false; + } + + m_state = Worker_Running; + + return true; +} + +bool BaseWorker::Stop(bool flush_cancel) +{ + if (m_state == Worker_Invalid || m_state == Worker_Stopped) + return false; + + if (m_state == Worker_Paused) + { + if (!Unpause()) + return false; + } + + m_state = Worker_Stopped; + Flush(flush_cancel); + + return true; +} + +bool BaseWorker::Pause() +{ + if (m_state != Worker_Running) + return false; + + m_state = Worker_Paused; + + return true; +} + + +bool BaseWorker::Unpause() +{ + if (m_state != Worker_Paused) + return false; + + m_state = Worker_Running; + + return true; +} + +/*********************** + * THREAD HANDLE STUFF * + ***********************/ + +void SWThreadHandle::DestroyThis() +{ + delete this; +} + +void SWThreadHandle::GetParams(ThreadParams *p) +{ + *p = m_params; +} + +ThreadPriority SWThreadHandle::GetPriority() +{ + return m_params.prio; +} + +ThreadState SWThreadHandle::GetState() +{ + return m_state; +} + +IThreadCreator *SWThreadHandle::Parent() +{ + return m_parent; +} + +bool SWThreadHandle::SetPriority(ThreadPriority prio) +{ + if (m_params.prio != ThreadPrio_Normal) + return false; + + m_params.prio = prio; + + return true; +} + +bool SWThreadHandle::Unpause() +{ + if (m_state != Thread_Paused) + return false; + + m_state = Thread_Running; + + return true; +} + +bool SWThreadHandle::WaitForThread() +{ + return false; +} + +SWThreadHandle::SWThreadHandle(IThreadCreator *parent, const ThreadParams *p, IThread *thread) : + m_state(Thread_Paused), m_params(*p), m_parent(parent), pThread(thread) +{ +} + +IThread *SWThreadHandle::GetThread() +{ + return pThread; +} diff --git a/core/thread/BaseWorker.h b/core/thread/BaseWorker.h new file mode 100644 index 00000000..20df57d2 --- /dev/null +++ b/core/thread/BaseWorker.h @@ -0,0 +1,86 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_BASEWORKER_H +#define _INCLUDE_SOURCEMOD_BASEWORKER_H + +#include "sh_list.h" +#include "ThreadSupport.h" + +#define SM_DEFAULT_THREADS_PER_FRAME 1 + +class BaseWorker; + +//SW = Simple Wrapper +class SWThreadHandle : public IThreadHandle +{ + friend class BaseWorker; +public: + SWThreadHandle(IThreadCreator *parent, const ThreadParams *p, IThread *thread); + IThread *GetThread(); +public: + //NOTE: We don't support this by default. + //It's specific usage that'd require many mutexes + virtual bool WaitForThread(); +public: + virtual void DestroyThis(); + virtual IThreadCreator *Parent(); + virtual void GetParams(ThreadParams *ptparams); +public: + //Priorities not supported by default. + virtual ThreadPriority GetPriority(); + virtual bool SetPriority(ThreadPriority prio); +public: + virtual ThreadState GetState(); + virtual bool Unpause(); +private: + ThreadState m_state; + ThreadParams m_params; + IThreadCreator *m_parent; + IThread *pThread; +}; + +class BaseWorker : public IThreadWorker +{ +public: + BaseWorker(); + virtual ~BaseWorker(); +public: //IWorker + virtual unsigned int RunFrame(); + //Controls the worker + virtual bool Pause(); + virtual bool Unpause(); + virtual bool Start(); + virtual bool Stop(bool flush_cancel); + //Flushes out any remaining threads + virtual unsigned int Flush(bool flush_cancel); + //returns status and number of threads in queue + virtual WorkerState GetStatus(unsigned int *numThreads); +public: //IThreadCreator + virtual void MakeThread(IThread *pThread); + virtual IThreadHandle *MakeThread(IThread *pThread, ThreadFlags flags); + virtual IThreadHandle *MakeThread(IThread *pThread, const ThreadParams *params); + virtual void GetPriorityBounds(ThreadPriority &max, ThreadPriority &min); +public: //BaseWorker + virtual void AddThreadToQueue(SWThreadHandle *pHandle); + virtual SWThreadHandle *PopThreadFromQueue(); + virtual void SetMaxThreadsPerFrame(unsigned int threads); + virtual unsigned int GetMaxThreadsPerFrame(); +protected: + SourceHook::List m_ThreadQueue; + unsigned int m_perFrame; + volatile WorkerState m_state; +}; + +#endif //_INCLUDE_SOURCEMOD_BASEWORKER_H diff --git a/core/thread/PosixThreads.cpp b/core/thread/PosixThreads.cpp new file mode 100644 index 00000000..b2a30aca --- /dev/null +++ b/core/thread/PosixThreads.cpp @@ -0,0 +1,301 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod Threader API + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Version: $Id$ + */ + +#include +#include "PosixThreads.h" +#include "ThreadWorker.h" + +IThreadWorker *PosixThreader::MakeWorker(bool threaded) +{ + if (threaded) + { + return new ThreadWorker(this, DEFAULT_THINK_TIME_MS); + } else { + return new BaseWorker(); + } +} + +void PosixThreader::DestroyWorker(IThreadWorker *pWorker) +{ + delete pWorker; +} + +void PosixThreader::ThreadSleep(unsigned int ms) +{ + usleep( ms * 1000 ); +} + +void PosixThreader::GetPriorityBounds(ThreadPriority &max, ThreadPriority &min) +{ + max = ThreadPrio_Normal; + min = ThreadPrio_Normal; +} + +IMutex *PosixThreader::MakeMutex() +{ + pthread_mutex_t mutex; + + if (pthread_mutex_init(&mutex, NULL) != 0) + return NULL; + + PosixMutex *pMutex = new PosixMutex(mutex); + + return pMutex; +} + + +void PosixThreader::MakeThread(IThread *pThread) +{ + ThreadParams defparams; + + defparams.flags = Thread_AutoRelease; + defparams.prio = ThreadPrio_Normal; + + MakeThread(pThread, &defparams); +} + +IThreadHandle *PosixThreader::MakeThread(IThread *pThread, ThreadFlags flags) +{ + ThreadParams defparams; + + defparams.flags = flags; + defparams.prio = ThreadPrio_Normal; + + return MakeThread(pThread, &defparams); +} + +void *Posix_ThreadGate(void *param) +{ + PosixThreader::ThreadHandle *pHandle = + reinterpret_cast(param); + + //Block this thread from being started initially. + pthread_mutex_lock(&pHandle->m_runlock); + //if we get here, we've obtained the lock and are allowed to run. + //unlock and continue. + pthread_mutex_unlock(&pHandle->m_runlock); + + pHandle->m_run->RunThread(pHandle); + + ThreadParams params; + pthread_mutex_lock(&pHandle->m_statelock); + pHandle->m_state = Thread_Done; + pHandle->GetParams(¶ms); + pthread_mutex_unlock(&pHandle->m_statelock); + + pHandle->m_run->OnTerminate(pHandle, false); + if (params.flags & Thread_AutoRelease) + delete pHandle; + + return 0; +} + +ThreadParams g_defparams; +IThreadHandle *PosixThreader::MakeThread(IThread *pThread, const ThreadParams *params) +{ + if (params == NULL) + params = &g_defparams; + + PosixThreader::ThreadHandle *pHandle = + new PosixThreader::ThreadHandle(this, pThread, params); + + pthread_mutex_lock(&pHandle->m_runlock); + + int err; + err = pthread_create(&pHandle->m_thread, NULL, Posix_ThreadGate, (void *)pHandle); + + if (err != 0) + { + pthread_mutex_unlock(&pHandle->m_runlock); + delete pHandle; + return NULL; + } + + //Don't bother setting priority... + + if (!(pHandle->m_params.flags & Thread_CreateSuspended)) + { + pHandle->m_state = Thread_Running; + err = pthread_mutex_unlock(&pHandle->m_runlock); + if (err != 0) + pHandle->m_state = Thread_Paused; + } + + return pHandle; +} + +IEventSignal *PosixThreader::MakeEventSignal() +{ + return new PosixEventSignal(); +} + +/***************** +**** Mutexes **** +*****************/ + +PosixThreader::PosixMutex::~PosixMutex() +{ + pthread_mutex_destroy(&m_mutex); +} + +bool PosixThreader::PosixMutex::TryLock() +{ + int err = pthread_mutex_trylock(&m_mutex); + + return (err == 0); +} + +void PosixThreader::PosixMutex::Lock() +{ + pthread_mutex_lock(&m_mutex); +} + +void PosixThreader::PosixMutex::Unlock() +{ + pthread_mutex_unlock(&m_mutex); +} + +void PosixThreader::PosixMutex::DestroyThis() +{ + delete this; +} + +/****************** +* Thread Handles * +******************/ + +PosixThreader::ThreadHandle::ThreadHandle(IThreader *parent, IThread *run, const ThreadParams *params) : + m_parent(parent), m_params(*params), m_run(run), m_state(Thread_Paused) +{ + pthread_mutex_init(&m_runlock, NULL); + pthread_mutex_init(&m_statelock, NULL); +} + +PosixThreader::ThreadHandle::~ThreadHandle() +{ + pthread_mutex_destroy(&m_runlock); + pthread_mutex_destroy(&m_statelock); +} + +bool PosixThreader::ThreadHandle::WaitForThread() +{ + void *arg; + + if (pthread_join(m_thread, &arg) != 0) + return false; + + return true; +} + +ThreadState PosixThreader::ThreadHandle::GetState() +{ + ThreadState state; + + pthread_mutex_lock(&m_statelock); + state = m_state; + pthread_mutex_unlock(&m_statelock); + + return state; +} + +IThreadCreator *PosixThreader::ThreadHandle::Parent() +{ + return m_parent; +} + +void PosixThreader::ThreadHandle::DestroyThis() +{ + if (m_params.flags & Thread_AutoRelease) + return; + + delete this; +} + +void PosixThreader::ThreadHandle::GetParams(ThreadParams *ptparams) +{ + if (!ptparams) + return; + + *ptparams = m_params; +} + +ThreadPriority PosixThreader::ThreadHandle::GetPriority() +{ + return ThreadPrio_Normal; +} + +bool PosixThreader::ThreadHandle::SetPriority(ThreadPriority prio) +{ + return (prio == ThreadPrio_Normal); +} + +bool PosixThreader::ThreadHandle::Unpause() +{ + if (m_state != Thread_Paused) + return false; + + m_state = Thread_Running; + + if (pthread_mutex_unlock(&m_runlock) != 0) + { + m_state = Thread_Paused; + return false; + } + + return true; +} + +/***************** + * EVENT SIGNALS * + *****************/ + +PosixThreader::PosixEventSignal::PosixEventSignal() +{ + pthread_cond_init(&m_cond, NULL); + pthread_mutex_init(&m_mutex, NULL); +} + +PosixThreader::PosixEventSignal::~PosixEventSignal() +{ + pthread_cond_destroy(&m_cond); + pthread_mutex_destroy(&m_mutex); +} + +void PosixThreader::PosixEventSignal::Wait() +{ + pthread_mutex_lock(&m_mutex); + pthread_cond_wait(&m_cond, &m_mutex); + pthread_mutex_unlock(&m_mutex); +} + +void PosixThreader::PosixEventSignal::Signal() +{ + pthread_mutex_lock(&m_mutex); + pthread_cond_broadcast(&m_cond); + pthread_mutex_unlock(&m_mutex); +} + +void PosixThreader::PosixEventSignal::DestroyThis() +{ + delete this; +} diff --git a/core/thread/PosixThreads.h b/core/thread/PosixThreads.h new file mode 100644 index 00000000..efa4248a --- /dev/null +++ b/core/thread/PosixThreads.h @@ -0,0 +1,107 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod Threader API + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_POSIXTHREADS_H_ +#define _INCLUDE_POSIXTHREADS_H_ + +#include +#include "IThreader.h" + +using namespace SourceMod; + +void *Posix_ThreadGate(void *param); + +class PosixThreader : public IThreader +{ +public: + class ThreadHandle : public IThreadHandle + { + friend class PosixThreader; + friend void *Posix_ThreadGate(void *param); + public: + ThreadHandle(IThreader *parent, IThread *run, const ThreadParams *params); + virtual ~ThreadHandle(); + public: + virtual bool WaitForThread(); + virtual void DestroyThis(); + virtual IThreadCreator *Parent(); + virtual void GetParams(ThreadParams *ptparams); + virtual ThreadPriority GetPriority(); + virtual bool SetPriority(ThreadPriority prio); + virtual ThreadState GetState(); + virtual bool Unpause(); + protected: + IThreader *m_parent; //Parent handle + pthread_t m_thread; //Windows HANDLE + ThreadParams m_params; //Current Parameters + IThread *m_run; //Runnable context + pthread_mutex_t m_statelock; + pthread_mutex_t m_runlock; + ThreadState m_state; //internal state + }; + class PosixMutex : public IMutex + { + public: + PosixMutex(pthread_mutex_t m) : m_mutex(m) + { + }; + virtual ~PosixMutex(); + public: + virtual bool TryLock(); + virtual void Lock(); + virtual void Unlock(); + virtual void DestroyThis(); + protected: + pthread_mutex_t m_mutex; + }; + class PosixEventSignal : public IEventSignal + { + public: + PosixEventSignal(); + virtual ~PosixEventSignal(); + public: + virtual void Wait(); + virtual void Signal(); + virtual void DestroyThis(); + protected: + pthread_cond_t m_cond; + pthread_mutex_t m_mutex; + }; +public: + IMutex *MakeMutex(); + void MakeThread(IThread *pThread); + IThreadHandle *MakeThread(IThread *pThread, ThreadFlags flags); + IThreadHandle *MakeThread(IThread *pThread, const ThreadParams *params); + void GetPriorityBounds(ThreadPriority &max, ThreadPriority &min); + void ThreadSleep(unsigned int ms); + IEventSignal *MakeEventSignal(); + IThreadWorker *MakeWorker(bool threaded); + void DestroyWorker(IThreadWorker *pWorker); +}; + +#if defined SM_DEFAULT_THREADER && !defined SM_MAIN_THREADER +#define SM_MAIN_THREADER PosixThreader; +typedef class PosixThreader MainThreader; +#endif + +#endif //_INCLUDE_POSIXTHREADS_H_ diff --git a/core/thread/ThreadWorker.cpp b/core/thread/ThreadWorker.cpp new file mode 100644 index 00000000..47ec9a00 --- /dev/null +++ b/core/thread/ThreadWorker.cpp @@ -0,0 +1,272 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#include "ThreadWorker.h" + +ThreadWorker::ThreadWorker() : + m_Threader(NULL), + m_QueueLock(NULL), + m_StateLock(NULL), + m_PauseSignal(NULL), + m_AddSignal(NULL), + me(NULL), + m_think_time(DEFAULT_THINK_TIME_MS) +{ + m_state = Worker_Invalid; +} + +ThreadWorker::ThreadWorker(IThreader *pThreader, unsigned int thinktime) : + m_Threader(pThreader), + m_QueueLock(NULL), + m_StateLock(NULL), + m_PauseSignal(NULL), + m_AddSignal(NULL), + me(NULL), + m_think_time(thinktime) +{ + if (m_Threader) + { + m_state = Worker_Stopped; + } else { + m_state = Worker_Invalid; + } +} + +ThreadWorker::~ThreadWorker() +{ + if (m_state != Worker_Stopped || m_state != Worker_Invalid) + Stop(true); + + if (m_ThreadQueue.size()) + Flush(true); +} + +void ThreadWorker::OnTerminate(IThreadHandle *pHandle, bool cancel) +{ + //we don't particularly care + return; +} + +void ThreadWorker::RunThread(IThreadHandle *pHandle) +{ + WorkerState this_state = Worker_Running; + size_t num; + + while (true) + { + /** + * Check number of items in the queue + */ + m_StateLock->Lock(); + this_state = m_state; + m_StateLock->Unlock(); + if (this_state != Worker_Stopped) + { + m_QueueLock->Lock(); + num = m_ThreadQueue.size(); + if (!num) + { + /** + * if none, wait for an item + */ + m_Waiting = true; + m_QueueLock->Unlock(); + /* first check if we should end again */ + if (this_state == Worker_Stopped) + { + break; + } + m_AddSignal->Wait(); + m_Waiting = false; + } else { + m_QueueLock->Unlock(); + } + } + m_StateLock->Lock(); + this_state = m_state; + m_StateLock->Unlock(); + if (this_state != Worker_Running) + { + if (this_state == Worker_Paused || this_state == Worker_Stopped) + { + //wait until the lock is cleared. + if (this_state == Worker_Paused) + { + m_PauseSignal->Wait(); + } + if (this_state == Worker_Stopped) + { + //if we're supposed to flush cleanrly, + // run all of the remaining frames first. + if (!m_FlushType) + { + while (m_ThreadQueue.size()) + RunFrame(); + } + break; + } + } + } + /** + * Run the frame. + */ + RunFrame(); + + /** + * wait in between threads if specified + */ + if (m_think_time) + m_Threader->ThreadSleep(m_think_time); + } +} + +SWThreadHandle *ThreadWorker::PopThreadFromQueue() +{ + if (m_state <= Worker_Stopped && !m_QueueLock) + return NULL; + + SWThreadHandle *swt; + m_QueueLock->Lock(); + swt = BaseWorker::PopThreadFromQueue(); + m_QueueLock->Unlock(); + + return swt; +} + +void ThreadWorker::AddThreadToQueue(SWThreadHandle *pHandle) +{ + if (m_state <= Worker_Stopped) + return; + + m_QueueLock->Lock(); + BaseWorker::AddThreadToQueue(pHandle); + if (m_Waiting) + { + m_AddSignal->Signal(); + } + m_QueueLock->Unlock(); +} + +WorkerState ThreadWorker::GetStatus(unsigned int *threads) +{ + WorkerState state; + + m_StateLock->Lock(); + state = BaseWorker::GetStatus(threads); + m_StateLock->Unlock(); + + return state; +} + +bool ThreadWorker::Start() +{ + if (m_state == Worker_Invalid) + { + if (m_Threader == NULL) + return false; + } else if (m_state != Worker_Stopped) { + return false; + } + + m_Waiting = false; + m_QueueLock = m_Threader->MakeMutex(); + m_StateLock = m_Threader->MakeMutex(); + m_PauseSignal = m_Threader->MakeEventSignal(); + m_AddSignal = m_Threader->MakeEventSignal(); + m_state = Worker_Running; + ThreadParams pt; + pt.flags = Thread_Default; + pt.prio = ThreadPrio_Normal; + me = m_Threader->MakeThread(this, &pt); + + return true; +} + +bool ThreadWorker::Stop(bool flush_cancel) +{ + if (m_state == Worker_Invalid || m_state == Worker_Stopped) + return false; + + WorkerState oldstate; + + //set new state + m_StateLock->Lock(); + oldstate = m_state; + m_state = Worker_Stopped; + m_FlushType = flush_cancel; + m_StateLock->Unlock(); + + if (oldstate == Worker_Paused) + { + Unpause(); + } else { + m_QueueLock->Lock(); + if (m_Waiting) + { + m_AddSignal->Signal(); + } + m_QueueLock->Unlock(); + } + + me->WaitForThread(); + //destroy it + me->DestroyThis(); + //flush all remaining events + Flush(true); + + //free mutex locks + m_QueueLock->DestroyThis(); + m_StateLock->DestroyThis(); + m_PauseSignal->DestroyThis(); + m_AddSignal->DestroyThis(); + + //invalidizzle + m_QueueLock = NULL; + m_StateLock = NULL; + m_PauseSignal = NULL; + m_AddSignal = NULL; + me = NULL; + + return true; +} + +bool ThreadWorker::Pause() +{ + if (m_state != Worker_Running) + return false; + + m_StateLock->Lock(); + m_state = Worker_Paused; + m_StateLock->Unlock(); + + return true; +} + + +bool ThreadWorker::Unpause() +{ + if (m_state != Worker_Paused) + return false; + + m_StateLock->Lock(); + m_state = Worker_Running; + m_StateLock->Unlock(); + m_PauseSignal->Signal(); + if (m_Waiting) + { + m_AddSignal->Signal(); + } + + return true; +} diff --git a/core/thread/ThreadWorker.h b/core/thread/ThreadWorker.h new file mode 100644 index 00000000..e1a99634 --- /dev/null +++ b/core/thread/ThreadWorker.h @@ -0,0 +1,54 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_THREADWORKER_H +#define _INCLUDE_SOURCEMOD_THREADWORKER_H + +#include "BaseWorker.h" + +#define DEFAULT_THINK_TIME_MS 500 + +class ThreadWorker : public BaseWorker, public IThread +{ +public: + ThreadWorker(); + ThreadWorker(IThreader *pThreader, unsigned int thinktime=DEFAULT_THINK_TIME_MS); + virtual ~ThreadWorker(); +public: //IThread + virtual void OnTerminate(IThreadHandle *pHandle, bool cancel); + virtual void RunThread(IThreadHandle *pHandle); +public: //IWorker + //Controls the worker + virtual bool Pause(); + virtual bool Unpause(); + virtual bool Start(); + virtual bool Stop(bool flush_cancel); + //returns status and number of threads in queue + virtual WorkerState GetStatus(unsigned int *numThreads); +public: //BaseWorker + virtual void AddThreadToQueue(SWThreadHandle *pHandle); + virtual SWThreadHandle *PopThreadFromQueue(); +protected: + IThreader *m_Threader; + IMutex *m_QueueLock; + IMutex *m_StateLock; + IEventSignal *m_PauseSignal; + IEventSignal *m_AddSignal; + IThreadHandle *me; + unsigned int m_think_time; + volatile bool m_Waiting; + volatile bool m_FlushType; +}; + +#endif //_INCLUDE_SOURCEMOD_THREADWORKER_H diff --git a/core/thread/WinThreads.cpp b/core/thread/WinThreads.cpp new file mode 100644 index 00000000..c67ffd1e --- /dev/null +++ b/core/thread/WinThreads.cpp @@ -0,0 +1,318 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#include "WinThreads.h" +#include "ThreadWorker.h" + +IThreadWorker *WinThreader::MakeWorker(bool threaded) +{ + if (threaded) + { + return new ThreadWorker(this, DEFAULT_THINK_TIME_MS); + } else { + return new BaseWorker(); + } +} + +void WinThreader::DestroyWorker(IThreadWorker *pWorker) +{ + delete pWorker; +} + +void WinThreader::ThreadSleep(unsigned int ms) +{ + Sleep((DWORD)ms); +} + +IMutex *WinThreader::MakeMutex() +{ + HANDLE mutex = CreateMutexA(NULL, FALSE, NULL); + + if (mutex == NULL) + return NULL; + + WinMutex *pMutex = new WinMutex(mutex); + + return pMutex; +} + +IThreadHandle *WinThreader::MakeThread(IThread *pThread, ThreadFlags flags) +{ + ThreadParams defparams; + + defparams.flags = flags; + defparams.prio = ThreadPrio_Normal; + + return MakeThread(pThread, &defparams); +} + +void WinThreader::MakeThread(IThread *pThread) +{ + ThreadParams defparams; + + defparams.flags = Thread_AutoRelease; + defparams.prio = ThreadPrio_Normal; + + MakeThread(pThread, &defparams); +} + +DWORD WINAPI Win32_ThreadGate(LPVOID param) +{ + WinThreader::ThreadHandle *pHandle = + reinterpret_cast(param); + + pHandle->m_run->RunThread(pHandle); + + ThreadParams params; + EnterCriticalSection(&pHandle->m_crit); + pHandle->m_state = Thread_Done; + pHandle->GetParams(¶ms); + LeaveCriticalSection(&pHandle->m_crit); + + pHandle->m_run->OnTerminate(pHandle, false); + if (params.flags & Thread_AutoRelease) + delete pHandle; + + return 0; +} + +void WinThreader::GetPriorityBounds(ThreadPriority &max, ThreadPriority &min) +{ + max = ThreadPrio_Maximum; + min = ThreadPrio_Minimum; +} + +ThreadParams g_defparams; +IThreadHandle *WinThreader::MakeThread(IThread *pThread, const ThreadParams *params) +{ + if (params == NULL) + params = &g_defparams; + + WinThreader::ThreadHandle *pHandle = + new WinThreader::ThreadHandle(this, NULL, pThread, params); + + DWORD tid; + pHandle->m_thread = + CreateThread(NULL, 0, &Win32_ThreadGate, (LPVOID)pHandle, CREATE_SUSPENDED, &tid); + + if (!pHandle->m_thread) + { + delete pHandle; + return NULL; + } + + if (pHandle->m_params.prio != ThreadPrio_Normal) + { + pHandle->SetPriority(pHandle->m_params.prio); + } + + if (!(pHandle->m_params.flags & Thread_CreateSuspended)) + { + pHandle->Unpause(); + } + + return pHandle; +} + +IEventSignal *WinThreader::MakeEventSignal() +{ + HANDLE event = CreateEventA(NULL, FALSE, FALSE, NULL); + + if (!event) + return NULL; + + WinEvent *pEvent = new WinEvent(event); + + return pEvent; +} + +/***************** + **** Mutexes **** + *****************/ + +WinThreader::WinMutex::~WinMutex() +{ + if (m_mutex) + { + CloseHandle(m_mutex); + m_mutex = NULL; + } +} + +bool WinThreader::WinMutex::TryLock() +{ + if (!m_mutex) + return false; + + if (WaitForSingleObject(m_mutex, 0) != WAIT_FAILED) + return true; + + return false; +} + +void WinThreader::WinMutex::Lock() +{ + if (!m_mutex) + return; + + WaitForSingleObject(m_mutex, INFINITE); +} + +void WinThreader::WinMutex::Unlock() +{ + if (!m_mutex) + return; + + ReleaseMutex(m_mutex); +} + +void WinThreader::WinMutex::DestroyThis() +{ + delete this; +} + +/****************** + * Thread Handles * + ******************/ + +WinThreader::ThreadHandle::ThreadHandle(IThreader *parent, HANDLE hthread, IThread *run, const ThreadParams *params) : + m_parent(parent), m_thread(hthread), m_run(run), m_params(*params), + m_state(Thread_Paused) +{ + InitializeCriticalSection(&m_crit); +} + +WinThreader::ThreadHandle::~ThreadHandle() +{ + if (m_thread) + { + CloseHandle(m_thread); + m_thread = NULL; + } + DeleteCriticalSection(&m_crit); +} + +bool WinThreader::ThreadHandle::WaitForThread() +{ + if (m_thread == NULL) + return false; + + if (WaitForSingleObject(m_thread, INFINITE) != 0) + return false; + + return true; +} + +ThreadState WinThreader::ThreadHandle::GetState() +{ + ThreadState state; + + EnterCriticalSection(&m_crit); + state = m_state; + LeaveCriticalSection(&m_crit); + + return state; +} + +IThreadCreator *WinThreader::ThreadHandle::Parent() +{ + return m_parent; +} + +void WinThreader::ThreadHandle::DestroyThis() +{ + if (m_params.flags & Thread_AutoRelease) + return; + + delete this; +} + +void WinThreader::ThreadHandle::GetParams(ThreadParams *ptparams) +{ + if (!ptparams) + return; + + *ptparams = m_params; +} + +ThreadPriority WinThreader::ThreadHandle::GetPriority() +{ + return m_params.prio; +} + +bool WinThreader::ThreadHandle::SetPriority(ThreadPriority prio) +{ + if (!m_thread) + return false; + + BOOL res = FALSE; + + if (prio >= ThreadPrio_Maximum) + res = SetThreadPriority(m_thread, THREAD_PRIORITY_HIGHEST); + else if (prio <= ThreadPrio_Minimum) + res = SetThreadPriority(m_thread, THREAD_PRIORITY_LOWEST); + else if (prio == ThreadPrio_Normal) + res = SetThreadPriority(m_thread, THREAD_PRIORITY_NORMAL); + else if (prio == ThreadPrio_High) + res = SetThreadPriority(m_thread, THREAD_PRIORITY_ABOVE_NORMAL); + else if (prio == ThreadPrio_Low) + res = SetThreadPriority(m_thread, THREAD_PRIORITY_BELOW_NORMAL); + + m_params.prio = prio; + + return (res != FALSE); +} + +bool WinThreader::ThreadHandle::Unpause() +{ + if (!m_thread) + return false; + + if (m_state != Thread_Paused) + return false; + + m_state = Thread_Running; + + if (ResumeThread(m_thread) == -1) + { + m_state = Thread_Paused; + return false; + } + + return true; +} + +/***************** + * EVENT SIGNALS * + *****************/ + +WinThreader::WinEvent::~WinEvent() +{ + CloseHandle(m_event); +} + +void WinThreader::WinEvent::Wait() +{ + WaitForSingleObject(m_event, INFINITE); +} + +void WinThreader::WinEvent::Signal() +{ + SetEvent(m_event); +} + +void WinThreader::WinEvent::DestroyThis() +{ + delete this; +} diff --git a/core/thread/WinThreads.h b/core/thread/WinThreads.h new file mode 100644 index 00000000..de693028 --- /dev/null +++ b/core/thread/WinThreads.h @@ -0,0 +1,98 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_WINTHREADS_H_ +#define _INCLUDE_WINTHREADS_H_ + +#include +#include "IThreader.h" + +using namespace SourceMod; + +DWORD WINAPI Win32_ThreadGate(LPVOID param); + +class WinThreader : public IThreader +{ +public: + class ThreadHandle : public IThreadHandle + { + friend class WinThreader; + friend DWORD WINAPI Win32_ThreadGate(LPVOID param); + public: + ThreadHandle(IThreader *parent, HANDLE hthread, IThread *run, const ThreadParams *params); + virtual ~ThreadHandle(); + public: + virtual bool WaitForThread(); + virtual void DestroyThis(); + virtual IThreadCreator *Parent(); + virtual void GetParams(ThreadParams *ptparams); + virtual ThreadPriority GetPriority(); + virtual bool SetPriority(ThreadPriority prio); + virtual ThreadState GetState(); + virtual bool Unpause(); + protected: + IThreader *m_parent; //Parent handle + HANDLE m_thread; //Windows HANDLE + ThreadParams m_params; //Current Parameters + IThread *m_run; //Runnable context + ThreadState m_state; //internal state + CRITICAL_SECTION m_crit; + }; + class WinMutex : public IMutex + { + public: + WinMutex(HANDLE mutex) : m_mutex(mutex) + { + }; + virtual ~WinMutex(); + public: + virtual bool TryLock(); + virtual void Lock(); + virtual void Unlock(); + virtual void DestroyThis(); + protected: + HANDLE m_mutex; + }; + class WinEvent : public IEventSignal + { + public: + WinEvent(HANDLE event) : m_event(event) + { + }; + virtual ~WinEvent(); + public: + virtual void Wait(); + virtual void Signal(); + virtual void DestroyThis(); + public: + HANDLE m_event; + }; +public: + IMutex *MakeMutex(); + void MakeThread(IThread *pThread); + IThreadHandle *MakeThread(IThread *pThread, ThreadFlags flags); + IThreadHandle *MakeThread(IThread *pThread, const ThreadParams *params); + void GetPriorityBounds(ThreadPriority &max, ThreadPriority &min); + void ThreadSleep(unsigned int ms); + IEventSignal *MakeEventSignal(); + IThreadWorker *MakeWorker(bool threaded); + void DestroyWorker(IThreadWorker *pWorker); +}; + +#if defined SM_DEFAULT_THREADER && !defined SM_MAIN_THREADER +#define SM_MAIN_THREADER WinThreader; +typedef class WinThreader MainThreader; +#endif + +#endif //_INCLUDE_WINTHREADS_H_ diff --git a/public/extensions/IThreader.h b/public/IThreader.h similarity index 100% rename from public/extensions/IThreader.h rename to public/IThreader.h