core/logic/Database.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Wed Oct 09 08:43:08 2013 -0400 (2013-10-09)
changeset 4138 da768ad428cf
parent 4027 core/Database.cpp@d2c15108b468
child 4349 4772045265f8
permissions -rw-r--r--
Move DBManager and DB natives from core to logic (bug 5953, r=fyren).
     1 /**
     2  * vim: set ts=4 sw=4 tw=99 noet :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 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 "ISourceMod.h"
    34 #include "HandleSys.h"
    35 #include "ExtensionSys.h"
    36 #include "PluginSys.h"
    37 #include <stdlib.h>
    38 #include <IThreader.h>
    39 
    40 #define DBPARSE_LEVEL_NONE		0
    41 #define DBPARSE_LEVEL_MAIN		1
    42 #define DBPARSE_LEVEL_DATABASE	2
    43 
    44 DBManager g_DBMan;
    45 static bool s_OneTimeThreaderErrorMsg = false;
    46 
    47 DBManager::DBManager() 
    48 	: m_Terminate(false),
    49 	  m_ParseLevel(0),
    50 	  m_ParseState(0),
    51 	  m_pDefault(NULL)
    52 {
    53 }
    54 
    55 static void FrameHook(bool simulating)
    56 {
    57 	g_DBMan.RunFrame();
    58 }
    59 
    60 void DBManager::OnSourceModAllInitialized()
    61 {
    62 	HandleAccess sec;
    63 
    64 	g_HandleSys.InitAccessDefaults(NULL, &sec);
    65 	sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
    66 	sec.access[HandleAccess_Clone] |= HANDLE_RESTRICT_IDENTITY;
    67 	
    68 	m_DriverType = g_HandleSys.CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    69 	m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    70 
    71 	g_ShareSys.AddInterface(NULL, this);
    72 
    73 	g_pSM->BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
    74 
    75 	g_PluginSys.AddPluginsListener(this);
    76 	
    77 	g_pSM->AddGameFrameHook(&FrameHook);
    78 }
    79 
    80 void DBManager::OnSourceModLevelChange(const char *mapName)
    81 {
    82 	SMCError err;
    83 	SMCStates states = {0, 0};
    84 
    85 	/* We lock and don't give up the lock until we're done.
    86 	 * This way the thread's search won't be searching through a
    87 	 * potentially empty/corrupt list, which would be very bad.
    88 	 */
    89 	ke::AutoLock lock(&m_ConfigLock);
    90 	if ((err = textparsers->ParseFile_SMC(m_Filename, this, &states)) != SMCError_Okay)
    91 	{
    92 		smcore.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
    93 		if (err != SMCError_Custom)
    94 		{
    95 			const char *txt = textparsers->GetSMCErrorString(err);
    96 			smcore.LogError("[SM] Line %d: %s", states.line, txt);
    97 		}
    98 	}
    99 }
   100 
   101 void DBManager::OnSourceModShutdown()
   102 {
   103 	g_pSM->RemoveGameFrameHook(&FrameHook);
   104 	KillWorkerThread();
   105 	g_PluginSys.RemovePluginsListener(this);
   106 	g_HandleSys.RemoveType(m_DatabaseType, g_pCoreIdent);
   107 	g_HandleSys.RemoveType(m_DriverType, g_pCoreIdent);
   108 	ClearConfigs();
   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 	ClearConfigs();
   139 	m_ParseLevel = 0;
   140 	m_ParseState = DBPARSE_LEVEL_NONE;
   141 	m_DefDriver.clear();
   142 }
   143 
   144 void DBManager::ClearConfigs()
   145 {
   146 	List<ConfDbInfo *>::iterator iter;
   147 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   148 		delete (*iter);
   149 	m_confs.clear();
   150 }
   151 
   152 ConfDbInfo s_CurInfo;
   153 SMCResult DBManager::ReadSMC_NewSection(const SMCStates *states, const char *name)
   154 {
   155 	if (m_ParseLevel)
   156 	{
   157 		m_ParseLevel++;
   158 		return SMCResult_Continue;
   159 	}
   160 
   161 	if (m_ParseState == DBPARSE_LEVEL_NONE)
   162 	{
   163 		if (strcmp(name, "Databases") == 0)
   164 		{
   165 			m_ParseState = DBPARSE_LEVEL_MAIN;
   166 		} else {
   167 			m_ParseLevel++;
   168 		}
   169 	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
   170 		s_CurInfo = ConfDbInfo();
   171 		s_CurInfo.name = name;
   172 		m_ParseState = DBPARSE_LEVEL_DATABASE;
   173 	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
   174 		m_ParseLevel++;
   175 	}
   176 
   177 	return SMCResult_Continue;
   178 }
   179 
   180 SMCResult DBManager::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   181 {
   182 	if (m_ParseLevel)
   183 	{
   184 		return SMCResult_Continue;
   185 	}
   186 
   187 	if (m_ParseState == DBPARSE_LEVEL_MAIN)
   188 	{
   189 		if (strcmp(key, "driver_default") == 0)
   190 		{
   191 			m_DefDriver.assign(value);
   192 		}
   193 	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
   194 		if (strcmp(key, "driver") == 0)
   195 		{
   196 			if (strcmp(value, "default") != 0)
   197 			{
   198 				s_CurInfo.driver = value;
   199 			}
   200 		} else if (strcmp(key, "database") == 0) {
   201 			s_CurInfo.database = value;
   202 		} else if (strcmp(key, "host") == 0) {
   203 			s_CurInfo.host = value;
   204 		} else if (strcmp(key, "user") == 0) {
   205 			s_CurInfo.user = value;
   206 		} else if (strcmp(key, "pass") == 0) {
   207 			s_CurInfo.pass = value;
   208 		} else if (strcmp(key, "timeout") == 0) {
   209 			s_CurInfo.info.maxTimeout = atoi(value);
   210 		} else if (strcmp(key, "port") == 0) {
   211 			s_CurInfo.info.port = atoi(value);
   212 		}
   213 	}
   214 
   215 	return SMCResult_Continue;
   216 }
   217 
   218 SMCResult DBManager::ReadSMC_LeavingSection(const SMCStates *states)
   219 {
   220 	if (m_ParseLevel)
   221 	{
   222 		m_ParseLevel--;
   223 		return SMCResult_Continue;
   224 	}
   225 
   226 	if (m_ParseState == DBPARSE_LEVEL_DATABASE)
   227 	{
   228 		ConfDbInfo *cdb = new ConfDbInfo();
   229 
   230 		cdb->name = s_CurInfo.name;
   231 		cdb->driver = s_CurInfo.driver;
   232 		cdb->host = s_CurInfo.host;
   233 		cdb->user = s_CurInfo.user;
   234 		cdb->pass = s_CurInfo.pass;
   235 		cdb->database = s_CurInfo.database;
   236 		cdb->realDriver = s_CurInfo.realDriver;
   237 		cdb->info.maxTimeout = s_CurInfo.info.maxTimeout;
   238 		cdb->info.port = s_CurInfo.info.port;
   239 
   240 		cdb->info.driver = cdb->driver.c_str();
   241 		cdb->info.database = cdb->database.c_str();
   242 		cdb->info.host = cdb->host.c_str();
   243 		cdb->info.user = cdb->user.c_str();
   244 		cdb->info.pass = cdb->pass.c_str();
   245 		
   246 		/* Save it.. */
   247 		m_confs.push_back(cdb);
   248 
   249 		/* Go up one level */
   250 		m_ParseState = DBPARSE_LEVEL_MAIN;
   251 	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
   252 		m_ParseState = DBPARSE_LEVEL_NONE;
   253 		return SMCResult_Halt;
   254 	}
   255 
   256 	return SMCResult_Continue;
   257 }
   258 
   259 void DBManager::ReadSMC_ParseEnd(bool halted, bool failed)
   260 {
   261 }
   262 
   263 bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
   264 {
   265 	ConfDbInfo *pInfo = GetDatabaseConf(name);
   266 
   267 	if (!pInfo)
   268 	{
   269 		if (pdr)
   270 		{
   271 			*pdr = NULL;
   272 		}
   273 		*pdb = NULL;
   274 		g_pSM->Format(error, maxlength, "Configuration \"%s\" not found", name);
   275 		return false;
   276 	}
   277 
   278 	const char *dname = pInfo->info.driver;
   279 	if (!pInfo->realDriver)
   280 	{
   281 		/* Try to assign a real driver pointer */
   282 		if (pInfo->info.driver[0] == '\0')
   283 		{
   284 			if (!m_pDefault && m_DefDriver.size() > 0)
   285 			{
   286 				m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
   287 			}
   288 			dname = m_DefDriver.size() ? m_DefDriver.c_str() : "default";
   289 			pInfo->realDriver = m_pDefault;
   290 		} else {
   291 			pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
   292 		}
   293 	}
   294 
   295 	if (pInfo->realDriver)
   296 	{
   297 		if (pdr)
   298 		{
   299 			*pdr = pInfo->realDriver;
   300 		}
   301 		*pdb = pInfo->realDriver->Connect(&pInfo->info, persistent, error, maxlength);
   302 		return (*pdb != NULL);
   303 	}
   304 
   305 	if (pdr)
   306 	{
   307 		*pdr = NULL;
   308 	}
   309 	*pdb = NULL;
   310 
   311 	g_pSM->Format(error, maxlength, "Driver \"%s\" not found", dname);
   312 
   313 	return false;
   314 }
   315 
   316 void DBManager::AddDriver(IDBDriver *pDriver)
   317 {
   318 	 /* Let's kill the worker.  Join the thread and let the queries flush.
   319 	  * This is kind of stupid but we just want to unload safely.
   320 	  * Rather than recreate the worker, we'll wait until someone throws 
   321 	  * another query through.
   322 	  */
   323 	KillWorkerThread();
   324 
   325 	 m_drivers.push_back(pDriver);
   326 }
   327 
   328 void DBManager::RemoveDriver(IDBDriver *pDriver)
   329 {
   330 	/* Again, we're forced to kill the worker.  How rude!
   331 	 * Doing this flushes the queue, and thus we don't need to 
   332 	 * clean anything else.  
   333 	 */
   334 	KillWorkerThread();
   335 
   336 	for (size_t i=0; i<m_drivers.size(); i++)
   337 	{
   338 		if (m_drivers[i] == pDriver)
   339 		{
   340 			m_drivers.erase(m_drivers.iterAt(i));
   341 			break;
   342 		}
   343 	}
   344 
   345 	/* Make sure NOTHING references this! */
   346 	List<ConfDbInfo *>::iterator iter;
   347 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   348 	{
   349 		ConfDbInfo &db = *(*iter);
   350 		if (db.realDriver == pDriver)
   351 		{
   352 			db.realDriver = NULL;
   353 		}
   354 	}
   355 
   356 	/* Now that the driver is gone, we have to test the think queue.
   357 	 * Whatever happens therein is up to the db op!
   358 	 */
   359 	Queue<IDBThreadOperation *>::iterator qiter = m_ThinkQueue.begin();
   360 	Queue<IDBThreadOperation *> templist;
   361 	while (qiter != m_ThinkQueue.end())
   362 	{
   363 		IDBThreadOperation *op = (*qiter);
   364 		if (op->GetDriver() == pDriver)
   365 		{
   366 			templist.push(op);
   367 			qiter = m_ThinkQueue.erase(qiter);
   368 		} else {
   369 			qiter++;
   370 		}
   371 	}
   372 
   373 	for (qiter = templist.begin();
   374 		 qiter != templist.end();
   375 		 qiter++)
   376 	{
   377 		IDBThreadOperation *op = (*qiter);
   378 		op->CancelThinkPart();
   379 		op->Destroy();
   380 	}
   381 }
   382 
   383 IDBDriver *DBManager::GetDefaultDriver()
   384 {
   385 	if (!m_pDefault && m_DefDriver.size() > 0)
   386 	{
   387 		m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
   388 	}
   389 
   390 	return m_pDefault;
   391 }
   392 
   393 Handle_t DBManager::CreateHandle(DBHandleType dtype, void *ptr, IdentityToken_t *pToken)
   394 {
   395 	HandleType_t type = 0;
   396 
   397 	if (dtype == DBHandle_Driver)
   398 	{
   399 		type = m_DriverType;
   400 	} else if (dtype == DBHandle_Database) {
   401 		type = m_DatabaseType;
   402 	} else {
   403 		return BAD_HANDLE;
   404 	}
   405 
   406 	return g_HandleSys.CreateHandle(type, ptr, pToken, g_pCoreIdent, NULL);
   407 }
   408 
   409 HandleError DBManager::ReadHandle(Handle_t hndl, DBHandleType dtype, void **ptr)
   410 {
   411 	HandleType_t type = 0;
   412 
   413 	if (dtype == DBHandle_Driver)
   414 	{
   415 		type = m_DriverType;
   416 	} else if (dtype == DBHandle_Database) {
   417 		type = m_DatabaseType;
   418 	} else {
   419 		return HandleError_Type;
   420 	}
   421 
   422 	HandleSecurity sec(NULL, g_pCoreIdent);
   423 
   424 	return g_HandleSys.ReadHandle(hndl, type, &sec, ptr);
   425 }
   426 
   427 HandleError DBManager::ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token)
   428 {
   429 	HandleSecurity sec(token, g_pCoreIdent);
   430 	return g_HandleSys.FreeHandle(hndl, &sec);
   431 }
   432 
   433 unsigned int DBManager::GetDriverCount()
   434 {
   435 	return (unsigned int)m_drivers.size();
   436 }
   437 
   438 IDBDriver *DBManager::GetDriver(unsigned int index)
   439 {
   440 	if (index >= m_drivers.size())
   441 	{
   442 		return NULL;
   443 	}
   444 
   445 	return m_drivers[index];
   446 }
   447 
   448 const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
   449 {
   450 	ConfDbInfo *info = GetDatabaseConf(name);
   451 	if (!info)
   452 	{
   453 		return NULL;
   454 	}
   455 
   456 	return &info->info;
   457 }
   458 
   459 ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
   460 {
   461 	List<ConfDbInfo *>::iterator iter;
   462 
   463 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   464 	{
   465 		ConfDbInfo &conf = *(*iter);
   466 		if (conf.name == name)
   467 		{
   468 			return &conf;
   469 		}
   470 	}
   471 
   472 	return NULL;
   473 }
   474 
   475 IDBDriver *DBManager::FindOrLoadDriver(const char *name)
   476 {
   477 	size_t last_size = m_drivers.size();
   478 	for (size_t i=0; i<last_size; i++)
   479 	{
   480 		if (strcmp(m_drivers[i]->GetIdentifier(), name) == 0)
   481 		{
   482 			return m_drivers[i];
   483 		}
   484 	}
   485 
   486 	char filename[PLATFORM_MAX_PATH];
   487 	g_pSM->Format(filename, sizeof(filename), "dbi.%s.ext", name);
   488 
   489 	IExtension *pExt = g_Extensions.LoadAutoExtension(filename);
   490 	if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size)
   491 	{
   492 		return NULL;
   493 	}
   494 
   495 	/* last_size is now gauranteed to be a valid index.
   496 	 * The identifier must match the name.
   497 	 */
   498 	if (strcmp(m_drivers[last_size]->GetIdentifier(), name) == 0)
   499 	{
   500 		return m_drivers[last_size];
   501 	}
   502 
   503 	return NULL;
   504 }
   505 
   506 void DBManager::KillWorkerThread()
   507 {
   508 	if (m_Worker)
   509 	{
   510 		{
   511 			ke::AutoLock lock(&m_QueueEvent);
   512 			m_Terminate = true;
   513 			m_QueueEvent.Notify();
   514 		}
   515 		m_Worker->Join();
   516 		m_Worker = NULL;
   517 		s_OneTimeThreaderErrorMsg = false;
   518 		m_Terminate = false;
   519 	}
   520 }
   521 
   522 static IdentityToken_t *s_pAddBlock = NULL;
   523 
   524 bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
   525 {
   526 	if (s_pAddBlock && op->GetOwner() == s_pAddBlock)
   527 	{
   528 		return false;
   529 	}
   530 
   531 	if (!m_Worker)
   532 	{
   533 		m_Worker = new ke::Thread(this, "SM SQL Worker");
   534 		if (!m_Worker->Succeeded())
   535 		{
   536 			if (!s_OneTimeThreaderErrorMsg)
   537 			{
   538 				smcore.LogError("[SM] Unable to create db threader (error unknown)");
   539 				s_OneTimeThreaderErrorMsg = true;
   540 			}
   541 			m_Worker = NULL;
   542 			return false;
   543 		}
   544 	}
   545 
   546 	/* Add to the queue */
   547 	{
   548 		ke::AutoLock lock(&m_QueueEvent);
   549 		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
   550 		queue.push(op);
   551 		m_QueueEvent.Notify();
   552 	}
   553 
   554 	return true;
   555 }
   556 
   557 void DBManager::Run()
   558 {
   559 	// Initialize DB threadsafety.
   560 	for (size_t i=0; i < m_drivers.size(); i++)
   561 	{
   562 		if (m_drivers[i]->IsThreadSafe())
   563 			m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
   564 		else
   565 			m_drSafety.push_back(false);
   566 	}
   567 
   568 	// Run actual worker thread logic.
   569 	ThreadMain();
   570 
   571 	// Shutdown DB threadsafety.
   572 	for (size_t i=0; i<m_drivers.size(); i++)
   573 	{
   574 		if (m_drSafety[i])
   575 			m_drivers[i]->ShutdownThreadSafety();
   576 	}
   577 	m_drSafety.clear();
   578 }
   579 
   580 void DBManager::ThreadMain()
   581 {
   582 	ke::AutoLock lock(&m_QueueEvent);
   583 
   584 	while (true) {
   585 		// The lock has been acquired. Grab everything we can out of the
   586 		// queue. Since we want to flush the queue even if we're terminated,
   587 		// we process all operations we can before checking to terminate.
   588 		// There's no risk of starvation since the main thread blocks on us
   589 		// terminating.
   590 		while (true)
   591 		{
   592 			Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
   593 			if (queue.empty())
   594 				break;
   595 
   596 			IDBThreadOperation *op = queue.first();
   597 			queue.pop();
   598 
   599 			// Unlock the queue when we run the query, so the main thread can
   600 			// keep pumping events. We re-acquire the lock to check for more
   601 			// items. It's okay if we terminate while unlocked; the main
   602 			// thread would be blocked and we'd need to flush the queue
   603 			// anyway, so after we've depleted the queue here, we'll just
   604 			// reach the terminate at the top of the loop.
   605 			{
   606 				ke::AutoUnlock unlock(&m_QueueEvent);
   607 				op->RunThreadPart();
   608 
   609 				ke::AutoLock lock(&m_ThinkLock);
   610 				m_ThinkQueue.push(op);
   611 			}
   612 		}
   613 
   614 		if (m_Terminate)
   615 			return;
   616 
   617 		// Release the lock and wait for a signal.
   618 		m_QueueEvent.Wait();
   619 	}
   620 }
   621 
   622 void DBManager::RunFrame()
   623 {
   624 	/* Don't bother if we're empty */
   625 	if (!m_ThinkQueue.size())
   626 	{
   627 		return;
   628 	}
   629 
   630 	/* Dump one thing per-frame so the server stays sane. */
   631 	IDBThreadOperation *op;
   632 	{
   633 		ke::AutoLock lock(&m_ThinkLock);
   634 		op = m_ThinkQueue.first();
   635 		m_ThinkQueue.pop();
   636 	}
   637 	op->RunThinkPart();
   638 	op->Destroy();
   639 }
   640 
   641 void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
   642 {
   643 	s_pAddBlock = pToken;
   644 
   645 	/* Kill the thread so we can flush everything into the think queue... */
   646 	KillWorkerThread();
   647 
   648 	/* Run all of the think operations.
   649  	 * Unlike the driver unloading example, we'll let these calls go through, 
   650 	 * since a plugin unloading is far more normal.
   651 	 */
   652 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   653 	Queue<IDBThreadOperation *> templist;
   654 	while (iter != m_ThinkQueue.end())
   655 	{
   656 		IDBThreadOperation *op = (*iter);
   657 		if (op->GetOwner() == pToken)
   658 		{
   659 			templist.push(op);
   660 			iter = m_ThinkQueue.erase(iter);
   661 		} else {
   662 			iter++;
   663 		}
   664 	}
   665 
   666 	for (iter = templist.begin();
   667 		iter != templist.end();
   668 		iter++)
   669 	{
   670 		IDBThreadOperation *op = (*iter);
   671 		op->RunThinkPart();
   672 		op->Destroy();
   673 	}
   674 
   675 	s_pAddBlock = NULL;
   676 }
   677 
   678 void DBManager::OnPluginUnloaded(IPlugin *plugin)
   679 {
   680 	/* Kill the thread so we can flush everything into the think queue... */
   681 	KillWorkerThread();
   682 
   683 	/* Mark the plugin as being unloaded so future database calls will ignore threading... */
   684 	plugin->SetProperty("DisallowDBThreads", NULL);
   685 
   686 	/* Run all of the think operations.
   687 	 * Unlike the driver unloading example, we'll let these calls go through, 
   688 	 * since a plugin unloading is far more normal.
   689 	 */
   690 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   691 	Queue<IDBThreadOperation *> templist;
   692 	while (iter != m_ThinkQueue.end())
   693 	{
   694 		IDBThreadOperation *op = (*iter);
   695 		if (op->GetOwner() == plugin->GetIdentity())
   696 		{
   697 			templist.push(op);
   698 			iter = m_ThinkQueue.erase(iter);
   699 		} else {
   700 			iter++;
   701 		}
   702 	}
   703 
   704 	for (iter = templist.begin();
   705 		 iter != templist.end();
   706 		 iter++)
   707 	{
   708 		IDBThreadOperation *op = (*iter);
   709 		op->RunThinkPart();
   710 		op->Destroy();
   711 	}
   712 }
   713 
   714 void DBManager::LockConfig()
   715 {
   716 	m_ConfigLock.Lock();
   717 }
   718 
   719 void DBManager::UnlockConfig()
   720 {
   721 	m_ConfigLock.Unlock();
   722 }
   723 
   724 const char *DBManager::GetDefaultDriverName()
   725 {
   726 	return m_DefDriver.c_str();
   727 }
   728 
   729 void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)
   730 {
   731 	g_Extensions.AddRawDependency(myself, driver->GetIdentity(), driver);
   732 }
   733