core/Database.cpp
author David Anderson <dvander@alliedmods.net>
Wed Aug 21 00:01:28 2013 -0700 (2013-08-21)
changeset 4027 d2c15108b468
parent 3872 4c93f74cae51
permissions -rw-r--r--
Rewrite DBManager to use the new synchronization primitives (bug 5870, 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 "sourcemod.h"
    34 #include "sm_stringutil.h"
    35 #include "Logger.h"
    36 #include <stdlib.h>
    37 #include <IThreader.h>
    38 #include "logic_bridge.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 void DBManager::OnSourceModAllInitialized()
    56 {
    57 	HandleAccess sec;
    58 
    59 	handlesys->InitAccessDefaults(NULL, &sec);
    60 	sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
    61 	sec.access[HandleAccess_Clone] |= HANDLE_RESTRICT_IDENTITY;
    62 	
    63 	m_DriverType = handlesys->CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    64 	m_DatabaseType = handlesys->CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    65 
    66 	sharesys->AddInterface(NULL, this);
    67 
    68 	g_SourceMod.BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
    69 
    70 	scripts->AddPluginsListener(this);
    71 }
    72 
    73 void DBManager::OnSourceModLevelChange(const char *mapName)
    74 {
    75 	SMCError err;
    76 	SMCStates states = {0, 0};
    77 
    78 	/* We lock and don't give up the lock until we're done.
    79 	 * This way the thread's search won't be searching through a
    80 	 * potentially empty/corrupt list, which would be very bad.
    81 	 */
    82 	ke::AutoLock lock(&m_ConfigLock);
    83 	if ((err = textparsers->ParseFile_SMC(m_Filename, this, &states)) != SMCError_Okay)
    84 	{
    85 		g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
    86 		if (err != SMCError_Custom)
    87 		{
    88 			const char *txt = textparsers->GetSMCErrorString(err);
    89 			g_Logger.LogError("[SM] Line %d: %s", states.line, txt);
    90 		}
    91 	}
    92 }
    93 
    94 void DBManager::OnSourceModShutdown()
    95 {
    96 	KillWorkerThread();
    97 	scripts->RemovePluginsListener(this);
    98 	handlesys->RemoveType(m_DatabaseType, g_pCoreIdent);
    99 	handlesys->RemoveType(m_DriverType, g_pCoreIdent);
   100 	ClearConfigs();
   101 }
   102 
   103 unsigned int DBManager::GetInterfaceVersion()
   104 {
   105 	return SMINTERFACE_DBI_VERSION;
   106 }
   107 
   108 const char *DBManager::GetInterfaceName()
   109 {
   110 	return SMINTERFACE_DBI_NAME;
   111 }
   112 
   113 void DBManager::OnHandleDestroy(HandleType_t type, void *object)
   114 {
   115 	if (type == m_DriverType)
   116 	{
   117 		/* Ignore */
   118 		return;
   119 	}
   120 
   121 	if (handlesys->TypeCheck(type, m_DatabaseType))
   122 	{
   123 		IDatabase *pdb = (IDatabase *)object;
   124 		pdb->Close();
   125 	}
   126 }
   127 
   128 void DBManager::ReadSMC_ParseStart()
   129 {
   130 	ClearConfigs();
   131 	m_ParseLevel = 0;
   132 	m_ParseState = DBPARSE_LEVEL_NONE;
   133 	m_DefDriver.clear();
   134 }
   135 
   136 void DBManager::ClearConfigs()
   137 {
   138 	List<ConfDbInfo *>::iterator iter;
   139 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   140 		delete (*iter);
   141 	m_confs.clear();
   142 }
   143 
   144 ConfDbInfo s_CurInfo;
   145 SMCResult DBManager::ReadSMC_NewSection(const SMCStates *states, const char *name)
   146 {
   147 	if (m_ParseLevel)
   148 	{
   149 		m_ParseLevel++;
   150 		return SMCResult_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 = name;
   164 		m_ParseState = DBPARSE_LEVEL_DATABASE;
   165 	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
   166 		m_ParseLevel++;
   167 	}
   168 
   169 	return SMCResult_Continue;
   170 }
   171 
   172 SMCResult DBManager::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   173 {
   174 	if (m_ParseLevel)
   175 	{
   176 		return SMCResult_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 = value;
   191 			}
   192 		} else if (strcmp(key, "database") == 0) {
   193 			s_CurInfo.database = value;
   194 		} else if (strcmp(key, "host") == 0) {
   195 			s_CurInfo.host = value;
   196 		} else if (strcmp(key, "user") == 0) {
   197 			s_CurInfo.user = value;
   198 		} else if (strcmp(key, "pass") == 0) {
   199 			s_CurInfo.pass = 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 SMCResult_Continue;
   208 }
   209 
   210 SMCResult DBManager::ReadSMC_LeavingSection(const SMCStates *states)
   211 {
   212 	if (m_ParseLevel)
   213 	{
   214 		m_ParseLevel--;
   215 		return SMCResult_Continue;
   216 	}
   217 
   218 	if (m_ParseState == DBPARSE_LEVEL_DATABASE)
   219 	{
   220 		ConfDbInfo *cdb = new ConfDbInfo();
   221 
   222 		cdb->name = s_CurInfo.name;
   223 		cdb->driver = s_CurInfo.driver;
   224 		cdb->host = s_CurInfo.host;
   225 		cdb->user = s_CurInfo.user;
   226 		cdb->pass = s_CurInfo.pass;
   227 		cdb->database = s_CurInfo.database;
   228 		cdb->realDriver = s_CurInfo.realDriver;
   229 		cdb->info.maxTimeout = s_CurInfo.info.maxTimeout;
   230 		cdb->info.port = s_CurInfo.info.port;
   231 
   232 		cdb->info.driver = cdb->driver.c_str();
   233 		cdb->info.database = cdb->database.c_str();
   234 		cdb->info.host = cdb->host.c_str();
   235 		cdb->info.user = cdb->user.c_str();
   236 		cdb->info.pass = cdb->pass.c_str();
   237 		
   238 		/* Save it.. */
   239 		m_confs.push_back(cdb);
   240 
   241 		/* Go up one level */
   242 		m_ParseState = DBPARSE_LEVEL_MAIN;
   243 	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
   244 		m_ParseState = DBPARSE_LEVEL_NONE;
   245 		return SMCResult_Halt;
   246 	}
   247 
   248 	return SMCResult_Continue;
   249 }
   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 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 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 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 (conf.name == name)
   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 = extsys->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_Worker)
   501 	{
   502 		{
   503 			ke::AutoLock lock(&m_QueueEvent);
   504 			m_Terminate = true;
   505 			m_QueueEvent.Notify();
   506 		}
   507 		m_Worker->Join();
   508 		m_Worker = NULL;
   509 		s_OneTimeThreaderErrorMsg = false;
   510 		m_Terminate = false;
   511 	}
   512 }
   513 
   514 static IdentityToken_t *s_pAddBlock = NULL;
   515 
   516 bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
   517 {
   518 	if (s_pAddBlock && op->GetOwner() == s_pAddBlock)
   519 	{
   520 		return false;
   521 	}
   522 
   523 	if (!m_Worker)
   524 	{
   525 		m_Worker = new ke::Thread(this, "SM SQL Worker");
   526 		if (!m_Worker->Succeeded())
   527 		{
   528 			if (!s_OneTimeThreaderErrorMsg)
   529 			{
   530 				g_Logger.LogError("[SM] Unable to create db threader (error unknown)");
   531 				s_OneTimeThreaderErrorMsg = true;
   532 			}
   533 			m_Worker = NULL;
   534 			return false;
   535 		}
   536 	}
   537 
   538 	/* Add to the queue */
   539 	{
   540 		ke::AutoLock lock(&m_QueueEvent);
   541 		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
   542 		queue.push(op);
   543 		m_QueueEvent.Notify();
   544 	}
   545 
   546 	return true;
   547 }
   548 
   549 void DBManager::Run()
   550 {
   551 	// Initialize DB threadsafety.
   552 	for (size_t i=0; i < m_drivers.size(); i++)
   553 	{
   554 		if (m_drivers[i]->IsThreadSafe())
   555 			m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
   556 		else
   557 			m_drSafety.push_back(false);
   558 	}
   559 
   560 	// Run actual worker thread logic.
   561 	ThreadMain();
   562 
   563 	// Shutdown DB threadsafety.
   564 	for (size_t i=0; i<m_drivers.size(); i++)
   565 	{
   566 		if (m_drSafety[i])
   567 			m_drivers[i]->ShutdownThreadSafety();
   568 	}
   569 	m_drSafety.clear();
   570 }
   571 
   572 void DBManager::ThreadMain()
   573 {
   574 	ke::AutoLock lock(&m_QueueEvent);
   575 
   576 	while (true) {
   577 		// The lock has been acquired. Grab everything we can out of the
   578 		// queue. Since we want to flush the queue even if we're terminated,
   579 		// we process all operations we can before checking to terminate.
   580 		// There's no risk of starvation since the main thread blocks on us
   581 		// terminating.
   582 		while (true)
   583 		{
   584 			Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
   585 			if (queue.empty())
   586 				break;
   587 
   588 			IDBThreadOperation *op = queue.first();
   589 			queue.pop();
   590 
   591 			// Unlock the queue when we run the query, so the main thread can
   592 			// keep pumping events. We re-acquire the lock to check for more
   593 			// items. It's okay if we terminate while unlocked; the main
   594 			// thread would be blocked and we'd need to flush the queue
   595 			// anyway, so after we've depleted the queue here, we'll just
   596 			// reach the terminate at the top of the loop.
   597 			{
   598 				ke::AutoUnlock unlock(&m_QueueEvent);
   599 				op->RunThreadPart();
   600 
   601 				ke::AutoLock lock(&m_ThinkLock);
   602 				m_ThinkQueue.push(op);
   603 			}
   604 		}
   605 
   606 		if (m_Terminate)
   607 			return;
   608 
   609 		// Release the lock and wait for a signal.
   610 		m_QueueEvent.Wait();
   611 	}
   612 }
   613 
   614 void DBManager::RunFrame()
   615 {
   616 	/* Don't bother if we're empty */
   617 	if (!m_ThinkQueue.size())
   618 	{
   619 		return;
   620 	}
   621 
   622 	/* Dump one thing per-frame so the server stays sane. */
   623 	IDBThreadOperation *op;
   624 	{
   625 		ke::AutoLock lock(&m_ThinkLock);
   626 		op = m_ThinkQueue.first();
   627 		m_ThinkQueue.pop();
   628 	}
   629 	op->RunThinkPart();
   630 	op->Destroy();
   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_ConfigLock.Lock();
   709 }
   710 
   711 void DBManager::UnlockConfig()
   712 {
   713 	m_ConfigLock.Unlock();
   714 }
   715 
   716 const char *DBManager::GetDefaultDriverName()
   717 {
   718 	return m_DefDriver.c_str();
   719 }
   720 
   721 void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)
   722 {
   723 	extsys->AddRawDependency(myself, driver->GetIdentity(), driver);
   724 }
   725