core/thread/ThreadWorker.cpp
author David Anderson <dvander@alliedmods.net>
Wed Feb 27 13:49:57 2008 +0000 (2008-02-27)
changeset 1900 457329ae65bd
parent 1330 ab94316bb52b
child 1947 e73a1e51833b
permissions -rw-r--r--
fixed amb1483 - GetEntPropString() rejected FIELD_MODELNAME/FIELD_SOUNDNAME
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "ThreadWorker.h"
    33 
    34 ThreadWorker::ThreadWorker(IThreadWorkerCallbacks *hooks) : BaseWorker(hooks),
    35 	m_Threader(NULL),
    36 	m_QueueLock(NULL),
    37 	m_StateLock(NULL),
    38 	m_PauseSignal(NULL),
    39 	m_AddSignal(NULL),
    40 	me(NULL),
    41 	m_think_time(DEFAULT_THINK_TIME_MS)
    42 {
    43 	m_state = Worker_Invalid;
    44 }
    45 
    46 ThreadWorker::ThreadWorker(IThreadWorkerCallbacks *hooks, IThreader *pThreader, unsigned int thinktime) : 
    47 	BaseWorker(hooks),
    48 	m_Threader(pThreader),
    49 	m_QueueLock(NULL),
    50 	m_StateLock(NULL),
    51 	m_PauseSignal(NULL),
    52 	m_AddSignal(NULL),
    53 	me(NULL),
    54 	m_think_time(thinktime)
    55 {
    56 	if (m_Threader)
    57 	{
    58 		m_state = Worker_Stopped;
    59 	} else {
    60 		m_state = Worker_Invalid;
    61 	}
    62 }
    63 
    64 ThreadWorker::~ThreadWorker()
    65 {
    66 	if (m_state != Worker_Stopped || m_state != Worker_Invalid)
    67 	{
    68 		Stop(true);
    69 	}
    70 
    71 	if (m_ThreadQueue.size())
    72 	{
    73 		Flush(true);
    74 	}
    75 }
    76 
    77 void ThreadWorker::OnTerminate(IThreadHandle *pHandle, bool cancel)
    78 {
    79 	//we don't particularly care
    80 	return;
    81 }
    82 
    83 void ThreadWorker::RunThread(IThreadHandle *pHandle)
    84 {
    85 	WorkerState this_state = Worker_Running;
    86 	size_t num;
    87 
    88 	if (m_pHooks)
    89 	{
    90 		m_pHooks->OnWorkerStart(this);
    91 	}
    92     
    93 	while (true)
    94 	{
    95 		/**
    96 		 * Check number of items in the queue
    97 		 */
    98 		m_StateLock->Lock();
    99 		this_state = m_state;
   100 		m_StateLock->Unlock();
   101 		if (this_state != Worker_Stopped)
   102 		{
   103 			m_QueueLock->Lock();
   104 			num = m_ThreadQueue.size();
   105 			if (!num)
   106 			{
   107 				/** 
   108 				 * if none, wait for an item
   109 				 */
   110 				m_Waiting = true;
   111 				m_QueueLock->Unlock();
   112 				/* first check if we should end again */
   113 				if (this_state == Worker_Stopped)
   114 				{
   115 					break;
   116 				}
   117 				m_AddSignal->Wait();
   118 				m_Waiting = false;
   119 			} else {
   120 				m_QueueLock->Unlock();
   121 			}
   122 		}
   123 		m_StateLock->Lock();
   124 		this_state = m_state;
   125 		m_StateLock->Unlock();
   126 		if (this_state != Worker_Running)
   127 		{
   128 			if (this_state == Worker_Paused || this_state == Worker_Stopped)
   129 			{
   130 				//wait until the lock is cleared.
   131 				if (this_state == Worker_Paused)
   132 				{
   133 					m_PauseSignal->Wait();
   134 				}
   135 				if (this_state == Worker_Stopped)
   136 				{
   137 					//if we're supposed to flush cleanrly, 
   138 					// run all of the remaining frames first.
   139 					if (!m_FlushType)
   140 					{
   141 						while (m_ThreadQueue.size())
   142 						{
   143 							RunFrame();
   144 						}
   145 					}
   146 					break;
   147 				}
   148 			}
   149 		}
   150 		/**
   151 		 * Run the frame.
   152 		 */
   153 		RunFrame();
   154 
   155 		/**
   156 		 * wait in between threads if specified
   157 		 */
   158 		if (m_think_time)
   159 		{
   160 			m_Threader->ThreadSleep(m_think_time);
   161 		}
   162 	}
   163 
   164 	if (m_pHooks)
   165 	{
   166 		m_pHooks->OnWorkerStop(this);
   167 	}
   168 }
   169 
   170 SWThreadHandle *ThreadWorker::PopThreadFromQueue()
   171 {
   172 	if (m_state <= Worker_Stopped && !m_QueueLock)
   173 	{
   174 		return NULL;
   175 	}
   176 
   177 	SWThreadHandle *swt;
   178 	m_QueueLock->Lock();
   179 	swt = BaseWorker::PopThreadFromQueue();
   180 	m_QueueLock->Unlock();
   181 
   182 	return swt;
   183 }
   184 
   185 void ThreadWorker::AddThreadToQueue(SWThreadHandle *pHandle)
   186 {
   187 	if (m_state <= Worker_Stopped)
   188 	{
   189 		return;
   190 	}
   191 
   192 	m_QueueLock->Lock();
   193 	BaseWorker::AddThreadToQueue(pHandle);
   194 	if (m_Waiting)
   195 	{
   196 		m_AddSignal->Signal();
   197 	}
   198 	m_QueueLock->Unlock();
   199 }
   200 
   201 WorkerState ThreadWorker::GetStatus(unsigned int *threads)
   202 {
   203 	WorkerState state;
   204 
   205 	m_StateLock->Lock();
   206 	state = BaseWorker::GetStatus(threads);
   207 	m_StateLock->Unlock();
   208 
   209 	return state;
   210 }
   211 
   212 bool ThreadWorker::Start()
   213 {
   214 	if (m_state == Worker_Invalid)
   215 	{
   216 		if (m_Threader == NULL)
   217 		{
   218 			return false;
   219 		}
   220 	} else if (m_state != Worker_Stopped) {
   221 		return false;
   222 	}
   223 
   224 	m_Waiting = false;
   225 	m_QueueLock = m_Threader->MakeMutex();
   226 	m_StateLock = m_Threader->MakeMutex();
   227 	m_PauseSignal = m_Threader->MakeEventSignal();
   228 	m_AddSignal = m_Threader->MakeEventSignal();
   229 	m_state = Worker_Running;
   230 	ThreadParams pt;
   231 	pt.flags = Thread_Default;
   232 	pt.prio = ThreadPrio_Normal;
   233 	me = m_Threader->MakeThread(this, &pt);
   234 
   235 	return true;
   236 }
   237 
   238 bool ThreadWorker::Stop(bool flush_cancel)
   239 {
   240 	if (m_state == Worker_Invalid || m_state == Worker_Stopped)
   241 	{
   242 		return false;
   243 	}
   244 
   245 	WorkerState oldstate;
   246 
   247 	//set new state
   248 	m_StateLock->Lock();
   249 	oldstate = m_state;
   250 	m_state = Worker_Stopped;
   251 	m_FlushType = flush_cancel;
   252 	m_StateLock->Unlock();
   253 
   254 	if (oldstate == Worker_Paused)
   255 	{
   256 		Unpause();
   257 	} else {
   258 		m_QueueLock->Lock();
   259 		if (m_Waiting)
   260 		{
   261 			m_AddSignal->Signal();
   262 		}
   263 		m_QueueLock->Unlock();
   264 	}
   265 
   266 	me->WaitForThread();
   267 	//destroy it
   268 	me->DestroyThis();
   269 	//flush all remaining events
   270 	Flush(true);
   271 
   272 	//free mutex locks
   273 	m_QueueLock->DestroyThis();
   274 	m_StateLock->DestroyThis();
   275 	m_PauseSignal->DestroyThis();
   276 	m_AddSignal->DestroyThis();
   277 
   278 	//invalidizzle
   279 	m_QueueLock = NULL;
   280 	m_StateLock = NULL;
   281 	m_PauseSignal = NULL;
   282 	m_AddSignal = NULL;
   283 	me = NULL;
   284 
   285 	return true;
   286 }
   287 
   288 bool ThreadWorker::Pause()
   289 {
   290 	if (m_state != Worker_Running)
   291 	{
   292 		return false;
   293 	}
   294 
   295 	m_StateLock->Lock();
   296 	m_state = Worker_Paused;
   297 	m_StateLock->Unlock();
   298 
   299 	return true;
   300 }
   301 
   302 
   303 bool ThreadWorker::Unpause()
   304 {
   305 	if (m_state != Worker_Paused)
   306 	{
   307 		return false;
   308 	}
   309 
   310 	m_StateLock->Lock();
   311 	m_state = Worker_Running;
   312 	m_StateLock->Unlock();
   313 	m_PauseSignal->Signal();
   314 	if (m_Waiting)
   315 	{
   316 		m_AddSignal->Signal();
   317 	}
   318 
   319 	return true;
   320 }