core/Database.cpp
author Matt Woodrow <pred@alliedmods.net>
Tue Mar 11 23:52:56 2008 +0000 (2008-03-11)
changeset 1921 f993e100e5a3
parent 1656 b0b86d57530c
child 1947 e73a1e51833b
permissions -rw-r--r--
Fixed bug in GetAdjustedTime()
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "Database.h"
    33 #include "HandleSys.h"
    34 #include "ShareSys.h"
    35 #include "sourcemod.h"
    36 #include "sm_stringutil.h"
    37 #include "Logger.h"
    38 #include "ExtensionSys.h"
    39 #include <stdlib.h>
    40 #include "ThreadSupport.h"
    41 
    42 #define DBPARSE_LEVEL_NONE		0
    43 #define DBPARSE_LEVEL_MAIN		1
    44 #define DBPARSE_LEVEL_DATABASE	2
    45 
    46 DBManager g_DBMan;
    47 static bool s_OneTimeThreaderErrorMsg = false;
    48 
    49 DBManager::DBManager() 
    50 : m_StrTab(512), m_ParseLevel(0), m_ParseState(0), m_pDefault(NULL)
    51 {
    52 }
    53 
    54 void DBManager::OnSourceModAllInitialized()
    55 {
    56 	HandleAccess sec;
    57 
    58 	g_HandleSys.InitAccessDefaults(NULL, &sec);
    59 	sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
    60 	sec.access[HandleAccess_Clone] |= HANDLE_RESTRICT_IDENTITY;
    61 	
    62 	m_DriverType = g_HandleSys.CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    63 	m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    64 
    65 	g_ShareSys.AddInterface(NULL, this);
    66 
    67 	g_SourceMod.BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
    68 
    69 	m_pConfigLock = g_pThreader->MakeMutex();
    70 	m_pThinkLock = g_pThreader->MakeMutex();
    71 	m_pQueueLock = g_pThreader->MakeMutex();
    72 
    73 	g_PluginSys.AddPluginsListener(this);
    74 }
    75 
    76 void DBManager::OnSourceModLevelChange(const char *mapName)
    77 {
    78 	SMCError err;
    79 	SMCStates states = {0, 0};
    80 
    81 	/* We lock and don't give up the lock until we're done.
    82 	 * This way the thread's search won't be searching through a
    83 	 * potentially empty/corrupt list, which would be very bad.
    84 	 */
    85 	m_pConfigLock->Lock();
    86 	if ((err = textparsers->ParseFile_SMC(m_Filename, this, &states)) != SMCError_Okay)
    87 	{
    88 		g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
    89 		if (err != SMCError_Custom)
    90 		{
    91 			const char *txt = textparsers->GetSMCErrorString(err);
    92 			g_Logger.LogError("[SM] Line %d: %s", states.line, txt);
    93 		}
    94 	}
    95 	m_pConfigLock->Unlock();
    96 }
    97 
    98 void DBManager::OnSourceModShutdown()
    99 {
   100 	KillWorkerThread();
   101 	g_PluginSys.RemovePluginsListener(this);
   102 	m_pConfigLock->DestroyThis();
   103 	m_pThinkLock->DestroyThis();
   104 	m_pQueueLock->DestroyThis();
   105 	g_HandleSys.RemoveType(m_DatabaseType, g_pCoreIdent);
   106 	g_HandleSys.RemoveType(m_DriverType, g_pCoreIdent);
   107 }
   108 
   109 unsigned int DBManager::GetInterfaceVersion()
   110 {
   111 	return SMINTERFACE_DBI_VERSION;
   112 }
   113 
   114 const char *DBManager::GetInterfaceName()
   115 {
   116 	return SMINTERFACE_DBI_NAME;
   117 }
   118 
   119 void DBManager::OnHandleDestroy(HandleType_t type, void *object)
   120 {
   121 	if (type == m_DriverType)
   122 	{
   123 		/* Ignore */
   124 		return;
   125 	}
   126 
   127 	if (g_HandleSys.TypeCheck(type, m_DatabaseType))
   128 	{
   129 		IDatabase *pdb = (IDatabase *)object;
   130 		pdb->Close();
   131 	}
   132 }
   133 
   134 void DBManager::ReadSMC_ParseStart()
   135 {
   136 	m_confs.clear();
   137 	m_ParseLevel = 0;
   138 	m_ParseState = DBPARSE_LEVEL_NONE;
   139 	m_StrTab.Reset();
   140 	m_DefDriver.clear();
   141 }
   142 
   143 ConfDbInfo s_CurInfo;
   144 SMCResult DBManager::ReadSMC_NewSection(const SMCStates *states, const char *name)
   145 {
   146 	if (m_ParseLevel)
   147 	{
   148 		m_ParseLevel++;
   149 		return SMCResult_Continue;
   150 	}
   151 
   152 	if (m_ParseState == DBPARSE_LEVEL_NONE)
   153 	{
   154 		if (strcmp(name, "Databases") == 0)
   155 		{
   156 			m_ParseState = DBPARSE_LEVEL_MAIN;
   157 		} else {
   158 			m_ParseLevel++;
   159 		}
   160 	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
   161 		s_CurInfo = ConfDbInfo();
   162 		s_CurInfo.name = m_StrTab.AddString(name);
   163 		m_ParseState = DBPARSE_LEVEL_DATABASE;
   164 	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
   165 		m_ParseLevel++;
   166 	}
   167 
   168 	return SMCResult_Continue;
   169 }
   170 
   171 SMCResult DBManager::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   172 {
   173 	if (m_ParseLevel)
   174 	{
   175 		return SMCResult_Continue;
   176 	}
   177 
   178 	if (m_ParseState == DBPARSE_LEVEL_MAIN)
   179 	{
   180 		if (strcmp(key, "driver_default") == 0)
   181 		{
   182 			m_DefDriver.assign(value);
   183 		}
   184 	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
   185 		if (strcmp(key, "driver") == 0)
   186 		{
   187 			if (strcmp(value, "default") != 0)
   188 			{
   189 				s_CurInfo.driver = m_StrTab.AddString(value);
   190 			}
   191 		} else if (strcmp(key, "database") == 0) {
   192 			s_CurInfo.database = m_StrTab.AddString(value);
   193 		} else if (strcmp(key, "host") == 0) {
   194 			s_CurInfo.host = m_StrTab.AddString(value);
   195 		} else if (strcmp(key, "user") == 0) {
   196 			s_CurInfo.user = m_StrTab.AddString(value);
   197 		} else if (strcmp(key, "pass") == 0) {
   198 			s_CurInfo.pass = m_StrTab.AddString(value);
   199 		} else if (strcmp(key, "timeout") == 0) {
   200 			s_CurInfo.info.maxTimeout = atoi(value);
   201 		} else if (strcmp(key, "port") == 0) {
   202 			s_CurInfo.info.port = atoi(value);
   203 		}
   204 	}
   205 
   206 	return SMCResult_Continue;
   207 }
   208 
   209 #define ASSIGN_VAR(var) \
   210 	if (s_CurInfo.var == -1) { \
   211 		s_CurInfo.info.var = ""; \
   212 	} else { \
   213 		s_CurInfo.info.var = m_StrTab.GetString(s_CurInfo.var); \
   214 	}
   215 
   216 SMCResult DBManager::ReadSMC_LeavingSection(const SMCStates *states)
   217 {
   218 	if (m_ParseLevel)
   219 	{
   220 		m_ParseLevel--;
   221 		return SMCResult_Continue;
   222 	}
   223 
   224 	if (m_ParseState == DBPARSE_LEVEL_DATABASE)
   225 	{
   226 		/* Set all of the info members to either a blank string
   227 		 * or the string pointer from the string table.
   228 		 */
   229 		ASSIGN_VAR(driver);
   230 		ASSIGN_VAR(database);
   231 		ASSIGN_VAR(host);
   232 		ASSIGN_VAR(user);
   233 		ASSIGN_VAR(pass);
   234 		
   235 		/* Save it.. */
   236 		m_confs.push_back(s_CurInfo);
   237 
   238 		/* Go up one level */
   239 		m_ParseState = DBPARSE_LEVEL_MAIN;
   240 	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
   241 		m_ParseState = DBPARSE_LEVEL_NONE;
   242 		return SMCResult_Halt;
   243 	}
   244 
   245 	return SMCResult_Continue;
   246 }
   247 #undef ASSIGN_VAR
   248 
   249 void DBManager::ReadSMC_ParseEnd(bool halted, bool failed)
   250 {
   251 }
   252 
   253 bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
   254 {
   255 	ConfDbInfo *pInfo = GetDatabaseConf(name);
   256 
   257 	if (!pInfo)
   258 	{
   259 		if (pdr)
   260 		{
   261 			*pdr = NULL;
   262 		}
   263 		*pdb = NULL;
   264 		UTIL_Format(error, maxlength, "Configuration \"%s\" not found", name);
   265 		return false;
   266 	}
   267 
   268 	const char *dname = pInfo->info.driver;
   269 	if (!pInfo->realDriver)
   270 	{
   271 		/* Try to assign a real driver pointer */
   272 		if (pInfo->info.driver[0] == '\0')
   273 		{
   274 			if (!m_pDefault && m_DefDriver.size() > 0)
   275 			{
   276 				m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
   277 			}
   278 			dname = m_DefDriver.size() ? m_DefDriver.c_str() : "default";
   279 			pInfo->realDriver = m_pDefault;
   280 		} else {
   281 			pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
   282 		}
   283 	}
   284 
   285 	if (pInfo->realDriver)
   286 	{
   287 		if (pdr)
   288 		{
   289 			*pdr = pInfo->realDriver;
   290 		}
   291 		*pdb = pInfo->realDriver->Connect(&pInfo->info, persistent, error, maxlength);
   292 		return (*pdb != NULL);
   293 	}
   294 
   295 	if (pdr)
   296 	{
   297 		*pdr = NULL;
   298 	}
   299 	*pdb = NULL;
   300 
   301 	UTIL_Format(error, maxlength, "Driver \"%s\" not found", dname);
   302 
   303 	return false;
   304 }
   305 
   306 void DBManager::AddDriver(IDBDriver *pDriver)
   307 {
   308 	 /* Let's kill the worker.  Join the thread and let the queries flush.
   309 	  * This is kind of stupid but we just want to unload safely.
   310 	  * Rather than recreate the worker, we'll wait until someone throws 
   311 	  * another query through.
   312 	  */
   313 	KillWorkerThread();
   314 
   315 	 m_drivers.push_back(pDriver);
   316 }
   317 
   318 void DBManager::RemoveDriver(IDBDriver *pDriver)
   319 {
   320 	/* Again, we're forced to kill the worker.  How rude!
   321 	 * Doing this flushes the queue, and thus we don't need to 
   322 	 * clean anything else.  
   323 	 */
   324 	KillWorkerThread();
   325 
   326 	for (size_t i=0; i<m_drivers.size(); i++)
   327 	{
   328 		if (m_drivers[i] == pDriver)
   329 		{
   330 			m_drivers.erase(m_drivers.iterAt(i));
   331 			break;
   332 		}
   333 	}
   334 
   335 	/* Make sure NOTHING references this! */
   336 	List<ConfDbInfo>::iterator iter;
   337 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   338 	{
   339 		ConfDbInfo &db = (*iter);
   340 		if (db.realDriver == pDriver)
   341 		{
   342 			db.realDriver = NULL;
   343 		}
   344 	}
   345 
   346 	/* Now that the driver is gone, we have to test the think queue.
   347 	 * Whatever happens therein is up to the db op!
   348 	 */
   349 	Queue<IDBThreadOperation *>::iterator qiter = m_ThinkQueue.begin();
   350 	Queue<IDBThreadOperation *> templist;
   351 	while (qiter != m_ThinkQueue.end())
   352 	{
   353 		IDBThreadOperation *op = (*qiter);
   354 		if (op->GetDriver() == pDriver)
   355 		{
   356 			templist.push(op);
   357 			qiter = m_ThinkQueue.erase(qiter);
   358 		} else {
   359 			qiter++;
   360 		}
   361 	}
   362 
   363 	for (qiter = templist.begin();
   364 		 qiter != templist.end();
   365 		 qiter++)
   366 	{
   367 		IDBThreadOperation *op = (*qiter);
   368 		op->CancelThinkPart();
   369 		op->Destroy();
   370 	}
   371 }
   372 
   373 IDBDriver *DBManager::GetDefaultDriver()
   374 {
   375 	if (!m_pDefault && m_DefDriver.size() > 0)
   376 	{
   377 		m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
   378 	}
   379 
   380 	return m_pDefault;
   381 }
   382 
   383 Handle_t DBManager::CreateHandle(DBHandleType dtype, void *ptr, IdentityToken_t *pToken)
   384 {
   385 	HandleType_t type = 0;
   386 
   387 	if (dtype == DBHandle_Driver)
   388 	{
   389 		type = m_DriverType;
   390 	} else if (dtype == DBHandle_Database) {
   391 		type = m_DatabaseType;
   392 	} else {
   393 		return BAD_HANDLE;
   394 	}
   395 
   396 	return g_HandleSys.CreateHandle(type, ptr, pToken, g_pCoreIdent, NULL);
   397 }
   398 
   399 HandleError DBManager::ReadHandle(Handle_t hndl, DBHandleType dtype, void **ptr)
   400 {
   401 	HandleType_t type = 0;
   402 
   403 	if (dtype == DBHandle_Driver)
   404 	{
   405 		type = m_DriverType;
   406 	} else if (dtype == DBHandle_Database) {
   407 		type = m_DatabaseType;
   408 	} else {
   409 		return HandleError_Type;
   410 	}
   411 
   412 	HandleSecurity sec(NULL, g_pCoreIdent);
   413 
   414 	return g_HandleSys.ReadHandle(hndl, type, &sec, ptr);
   415 }
   416 
   417 HandleError DBManager::ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token)
   418 {
   419 	HandleSecurity sec(token, g_pCoreIdent);
   420 	return g_HandleSys.FreeHandle(hndl, &sec);
   421 }
   422 
   423 unsigned int DBManager::GetDriverCount()
   424 {
   425 	return (unsigned int)m_drivers.size();
   426 }
   427 
   428 IDBDriver *DBManager::GetDriver(unsigned int index)
   429 {
   430 	if (index >= m_drivers.size())
   431 	{
   432 		return NULL;
   433 	}
   434 
   435 	return m_drivers[index];
   436 }
   437 
   438 const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
   439 {
   440 	ConfDbInfo *info = GetDatabaseConf(name);
   441 	if (!info)
   442 	{
   443 		return NULL;
   444 	}
   445 
   446 	return &info->info;
   447 }
   448 
   449 ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
   450 {
   451 	List<ConfDbInfo>::iterator iter;
   452 
   453 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   454 	{
   455 		ConfDbInfo &conf = (*iter);
   456 		if (strcmp(m_StrTab.GetString(conf.name), name) == 0)
   457 		{
   458 			return &conf;
   459 		}
   460 	}
   461 
   462 	return NULL;
   463 }
   464 
   465 IDBDriver *DBManager::FindOrLoadDriver(const char *name)
   466 {
   467 	size_t last_size = m_drivers.size();
   468 	for (size_t i=0; i<last_size; i++)
   469 	{
   470 		if (strcmp(m_drivers[i]->GetIdentifier(), name) == 0)
   471 		{
   472 			return m_drivers[i];
   473 		}
   474 	}
   475 
   476 	char filename[PLATFORM_MAX_PATH];
   477 	UTIL_Format(filename, sizeof(filename), "dbi.%s.ext", name);
   478 
   479 	IExtension *pExt = g_Extensions.LoadAutoExtension(filename);
   480 	if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size)
   481 	{
   482 		return NULL;
   483 	}
   484 
   485 	/* last_size is now gauranteed to be a valid index.
   486 	 * The identifier must match the name.
   487 	 */
   488 	if (strcmp(m_drivers[last_size]->GetIdentifier(), name) == 0)
   489 	{
   490 		return m_drivers[last_size];
   491 	}
   492 
   493 	return NULL;
   494 }
   495 
   496 void DBManager::KillWorkerThread()
   497 {
   498 	if (m_pWorker)
   499 	{
   500 		m_pWorker->Stop(false);
   501 		g_pThreader->DestroyWorker(m_pWorker);
   502 		m_pWorker = NULL;
   503 		s_OneTimeThreaderErrorMsg = false;
   504 	}
   505 }
   506 
   507 static IdentityToken_t *s_pAddBlock = NULL;
   508 
   509 bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
   510 {
   511 	if (s_pAddBlock && op->GetOwner() == s_pAddBlock)
   512 	{
   513 		return false;
   514 	}
   515 
   516 	if (!m_pWorker)
   517 	{
   518 		m_pWorker = g_pThreader->MakeWorker(this, true);
   519 		if (!m_pWorker)
   520 		{
   521 			if (!s_OneTimeThreaderErrorMsg)
   522 			{
   523 				g_Logger.LogError("[SM] Unable to create db threader (error unknown)");
   524 				s_OneTimeThreaderErrorMsg = true;
   525 			}
   526 			return false;
   527 		}
   528 		if (!m_pWorker->Start())
   529 		{
   530 			if (!s_OneTimeThreaderErrorMsg)
   531 			{
   532 				g_Logger.LogError("[SM] Unable to start db threader (error unknown)");
   533 				s_OneTimeThreaderErrorMsg = true;
   534 			}
   535 			g_pThreader->DestroyWorker(m_pWorker);
   536 			m_pWorker = NULL;
   537 			return false;
   538 		}
   539 	}
   540 
   541 	/* Add to the queue */
   542 	{
   543 		m_pQueueLock->Lock();
   544 		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
   545 		queue.push(op);
   546 		m_pQueueLock->Unlock();
   547 	}
   548 
   549 	/* Make the thread */
   550 	m_pWorker->MakeThread(this);
   551 
   552 	return true;
   553 }
   554 
   555 void DBManager::OnWorkerStart(IThreadWorker *pWorker)
   556 {
   557 	m_drSafety.clear();
   558 	for (size_t i=0; i<m_drivers.size(); i++)
   559 	{
   560 		if (m_drivers[i]->IsThreadSafe())
   561 		{
   562 			m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
   563 		} else {
   564 			m_drSafety.push_back(false);
   565 		}
   566 	}
   567 }
   568 
   569 void DBManager::OnWorkerStop(IThreadWorker *pWorker)
   570 {
   571 	for (size_t i=0; i<m_drivers.size(); i++)
   572 	{
   573 		if (m_drSafety[i])
   574 		{
   575 			m_drivers[i]->ShutdownThreadSafety();
   576 		}
   577 	}
   578 	m_drSafety.clear();
   579 }
   580 
   581 void DBManager::RunThread(IThreadHandle *pThread)
   582 {
   583 	IDBThreadOperation *op = NULL;
   584 
   585 	/* Get something from the queue */
   586 	{
   587 		m_pQueueLock->Lock();
   588 		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
   589 		if (!queue.empty())
   590 		{
   591 			op = queue.first();
   592 			queue.pop();
   593 		}
   594 		m_pQueueLock->Unlock();
   595 	}
   596 
   597 	/* whoa.  hi.  did we get something? we should have. */
   598 	if (!op)
   599 	{
   600 		/* wtf? */
   601 		return;
   602 	}
   603 
   604 	op->RunThreadPart();
   605 
   606 	m_pThinkLock->Lock();
   607 	m_ThinkQueue.push(op);
   608 	m_pThinkLock->Unlock();
   609 }
   610 
   611 void DBManager::RunFrame()
   612 {
   613 	/* Don't bother if we're empty */
   614 	if (!m_ThinkQueue.size())
   615 	{
   616 		return;
   617 	}
   618 
   619 	/* Dump one thing per-frame so the server stays sane. */
   620 	m_pThinkLock->Lock();
   621 	IDBThreadOperation *op = m_ThinkQueue.first();
   622 	m_ThinkQueue.pop();
   623 	m_pThinkLock->Unlock();
   624 	op->RunThinkPart();
   625 	op->Destroy();
   626 }
   627 
   628 void DBManager::OnTerminate(IThreadHandle *pThread, bool cancel)
   629 {
   630 	/* Do nothing */
   631 }
   632 
   633 void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
   634 {
   635 	s_pAddBlock = pToken;
   636 
   637 	/* Kill the thread so we can flush everything into the think queue... */
   638 	KillWorkerThread();
   639 
   640 	/* Run all of the think operations.
   641  	 * Unlike the driver unloading example, we'll let these calls go through, 
   642 	 * since a plugin unloading is far more normal.
   643 	 */
   644 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   645 	Queue<IDBThreadOperation *> templist;
   646 	while (iter != m_ThinkQueue.end())
   647 	{
   648 		IDBThreadOperation *op = (*iter);
   649 		if (op->GetOwner() == pToken)
   650 		{
   651 			templist.push(op);
   652 			iter = m_ThinkQueue.erase(iter);
   653 		} else {
   654 			iter++;
   655 		}
   656 	}
   657 
   658 	for (iter = templist.begin();
   659 		iter != templist.end();
   660 		iter++)
   661 	{
   662 		IDBThreadOperation *op = (*iter);
   663 		op->RunThinkPart();
   664 		op->Destroy();
   665 	}
   666 
   667 	s_pAddBlock = NULL;
   668 }
   669 
   670 void DBManager::OnPluginUnloaded(IPlugin *plugin)
   671 {
   672 	/* Kill the thread so we can flush everything into the think queue... */
   673 	KillWorkerThread();
   674 
   675 	/* Mark the plugin as being unloaded so future database calls will ignore threading... */
   676 	plugin->SetProperty("DisallowDBThreads", NULL);
   677 
   678 	/* Run all of the think operations.
   679 	 * Unlike the driver unloading example, we'll let these calls go through, 
   680 	 * since a plugin unloading is far more normal.
   681 	 */
   682 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   683 	Queue<IDBThreadOperation *> templist;
   684 	while (iter != m_ThinkQueue.end())
   685 	{
   686 		IDBThreadOperation *op = (*iter);
   687 		if (op->GetOwner() == plugin->GetIdentity())
   688 		{
   689 			templist.push(op);
   690 			iter = m_ThinkQueue.erase(iter);
   691 		} else {
   692 			iter++;
   693 		}
   694 	}
   695 
   696 	for (iter = templist.begin();
   697 		 iter != templist.end();
   698 		 iter++)
   699 	{
   700 		IDBThreadOperation *op = (*iter);
   701 		op->RunThinkPart();
   702 		op->Destroy();
   703 	}
   704 }
   705 
   706 void DBManager::LockConfig()
   707 {
   708 	m_pConfigLock->Lock();
   709 }
   710 
   711 void DBManager::UnlockConfig()
   712 {
   713 	m_pConfigLock->Unlock();
   714 }
   715 
   716 const char *DBManager::GetDefaultDriverName()
   717 {
   718 	return m_DefDriver.c_str();
   719 }