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