1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/thread/ThreadWorker.cpp Sun Jul 06 04:49:55 2008 +0000
1.3 @@ -0,0 +1,320 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include "ThreadWorker.h"
1.36 +
1.37 +ThreadWorker::ThreadWorker(IThreadWorkerCallbacks *hooks) : BaseWorker(hooks),
1.38 + m_Threader(NULL),
1.39 + m_QueueLock(NULL),
1.40 + m_StateLock(NULL),
1.41 + m_PauseSignal(NULL),
1.42 + m_AddSignal(NULL),
1.43 + me(NULL),
1.44 + m_think_time(DEFAULT_THINK_TIME_MS)
1.45 +{
1.46 + m_state = Worker_Invalid;
1.47 +}
1.48 +
1.49 +ThreadWorker::ThreadWorker(IThreadWorkerCallbacks *hooks, IThreader *pThreader, unsigned int thinktime) :
1.50 + BaseWorker(hooks),
1.51 + m_Threader(pThreader),
1.52 + m_QueueLock(NULL),
1.53 + m_StateLock(NULL),
1.54 + m_PauseSignal(NULL),
1.55 + m_AddSignal(NULL),
1.56 + me(NULL),
1.57 + m_think_time(thinktime)
1.58 +{
1.59 + if (m_Threader)
1.60 + {
1.61 + m_state = Worker_Stopped;
1.62 + } else {
1.63 + m_state = Worker_Invalid;
1.64 + }
1.65 +}
1.66 +
1.67 +ThreadWorker::~ThreadWorker()
1.68 +{
1.69 + if (m_state != Worker_Stopped || m_state != Worker_Invalid)
1.70 + {
1.71 + Stop(true);
1.72 + }
1.73 +
1.74 + if (m_ThreadQueue.size())
1.75 + {
1.76 + Flush(true);
1.77 + }
1.78 +}
1.79 +
1.80 +void ThreadWorker::OnTerminate(IThreadHandle *pHandle, bool cancel)
1.81 +{
1.82 + //we don't particularly care
1.83 + return;
1.84 +}
1.85 +
1.86 +void ThreadWorker::RunThread(IThreadHandle *pHandle)
1.87 +{
1.88 + WorkerState this_state = Worker_Running;
1.89 + size_t num;
1.90 +
1.91 + if (m_pHooks)
1.92 + {
1.93 + m_pHooks->OnWorkerStart(this);
1.94 + }
1.95 +
1.96 + while (true)
1.97 + {
1.98 + /**
1.99 + * Check number of items in the queue
1.100 + */
1.101 + m_StateLock->Lock();
1.102 + this_state = m_state;
1.103 + m_StateLock->Unlock();
1.104 + if (this_state != Worker_Stopped)
1.105 + {
1.106 + m_QueueLock->Lock();
1.107 + num = m_ThreadQueue.size();
1.108 + if (!num)
1.109 + {
1.110 + /**
1.111 + * if none, wait for an item
1.112 + */
1.113 + m_Waiting = true;
1.114 + m_QueueLock->Unlock();
1.115 + /* first check if we should end again */
1.116 + if (this_state == Worker_Stopped)
1.117 + {
1.118 + break;
1.119 + }
1.120 + m_AddSignal->Wait();
1.121 + m_Waiting = false;
1.122 + } else {
1.123 + m_QueueLock->Unlock();
1.124 + }
1.125 + }
1.126 + m_StateLock->Lock();
1.127 + this_state = m_state;
1.128 + m_StateLock->Unlock();
1.129 + if (this_state != Worker_Running)
1.130 + {
1.131 + if (this_state == Worker_Paused || this_state == Worker_Stopped)
1.132 + {
1.133 + //wait until the lock is cleared.
1.134 + if (this_state == Worker_Paused)
1.135 + {
1.136 + m_PauseSignal->Wait();
1.137 + }
1.138 + if (this_state == Worker_Stopped)
1.139 + {
1.140 + //if we're supposed to flush cleanrly,
1.141 + // run all of the remaining frames first.
1.142 + if (!m_FlushType)
1.143 + {
1.144 + while (m_ThreadQueue.size())
1.145 + {
1.146 + RunFrame();
1.147 + }
1.148 + }
1.149 + break;
1.150 + }
1.151 + }
1.152 + }
1.153 + /**
1.154 + * Run the frame.
1.155 + */
1.156 + RunFrame();
1.157 +
1.158 + /**
1.159 + * wait in between threads if specified
1.160 + */
1.161 + if (m_think_time)
1.162 + {
1.163 + m_Threader->ThreadSleep(m_think_time);
1.164 + }
1.165 + }
1.166 +
1.167 + if (m_pHooks)
1.168 + {
1.169 + m_pHooks->OnWorkerStop(this);
1.170 + }
1.171 +}
1.172 +
1.173 +SWThreadHandle *ThreadWorker::PopThreadFromQueue()
1.174 +{
1.175 + if (m_state <= Worker_Stopped && !m_QueueLock)
1.176 + {
1.177 + return NULL;
1.178 + }
1.179 +
1.180 + SWThreadHandle *swt;
1.181 + m_QueueLock->Lock();
1.182 + swt = BaseWorker::PopThreadFromQueue();
1.183 + m_QueueLock->Unlock();
1.184 +
1.185 + return swt;
1.186 +}
1.187 +
1.188 +void ThreadWorker::AddThreadToQueue(SWThreadHandle *pHandle)
1.189 +{
1.190 + if (m_state <= Worker_Stopped)
1.191 + {
1.192 + return;
1.193 + }
1.194 +
1.195 + m_QueueLock->Lock();
1.196 + BaseWorker::AddThreadToQueue(pHandle);
1.197 + if (m_Waiting)
1.198 + {
1.199 + m_AddSignal->Signal();
1.200 + }
1.201 + m_QueueLock->Unlock();
1.202 +}
1.203 +
1.204 +WorkerState ThreadWorker::GetStatus(unsigned int *threads)
1.205 +{
1.206 + WorkerState state;
1.207 +
1.208 + m_StateLock->Lock();
1.209 + state = BaseWorker::GetStatus(threads);
1.210 + m_StateLock->Unlock();
1.211 +
1.212 + return state;
1.213 +}
1.214 +
1.215 +bool ThreadWorker::Start()
1.216 +{
1.217 + if (m_state == Worker_Invalid)
1.218 + {
1.219 + if (m_Threader == NULL)
1.220 + {
1.221 + return false;
1.222 + }
1.223 + } else if (m_state != Worker_Stopped) {
1.224 + return false;
1.225 + }
1.226 +
1.227 + m_Waiting = false;
1.228 + m_QueueLock = m_Threader->MakeMutex();
1.229 + m_StateLock = m_Threader->MakeMutex();
1.230 + m_PauseSignal = m_Threader->MakeEventSignal();
1.231 + m_AddSignal = m_Threader->MakeEventSignal();
1.232 + m_state = Worker_Running;
1.233 + ThreadParams pt;
1.234 + pt.flags = Thread_Default;
1.235 + pt.prio = ThreadPrio_Normal;
1.236 + me = m_Threader->MakeThread(this, &pt);
1.237 +
1.238 + return true;
1.239 +}
1.240 +
1.241 +bool ThreadWorker::Stop(bool flush_cancel)
1.242 +{
1.243 + if (m_state == Worker_Invalid || m_state == Worker_Stopped)
1.244 + {
1.245 + return false;
1.246 + }
1.247 +
1.248 + WorkerState oldstate;
1.249 +
1.250 + //set new state
1.251 + m_StateLock->Lock();
1.252 + oldstate = m_state;
1.253 + m_state = Worker_Stopped;
1.254 + m_FlushType = flush_cancel;
1.255 + m_StateLock->Unlock();
1.256 +
1.257 + if (oldstate == Worker_Paused)
1.258 + {
1.259 + Unpause();
1.260 + } else {
1.261 + m_QueueLock->Lock();
1.262 + if (m_Waiting)
1.263 + {
1.264 + m_AddSignal->Signal();
1.265 + }
1.266 + m_QueueLock->Unlock();
1.267 + }
1.268 +
1.269 + me->WaitForThread();
1.270 + //destroy it
1.271 + me->DestroyThis();
1.272 + //flush all remaining events
1.273 + Flush(true);
1.274 +
1.275 + //free mutex locks
1.276 + m_QueueLock->DestroyThis();
1.277 + m_StateLock->DestroyThis();
1.278 + m_PauseSignal->DestroyThis();
1.279 + m_AddSignal->DestroyThis();
1.280 +
1.281 + //invalidizzle
1.282 + m_QueueLock = NULL;
1.283 + m_StateLock = NULL;
1.284 + m_PauseSignal = NULL;
1.285 + m_AddSignal = NULL;
1.286 + me = NULL;
1.287 +
1.288 + return true;
1.289 +}
1.290 +
1.291 +bool ThreadWorker::Pause()
1.292 +{
1.293 + if (m_state != Worker_Running)
1.294 + {
1.295 + return false;
1.296 + }
1.297 +
1.298 + m_StateLock->Lock();
1.299 + m_state = Worker_Paused;
1.300 + m_StateLock->Unlock();
1.301 +
1.302 + return true;
1.303 +}
1.304 +
1.305 +
1.306 +bool ThreadWorker::Unpause()
1.307 +{
1.308 + if (m_state != Worker_Paused)
1.309 + {
1.310 + return false;
1.311 + }
1.312 +
1.313 + m_StateLock->Lock();
1.314 + m_state = Worker_Running;
1.315 + m_StateLock->Unlock();
1.316 + m_PauseSignal->Signal();
1.317 + if (m_Waiting)
1.318 + {
1.319 + m_AddSignal->Signal();
1.320 + }
1.321 +
1.322 + return true;
1.323 +}