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