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