core/logic/Database.cpp
author Ruben Gonzalez <drifter01620@gmail.com>
Thu May 22 09:49:27 2014 -0400 (2014-05-22)
changeset 4378 568f4497ef4a
parent 4349 4772045265f8
permissions -rw-r--r--
Update CS:GO team score gamedata.
     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 	/* Someone unloaded the default driver? Silly.. */
   357 	if (pDriver == m_pDefault)
   358 	{
   359 		m_pDefault = NULL;
   360 	}
   361 
   362 	/* Now that the driver is gone, we have to test the think queue.
   363 	 * Whatever happens therein is up to the db op!
   364 	 */
   365 	Queue<IDBThreadOperation *>::iterator qiter = m_ThinkQueue.begin();
   366 	Queue<IDBThreadOperation *> templist;
   367 	while (qiter != m_ThinkQueue.end())
   368 	{
   369 		IDBThreadOperation *op = (*qiter);
   370 		if (op->GetDriver() == pDriver)
   371 		{
   372 			templist.push(op);
   373 			qiter = m_ThinkQueue.erase(qiter);
   374 		} else {
   375 			qiter++;
   376 		}
   377 	}
   378 
   379 	for (qiter = templist.begin();
   380 		 qiter != templist.end();
   381 		 qiter++)
   382 	{
   383 		IDBThreadOperation *op = (*qiter);
   384 		op->CancelThinkPart();
   385 		op->Destroy();
   386 	}
   387 }
   388 
   389 IDBDriver *DBManager::GetDefaultDriver()
   390 {
   391 	if (!m_pDefault && m_DefDriver.size() > 0)
   392 	{
   393 		m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
   394 	}
   395 
   396 	return m_pDefault;
   397 }
   398 
   399 Handle_t DBManager::CreateHandle(DBHandleType dtype, void *ptr, IdentityToken_t *pToken)
   400 {
   401 	HandleType_t type = 0;
   402 
   403 	if (dtype == DBHandle_Driver)
   404 	{
   405 		type = m_DriverType;
   406 	} else if (dtype == DBHandle_Database) {
   407 		type = m_DatabaseType;
   408 	} else {
   409 		return BAD_HANDLE;
   410 	}
   411 
   412 	return g_HandleSys.CreateHandle(type, ptr, pToken, g_pCoreIdent, NULL);
   413 }
   414 
   415 HandleError DBManager::ReadHandle(Handle_t hndl, DBHandleType dtype, void **ptr)
   416 {
   417 	HandleType_t type = 0;
   418 
   419 	if (dtype == DBHandle_Driver)
   420 	{
   421 		type = m_DriverType;
   422 	} else if (dtype == DBHandle_Database) {
   423 		type = m_DatabaseType;
   424 	} else {
   425 		return HandleError_Type;
   426 	}
   427 
   428 	HandleSecurity sec(NULL, g_pCoreIdent);
   429 
   430 	return g_HandleSys.ReadHandle(hndl, type, &sec, ptr);
   431 }
   432 
   433 HandleError DBManager::ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token)
   434 {
   435 	HandleSecurity sec(token, g_pCoreIdent);
   436 	return g_HandleSys.FreeHandle(hndl, &sec);
   437 }
   438 
   439 unsigned int DBManager::GetDriverCount()
   440 {
   441 	return (unsigned int)m_drivers.size();
   442 }
   443 
   444 IDBDriver *DBManager::GetDriver(unsigned int index)
   445 {
   446 	if (index >= m_drivers.size())
   447 	{
   448 		return NULL;
   449 	}
   450 
   451 	return m_drivers[index];
   452 }
   453 
   454 const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
   455 {
   456 	ConfDbInfo *info = GetDatabaseConf(name);
   457 	if (!info)
   458 	{
   459 		return NULL;
   460 	}
   461 
   462 	return &info->info;
   463 }
   464 
   465 ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
   466 {
   467 	List<ConfDbInfo *>::iterator iter;
   468 
   469 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   470 	{
   471 		ConfDbInfo &conf = *(*iter);
   472 		if (conf.name == name)
   473 		{
   474 			return &conf;
   475 		}
   476 	}
   477 
   478 	return NULL;
   479 }
   480 
   481 IDBDriver *DBManager::FindOrLoadDriver(const char *name)
   482 {
   483 	size_t last_size = m_drivers.size();
   484 	for (size_t i=0; i<last_size; i++)
   485 	{
   486 		if (strcmp(m_drivers[i]->GetIdentifier(), name) == 0)
   487 		{
   488 			return m_drivers[i];
   489 		}
   490 	}
   491 
   492 	char filename[PLATFORM_MAX_PATH];
   493 	g_pSM->Format(filename, sizeof(filename), "dbi.%s.ext", name);
   494 
   495 	IExtension *pExt = g_Extensions.LoadAutoExtension(filename);
   496 	if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size)
   497 	{
   498 		return NULL;
   499 	}
   500 
   501 	/* last_size is now gauranteed to be a valid index.
   502 	 * The identifier must match the name.
   503 	 */
   504 	if (strcmp(m_drivers[last_size]->GetIdentifier(), name) == 0)
   505 	{
   506 		return m_drivers[last_size];
   507 	}
   508 
   509 	return NULL;
   510 }
   511 
   512 void DBManager::KillWorkerThread()
   513 {
   514 	if (m_Worker)
   515 	{
   516 		{
   517 			ke::AutoLock lock(&m_QueueEvent);
   518 			m_Terminate = true;
   519 			m_QueueEvent.Notify();
   520 		}
   521 		m_Worker->Join();
   522 		m_Worker = NULL;
   523 		s_OneTimeThreaderErrorMsg = false;
   524 		m_Terminate = false;
   525 	}
   526 }
   527 
   528 static IdentityToken_t *s_pAddBlock = NULL;
   529 
   530 bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
   531 {
   532 	if (s_pAddBlock && op->GetOwner() == s_pAddBlock)
   533 	{
   534 		return false;
   535 	}
   536 
   537 	if (!m_Worker)
   538 	{
   539 		m_Worker = new ke::Thread(this, "SM SQL Worker");
   540 		if (!m_Worker->Succeeded())
   541 		{
   542 			if (!s_OneTimeThreaderErrorMsg)
   543 			{
   544 				smcore.LogError("[SM] Unable to create db threader (error unknown)");
   545 				s_OneTimeThreaderErrorMsg = true;
   546 			}
   547 			m_Worker = NULL;
   548 			return false;
   549 		}
   550 	}
   551 
   552 	/* Add to the queue */
   553 	{
   554 		ke::AutoLock lock(&m_QueueEvent);
   555 		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
   556 		queue.push(op);
   557 		m_QueueEvent.Notify();
   558 	}
   559 
   560 	return true;
   561 }
   562 
   563 void DBManager::Run()
   564 {
   565 	// Initialize DB threadsafety.
   566 	for (size_t i=0; i < m_drivers.size(); i++)
   567 	{
   568 		if (m_drivers[i]->IsThreadSafe())
   569 			m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
   570 		else
   571 			m_drSafety.push_back(false);
   572 	}
   573 
   574 	// Run actual worker thread logic.
   575 	ThreadMain();
   576 
   577 	// Shutdown DB threadsafety.
   578 	for (size_t i=0; i<m_drivers.size(); i++)
   579 	{
   580 		if (m_drSafety[i])
   581 			m_drivers[i]->ShutdownThreadSafety();
   582 	}
   583 	m_drSafety.clear();
   584 }
   585 
   586 void DBManager::ThreadMain()
   587 {
   588 	ke::AutoLock lock(&m_QueueEvent);
   589 
   590 	while (true) {
   591 		// The lock has been acquired. Grab everything we can out of the
   592 		// queue. Since we want to flush the queue even if we're terminated,
   593 		// we process all operations we can before checking to terminate.
   594 		// There's no risk of starvation since the main thread blocks on us
   595 		// terminating.
   596 		while (true)
   597 		{
   598 			Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
   599 			if (queue.empty())
   600 				break;
   601 
   602 			IDBThreadOperation *op = queue.first();
   603 			queue.pop();
   604 
   605 			// Unlock the queue when we run the query, so the main thread can
   606 			// keep pumping events. We re-acquire the lock to check for more
   607 			// items. It's okay if we terminate while unlocked; the main
   608 			// thread would be blocked and we'd need to flush the queue
   609 			// anyway, so after we've depleted the queue here, we'll just
   610 			// reach the terminate at the top of the loop.
   611 			{
   612 				ke::AutoUnlock unlock(&m_QueueEvent);
   613 				op->RunThreadPart();
   614 
   615 				ke::AutoLock lock(&m_ThinkLock);
   616 				m_ThinkQueue.push(op);
   617 			}
   618 		}
   619 
   620 		if (m_Terminate)
   621 			return;
   622 
   623 		// Release the lock and wait for a signal.
   624 		m_QueueEvent.Wait();
   625 	}
   626 }
   627 
   628 void DBManager::RunFrame()
   629 {
   630 	/* Don't bother if we're empty */
   631 	if (!m_ThinkQueue.size())
   632 	{
   633 		return;
   634 	}
   635 
   636 	/* Dump one thing per-frame so the server stays sane. */
   637 	IDBThreadOperation *op;
   638 	{
   639 		ke::AutoLock lock(&m_ThinkLock);
   640 		op = m_ThinkQueue.first();
   641 		m_ThinkQueue.pop();
   642 	}
   643 	op->RunThinkPart();
   644 	op->Destroy();
   645 }
   646 
   647 void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
   648 {
   649 	s_pAddBlock = pToken;
   650 
   651 	/* Kill the thread so we can flush everything into the think queue... */
   652 	KillWorkerThread();
   653 
   654 	/* Run all of the think operations.
   655  	 * Unlike the driver unloading example, we'll let these calls go through, 
   656 	 * since a plugin unloading is far more normal.
   657 	 */
   658 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   659 	Queue<IDBThreadOperation *> templist;
   660 	while (iter != m_ThinkQueue.end())
   661 	{
   662 		IDBThreadOperation *op = (*iter);
   663 		if (op->GetOwner() == pToken)
   664 		{
   665 			templist.push(op);
   666 			iter = m_ThinkQueue.erase(iter);
   667 		} else {
   668 			iter++;
   669 		}
   670 	}
   671 
   672 	for (iter = templist.begin();
   673 		iter != templist.end();
   674 		iter++)
   675 	{
   676 		IDBThreadOperation *op = (*iter);
   677 		op->RunThinkPart();
   678 		op->Destroy();
   679 	}
   680 
   681 	s_pAddBlock = NULL;
   682 }
   683 
   684 void DBManager::OnPluginUnloaded(IPlugin *plugin)
   685 {
   686 	/* Kill the thread so we can flush everything into the think queue... */
   687 	KillWorkerThread();
   688 
   689 	/* Mark the plugin as being unloaded so future database calls will ignore threading... */
   690 	plugin->SetProperty("DisallowDBThreads", NULL);
   691 
   692 	/* Run all of the think operations.
   693 	 * Unlike the driver unloading example, we'll let these calls go through, 
   694 	 * since a plugin unloading is far more normal.
   695 	 */
   696 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   697 	Queue<IDBThreadOperation *> templist;
   698 	while (iter != m_ThinkQueue.end())
   699 	{
   700 		IDBThreadOperation *op = (*iter);
   701 		if (op->GetOwner() == plugin->GetIdentity())
   702 		{
   703 			templist.push(op);
   704 			iter = m_ThinkQueue.erase(iter);
   705 		} else {
   706 			iter++;
   707 		}
   708 	}
   709 
   710 	for (iter = templist.begin();
   711 		 iter != templist.end();
   712 		 iter++)
   713 	{
   714 		IDBThreadOperation *op = (*iter);
   715 		op->RunThinkPart();
   716 		op->Destroy();
   717 	}
   718 }
   719 
   720 void DBManager::LockConfig()
   721 {
   722 	m_ConfigLock.Lock();
   723 }
   724 
   725 void DBManager::UnlockConfig()
   726 {
   727 	m_ConfigLock.Unlock();
   728 }
   729 
   730 const char *DBManager::GetDefaultDriverName()
   731 {
   732 	return m_DefDriver.c_str();
   733 }
   734 
   735 void DBManager::AddDependency(IExtension *myself, IDBDriver *driver)
   736 {
   737 	g_Extensions.AddRawDependency(myself, driver->GetIdentity(), driver);
   738 }
   739