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: 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: me(NULL), dvander@2726: m_think_time(thinktime) dvander@2726: { dvander@4025: m_state = m_Threader ? Worker_Stopped : Worker_Invalid; dvander@2726: } dvander@2726: dvander@2726: ThreadWorker::~ThreadWorker() dvander@2726: { dvander@2726: if (m_state != Worker_Stopped || m_state != Worker_Invalid) dvander@2726: Stop(true); dvander@4025: dvander@2726: if (m_ThreadQueue.size()) dvander@2726: Flush(true); 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@4025: if (m_pHooks) dvander@4025: m_pHooks->OnWorkerStart(this); dvander@2726: dvander@4025: ke::AutoLock lock(&monitor_); dvander@4025: dvander@2726: while (true) dvander@2726: { dvander@4025: if (m_state == Worker_Paused) dvander@2726: { dvander@4025: // Wait until we're told to wake up. dvander@4025: monitor_.Wait(); dvander@4025: continue; dvander@2726: } dvander@4025: dvander@4025: if (m_state == Worker_Stopped) dvander@2726: { dvander@4025: // We've been told to stop entirely. If we've also been told to dvander@4025: // flush the queue, do that now. dvander@4025: while (!m_ThreadQueue.empty()) dvander@2726: { dvander@4025: // Release the lock since PopThreadFromQueue() will re-acquire it. The dvander@4025: // main thread is blocking anyway. dvander@4025: ke::AutoUnlock unlock(&monitor_); dvander@4025: RunFrame(); dvander@2726: } dvander@4025: assert(m_state == Worker_Stopped); dvander@4025: return; dvander@2726: } dvander@4025: dvander@4025: assert(m_state == Worker_Running); dvander@4025: dvander@4025: // Process one frame. dvander@4025: WorkerState oldstate = m_state; dvander@4025: { dvander@4025: ke::AutoUnlock unlock(&monitor_); dvander@4025: RunFrame(); dvander@4025: } dvander@2726: dvander@4025: // If the state changed, loop back and process the new state. dvander@4025: if (m_state != oldstate) dvander@4025: continue; dvander@4025: dvander@4025: // If the thread queue is now empty, wait for a signal. Otherwise, if dvander@4025: // we're on a delay, wait for either a notification or a timeout to dvander@4025: // process the next item. If the queue has items and we don't have a dvander@4025: // delay, then we just loop around and keep processing. dvander@4025: if (m_ThreadQueue.empty()) dvander@4025: monitor_.Wait(); dvander@4025: else if (m_think_time) dvander@4025: monitor_.Wait(m_think_time); dvander@2726: } dvander@2726: dvander@2726: { dvander@4025: ke::AutoUnlock unlock(&monitor_); dvander@4025: if (m_pHooks) dvander@4025: m_pHooks->OnWorkerStop(this); dvander@2726: } dvander@2726: } dvander@2726: dvander@2726: SWThreadHandle *ThreadWorker::PopThreadFromQueue() dvander@2726: { dvander@4025: ke::AutoLock lock(&monitor_); dvander@4025: if (m_state <= Worker_Stopped) dvander@2726: return NULL; dvander@2726: dvander@4025: return BaseWorker::PopThreadFromQueue(); dvander@2726: } dvander@2726: dvander@2726: void ThreadWorker::AddThreadToQueue(SWThreadHandle *pHandle) dvander@2726: { dvander@4025: ke::AutoLock lock(&monitor_); dvander@2726: if (m_state <= Worker_Stopped) dvander@2726: return; dvander@2726: dvander@2726: BaseWorker::AddThreadToQueue(pHandle); dvander@4025: monitor_.Notify(); dvander@2726: } dvander@2726: dvander@2726: WorkerState ThreadWorker::GetStatus(unsigned int *threads) dvander@2726: { dvander@4025: ke::AutoLock lock(&monitor_); dvander@4025: return BaseWorker::GetStatus(threads); dvander@2726: } dvander@2726: asherkin@3704: void ThreadWorker::SetThinkTimePerFrame(unsigned int thinktime) asherkin@3704: { asherkin@3704: m_think_time = thinktime; asherkin@3704: } asherkin@3704: dvander@2726: bool ThreadWorker::Start() dvander@2726: { dvander@4025: if (m_state == Worker_Invalid && m_Threader == NULL) dvander@2726: return false; dvander@2726: dvander@4025: if (m_state != Worker_Stopped) dvander@4025: return false; dvander@4025: dvander@2726: m_state = Worker_Running; dvander@4025: 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: return true; dvander@2726: } dvander@2726: dvander@2726: bool ThreadWorker::Stop(bool flush_cancel) dvander@2726: { dvander@4025: // Change the state to signal a stop, and then trigger a notify. dvander@2726: { dvander@4025: ke::AutoLock lock(&monitor_); dvander@4025: if (m_state == Worker_Invalid || m_state == Worker_Stopped) dvander@4025: return false; dvander@2726: dvander@4025: m_state = Worker_Stopped; dvander@4025: m_FlushType = flush_cancel; dvander@4025: monitor_.Notify(); 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: me = NULL; dvander@2726: return true; dvander@2726: } dvander@2726: dvander@2726: bool ThreadWorker::Pause() dvander@2726: { dvander@2726: if (m_state != Worker_Running) dvander@2726: return false; dvander@2726: dvander@4025: ke::AutoLock lock(&monitor_); dvander@2726: m_state = Worker_Paused; dvander@4025: monitor_.Notify(); 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: return false; dvander@2726: dvander@4025: ke::AutoLock lock(&monitor_); dvander@2726: m_state = Worker_Running; dvander@4025: monitor_.Notify(); dvander@2726: return true; dvander@2726: }