219 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/**
 | 
						|
 * vim: set ts=4 :
 | 
						|
 * =============================================================================
 | 
						|
 * SourceMod
 | 
						|
 * Copyright (C) 2004-2008 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, version 3.0, as published by the
 | 
						|
 * Free Software Foundation.
 | 
						|
 * 
 | 
						|
 * 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, see <http://www.gnu.org/licenses/>.
 | 
						|
 *
 | 
						|
 * As a special exception, AlliedModders LLC gives you permission to link the
 | 
						|
 * code of this program (as well as its derivative works) to "Half-Life 2," the
 | 
						|
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 | 
						|
 * by the Valve Corporation.  You must obey the GNU General Public License in
 | 
						|
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
 | 
						|
 * this exception to all derivative works.  AlliedModders LLC defines further
 | 
						|
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 | 
						|
 * or <http://www.sourcemod.net/license.php>.
 | 
						|
 *
 | 
						|
 * Version: $Id$
 | 
						|
 */
 | 
						|
 | 
						|
#include "ThreadWorker.h"
 | 
						|
 | 
						|
ThreadWorker::ThreadWorker(IThreadWorkerCallbacks *hooks) : BaseWorker(hooks),
 | 
						|
	m_Threader(NULL),
 | 
						|
	me(NULL),
 | 
						|
	m_think_time(DEFAULT_THINK_TIME_MS)
 | 
						|
{
 | 
						|
	m_state = Worker_Invalid;
 | 
						|
}
 | 
						|
 | 
						|
ThreadWorker::ThreadWorker(IThreadWorkerCallbacks *hooks, IThreader *pThreader, unsigned int thinktime) : 
 | 
						|
	BaseWorker(hooks),
 | 
						|
	m_Threader(pThreader),
 | 
						|
	me(NULL),
 | 
						|
	m_think_time(thinktime)
 | 
						|
{
 | 
						|
	m_state = m_Threader ? Worker_Stopped : 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)
 | 
						|
{
 | 
						|
	if (m_pHooks)
 | 
						|
		m_pHooks->OnWorkerStart(this);
 | 
						|
 | 
						|
	ke::AutoLock lock(&monitor_);
 | 
						|
 | 
						|
	while (true)
 | 
						|
	{
 | 
						|
		if (m_state == Worker_Paused)
 | 
						|
		{
 | 
						|
			// Wait until we're told to wake up.
 | 
						|
			monitor_.Wait();
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (m_state == Worker_Stopped)
 | 
						|
		{
 | 
						|
			// We've been told to stop entirely. If we've also been told to
 | 
						|
			// flush the queue, do that now.
 | 
						|
			while (!m_ThreadQueue.empty())
 | 
						|
			{
 | 
						|
				// Release the lock since PopThreadFromQueue() will re-acquire it. The
 | 
						|
				// main thread is blocking anyway.
 | 
						|
				ke::AutoUnlock unlock(&monitor_);
 | 
						|
				RunFrame();
 | 
						|
			}
 | 
						|
			assert(m_state == Worker_Stopped);
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		assert(m_state == Worker_Running);
 | 
						|
 | 
						|
		// Process one frame.
 | 
						|
		WorkerState oldstate = m_state;
 | 
						|
		{
 | 
						|
			ke::AutoUnlock unlock(&monitor_);
 | 
						|
			RunFrame();
 | 
						|
		}
 | 
						|
 | 
						|
		// If the state changed, loop back and process the new state.
 | 
						|
		if (m_state != oldstate)
 | 
						|
			continue;
 | 
						|
		
 | 
						|
		// If the thread queue is now empty, wait for a signal. Otherwise, if
 | 
						|
		// we're on a delay, wait for either a notification or a timeout to
 | 
						|
		// process the next item. If the queue has items and we don't have a
 | 
						|
		// delay, then we just loop around and keep processing.
 | 
						|
		if (m_ThreadQueue.empty())
 | 
						|
			monitor_.Wait();
 | 
						|
		else if (m_think_time)
 | 
						|
			monitor_.Wait(m_think_time);
 | 
						|
	}
 | 
						|
 | 
						|
	{
 | 
						|
		ke::AutoUnlock unlock(&monitor_);
 | 
						|
		if (m_pHooks)
 | 
						|
			m_pHooks->OnWorkerStop(this);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
SWThreadHandle *ThreadWorker::PopThreadFromQueue()
 | 
						|
{
 | 
						|
	ke::AutoLock lock(&monitor_);
 | 
						|
	if (m_state <= Worker_Stopped)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	return BaseWorker::PopThreadFromQueue();
 | 
						|
}
 | 
						|
 | 
						|
void ThreadWorker::AddThreadToQueue(SWThreadHandle *pHandle)
 | 
						|
{
 | 
						|
	ke::AutoLock lock(&monitor_);
 | 
						|
	if (m_state <= Worker_Stopped)
 | 
						|
		return;
 | 
						|
 | 
						|
	BaseWorker::AddThreadToQueue(pHandle);
 | 
						|
	monitor_.Notify();
 | 
						|
}
 | 
						|
 | 
						|
WorkerState ThreadWorker::GetStatus(unsigned int *threads)
 | 
						|
{
 | 
						|
	ke::AutoLock lock(&monitor_);
 | 
						|
	return BaseWorker::GetStatus(threads);
 | 
						|
}
 | 
						|
 | 
						|
void ThreadWorker::SetThinkTimePerFrame(unsigned int thinktime)
 | 
						|
{
 | 
						|
	m_think_time = thinktime;
 | 
						|
}
 | 
						|
 | 
						|
bool ThreadWorker::Start()
 | 
						|
{
 | 
						|
	if (m_state == Worker_Invalid && m_Threader == NULL)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (m_state != Worker_Stopped)
 | 
						|
		return false;
 | 
						|
 | 
						|
	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)
 | 
						|
{
 | 
						|
	// Change the state to signal a stop, and then trigger a notify.
 | 
						|
	{
 | 
						|
		ke::AutoLock lock(&monitor_);
 | 
						|
		if (m_state == Worker_Invalid || m_state == Worker_Stopped)
 | 
						|
			return false;
 | 
						|
 | 
						|
		m_state = Worker_Stopped;
 | 
						|
		m_FlushType = flush_cancel;
 | 
						|
		monitor_.Notify();
 | 
						|
	}
 | 
						|
 | 
						|
	me->WaitForThread();
 | 
						|
	//destroy it
 | 
						|
	me->DestroyThis();
 | 
						|
	//flush all remaining events
 | 
						|
	Flush(true);
 | 
						|
 | 
						|
	me = NULL;
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
bool ThreadWorker::Pause()
 | 
						|
{
 | 
						|
	if (m_state != Worker_Running)
 | 
						|
		return false;
 | 
						|
 | 
						|
	ke::AutoLock lock(&monitor_);
 | 
						|
	m_state = Worker_Paused;
 | 
						|
	monitor_.Notify();
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool ThreadWorker::Unpause()
 | 
						|
{
 | 
						|
	if (m_state != Worker_Paused)
 | 
						|
		return false;
 | 
						|
 | 
						|
	ke::AutoLock lock(&monitor_);
 | 
						|
	m_state = Worker_Running;
 | 
						|
	monitor_.Notify();
 | 
						|
	return true;
 | 
						|
}
 |