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