dvander@2726: /** dvander@2726: * vim: set ts=4 : dvander@2726: * ============================================================================= dvander@2726: * SourceMod dvander@2726: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. dvander@2726: * ============================================================================= dvander@2726: * dvander@2726: * This program is free software; you can redistribute it and/or modify it under dvander@2726: * the terms of the GNU General Public License, version 3.0, as published by the dvander@2726: * Free Software Foundation. dvander@2726: * dvander@2726: * This program is distributed in the hope that it will be useful, but WITHOUT dvander@2726: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS dvander@2726: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more dvander@2726: * details. dvander@2726: * dvander@2726: * You should have received a copy of the GNU General Public License along with dvander@2726: * this program. If not, see . dvander@2726: * dvander@2726: * As a special exception, AlliedModders LLC gives you permission to link the dvander@2726: * code of this program (as well as its derivative works) to "Half-Life 2," the dvander@2726: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software dvander@2726: * by the Valve Corporation. You must obey the GNU General Public License in dvander@2726: * all respects for all other code used. Additionally, AlliedModders LLC grants dvander@2726: * this exception to all derivative works. AlliedModders LLC defines further dvander@2726: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), dvander@2726: * or . dvander@2726: * dvander@2726: * Version: $Id$ dvander@2726: */ dvander@2726: dvander@2726: #include "ThreadWorker.h" dvander@2726: dvander@2726: ThreadWorker::ThreadWorker(IThreadWorkerCallbacks *hooks) : BaseWorker(hooks), dvander@2726: m_Threader(NULL), dvander@2726: m_QueueLock(NULL), dvander@2726: m_StateLock(NULL), dvander@2726: m_PauseSignal(NULL), dvander@2726: m_AddSignal(NULL), dvander@2726: me(NULL), dvander@2726: m_think_time(DEFAULT_THINK_TIME_MS) dvander@2726: { dvander@2726: m_state = Worker_Invalid; dvander@2726: } dvander@2726: dvander@2726: ThreadWorker::ThreadWorker(IThreadWorkerCallbacks *hooks, IThreader *pThreader, unsigned int thinktime) : dvander@2726: BaseWorker(hooks), dvander@2726: m_Threader(pThreader), dvander@2726: m_QueueLock(NULL), dvander@2726: m_StateLock(NULL), dvander@2726: m_PauseSignal(NULL), dvander@2726: m_AddSignal(NULL), dvander@2726: me(NULL), dvander@2726: m_think_time(thinktime) dvander@2726: { dvander@2726: if (m_Threader) dvander@2726: { dvander@2726: m_state = Worker_Stopped; dvander@2726: } else { dvander@2726: m_state = Worker_Invalid; dvander@2726: } dvander@2726: } dvander@2726: dvander@2726: ThreadWorker::~ThreadWorker() dvander@2726: { dvander@2726: if (m_state != Worker_Stopped || m_state != Worker_Invalid) dvander@2726: { dvander@2726: Stop(true); dvander@2726: } dvander@2726: dvander@2726: if (m_ThreadQueue.size()) dvander@2726: { dvander@2726: Flush(true); dvander@2726: } dvander@2726: } dvander@2726: dvander@2726: void ThreadWorker::OnTerminate(IThreadHandle *pHandle, bool cancel) dvander@2726: { dvander@2726: //we don't particularly care dvander@2726: return; dvander@2726: } dvander@2726: dvander@2726: void ThreadWorker::RunThread(IThreadHandle *pHandle) dvander@2726: { dvander@2726: WorkerState this_state = Worker_Running; dvander@2726: size_t num; dvander@2726: dvander@2726: if (m_pHooks) dvander@2726: { dvander@2726: m_pHooks->OnWorkerStart(this); dvander@2726: } dvander@2726: dvander@2726: while (true) dvander@2726: { dvander@2726: /** dvander@2726: * Check number of items in the queue dvander@2726: */ dvander@2726: m_StateLock->Lock(); dvander@2726: this_state = m_state; dvander@2726: m_StateLock->Unlock(); dvander@2726: if (this_state != Worker_Stopped) dvander@2726: { dvander@2726: m_QueueLock->Lock(); dvander@2726: num = m_ThreadQueue.size(); dvander@2726: if (!num) dvander@2726: { dvander@2726: /** dvander@2726: * if none, wait for an item dvander@2726: */ dvander@2726: m_Waiting = true; dvander@2726: m_QueueLock->Unlock(); dvander@2726: /* first check if we should end again */ dvander@2726: if (this_state == Worker_Stopped) dvander@2726: { dvander@2726: break; dvander@2726: } dvander@2726: m_AddSignal->Wait(); dvander@2726: m_Waiting = false; dvander@2726: } else { dvander@2726: m_QueueLock->Unlock(); dvander@2726: } dvander@2726: } dvander@2726: m_StateLock->Lock(); dvander@2726: this_state = m_state; dvander@2726: m_StateLock->Unlock(); dvander@2726: if (this_state != Worker_Running) dvander@2726: { dvander@2726: if (this_state == Worker_Paused || this_state == Worker_Stopped) dvander@2726: { dvander@2726: //wait until the lock is cleared. dvander@2726: if (this_state == Worker_Paused) dvander@2726: { dvander@2726: m_PauseSignal->Wait(); dvander@2726: } dvander@2726: if (this_state == Worker_Stopped) dvander@2726: { dvander@2726: //if we're supposed to flush cleanrly, dvander@2726: // run all of the remaining frames first. dvander@2726: if (!m_FlushType) dvander@2726: { dvander@2726: while (m_ThreadQueue.size()) dvander@2726: { dvander@2726: RunFrame(); dvander@2726: } dvander@2726: } dvander@2726: break; dvander@2726: } dvander@2726: } dvander@2726: } dvander@2726: /** dvander@2726: * Run the frame. dvander@2726: */ dvander@2726: RunFrame(); dvander@2726: dvander@2726: /** dvander@2726: * wait in between threads if specified dvander@2726: */ dvander@2726: if (m_think_time) dvander@2726: { dvander@2726: m_Threader->ThreadSleep(m_think_time); dvander@2726: } dvander@2726: } dvander@2726: dvander@2726: if (m_pHooks) dvander@2726: { dvander@2726: m_pHooks->OnWorkerStop(this); dvander@2726: } dvander@2726: } dvander@2726: dvander@2726: SWThreadHandle *ThreadWorker::PopThreadFromQueue() dvander@2726: { dvander@2726: if (m_state <= Worker_Stopped && !m_QueueLock) dvander@2726: { dvander@2726: return NULL; dvander@2726: } dvander@2726: dvander@2726: SWThreadHandle *swt; dvander@2726: m_QueueLock->Lock(); dvander@2726: swt = BaseWorker::PopThreadFromQueue(); dvander@2726: m_QueueLock->Unlock(); dvander@2726: dvander@2726: return swt; dvander@2726: } dvander@2726: dvander@2726: void ThreadWorker::AddThreadToQueue(SWThreadHandle *pHandle) dvander@2726: { dvander@2726: if (m_state <= Worker_Stopped) dvander@2726: { dvander@2726: return; dvander@2726: } dvander@2726: dvander@2726: m_QueueLock->Lock(); dvander@2726: BaseWorker::AddThreadToQueue(pHandle); dvander@2726: if (m_Waiting) dvander@2726: { dvander@2726: m_AddSignal->Signal(); dvander@2726: } dvander@2726: m_QueueLock->Unlock(); dvander@2726: } dvander@2726: dvander@2726: WorkerState ThreadWorker::GetStatus(unsigned int *threads) dvander@2726: { dvander@2726: WorkerState state; dvander@2726: dvander@2726: m_StateLock->Lock(); dvander@2726: state = BaseWorker::GetStatus(threads); dvander@2726: m_StateLock->Unlock(); dvander@2726: dvander@2726: return state; dvander@2726: } dvander@2726: dvander@2726: bool ThreadWorker::Start() dvander@2726: { dvander@2726: if (m_state == Worker_Invalid) dvander@2726: { dvander@2726: if (m_Threader == NULL) dvander@2726: { dvander@2726: return false; dvander@2726: } dvander@2726: } else if (m_state != Worker_Stopped) { dvander@2726: return false; dvander@2726: } dvander@2726: dvander@2726: m_Waiting = false; dvander@2726: m_QueueLock = m_Threader->MakeMutex(); dvander@2726: m_StateLock = m_Threader->MakeMutex(); dvander@2726: m_PauseSignal = m_Threader->MakeEventSignal(); dvander@2726: m_AddSignal = m_Threader->MakeEventSignal(); dvander@2726: m_state = Worker_Running; dvander@2726: ThreadParams pt; dvander@2726: pt.flags = Thread_Default; dvander@2726: pt.prio = ThreadPrio_Normal; dvander@2726: me = m_Threader->MakeThread(this, &pt); dvander@2726: dvander@2726: return true; dvander@2726: } dvander@2726: dvander@2726: bool ThreadWorker::Stop(bool flush_cancel) dvander@2726: { dvander@2726: if (m_state == Worker_Invalid || m_state == Worker_Stopped) dvander@2726: { dvander@2726: return false; dvander@2726: } dvander@2726: dvander@2726: WorkerState oldstate; dvander@2726: dvander@2726: //set new state dvander@2726: m_StateLock->Lock(); dvander@2726: oldstate = m_state; dvander@2726: m_state = Worker_Stopped; dvander@2726: m_FlushType = flush_cancel; dvander@2726: m_StateLock->Unlock(); dvander@2726: dvander@2726: if (oldstate == Worker_Paused) dvander@2726: { dvander@2726: Unpause(); dvander@2726: } else { dvander@2726: m_QueueLock->Lock(); dvander@2726: if (m_Waiting) dvander@2726: { dvander@2726: m_AddSignal->Signal(); dvander@2726: } dvander@2726: m_QueueLock->Unlock(); dvander@2726: } dvander@2726: dvander@2726: me->WaitForThread(); dvander@2726: //destroy it dvander@2726: me->DestroyThis(); dvander@2726: //flush all remaining events dvander@2726: Flush(true); dvander@2726: dvander@2726: //free mutex locks dvander@2726: m_QueueLock->DestroyThis(); dvander@2726: m_StateLock->DestroyThis(); dvander@2726: m_PauseSignal->DestroyThis(); dvander@2726: m_AddSignal->DestroyThis(); dvander@2726: dvander@2726: //invalidizzle dvander@2726: m_QueueLock = NULL; dvander@2726: m_StateLock = NULL; dvander@2726: m_PauseSignal = NULL; dvander@2726: m_AddSignal = NULL; dvander@2726: me = NULL; dvander@2726: dvander@2726: return true; dvander@2726: } dvander@2726: dvander@2726: bool ThreadWorker::Pause() dvander@2726: { dvander@2726: if (m_state != Worker_Running) dvander@2726: { dvander@2726: return false; dvander@2726: } dvander@2726: dvander@2726: m_StateLock->Lock(); dvander@2726: m_state = Worker_Paused; dvander@2726: m_StateLock->Unlock(); dvander@2726: dvander@2726: return true; dvander@2726: } dvander@2726: dvander@2726: dvander@2726: bool ThreadWorker::Unpause() dvander@2726: { dvander@2726: if (m_state != Worker_Paused) dvander@2726: { dvander@2726: return false; dvander@2726: } dvander@2726: dvander@2726: m_StateLock->Lock(); dvander@2726: m_state = Worker_Running; dvander@2726: m_StateLock->Unlock(); dvander@2726: m_PauseSignal->Signal(); dvander@2726: if (m_Waiting) dvander@2726: { dvander@2726: m_AddSignal->Signal(); dvander@2726: } dvander@2726: dvander@2726: return true; dvander@2726: }