Rewrite DBManager to use the new synchronization primitives (bug 5870, r=fyren).
authorDavid Anderson <dvander@alliedmods.net>
Wed Aug 21 00:01:28 2013 -0700 (2013-08-21)
changeset 4027d2c15108b468
parent 4026 684cf3490051
child 4028 4b3d32330007
Rewrite DBManager to use the new synchronization primitives (bug 5870, r=fyren).
core/AMBuilder
core/Database.cpp
core/Database.h
     1.1 --- a/core/AMBuilder	Tue Aug 20 23:59:11 2013 -0700
     1.2 +++ b/core/AMBuilder	Wed Aug 21 00:01:28 2013 -0700
     1.3 @@ -28,6 +28,10 @@
     1.4  	binary = Cpp.LibraryBuilder(name, AMBuild, extension, compiler)
     1.5  	SM.PreSetupHL2Job(extension, binary, i)
     1.6  
     1.7 +	if AMBuild.target['platform'] == 'linux':
     1.8 +		compiler['POSTLINKFLAGS'].append('-lpthread')
     1.9 +		compiler['POSTLINKFLAGS'].append('-lrt')
    1.10 +
    1.11  	if i in ['csgo', 'dota']:
    1.12  		if AMBuild.target['platform'] == 'linux':
    1.13  			link = os.path.join(os.path.join(AMBuild.outputFolder, extension.workFolder), 'libprotobuf.a')
     2.1 --- a/core/Database.cpp	Tue Aug 20 23:59:11 2013 -0700
     2.2 +++ b/core/Database.cpp	Wed Aug 21 00:01:28 2013 -0700
     2.3 @@ -1,5 +1,5 @@
     2.4  /**
     2.5 - * vim: set ts=4 :
     2.6 + * vim: set ts=4 sw=4 tw=99 noet :
     2.7   * =============================================================================
     2.8   * SourceMod
     2.9   * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
    2.10 @@ -45,7 +45,10 @@
    2.11  static bool s_OneTimeThreaderErrorMsg = false;
    2.12  
    2.13  DBManager::DBManager() 
    2.14 -: m_ParseLevel(0), m_ParseState(0), m_pDefault(NULL)
    2.15 +	: m_Terminate(false),
    2.16 +	  m_ParseLevel(0),
    2.17 +	  m_ParseState(0),
    2.18 +	  m_pDefault(NULL)
    2.19  {
    2.20  }
    2.21  
    2.22 @@ -64,10 +67,6 @@
    2.23  
    2.24  	g_SourceMod.BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
    2.25  
    2.26 -	m_pConfigLock = g_pThreader->MakeMutex();
    2.27 -	m_pThinkLock = g_pThreader->MakeMutex();
    2.28 -	m_pQueueLock = g_pThreader->MakeMutex();
    2.29 -
    2.30  	scripts->AddPluginsListener(this);
    2.31  }
    2.32  
    2.33 @@ -80,7 +79,7 @@
    2.34  	 * This way the thread's search won't be searching through a
    2.35  	 * potentially empty/corrupt list, which would be very bad.
    2.36  	 */
    2.37 -	m_pConfigLock->Lock();
    2.38 +	ke::AutoLock lock(&m_ConfigLock);
    2.39  	if ((err = textparsers->ParseFile_SMC(m_Filename, this, &states)) != SMCError_Okay)
    2.40  	{
    2.41  		g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
    2.42 @@ -90,16 +89,12 @@
    2.43  			g_Logger.LogError("[SM] Line %d: %s", states.line, txt);
    2.44  		}
    2.45  	}
    2.46 -	m_pConfigLock->Unlock();
    2.47  }
    2.48  
    2.49  void DBManager::OnSourceModShutdown()
    2.50  {
    2.51  	KillWorkerThread();
    2.52  	scripts->RemovePluginsListener(this);
    2.53 -	m_pConfigLock->DestroyThis();
    2.54 -	m_pThinkLock->DestroyThis();
    2.55 -	m_pQueueLock->DestroyThis();
    2.56  	handlesys->RemoveType(m_DatabaseType, g_pCoreIdent);
    2.57  	handlesys->RemoveType(m_DriverType, g_pCoreIdent);
    2.58  	ClearConfigs();
    2.59 @@ -502,12 +497,17 @@
    2.60  
    2.61  void DBManager::KillWorkerThread()
    2.62  {
    2.63 -	if (m_pWorker)
    2.64 +	if (m_Worker)
    2.65  	{
    2.66 -		m_pWorker->Stop(false);
    2.67 -		g_pThreader->DestroyWorker(m_pWorker);
    2.68 -		m_pWorker = NULL;
    2.69 +		{
    2.70 +			ke::AutoLock lock(&m_QueueEvent);
    2.71 +			m_Terminate = true;
    2.72 +			m_QueueEvent.Notify();
    2.73 +		}
    2.74 +		m_Worker->Join();
    2.75 +		m_Worker = NULL;
    2.76  		s_OneTimeThreaderErrorMsg = false;
    2.77 +		m_Terminate = false;
    2.78  	}
    2.79  }
    2.80  
    2.81 @@ -520,99 +520,95 @@
    2.82  		return false;
    2.83  	}
    2.84  
    2.85 -	if (!m_pWorker)
    2.86 +	if (!m_Worker)
    2.87  	{
    2.88 -		m_pWorker = g_pThreader->MakeWorker(this, true);
    2.89 -		if (!m_pWorker)
    2.90 +		m_Worker = new ke::Thread(this, "SM SQL Worker");
    2.91 +		if (!m_Worker->Succeeded())
    2.92  		{
    2.93  			if (!s_OneTimeThreaderErrorMsg)
    2.94  			{
    2.95  				g_Logger.LogError("[SM] Unable to create db threader (error unknown)");
    2.96  				s_OneTimeThreaderErrorMsg = true;
    2.97  			}
    2.98 -			return false;
    2.99 -		}
   2.100 -		if (!m_pWorker->Start())
   2.101 -		{
   2.102 -			if (!s_OneTimeThreaderErrorMsg)
   2.103 -			{
   2.104 -				g_Logger.LogError("[SM] Unable to start db threader (error unknown)");
   2.105 -				s_OneTimeThreaderErrorMsg = true;
   2.106 -			}
   2.107 -			g_pThreader->DestroyWorker(m_pWorker);
   2.108 -			m_pWorker = NULL;
   2.109 +			m_Worker = NULL;
   2.110  			return false;
   2.111  		}
   2.112  	}
   2.113  
   2.114  	/* Add to the queue */
   2.115  	{
   2.116 -		m_pQueueLock->Lock();
   2.117 +		ke::AutoLock lock(&m_QueueEvent);
   2.118  		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
   2.119  		queue.push(op);
   2.120 -		m_pQueueLock->Unlock();
   2.121 +		m_QueueEvent.Notify();
   2.122  	}
   2.123  
   2.124 -	/* Make the thread */
   2.125 -	m_pWorker->MakeThread(this);
   2.126 -
   2.127  	return true;
   2.128  }
   2.129  
   2.130 -void DBManager::OnWorkerStart(IThreadWorker *pWorker)
   2.131 +void DBManager::Run()
   2.132  {
   2.133 -	m_drSafety.clear();
   2.134 -	for (size_t i=0; i<m_drivers.size(); i++)
   2.135 +	// Initialize DB threadsafety.
   2.136 +	for (size_t i=0; i < m_drivers.size(); i++)
   2.137  	{
   2.138  		if (m_drivers[i]->IsThreadSafe())
   2.139 -		{
   2.140  			m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
   2.141 -		} else {
   2.142 +		else
   2.143  			m_drSafety.push_back(false);
   2.144 -		}
   2.145  	}
   2.146 -}
   2.147  
   2.148 -void DBManager::OnWorkerStop(IThreadWorker *pWorker)
   2.149 -{
   2.150 +	// Run actual worker thread logic.
   2.151 +	ThreadMain();
   2.152 +
   2.153 +	// Shutdown DB threadsafety.
   2.154  	for (size_t i=0; i<m_drivers.size(); i++)
   2.155  	{
   2.156  		if (m_drSafety[i])
   2.157 -		{
   2.158  			m_drivers[i]->ShutdownThreadSafety();
   2.159 -		}
   2.160  	}
   2.161  	m_drSafety.clear();
   2.162  }
   2.163  
   2.164 -void DBManager::RunThread(IThreadHandle *pThread)
   2.165 +void DBManager::ThreadMain()
   2.166  {
   2.167 -	IDBThreadOperation *op = NULL;
   2.168 +	ke::AutoLock lock(&m_QueueEvent);
   2.169 +
   2.170 +	while (true) {
   2.171 +		// The lock has been acquired. Grab everything we can out of the
   2.172 +		// queue. Since we want to flush the queue even if we're terminated,
   2.173 +		// we process all operations we can before checking to terminate.
   2.174 +		// There's no risk of starvation since the main thread blocks on us
   2.175 +		// terminating.
   2.176 +		while (true)
   2.177 +		{
   2.178 +			Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
   2.179 +			if (queue.empty())
   2.180 +				break;
   2.181  
   2.182 -	/* Get something from the queue */
   2.183 -	{
   2.184 -		m_pQueueLock->Lock();
   2.185 -		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
   2.186 -		if (!queue.empty())
   2.187 -		{
   2.188 -			op = queue.first();
   2.189 +			IDBThreadOperation *op = queue.first();
   2.190  			queue.pop();
   2.191 +
   2.192 +			// Unlock the queue when we run the query, so the main thread can
   2.193 +			// keep pumping events. We re-acquire the lock to check for more
   2.194 +			// items. It's okay if we terminate while unlocked; the main
   2.195 +			// thread would be blocked and we'd need to flush the queue
   2.196 +			// anyway, so after we've depleted the queue here, we'll just
   2.197 +			// reach the terminate at the top of the loop.
   2.198 +			{
   2.199 +				ke::AutoUnlock unlock(&m_QueueEvent);
   2.200 +				op->RunThreadPart();
   2.201 +
   2.202 +				ke::AutoLock lock(&m_ThinkLock);
   2.203 +				m_ThinkQueue.push(op);
   2.204 +			}
   2.205  		}
   2.206 -		m_pQueueLock->Unlock();
   2.207 -	}
   2.208 +
   2.209 +		if (m_Terminate)
   2.210 +			return;
   2.211  
   2.212 -	/* whoa.  hi.  did we get something? we should have. */
   2.213 -	if (!op)
   2.214 -	{
   2.215 -		/* wtf? */
   2.216 -		return;
   2.217 +		// Release the lock and wait for a signal.
   2.218 +		m_QueueEvent.Wait();
   2.219  	}
   2.220 -
   2.221 -	op->RunThreadPart();
   2.222 -
   2.223 -	m_pThinkLock->Lock();
   2.224 -	m_ThinkQueue.push(op);
   2.225 -	m_pThinkLock->Unlock();
   2.226  }
   2.227  
   2.228  void DBManager::RunFrame()
   2.229 @@ -624,19 +620,16 @@
   2.230  	}
   2.231  
   2.232  	/* Dump one thing per-frame so the server stays sane. */
   2.233 -	m_pThinkLock->Lock();
   2.234 -	IDBThreadOperation *op = m_ThinkQueue.first();
   2.235 -	m_ThinkQueue.pop();
   2.236 -	m_pThinkLock->Unlock();
   2.237 +	IDBThreadOperation *op;
   2.238 +	{
   2.239 +		ke::AutoLock lock(&m_ThinkLock);
   2.240 +		op = m_ThinkQueue.first();
   2.241 +		m_ThinkQueue.pop();
   2.242 +	}
   2.243  	op->RunThinkPart();
   2.244  	op->Destroy();
   2.245  }
   2.246  
   2.247 -void DBManager::OnTerminate(IThreadHandle *pThread, bool cancel)
   2.248 -{
   2.249 -	/* Do nothing */
   2.250 -}
   2.251 -
   2.252  void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
   2.253  {
   2.254  	s_pAddBlock = pToken;
   2.255 @@ -712,12 +705,12 @@
   2.256  
   2.257  void DBManager::LockConfig()
   2.258  {
   2.259 -	m_pConfigLock->Lock();
   2.260 +	m_ConfigLock.Lock();
   2.261  }
   2.262  
   2.263  void DBManager::UnlockConfig()
   2.264  {
   2.265 -	m_pConfigLock->Unlock();
   2.266 +	m_ConfigLock.Unlock();
   2.267  }
   2.268  
   2.269  const char *DBManager::GetDefaultDriverName()
     3.1 --- a/core/Database.h	Tue Aug 20 23:59:11 2013 -0700
     3.2 +++ b/core/Database.h	Wed Aug 21 00:01:28 2013 -0700
     3.3 @@ -1,5 +1,5 @@
     3.4  /**
     3.5 - * vim: set ts=4 :
     3.6 + * vim: set ts=4 sw=4 tw=99 noet :
     3.7   * =============================================================================
     3.8   * SourceMod
     3.9   * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
    3.10 @@ -41,6 +41,7 @@
    3.11  #include "sm_memtable.h"
    3.12  #include <IThreader.h>
    3.13  #include <IPluginSys.h>
    3.14 +#include <ke_thread_utils.h>
    3.15  #include "sm_simple_prioqueue.h"
    3.16  
    3.17  using namespace SourceHook;
    3.18 @@ -65,9 +66,8 @@
    3.19  	public SMGlobalClass,
    3.20  	public IHandleTypeDispatch,
    3.21  	public ITextListener_SMC,
    3.22 -	public IThread,
    3.23 -	public IThreadWorkerCallbacks,
    3.24 -	public IPluginsListener
    3.25 +	public IPluginsListener,
    3.26 +	public ke::IRunnable
    3.27  {
    3.28  public:
    3.29  	DBManager();
    3.30 @@ -98,12 +98,9 @@
    3.31  	SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
    3.32  	SMCResult ReadSMC_LeavingSection(const SMCStates *states);
    3.33  	void ReadSMC_ParseEnd(bool halted, bool failed);
    3.34 -public: //IThread
    3.35 -	void RunThread(IThreadHandle *pThread);
    3.36 -	void OnTerminate(IThreadHandle *pThread, bool cancel);
    3.37 -public: //IThreadWorkerCallbacks
    3.38 -	void OnWorkerStart(IThreadWorker *pWorker);
    3.39 -	void OnWorkerStop(IThreadWorker *pWorker);
    3.40 +public: //ke::IRunnable
    3.41 +	void Run();
    3.42 +	void ThreadMain();
    3.43  public: //IPluginsListener
    3.44  	void OnPluginUnloaded(IPlugin *plugin);
    3.45  public:
    3.46 @@ -129,10 +126,11 @@
    3.47  	PrioQueue<IDBThreadOperation *> m_OpQueue;
    3.48  	Queue<IDBThreadOperation *> m_ThinkQueue;
    3.49  	CVector<bool> m_drSafety;			/* which drivers are safe? */
    3.50 -	IThreadWorker *m_pWorker;			/* Worker thread object */
    3.51 -	IMutex *m_pConfigLock;				/* Configuration lock */
    3.52 -	IMutex *m_pQueueLock;				/* Queue safety lock */
    3.53 -	IMutex *m_pThinkLock;				/* Think-queue lock */
    3.54 +	ke::AutoPtr<ke::Thread> m_Worker;
    3.55 +	ke::ConditionVariable m_QueueEvent;
    3.56 +	ke::Mutex m_ConfigLock;
    3.57 +	ke::Mutex m_ThinkLock;
    3.58 +	bool m_Terminate;
    3.59  
    3.60  	List<ConfDbInfo *> m_confs;
    3.61  	HandleType_t m_DriverType;