core/Database.cpp
author David Anderson <dvander@alliedmods.net>
Tue Jul 24 02:24:37 2007 +0000 (2007-07-24)
changeset 1157 61c7092246c6
parent 1096 8e11b2ebdc22
child 1201 fb159560c3fe
permissions -rw-r--r--
added amb670 (exposed IDBThreadOperation to extensions)
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ===============================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include "Database.h"
    16 #include "HandleSys.h"
    17 #include "ShareSys.h"
    18 #include "sourcemod.h"
    19 #include "sm_stringutil.h"
    20 #include "TextParsers.h"
    21 #include "Logger.h"
    22 #include "ExtensionSys.h"
    23 #include <stdlib.h>
    24 #include "ThreadSupport.h"
    25 
    26 #define DBPARSE_LEVEL_NONE		0
    27 #define DBPARSE_LEVEL_MAIN		1
    28 #define DBPARSE_LEVEL_DATABASE	2
    29 
    30 DBManager g_DBMan;
    31 static bool s_OneTimeThreaderErrorMsg = false;
    32 
    33 DBManager::DBManager() 
    34 : m_StrTab(512), m_ParseLevel(0), m_ParseState(0), m_pDefault(NULL)
    35 {
    36 }
    37 
    38 void DBManager::OnSourceModAllInitialized()
    39 {
    40 	HandleAccess sec;
    41 
    42 	g_HandleSys.InitAccessDefaults(NULL, &sec);
    43 	sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
    44 	sec.access[HandleAccess_Clone] |= HANDLE_RESTRICT_IDENTITY;
    45 	
    46 	m_DriverType = g_HandleSys.CreateType("IDriver", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    47 	m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    48 
    49 	g_ShareSys.AddInterface(NULL, this);
    50 
    51 	g_SourceMod.BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
    52 
    53 	m_pConfigLock = g_pThreader->MakeMutex();
    54 	m_pThinkLock = g_pThreader->MakeMutex();
    55 	m_pQueueLock = g_pThreader->MakeMutex();
    56 
    57 	g_PluginSys.AddPluginsListener(this);
    58 }
    59 
    60 void DBManager::OnSourceModLevelChange(const char *mapName)
    61 {
    62 	SMCParseError err;
    63 	unsigned int line = 0;
    64 
    65 	/* We lock and don't give up the lock until we're done.
    66 	 * This way the thread's search won't be searching through a
    67 	 * potentially empty/corrupt list, which would be very bad.
    68 	 */
    69 	m_pConfigLock->Lock();
    70 	if ((err = g_TextParser.ParseFile_SMC(m_Filename, this, &line, NULL)) != SMCParse_Okay)
    71 	{
    72 		g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
    73 		if (err != SMCParse_Custom)
    74 		{
    75 			const char *txt = g_TextParser.GetSMCErrorString(err);
    76 			g_Logger.LogError("[SM] Line %d: %s", line, txt);
    77 		}
    78 	}
    79 	m_pConfigLock->Unlock();
    80 
    81 	g_PluginSys.RemovePluginsListener(this);
    82 }
    83 
    84 void DBManager::OnSourceModShutdown()
    85 {
    86 	KillWorkerThread();
    87 	m_pConfigLock->DestroyThis();
    88 	m_pThinkLock->DestroyThis();
    89 	m_pQueueLock->DestroyThis();
    90 	g_HandleSys.RemoveType(m_DatabaseType, g_pCoreIdent);
    91 	g_HandleSys.RemoveType(m_DriverType, g_pCoreIdent);
    92 }
    93 
    94 unsigned int DBManager::GetInterfaceVersion()
    95 {
    96 	return SMINTERFACE_DBI_VERSION;
    97 }
    98 
    99 const char *DBManager::GetInterfaceName()
   100 {
   101 	return SMINTERFACE_DBI_NAME;
   102 }
   103 
   104 void DBManager::OnHandleDestroy(HandleType_t type, void *object)
   105 {
   106 	if (type == m_DriverType)
   107 	{
   108 		/* Ignore */
   109 		return;
   110 	}
   111 
   112 	if (g_HandleSys.TypeCheck(type, m_DatabaseType))
   113 	{
   114 		IDatabase *pdb = (IDatabase *)object;
   115 		pdb->Close();
   116 	}
   117 }
   118 
   119 void DBManager::ReadSMC_ParseStart()
   120 {
   121 	m_confs.clear();
   122 	m_ParseLevel = 0;
   123 	m_ParseState = DBPARSE_LEVEL_NONE;
   124 	m_StrTab.Reset();
   125 	m_DefDriver.clear();
   126 }
   127 
   128 ConfDbInfo s_CurInfo;
   129 SMCParseResult DBManager::ReadSMC_NewSection(const char *name, bool opt_quotes)
   130 {
   131 	if (m_ParseLevel)
   132 	{
   133 		m_ParseLevel++;
   134 		return SMCParse_Continue;
   135 	}
   136 
   137 	if (m_ParseState == DBPARSE_LEVEL_NONE)
   138 	{
   139 		if (strcmp(name, "Databases") == 0)
   140 		{
   141 			m_ParseState = DBPARSE_LEVEL_MAIN;
   142 		} else {
   143 			m_ParseLevel++;
   144 		}
   145 	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
   146 		s_CurInfo = ConfDbInfo();
   147 		s_CurInfo.name = m_StrTab.AddString(name);
   148 		m_ParseState = DBPARSE_LEVEL_DATABASE;
   149 	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
   150 		m_ParseLevel++;
   151 	}
   152 
   153 	return SMCParse_Continue;
   154 }
   155 
   156 SMCParseResult DBManager::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
   157 {
   158 	if (m_ParseLevel)
   159 	{
   160 		return SMCParse_Continue;
   161 	}
   162 
   163 	if (m_ParseState == DBPARSE_LEVEL_MAIN)
   164 	{
   165 		if (strcmp(key, "driver_default") == 0)
   166 		{
   167 			m_DefDriver.assign(value);
   168 		}
   169 	} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
   170 		if (strcmp(key, "driver") == 0)
   171 		{
   172 			if (strcmp(value, "default") != 0)
   173 			{
   174 				s_CurInfo.driver = m_StrTab.AddString(value);
   175 			}
   176 		} else if (strcmp(key, "database") == 0) {
   177 			s_CurInfo.database = m_StrTab.AddString(value);
   178 		} else if (strcmp(key, "host") == 0) {
   179 			s_CurInfo.host = m_StrTab.AddString(value);
   180 		} else if (strcmp(key, "user") == 0) {
   181 			s_CurInfo.user = m_StrTab.AddString(value);
   182 		} else if (strcmp(key, "pass") == 0) {
   183 			s_CurInfo.pass = m_StrTab.AddString(value);
   184 		} else if (strcmp(key, "timeout") == 0) {
   185 			s_CurInfo.info.maxTimeout = atoi(value);
   186 		} else if (strcmp(key, "port") == 0) {
   187 			s_CurInfo.info.port = atoi(value);
   188 		}
   189 	}
   190 
   191 	return SMCParse_Continue;
   192 }
   193 
   194 #define ASSIGN_VAR(var) \
   195 	if (s_CurInfo.var == -1) { \
   196 		s_CurInfo.info.var = ""; \
   197 	} else { \
   198 		s_CurInfo.info.var = m_StrTab.GetString(s_CurInfo.var); \
   199 	}
   200 
   201 SMCParseResult DBManager::ReadSMC_LeavingSection()
   202 {
   203 	if (m_ParseLevel)
   204 	{
   205 		m_ParseLevel--;
   206 		return SMCParse_Continue;
   207 	}
   208 
   209 	if (m_ParseState == DBPARSE_LEVEL_DATABASE)
   210 	{
   211 		/* Set all of the info members to either a blank string
   212 		 * or the string pointer from the string table.
   213 		 */
   214 		ASSIGN_VAR(driver);
   215 		ASSIGN_VAR(database);
   216 		ASSIGN_VAR(host);
   217 		ASSIGN_VAR(user);
   218 		ASSIGN_VAR(pass);
   219 		
   220 		/* Save it.. */
   221 		m_confs.push_back(s_CurInfo);
   222 
   223 		/* Go up one level */
   224 		m_ParseState = DBPARSE_LEVEL_MAIN;
   225 	} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
   226 		m_ParseState = DBPARSE_LEVEL_NONE;
   227 		return SMCParse_Halt;
   228 	}
   229 
   230 	return SMCParse_Continue;
   231 }
   232 #undef ASSIGN_VAR
   233 
   234 void DBManager::ReadSMC_ParseEnd(bool halted, bool failed)
   235 {
   236 }
   237 
   238 bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
   239 {
   240 	ConfDbInfo *pInfo = GetDatabaseConf(name);
   241 
   242 	if (!pInfo)
   243 	{
   244 		if (pdr)
   245 		{
   246 			*pdr = NULL;
   247 		}
   248 		*pdb = NULL;
   249 		UTIL_Format(error, maxlength, "Configuration \"%s\" not found", name);
   250 		return false;
   251 	}
   252 
   253 	const char *dname = pInfo->info.driver;
   254 	if (!pInfo->realDriver)
   255 	{
   256 		/* Try to assign a real driver pointer */
   257 		if (pInfo->info.driver[0] == '\0')
   258 		{
   259 			if (!m_pDefault && m_DefDriver.size() > 0)
   260 			{
   261 				m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
   262 			}
   263 			dname = m_DefDriver.size() ? m_DefDriver.c_str() : "default";
   264 			pInfo->realDriver = m_pDefault;
   265 		} else {
   266 			pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
   267 		}
   268 	}
   269 
   270 	if (pInfo->realDriver)
   271 	{
   272 		if (pdr)
   273 		{
   274 			*pdr = pInfo->realDriver;
   275 		}
   276 		*pdb = pInfo->realDriver->Connect(&pInfo->info, persistent, error, maxlength);
   277 		return (*pdb != NULL);
   278 	}
   279 
   280 	if (pdr)
   281 	{
   282 		*pdr = NULL;
   283 	}
   284 	*pdb = NULL;
   285 
   286 	UTIL_Format(error, maxlength, "Driver \"%s\" not found", dname);
   287 
   288 	return false;
   289 }
   290 
   291 void DBManager::AddDriver(IDBDriver *pDriver)
   292 {
   293 	 /* Let's kill the worker.  Join the thread and let the queries flush.
   294 	  * This is kind of stupid but we just want to unload safely.
   295 	  * Rather than recreate the worker, we'll wait until someone throws 
   296 	  * another query through.
   297 	  */
   298 	KillWorkerThread();
   299 
   300 	 m_drivers.push_back(pDriver);
   301 }
   302 
   303 void DBManager::RemoveDriver(IDBDriver *pDriver)
   304 {
   305 	/* Again, we're forced to kill the worker.  How rude!
   306 	 * Doing this flushes the queue, and thus we don't need to 
   307 	 * clean anything else.  
   308 	 */
   309 	KillWorkerThread();
   310 
   311 	for (size_t i=0; i<m_drivers.size(); i++)
   312 	{
   313 		if (m_drivers[i] == pDriver)
   314 		{
   315 			m_drivers.erase(m_drivers.iterAt(i));
   316 			return;
   317 		}
   318 	}
   319 
   320 	/* Make sure NOTHING references this! */
   321 	List<ConfDbInfo>::iterator iter;
   322 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   323 	{
   324 		ConfDbInfo &db = (*iter);
   325 		if (db.realDriver == pDriver)
   326 		{
   327 			db.realDriver = NULL;
   328 		}
   329 	}
   330 
   331 	/* Now that the driver is gone, we have to test the think queue.
   332 	 * Whatever happens therein is up to the db op!
   333 	 */
   334 	Queue<IDBThreadOperation *>::iterator qiter = m_ThinkQueue.begin();
   335 	Queue<IDBThreadOperation *> templist;
   336 	while (qiter != m_ThinkQueue.end())
   337 	{
   338 		IDBThreadOperation *op = (*qiter);
   339 		if (op->GetDriver() == pDriver)
   340 		{
   341 			templist.push(op);
   342 			qiter = m_ThinkQueue.erase(qiter);
   343 		} else {
   344 			qiter++;
   345 		}
   346 	}
   347 
   348 	for (qiter = templist.begin();
   349 		 qiter != templist.end();
   350 		 qiter++)
   351 	{
   352 		IDBThreadOperation *op = (*qiter);
   353 		op->CancelThinkPart();
   354 		op->Destroy();
   355 	}
   356 }
   357 
   358 IDBDriver *DBManager::GetDefaultDriver()
   359 {
   360 	if (!m_pDefault && m_DefDriver.size() > 0)
   361 	{
   362 		m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
   363 	}
   364 
   365 	return m_pDefault;
   366 }
   367 
   368 Handle_t DBManager::CreateHandle(DBHandleType dtype, void *ptr, IdentityToken_t *pToken)
   369 {
   370 	HandleType_t type = 0;
   371 
   372 	if (dtype == DBHandle_Driver)
   373 	{
   374 		type = m_DriverType;
   375 	} else if (dtype == DBHandle_Database) {
   376 		type = m_DatabaseType;
   377 	} else {
   378 		return BAD_HANDLE;
   379 	}
   380 
   381 	return g_HandleSys.CreateHandle(type, ptr, pToken, g_pCoreIdent, NULL);
   382 }
   383 
   384 HandleError DBManager::ReadHandle(Handle_t hndl, DBHandleType dtype, void **ptr)
   385 {
   386 	HandleType_t type = 0;
   387 
   388 	if (dtype == DBHandle_Driver)
   389 	{
   390 		type = m_DriverType;
   391 	} else if (dtype == DBHandle_Database) {
   392 		type = m_DatabaseType;
   393 	} else {
   394 		return HandleError_Type;
   395 	}
   396 
   397 	HandleSecurity sec(NULL, g_pCoreIdent);
   398 
   399 	return g_HandleSys.ReadHandle(hndl, type, &sec, ptr);
   400 }
   401 
   402 HandleError DBManager::ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token)
   403 {
   404 	HandleSecurity sec(token, g_pCoreIdent);
   405 	return g_HandleSys.FreeHandle(hndl, &sec);
   406 }
   407 
   408 unsigned int DBManager::GetDriverCount()
   409 {
   410 	return (unsigned int)m_drivers.size();
   411 }
   412 
   413 IDBDriver *DBManager::GetDriver(unsigned int index)
   414 {
   415 	if (index >= m_drivers.size())
   416 	{
   417 		return NULL;
   418 	}
   419 
   420 	return m_drivers[index];
   421 }
   422 
   423 const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
   424 {
   425 	ConfDbInfo *info = GetDatabaseConf(name);
   426 	if (!info)
   427 	{
   428 		return NULL;
   429 	}
   430 
   431 	return &info->info;
   432 }
   433 
   434 ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
   435 {
   436 	List<ConfDbInfo>::iterator iter;
   437 
   438 	for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
   439 	{
   440 		ConfDbInfo &conf = (*iter);
   441 		if (strcmp(m_StrTab.GetString(conf.name), name) == 0)
   442 		{
   443 			return &conf;
   444 		}
   445 	}
   446 
   447 	return NULL;
   448 }
   449 
   450 IDBDriver *DBManager::FindOrLoadDriver(const char *name)
   451 {
   452 	size_t last_size = m_drivers.size();
   453 	for (size_t i=0; i<last_size; i++)
   454 	{
   455 		if (strcmp(m_drivers[i]->GetIdentifier(), name) == 0)
   456 		{
   457 			return m_drivers[i];
   458 		}
   459 	}
   460 
   461 	char filename[PLATFORM_MAX_PATH];
   462 	UTIL_Format(filename, sizeof(filename), "dbi.%s.ext", name);
   463 
   464 	IExtension *pExt = g_Extensions.LoadAutoExtension(filename);
   465 	if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size)
   466 	{
   467 		return NULL;
   468 	}
   469 
   470 	/* last_size is now gauranteed to be a valid index.
   471 	 * The identifier must match the name.
   472 	 */
   473 	if (strcmp(m_drivers[last_size]->GetIdentifier(), name) == 0)
   474 	{
   475 		return m_drivers[last_size];
   476 	}
   477 
   478 	return NULL;
   479 }
   480 
   481 void DBManager::KillWorkerThread()
   482 {
   483 	if (m_pWorker)
   484 	{
   485 		m_pWorker->Stop(false);
   486 		g_pThreader->DestroyWorker(m_pWorker);
   487 		m_pWorker = NULL;
   488 		s_OneTimeThreaderErrorMsg = false;
   489 	}
   490 }
   491 
   492 static IdentityToken_t *s_pAddBlock = NULL;
   493 
   494 bool DBManager::AddToThreadQueue(IDBThreadOperation *op, PrioQueueLevel prio)
   495 {
   496 	if (s_pAddBlock && op->GetOwner() == s_pAddBlock)
   497 	{
   498 		return false;
   499 	}
   500 
   501 	if (!m_pWorker)
   502 	{
   503 		m_pWorker = g_pThreader->MakeWorker(this, true);
   504 		if (!m_pWorker)
   505 		{
   506 			if (!s_OneTimeThreaderErrorMsg)
   507 			{
   508 				g_Logger.LogError("[SM] Unable to create db threader (error unknown)");
   509 				s_OneTimeThreaderErrorMsg = true;
   510 			}
   511 			return false;
   512 		}
   513 		if (!m_pWorker->Start())
   514 		{
   515 			if (!s_OneTimeThreaderErrorMsg)
   516 			{
   517 				g_Logger.LogError("[SM] Unable to start db threader (error unknown)");
   518 				s_OneTimeThreaderErrorMsg = true;
   519 			}
   520 			g_pThreader->DestroyWorker(m_pWorker);
   521 			m_pWorker = NULL;
   522 			return false;
   523 		}
   524 	}
   525 
   526 	/* Add to the queue */
   527 	{
   528 		m_pQueueLock->Lock();
   529 		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetQueue(prio);
   530 		queue.push(op);
   531 		m_pQueueLock->Unlock();
   532 	}
   533 
   534 	/* Make the thread */
   535 	m_pWorker->MakeThread(this);
   536 
   537 	return true;
   538 }
   539 
   540 void DBManager::OnWorkerStart(IThreadWorker *pWorker)
   541 {
   542 	m_drSafety.clear();
   543 	for (size_t i=0; i<m_drivers.size(); i++)
   544 	{
   545 		if (m_drivers[i]->IsThreadSafe())
   546 		{
   547 			m_drSafety.push_back(m_drivers[i]->InitializeThreadSafety());
   548 		} else {
   549 			m_drSafety.push_back(false);
   550 		}
   551 	}
   552 }
   553 
   554 void DBManager::OnWorkerStop(IThreadWorker *pWorker)
   555 {
   556 	for (size_t i=0; i<m_drivers.size(); i++)
   557 	{
   558 		if (m_drSafety[i])
   559 		{
   560 			m_drivers[i]->ShutdownThreadSafety();
   561 		}
   562 	}
   563 	m_drSafety.clear();
   564 }
   565 
   566 void DBManager::RunThread(IThreadHandle *pThread)
   567 {
   568 	IDBThreadOperation *op = NULL;
   569 
   570 	/* Get something from the queue */
   571 	{
   572 		m_pQueueLock->Lock();
   573 		Queue<IDBThreadOperation *> &queue = m_OpQueue.GetLikelyQueue();
   574 		if (!queue.empty())
   575 		{
   576 			op = queue.first();
   577 			queue.pop();
   578 		}
   579 		m_pQueueLock->Unlock();
   580 	}
   581 
   582 	/* whoa.  hi.  did we get something? we should have. */
   583 	if (!op)
   584 	{
   585 		/* wtf? */
   586 		return;
   587 	}
   588 
   589 	op->RunThreadPart();
   590 
   591 	m_pThinkLock->Lock();
   592 	m_ThinkQueue.push(op);
   593 	m_pThinkLock->Unlock();
   594 }
   595 
   596 void DBManager::RunFrame()
   597 {
   598 	/* Don't bother if we're empty */
   599 	if (!m_ThinkQueue.size())
   600 	{
   601 		return;
   602 	}
   603 
   604 	/* Dump one thing per-frame so the server stays sane. */
   605 	m_pThinkLock->Lock();
   606 	IDBThreadOperation *op = m_ThinkQueue.first();
   607 	m_ThinkQueue.pop();
   608 	m_pThinkLock->Unlock();
   609 	op->RunThinkPart();
   610 	op->Destroy();
   611 }
   612 
   613 void DBManager::OnTerminate(IThreadHandle *pThread, bool cancel)
   614 {
   615 	/* Do nothing */
   616 }
   617 
   618 void DBManager::OnSourceModIdentityDropped(IdentityToken_t *pToken)
   619 {
   620 	s_pAddBlock = pToken;
   621 
   622 	/* Kill the thread so we can flush everything into the think queue... */
   623 	KillWorkerThread();
   624 
   625 	/* Run all of the think operations.
   626  	 * Unlike the driver unloading example, we'll let these calls go through, 
   627 	 * since a plugin unloading is far more normal.
   628 	 */
   629 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   630 	Queue<IDBThreadOperation *> templist;
   631 	while (iter != m_ThinkQueue.end())
   632 	{
   633 		IDBThreadOperation *op = (*iter);
   634 		if (op->GetOwner() == pToken)
   635 		{
   636 			templist.push(op);
   637 			iter = m_ThinkQueue.erase(iter);
   638 		} else {
   639 			iter++;
   640 		}
   641 	}
   642 
   643 	for (iter = templist.begin();
   644 		iter != templist.end();
   645 		iter++)
   646 	{
   647 		IDBThreadOperation *op = (*iter);
   648 		op->RunThinkPart();
   649 		op->Destroy();
   650 	}
   651 
   652 	s_pAddBlock = NULL;
   653 }
   654 
   655 void DBManager::OnPluginUnloaded(IPlugin *plugin)
   656 {
   657 	/* Kill the thread so we can flush everything into the think queue... */
   658 	KillWorkerThread();
   659 
   660 	/* Mark the plugin as being unloaded so future database calls will ignore threading... */
   661 	plugin->SetProperty("DisallowDBThreads", NULL);
   662 
   663 	/* Run all of the think operations.
   664 	 * Unlike the driver unloading example, we'll let these calls go through, 
   665 	 * since a plugin unloading is far more normal.
   666 	 */
   667 	Queue<IDBThreadOperation *>::iterator iter = m_ThinkQueue.begin();
   668 	Queue<IDBThreadOperation *> templist;
   669 	while (iter != m_ThinkQueue.end())
   670 	{
   671 		IDBThreadOperation *op = (*iter);
   672 		if (op->GetOwner() == plugin->GetIdentity())
   673 		{
   674 			templist.push(op);
   675 			iter = m_ThinkQueue.erase(iter);
   676 		} else {
   677 			iter++;
   678 		}
   679 	}
   680 
   681 	for (iter = templist.begin();
   682 		 iter != templist.end();
   683 		 iter++)
   684 	{
   685 		IDBThreadOperation *op = (*iter);
   686 		op->RunThinkPart();
   687 		op->Destroy();
   688 	}
   689 }
   690 
   691 void DBManager::LockConfig()
   692 {
   693 	m_pConfigLock->Lock();
   694 }
   695 
   696 void DBManager::UnlockConfig()
   697 {
   698 	m_pConfigLock->Unlock();
   699 }
   700 
   701 const char *DBManager::GetDefaultDriverName()
   702 {
   703 	return m_DefDriver.c_str();
   704 }