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