core/thread/ThreadWorker.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1231 17badaf66ce4
child 1947 e73a1e51833b
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

(This day shall henceforth be known as the Eighty Column Massacre of '07)
     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 }