1.1 --- a/core/Database.cpp Fri Jul 13 19:19:30 2007 +0000
1.2 +++ b/core/Database.cpp Fri Jul 13 19:20:12 2007 +0000
1.3 @@ -21,12 +21,14 @@
1.4 #include "Logger.h"
1.5 #include "ExtensionSys.h"
1.6 #include <stdlib.h>
1.7 +#include "ThreadSupport.h"
1.8
1.9 #define DBPARSE_LEVEL_NONE 0
1.10 #define DBPARSE_LEVEL_MAIN 1
1.11 #define DBPARSE_LEVEL_DATABASE 2
1.12
1.13 DBManager g_DBMan;
1.14 +static bool s_OneTimeThreaderErrorMsg = false;
1.15
1.16 DBManager::DBManager()
1.17 : m_StrTab(512), m_ParseLevel(0), m_ParseState(0), m_pDefault(NULL)
1.18 @@ -47,12 +49,24 @@
1.19 g_ShareSys.AddInterface(NULL, this);
1.20
1.21 g_SourceMod.BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
1.22 +
1.23 + m_pConfigLock = g_pThreader->MakeMutex();
1.24 + m_pThinkLock = g_pThreader->MakeMutex();
1.25 + m_pQueueLock = g_pThreader->MakeMutex();
1.26 +
1.27 + g_PluginSys.AddPluginsListener(this);
1.28 }
1.29
1.30 void DBManager::OnSourceModLevelChange(const char *mapName)
1.31 {
1.32 SMCParseError err;
1.33 unsigned int line = 0;
1.34 +
1.35 + /* We lock and don't give up the lock until we're done.
1.36 + * This way the thread's search won't be searching through a
1.37 + * potentially empty/corrupt list, which would be very bad.
1.38 + */
1.39 + m_pConfigLock->Lock();
1.40 if ((err = g_TextParser.ParseFile_SMC(m_Filename, this, &line, NULL)) != SMCParse_Okay)
1.41 {
1.42 g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
1.43 @@ -62,10 +76,17 @@
1.44 g_Logger.LogError("[SM] Line %d: %s", line, txt);
1.45 }
1.46 }
1.47 + m_pConfigLock->Unlock();
1.48 +
1.49 + g_PluginSys.RemovePluginsListener(this);
1.50 }
1.51
1.52 void DBManager::OnSourceModShutdown()
1.53 {
1.54 + KillWorkerThread();
1.55 + m_pConfigLock->DestroyThis();
1.56 + m_pThinkLock->DestroyThis();
1.57 + m_pQueueLock->DestroyThis();
1.58 g_HandleSys.RemoveType(m_DatabaseType, g_pCoreIdent);
1.59 g_HandleSys.RemoveType(m_DriverType, g_pCoreIdent);
1.60 }
1.61 @@ -269,11 +290,24 @@
1.62
1.63 void DBManager::AddDriver(IDBDriver *pDriver)
1.64 {
1.65 + /* Let's kill the worker. Join the thread and let the queries flush.
1.66 + * This is kind of stupid but we just want to unload safely.
1.67 + * Rather than recreate the worker, we'll wait until someone throws
1.68 + * another query through.
1.69 + */
1.70 + KillWorkerThread();
1.71 +
1.72 m_drivers.push_back(pDriver);
1.73 }
1.74
1.75 void DBManager::RemoveDriver(IDBDriver *pDriver)
1.76 {
1.77 + /* Again, we're forced to kill the worker. How rude!
1.78 + * Doing this flushes the queue, and thus we don't need to
1.79 + * clean anything else.
1.80 + */
1.81 + KillWorkerThread();
1.82 +
1.83 for (size_t i=0; i<m_drivers.size(); i++)
1.84 {
1.85 if (m_drivers[i] == pDriver)
1.86 @@ -293,6 +327,31 @@
1.87 db.realDriver = NULL;
1.88 }
1.89 }
1.90 +
1.91 + /* Now that the driver is gone, we have to test the think queue.
1.92 + * Whatever happens therein is up to the db op!
1.93 + */
1.94 + Queue<IDBThreadOperation *>::iterator qiter = m_ThinkQueue.begin();
1.95 + Queue<IDBThreadOperation *> templist;
1.96 + while (qiter != m_ThinkQueue.end())
1.97 + {
1.98 + IDBThreadOperation *op = (*qiter);
1.99 + if (op->GetDriver() == pDriver)
1.100 + {
1.101 + templist.push(op);
1.102 + qiter = m_ThinkQueue.erase(qiter);
1.103 + } else {
1.104 + qiter++;
1.105 + }
1.106 + }
1.107 +
1.108 + for (qiter = templist.begin();
1.109 + qiter != templist.end();
1.110 + qiter++)
1.111 + {
1.112 + IDBThreadOperation *op = (*qiter);
1.113 + op->CancelThinkPart();
1.114 + }
1.115 }
1.116
1.117 IDBDriver *DBManager::GetDefaultDriver()
1.118 @@ -417,3 +476,182 @@
1.119
1.120 return NULL;
1.121 }
1.122 +
1.123 +void DBManager::KillWorkerThread()
1.124 +{
1.125 + if (m_pWorker)
1.126 + {
1.127 + m_pWorker->Stop(false);
1.128 + g_pThreader->DestroyWorker(m_pWorker);
1.129 + m_pWorker = NULL;
1.130 + s_OneTimeThreaderErrorMsg = false;
1.131 + }
1.132 +}
1.133 +
1.134 +bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
1.135 +{
1.136 + if (!m_pWorker)
1.137 + {
1.138 + m_pWorker = g_pThreader->MakeWorker(this, true);
1.139 + if (!m_pWorker)
1.140 + {
1.141 + if (!s_OneTimeThreaderErrorMsg)
1.142 + {
1.143 + g_Logger.LogError("[SM] Unable to create db threader (error unknown)");
1.144 + s_OneTimeThreaderErrorMsg = true;
1.145 + }
1.146 + return false;
1.147 + }
1.148 + if (!m_pWorker->Start())
1.149 + {
1.150 + if (!s_OneTimeThreaderErrorMsg)
1.151 + {
1.152 + g_Logger.LogError("[SM] Unable to start db threader (error unknown)");
1.153 + s_OneTimeThreaderErrorMsg = true;
1.154 + }
1.155 + g_pThreader->DestroyWorker(m_pWorker);
1.156 + m_pWorker = NULL;
1.157 + return false;
1.158 + }
1.159 + }
1.160 +
1.161 + /* Add to the queue */
1.162 + {
1.163 + m_pQueueLock->Lock();
1.164 + Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
1.165 + queue.push(op);
1.166 + m_pQueueLock->Unlock();
1.167 + }
1.168 +
1.169 + /* Make the thread */
1.170 + m_pWorker->MakeThread(this);
1.171 +
1.172 + return true;
1.173 +}
1.174 +
1.175 +void DBManager::OnWorkerStart(IThreadWorker *pWorker)
1.176 +{
1.177 + m_drSafety.clear();
1.178 + for (size_t i=0; i<m_drivers.size(); i++)
1.179 + {
1.180 + if (m_drivers[i]->IsThreadSafe())
1.181 + {
1.182 + m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
1.183 + } else {
1.184 + m_drSafety.push_back(false);
1.185 + }
1.186 + }
1.187 +}
1.188 +
1.189 +void DBManager::OnWorkerStop(IThreadWorker *pWorker)
1.190 +{
1.191 + for (size_t i=0; i<m_drivers.size(); i++)
1.192 + {
1.193 + if (m_drSafety[i])
1.194 + {
1.195 + m_drivers[i]->ShutdownThreadSafety();
1.196 + }
1.197 + }
1.198 + m_drSafety.clear();
1.199 +}
1.200 +
1.201 +void DBManager::RunThread(IThreadHandle *pThread)
1.202 +{
1.203 + IDBThreadOperation *op = NULL;
1.204 +
1.205 + /* Get something from the queue */
1.206 + {
1.207 + m_pQueueLock->Lock();
1.208 + Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
1.209 + if (!queue.empty())
1.210 + {
1.211 + op = queue.first();
1.212 + queue.pop();
1.213 + }
1.214 + m_pQueueLock->Unlock();
1.215 + }
1.216 +
1.217 + /* whoa. hi. did we get something? we should have. */
1.218 + if (!op)
1.219 + {
1.220 + /* wtf? */
1.221 + return;
1.222 + }
1.223 +
1.224 + op->RunThreadPart();
1.225 +
1.226 + m_pThinkLock->Lock();
1.227 + m_ThinkQueue.push(op);
1.228 + m_pThinkLock->Unlock();
1.229 +}
1.230 +
1.231 +void DBManager::RunFrame()
1.232 +{
1.233 + /* Don't bother if we're empty */
1.234 + if (!m_ThinkQueue.size())
1.235 + {
1.236 + return;
1.237 + }
1.238 +
1.239 + /* Dump one thing per-frame so the server stays sane. */
1.240 + m_pThinkLock->Lock();
1.241 + IDBThreadOperation *op = m_ThinkQueue.first();
1.242 + m_ThinkQueue.pop();
1.243 + m_pThinkLock->Unlock();
1.244 + op->RunThinkPart();
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::OnPluginUnloaded(IPlugin *plugin)
1.253 +{
1.254 + /* Kill the thread so we can flush everything into the think queue... */
1.255 + KillWorkerThread();
1.256 +
1.257 + /* Mark the plugin as being unloaded so future database calls will ignore threading... */
1.258 + plugin->SetProperty("DisallowDBThreads", NULL);
1.259 +
1.260 + /* Run all of the think operations.
1.261 + * Unlike the driver unloading example, we'll let these calls go through,
1.262 + * since a plugin unloading is far more normal.
1.263 + */
1.264 + Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
1.265 + Queue<IDBThreadOperation *> templist;
1.266 + while (iter != m_ThinkQueue.end())
1.267 + {
1.268 + IDBThreadOperation *op = (*iter);
1.269 + if (op->GetPlugin() == plugin)
1.270 + {
1.271 + templist.push(op);
1.272 + iter = m_ThinkQueue.erase(iter);
1.273 + } else {
1.274 + iter++;
1.275 + }
1.276 + }
1.277 +
1.278 + for (iter = templist.begin();
1.279 + iter != templist.end();
1.280 + iter++)
1.281 + {
1.282 + IDBThreadOperation *op = (*iter);
1.283 + op->RunThinkPart();
1.284 + }
1.285 +}
1.286 +
1.287 +void DBManager::LockConfig()
1.288 +{
1.289 + m_pConfigLock->Lock();
1.290 +}
1.291 +
1.292 +void DBManager::UnlockConfig()
1.293 +{
1.294 + m_pConfigLock->Unlock();
1.295 +}
1.296 +
1.297 +const char *DBManager::GetDefaultDriverName()
1.298 +{
1.299 + return m_DefDriver.c_str();
1.300 +}