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