Rewrite DBManager to use the new synchronization primitives (bug 5870, r=fyren).
Rewrite DBManager to use the new synchronization primitives (
bug 5870, r=fyren).
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;