core/Database.cpp
author Fyren <fyrenmoo@gmail.com>
Sat Oct 16 03:52:36 2010 -0700 (2010-10-16)
changeset 3139 2c16c25d437a
parent 2726 c96f3fab8211
child 3140 3f461eb8edf2
permissions -rw-r--r--
Use SH strings for database configs (bug 4286, r=dvander)
     1 /**
     2  * vim: set ts=4 :
     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 "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 <IThreader.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_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_DefDriver.clear();
   140 }
   141 
   142 ConfDbInfo s_CurInfo;
   143 SMCResult DBManager::ReadSMC_NewSection(const SMCStates *states, const char *name)
   144 {
   145 	if (m_ParseLevel)
   146 	{
   147 		m_ParseLevel++;
   148 		return SMCResult_Continue;
   149 	}
   150 
   151 	if (m_ParseState == DBPARSE_LEVEL_NONE)
   152 	{
   153 		if (strcmp(name, "Databases") == 0)
   154 		{
   155 			m_ParseState = DBPARSE_LEVEL_MAIN;
   156 		} else {
   157 			m_ParseLevel++;
   158 		}
   159 	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
   160 		s_CurInfo = ConfDbInfo();
   161 		s_CurInfo.name = name;
   162 		m_ParseState = DBPARSE_LEVEL_DATABASE;
   163 	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
   164 		m_ParseLevel++;
   165 	}
   166 
   167 	return SMCResult_Continue;
   168 }
   169 
   170 SMCResult DBManager::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
   171 {
   172 	if (m_ParseLevel)
   173 	{
   174 		return SMCResult_Continue;
   175 	}
   176 
   177 	if (m_ParseState == DBPARSE_LEVEL_MAIN)
   178 	{
   179 		if (strcmp(key, "driver_default") == 0)
   180 		{
   181 			m_DefDriver.assign(value);
   182 		}
   183 	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
   184 		if (strcmp(key, "driver") == 0)
   185 		{
   186 			if (strcmp(value, "default") != 0)
   187 			{
   188 				s_CurInfo.driver = value;
   189 			}
   190 		} else if (strcmp(key, "database") == 0) {
   191 			s_CurInfo.database = value;
   192 		} else if (strcmp(key, "host") == 0) {
   193 			s_CurInfo.host = value;
   194 		} else if (strcmp(key, "user") == 0) {
   195 			s_CurInfo.user = value;
   196 		} else if (strcmp(key, "pass") == 0) {
   197 			s_CurInfo.pass = value;
   198 		} else if (strcmp(key, "timeout") == 0) {
   199 			s_CurInfo.info.maxTimeout = atoi(value);
   200 		} else if (strcmp(key, "port") == 0) {
   201 			s_CurInfo.info.port = atoi(value);
   202 		}
   203 	}
   204 
   205 	return SMCResult_Continue;
   206 }
   207 
   208 #define ASSIGN_VAR(var) \
   209 	if (s_CurInfo.var == "") { \
   210 		s_CurInfo.info.var = ""; \
   211 	} else { \
   212 		s_CurInfo.info.var = s_CurInfo.var.c_str(); \
   213 	}
   214 
   215 SMCResult DBManager::ReadSMC_LeavingSection(const SMCStates *states)
   216 {
   217 	if (m_ParseLevel)
   218 	{
   219 		m_ParseLevel--;
   220 		return SMCResult_Continue;
   221 	}
   222 
   223 	if (m_ParseState == DBPARSE_LEVEL_DATABASE)
   224 	{
   225 		/* Set all of the info members to either a blank string
   226 		 * or the string pointer from the string table.
   227 		 */
   228 		ASSIGN_VAR(driver);
   229 		ASSIGN_VAR(database);
   230 		ASSIGN_VAR(host);
   231 		ASSIGN_VAR(user);
   232 		ASSIGN_VAR(pass);
   233 		
   234 		/* Save it.. */
   235 		m_confs.push_back(s_CurInfo);
   236 
   237 		/* Go up one level */
   238 		m_ParseState = DBPARSE_LEVEL_MAIN;
   239 	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
   240 		m_ParseState = DBPARSE_LEVEL_NONE;
   241 		return SMCResult_Halt;
   242 	}
   243 
   244 	return SMCResult_Continue;
   245 }
   246 #undef ASSIGN_VAR
   247 
   248 void DBManager::ReadSMC_ParseEnd(bool halted, bool failed)
   249 {
   250 }
   251 
   252 bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
   253 {
   254 	ConfDbInfo *pInfo = GetDatabaseConf(name);
   255 
   256 	if (!pInfo)
   257 	{
   258 		if (pdr)
   259 		{
   260 			*pdr = NULL;
   261 		}
   262 		*pdb = NULL;
   263 		UTIL_Format(error, maxlength, "Configuration \"%s\" not found", name);
   264 		return false;
   265 	}
   266 
   267 	const char *dname = pInfo->info.driver;
   268 	if (!pInfo->realDriver)
   269 	{
   270 		/* Try to assign a real driver pointer */
   271 		if (pInfo->info.driver[0] == '\0')
   272 		{
   273 			if (!m_pDefault && m_DefDriver.size() > 0)
   274 			{
   275 				m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
   276 			}
   277 			dname = m_DefDriver.size() ? m_DefDriver.c_str() : "default";
   278 			pInfo->realDriver = m_pDefault;
   279 		} else {
   280 			pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
   281 		}
   282 	}
   283 
   284 	if (pInfo->realDriver)
   285 	{
   286 		if (pdr)
   287 		{
   288 			*pdr = pInfo->realDriver;
   289 		}
   290 		*pdb = pInfo->realDriver->Connect(&pInfo->info, persistent, error, maxlength);
   291 		return (*pdb != NULL);
   292 	}
   293 
   294 	if (pdr)
   295 	{
   296 		*pdr = NULL;
   297 	}
   298 	*pdb = NULL;
   299 
   300 	UTIL_Format(error, maxlength, "Driver \"%s\" not found", dname);
   301 
   302 	return false;
   303 }
   304 
   305 void DBManager::AddDriver(IDBDriver *pDriver)
   306 {
   307 	 /* Let's kill the worker.  Join the thread and let the queries flush.
   308 	  * This is kind of stupid but we just want to unload safely.
   309 	  * Rather than recreate the worker, we'll wait until someone throws 
   310 	  * another query through.
   311 	  */
   312 	KillWorkerThread();
   313 
   314 	 m_drivers.push_back(pDriver);
   315 }
   316 
   317 void DBManager::RemoveDriver(IDBDriver *pDriver)
   318 {
   319 	/* Again, we're forced to kill the worker.  How rude!
   320 	 * Doing this flushes the queue, and thus we don't need to 
   321 	 * clean anything else.  
   322 	 */
   323 	KillWorkerThread();
   324 
   325 	for (size_t i=0; i<m_drivers.size(); i++)
   326 	{
   327 		if (m_drivers[i] == pDriver)
   328 		{
   329 			m_drivers.erase(m_drivers.iterAt(i));
   330 			break;
   331 		}
   332 	}
   333 
   334 	/* Make sure NOTHING references this! */
   335 	List<ConfDbInfo>::iterator iter;
   336 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   337 	{
   338 		ConfDbInfo &db = (*iter);
   339 		if (db.realDriver == pDriver)
   340 		{
   341 			db.realDriver = NULL;
   342 		}
   343 	}
   344 
   345 	/* Now that the driver is gone, we have to test the think queue.
   346 	 * Whatever happens therein is up to the db op!
   347 	 */
   348 	Queue<IDBThreadOperation *>::iterator qiter = m_ThinkQueue.begin();
   349 	Queue<IDBThreadOperation *> templist;
   350 	while (qiter != m_ThinkQueue.end())
   351 	{
   352 		IDBThreadOperation *op = (*qiter);
   353 		if (op->GetDriver() == pDriver)
   354 		{
   355 			templist.push(op);
   356 			qiter = m_ThinkQueue.erase(qiter);
   357 		} else {
   358 			qiter++;
   359 		}
   360 	}
   361 
   362 	for (qiter = templist.begin();
   363 		 qiter != templist.end();
   364 		 qiter++)
   365 	{
   366 		IDBThreadOperation *op = (*qiter);
   367 		op->CancelThinkPart();
   368 		op->Destroy();
   369 	}
   370 }
   371 
   372 IDBDriver *DBManager::GetDefaultDriver()
   373 {
   374 	if (!m_pDefault && m_DefDriver.size() > 0)
   375 	{
   376 		m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
   377 	}
   378 
   379 	return m_pDefault;
   380 }
   381 
   382 Handle_t DBManager::CreateHandle(DBHandleType dtype, void *ptr, IdentityToken_t *pToken)
   383 {
   384 	HandleType_t type = 0;
   385 
   386 	if (dtype == DBHandle_Driver)
   387 	{
   388 		type = m_DriverType;
   389 	} else if (dtype == DBHandle_Database) {
   390 		type = m_DatabaseType;
   391 	} else {
   392 		return BAD_HANDLE;
   393 	}
   394 
   395 	return g_HandleSys.CreateHandle(type, ptr, pToken, g_pCoreIdent, NULL);
   396 }
   397 
   398 HandleError DBManager::ReadHandle(Handle_t hndl, DBHandleType dtype, void **ptr)
   399 {
   400 	HandleType_t type = 0;
   401 
   402 	if (dtype == DBHandle_Driver)
   403 	{
   404 		type = m_DriverType;
   405 	} else if (dtype == DBHandle_Database) {
   406 		type = m_DatabaseType;
   407 	} else {
   408 		return HandleError_Type;
   409 	}
   410 
   411 	HandleSecurity sec(NULL, g_pCoreIdent);
   412 
   413 	return g_HandleSys.ReadHandle(hndl, type, &sec, ptr);
   414 }
   415 
   416 HandleError DBManager::ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token)
   417 {
   418 	HandleSecurity sec(token, g_pCoreIdent);
   419 	return g_HandleSys.FreeHandle(hndl, &sec);
   420 }
   421 
   422 unsigned int DBManager::GetDriverCount()
   423 {
   424 	return (unsigned int)m_drivers.size();
   425 }
   426 
   427 IDBDriver *DBManager::GetDriver(unsigned int index)
   428 {
   429 	if (index >= m_drivers.size())
   430 	{
   431 		return NULL;
   432 	}
   433 
   434 	return m_drivers[index];
   435 }
   436 
   437 const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
   438 {
   439 	ConfDbInfo *info = GetDatabaseConf(name);
   440 	if (!info)
   441 	{
   442 		return NULL;
   443 	}
   444 
   445 	return &info->info;
   446 }
   447 
   448 ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
   449 {
   450 	List<ConfDbInfo>::iterator iter;
   451 
   452 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   453 	{
   454 		ConfDbInfo &conf = (*iter);
   455 		if (conf.name == name)
   456 		{
   457 			return &conf;
   458 		}
   459 	}
   460 
   461 	return NULL;
   462 }
   463 
   464 IDBDriver *DBManager::FindOrLoadDriver(const char *name)
   465 {
   466 	size_t last_size = m_drivers.size();
   467 	for (size_t i=0; i<last_size; i++)
   468 	{
   469 		if (strcmp(m_drivers[i]->GetIdentifier(), name) == 0)
   470 		{
   471 			return m_drivers[i];
   472 		}
   473 	}
   474 
   475 	char filename[PLATFORM_MAX_PATH];
   476 	UTIL_Format(filename, sizeof(filename), "dbi.%s.ext", name);
   477 
   478 	IExtension *pExt = g_Extensions.LoadAutoExtension(filename);
   479 	if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size)
   480 	{
   481 		return NULL;
   482 	}
   483 
   484 	/* last_size is now gauranteed to be a valid index.
   485 	 * The identifier must match the name.
   486 	 */
   487 	if (strcmp(m_drivers[last_size]->GetIdentifier(), name) == 0)
   488 	{
   489 		return m_drivers[last_size];
   490 	}
   491 
   492 	return NULL;
   493 }
   494 
   495 void DBManager::KillWorkerThread()
   496 {
   497 	if (m_pWorker)
   498 	{
   499 		m_pWorker->Stop(false);
   500 		g_pThreader->DestroyWorker(m_pWorker);
   501 		m_pWorker = NULL;
   502 		s_OneTimeThreaderErrorMsg = false;
   503 	}
   504 }
   505 
   506 static IdentityToken_t *s_pAddBlock = NULL;
   507 
   508 bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
   509 {
   510 	if (s_pAddBlock && op->GetOwner() == s_pAddBlock)
   511 	{
   512 		return false;
   513 	}
   514 
   515 	if (!m_pWorker)
   516 	{
   517 		m_pWorker = g_pThreader->MakeWorker(this, true);
   518 		if (!m_pWorker)
   519 		{
   520 			if (!s_OneTimeThreaderErrorMsg)
   521 			{
   522 				g_Logger.LogError("[SM] Unable to create db threader (error unknown)");
   523 				s_OneTimeThreaderErrorMsg = true;
   524 			}
   525 			return false;
   526 		}
   527 		if (!m_pWorker->Start())
   528 		{
   529 			if (!s_OneTimeThreaderErrorMsg)
   530 			{
   531 				g_Logger.LogError("[SM] Unable to start db threader (error unknown)");
   532 				s_OneTimeThreaderErrorMsg = true;
   533 			}
   534 			g_pThreader->DestroyWorker(m_pWorker);
   535 			m_pWorker = NULL;
   536 			return false;
   537 		}
   538 	}
   539 
   540 	/* Add to the queue */
   541 	{
   542 		m_pQueueLock->Lock();
   543 		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
   544 		queue.push(op);
   545 		m_pQueueLock->Unlock();
   546 	}
   547 
   548 	/* Make the thread */
   549 	m_pWorker->MakeThread(this);
   550 
   551 	return true;
   552 }
   553 
   554 void DBManager::OnWorkerStart(IThreadWorker *pWorker)
   555 {
   556 	m_drSafety.clear();
   557 	for (size_t i=0; i<m_drivers.size(); i++)
   558 	{
   559 		if (m_drivers[i]->IsThreadSafe())
   560 		{
   561 			m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
   562 		} else {
   563 			m_drSafety.push_back(false);
   564 		}
   565 	}
   566 }
   567 
   568 void DBManager::OnWorkerStop(IThreadWorker *pWorker)
   569 {
   570 	for (size_t i=0; i<m_drivers.size(); i++)
   571 	{
   572 		if (m_drSafety[i])
   573 		{
   574 			m_drivers[i]->ShutdownThreadSafety();
   575 		}
   576 	}
   577 	m_drSafety.clear();
   578 }
   579 
   580 void DBManager::RunThread(IThreadHandle *pThread)
   581 {
   582 	IDBThreadOperation *op = NULL;
   583 
   584 	/* Get something from the queue */
   585 	{
   586 		m_pQueueLock->Lock();
   587 		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
   588 		if (!queue.empty())
   589 		{
   590 			op = queue.first();
   591 			queue.pop();
   592 		}
   593 		m_pQueueLock->Unlock();
   594 	}
   595 
   596 	/* whoa.  hi.  did we get something? we should have. */
   597 	if (!op)
   598 	{
   599 		/* wtf? */
   600 		return;
   601 	}
   602 
   603 	op->RunThreadPart();
   604 
   605 	m_pThinkLock->Lock();
   606 	m_ThinkQueue.push(op);
   607 	m_pThinkLock->Unlock();
   608 }
   609 
   610 void DBManager::RunFrame()
   611 {
   612 	/* Don't bother if we're empty */
   613 	if (!m_ThinkQueue.size())
   614 	{
   615 		return;
   616 	}
   617 
   618 	/* Dump one thing per-frame so the server stays sane. */
   619 	m_pThinkLock->Lock();
   620 	IDBThreadOperation *op = m_ThinkQueue.first();
   621 	m_ThinkQueue.pop();
   622 	m_pThinkLock->Unlock();
   623 	op->RunThinkPart();
   624 	op->Destroy();
   625 }
   626 
   627 void DBManager::OnTerminate(IThreadHandle *pThread, bool cancel)
   628 {
   629 	/* Do nothing */
   630 }
   631 
   632 void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
   633 {
   634 	s_pAddBlock = pToken;
   635 
   636 	/* Kill the thread so we can flush everything into the think queue... */
   637 	KillWorkerThread();
   638 
   639 	/* Run all of the think operations.
   640  	 * Unlike the driver unloading example, we'll let these calls go through, 
   641 	 * since a plugin unloading is far more normal.
   642 	 */
   643 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   644 	Queue<IDBThreadOperation *> templist;
   645 	while (iter != m_ThinkQueue.end())
   646 	{
   647 		IDBThreadOperation *op = (*iter);
   648 		if (op->GetOwner() == pToken)
   649 		{
   650 			templist.push(op);
   651 			iter = m_ThinkQueue.erase(iter);
   652 		} else {
   653 			iter++;
   654 		}
   655 	}
   656 
   657 	for (iter = templist.begin();
   658 		iter != templist.end();
   659 		iter++)
   660 	{
   661 		IDBThreadOperation *op = (*iter);
   662 		op->RunThinkPart();
   663 		op->Destroy();
   664 	}
   665 
   666 	s_pAddBlock = NULL;
   667 }
   668 
   669 void DBManager::OnPluginUnloaded(IPlugin *plugin)
   670 {
   671 	/* Kill the thread so we can flush everything into the think queue... */
   672 	KillWorkerThread();
   673 
   674 	/* Mark the plugin as being unloaded so future database calls will ignore threading... */
   675 	plugin->SetProperty("DisallowDBThreads", NULL);
   676 
   677 	/* Run all of the think operations.
   678 	 * Unlike the driver unloading example, we'll let these calls go through, 
   679 	 * since a plugin unloading is far more normal.
   680 	 */
   681 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   682 	Queue<IDBThreadOperation *> templist;
   683 	while (iter != m_ThinkQueue.end())
   684 	{
   685 		IDBThreadOperation *op = (*iter);
   686 		if (op->GetOwner() == plugin->GetIdentity())
   687 		{
   688 			templist.push(op);
   689 			iter = m_ThinkQueue.erase(iter);
   690 		} else {
   691 			iter++;
   692 		}
   693 	}
   694 
   695 	for (iter = templist.begin();
   696 		 iter != templist.end();
   697 		 iter++)
   698 	{
   699 		IDBThreadOperation *op = (*iter);
   700 		op->RunThinkPart();
   701 		op->Destroy();
   702 	}
   703 }
   704 
   705 void DBManager::LockConfig()
   706 {
   707 	m_pConfigLock->Lock();
   708 }
   709 
   710 void DBManager::UnlockConfig()
   711 {
   712 	m_pConfigLock->Unlock();
   713 }
   714 
   715 const char *DBManager::GetDefaultDriverName()
   716 {
   717 	return m_DefDriver.c_str();
   718 }
   719 
   720 void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)
   721 {
   722 	g_Extensions.AddRawDependency(myself, driver->GetIdentity(), driver);
   723 }
   724