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