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