core/Database.cpp
changeset 4027 d2c15108b468
parent 3872 4c93f74cae51
     1.1 --- a/core/Database.cpp	Tue Aug 20 23:59:11 2013 -0700
     1.2 +++ b/core/Database.cpp	Wed Aug 21 00:01:28 2013 -0700
     1.3 @@ -1,5 +1,5 @@
     1.4  /**
     1.5 - * vim: set ts=4 :
     1.6 + * vim: set ts=4 sw=4 tw=99 noet :
     1.7   * =============================================================================
     1.8   * SourceMod
     1.9   * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
    1.10 @@ -45,7 +45,10 @@
    1.11  static bool s_OneTimeThreaderErrorMsg = false;
    1.12  
    1.13  DBManager::DBManager() 
    1.14 -: m_ParseLevel(0), m_ParseState(0), m_pDefault(NULL)
    1.15 +	: m_Terminate(false),
    1.16 +	  m_ParseLevel(0),
    1.17 +	  m_ParseState(0),
    1.18 +	  m_pDefault(NULL)
    1.19  {
    1.20  }
    1.21  
    1.22 @@ -64,10 +67,6 @@
    1.23  
    1.24  	g_SourceMod.BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
    1.25  
    1.26 -	m_pConfigLock = g_pThreader->MakeMutex();
    1.27 -	m_pThinkLock = g_pThreader->MakeMutex();
    1.28 -	m_pQueueLock = g_pThreader->MakeMutex();
    1.29 -
    1.30  	scripts->AddPluginsListener(this);
    1.31  }
    1.32  
    1.33 @@ -80,7 +79,7 @@
    1.34  	 * This way the thread's search won't be searching through a
    1.35  	 * potentially empty/corrupt list, which would be very bad.
    1.36  	 */
    1.37 -	m_pConfigLock->Lock();
    1.38 +	ke::AutoLock lock(&m_ConfigLock);
    1.39  	if ((err = textparsers->ParseFile_SMC(m_Filename, this, &states)) != SMCError_Okay)
    1.40  	{
    1.41  		g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
    1.42 @@ -90,16 +89,12 @@
    1.43  			g_Logger.LogError("[SM] Line %d: %s", states.line, txt);
    1.44  		}
    1.45  	}
    1.46 -	m_pConfigLock->Unlock();
    1.47  }
    1.48  
    1.49  void DBManager::OnSourceModShutdown()
    1.50  {
    1.51  	KillWorkerThread();
    1.52  	scripts->RemovePluginsListener(this);
    1.53 -	m_pConfigLock->DestroyThis();
    1.54 -	m_pThinkLock->DestroyThis();
    1.55 -	m_pQueueLock->DestroyThis();
    1.56  	handlesys->RemoveType(m_DatabaseType, g_pCoreIdent);
    1.57  	handlesys->RemoveType(m_DriverType, g_pCoreIdent);
    1.58  	ClearConfigs();
    1.59 @@ -502,12 +497,17 @@
    1.60  
    1.61  void DBManager::KillWorkerThread()
    1.62  {
    1.63 -	if (m_pWorker)
    1.64 +	if (m_Worker)
    1.65  	{
    1.66 -		m_pWorker->Stop(false);
    1.67 -		g_pThreader->DestroyWorker(m_pWorker);
    1.68 -		m_pWorker = NULL;
    1.69 +		{
    1.70 +			ke::AutoLock lock(&m_QueueEvent);
    1.71 +			m_Terminate = true;
    1.72 +			m_QueueEvent.Notify();
    1.73 +		}
    1.74 +		m_Worker->Join();
    1.75 +		m_Worker = NULL;
    1.76  		s_OneTimeThreaderErrorMsg = false;
    1.77 +		m_Terminate = false;
    1.78  	}
    1.79  }
    1.80  
    1.81 @@ -520,99 +520,95 @@
    1.82  		return false;
    1.83  	}
    1.84  
    1.85 -	if (!m_pWorker)
    1.86 +	if (!m_Worker)
    1.87  	{
    1.88 -		m_pWorker = g_pThreader->MakeWorker(this, true);
    1.89 -		if (!m_pWorker)
    1.90 +		m_Worker = new ke::Thread(this, "SM SQL Worker");
    1.91 +		if (!m_Worker->Succeeded())
    1.92  		{
    1.93  			if (!s_OneTimeThreaderErrorMsg)
    1.94  			{
    1.95  				g_Logger.LogError("[SM] Unable to create db threader (error unknown)");
    1.96  				s_OneTimeThreaderErrorMsg = true;
    1.97  			}
    1.98 -			return false;
    1.99 -		}
   1.100 -		if (!m_pWorker->Start())
   1.101 -		{
   1.102 -			if (!s_OneTimeThreaderErrorMsg)
   1.103 -			{
   1.104 -				g_Logger.LogError("[SM] Unable to start db threader (error unknown)");
   1.105 -				s_OneTimeThreaderErrorMsg = true;
   1.106 -			}
   1.107 -			g_pThreader->DestroyWorker(m_pWorker);
   1.108 -			m_pWorker = NULL;
   1.109 +			m_Worker = NULL;
   1.110  			return false;
   1.111  		}
   1.112  	}
   1.113  
   1.114  	/* Add to the queue */
   1.115  	{
   1.116 -		m_pQueueLock->Lock();
   1.117 +		ke::AutoLock lock(&m_QueueEvent);
   1.118  		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
   1.119  		queue.push(op);
   1.120 -		m_pQueueLock->Unlock();
   1.121 +		m_QueueEvent.Notify();
   1.122  	}
   1.123  
   1.124 -	/* Make the thread */
   1.125 -	m_pWorker->MakeThread(this);
   1.126 -
   1.127  	return true;
   1.128  }
   1.129  
   1.130 -void DBManager::OnWorkerStart(IThreadWorker *pWorker)
   1.131 +void DBManager::Run()
   1.132  {
   1.133 -	m_drSafety.clear();
   1.134 -	for (size_t i=0; i<m_drivers.size(); i++)
   1.135 +	// Initialize DB threadsafety.
   1.136 +	for (size_t i=0; i < m_drivers.size(); i++)
   1.137  	{
   1.138  		if (m_drivers[i]->IsThreadSafe())
   1.139 -		{
   1.140  			m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
   1.141 -		} else {
   1.142 +		else
   1.143  			m_drSafety.push_back(false);
   1.144 -		}
   1.145  	}
   1.146 -}
   1.147  
   1.148 -void DBManager::OnWorkerStop(IThreadWorker *pWorker)
   1.149 -{
   1.150 +	// Run actual worker thread logic.
   1.151 +	ThreadMain();
   1.152 +
   1.153 +	// Shutdown DB threadsafety.
   1.154  	for (size_t i=0; i<m_drivers.size(); i++)
   1.155  	{
   1.156  		if (m_drSafety[i])
   1.157 -		{
   1.158  			m_drivers[i]->ShutdownThreadSafety();
   1.159 -		}
   1.160  	}
   1.161  	m_drSafety.clear();
   1.162  }
   1.163  
   1.164 -void DBManager::RunThread(IThreadHandle *pThread)
   1.165 +void DBManager::ThreadMain()
   1.166  {
   1.167 -	IDBThreadOperation *op = NULL;
   1.168 +	ke::AutoLock lock(&m_QueueEvent);
   1.169 +
   1.170 +	while (true) {
   1.171 +		// The lock has been acquired. Grab everything we can out of the
   1.172 +		// queue. Since we want to flush the queue even if we're terminated,
   1.173 +		// we process all operations we can before checking to terminate.
   1.174 +		// There's no risk of starvation since the main thread blocks on us
   1.175 +		// terminating.
   1.176 +		while (true)
   1.177 +		{
   1.178 +			Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
   1.179 +			if (queue.empty())
   1.180 +				break;
   1.181  
   1.182 -	/* Get something from the queue */
   1.183 -	{
   1.184 -		m_pQueueLock->Lock();
   1.185 -		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
   1.186 -		if (!queue.empty())
   1.187 -		{
   1.188 -			op = queue.first();
   1.189 +			IDBThreadOperation *op = queue.first();
   1.190  			queue.pop();
   1.191 +
   1.192 +			// Unlock the queue when we run the query, so the main thread can
   1.193 +			// keep pumping events. We re-acquire the lock to check for more
   1.194 +			// items. It's okay if we terminate while unlocked; the main
   1.195 +			// thread would be blocked and we'd need to flush the queue
   1.196 +			// anyway, so after we've depleted the queue here, we'll just
   1.197 +			// reach the terminate at the top of the loop.
   1.198 +			{
   1.199 +				ke::AutoUnlock unlock(&m_QueueEvent);
   1.200 +				op->RunThreadPart();
   1.201 +
   1.202 +				ke::AutoLock lock(&m_ThinkLock);
   1.203 +				m_ThinkQueue.push(op);
   1.204 +			}
   1.205  		}
   1.206 -		m_pQueueLock->Unlock();
   1.207 -	}
   1.208 +
   1.209 +		if (m_Terminate)
   1.210 +			return;
   1.211  
   1.212 -	/* whoa.  hi.  did we get something? we should have. */
   1.213 -	if (!op)
   1.214 -	{
   1.215 -		/* wtf? */
   1.216 -		return;
   1.217 +		// Release the lock and wait for a signal.
   1.218 +		m_QueueEvent.Wait();
   1.219  	}
   1.220 -
   1.221 -	op->RunThreadPart();
   1.222 -
   1.223 -	m_pThinkLock->Lock();
   1.224 -	m_ThinkQueue.push(op);
   1.225 -	m_pThinkLock->Unlock();
   1.226  }
   1.227  
   1.228  void DBManager::RunFrame()
   1.229 @@ -624,19 +620,16 @@
   1.230  	}
   1.231  
   1.232  	/* Dump one thing per-frame so the server stays sane. */
   1.233 -	m_pThinkLock->Lock();
   1.234 -	IDBThreadOperation *op = m_ThinkQueue.first();
   1.235 -	m_ThinkQueue.pop();
   1.236 -	m_pThinkLock->Unlock();
   1.237 +	IDBThreadOperation *op;
   1.238 +	{
   1.239 +		ke::AutoLock lock(&m_ThinkLock);
   1.240 +		op = m_ThinkQueue.first();
   1.241 +		m_ThinkQueue.pop();
   1.242 +	}
   1.243  	op->RunThinkPart();
   1.244  	op->Destroy();
   1.245  }
   1.246  
   1.247 -void DBManager::OnTerminate(IThreadHandle *pThread, bool cancel)
   1.248 -{
   1.249 -	/* Do nothing */
   1.250 -}
   1.251 -
   1.252  void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
   1.253  {
   1.254  	s_pAddBlock = pToken;
   1.255 @@ -712,12 +705,12 @@
   1.256  
   1.257  void DBManager::LockConfig()
   1.258  {
   1.259 -	m_pConfigLock->Lock();
   1.260 +	m_ConfigLock.Lock();
   1.261  }
   1.262  
   1.263  void DBManager::UnlockConfig()
   1.264  {
   1.265 -	m_pConfigLock->Unlock();
   1.266 +	m_ConfigLock.Unlock();
   1.267  }
   1.268  
   1.269  const char *DBManager::GetDefaultDriverName()