source-engine/utils/common/MySqlDatabase.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

193 lines
5.6 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "MySqlDatabase.h"
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CMySqlDatabase::CMySqlDatabase()
{
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
// blocks until db process thread has stopped
//-----------------------------------------------------------------------------
CMySqlDatabase::~CMySqlDatabase()
{
// flag the thread to stop
m_bRunThread = false;
// pulse the thread to make it run
::SetEvent(m_hEvent);
// make sure it's done
::EnterCriticalSection(&m_csThread);
::LeaveCriticalSection(&m_csThread);
}
//-----------------------------------------------------------------------------
// Purpose: Thread access function
//-----------------------------------------------------------------------------
static DWORD WINAPI staticThreadFunc(void *param)
{
((CMySqlDatabase *)param)->RunThread();
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Establishes connection to the database and sets up this object to handle db command
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CMySqlDatabase::Initialize()
{
// prepare critical sections
//!! need to download SDK and replace these with InitializeCriticalSectionAndSpinCount() calls
::InitializeCriticalSection(&m_csThread);
::InitializeCriticalSection(&m_csInQueue);
::InitializeCriticalSection(&m_csOutQueue);
::InitializeCriticalSection(&m_csDBAccess);
// initialize wait calls
m_hEvent = ::CreateEvent(NULL, false, true, NULL);
// start the DB-access thread
m_bRunThread = true;
unsigned long threadID;
::CreateThread(NULL, 0, staticThreadFunc, this, 0, &threadID);
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Main thread loop
//-----------------------------------------------------------------------------
void CMySqlDatabase::RunThread()
{
::EnterCriticalSection(&m_csThread);
while (m_bRunThread)
{
if (m_InQueue.Count() > 0)
{
// get a dispatched DB request
::EnterCriticalSection(&m_csInQueue);
// pop the front of the queue
int headIndex = m_InQueue.Head();
msg_t msg = m_InQueue[headIndex];
m_InQueue.Remove(headIndex);
::LeaveCriticalSection(&m_csInQueue);
::EnterCriticalSection(&m_csDBAccess);
// run sqldb command
msg.result = msg.cmd->RunCommand();
::LeaveCriticalSection(&m_csDBAccess);
if (msg.replyTarget)
{
// put the results in the outgoing queue
::EnterCriticalSection(&m_csOutQueue);
m_OutQueue.AddToTail(msg);
::LeaveCriticalSection(&m_csOutQueue);
// wake up out queue
msg.replyTarget->WakeUp();
}
else
{
// there is no return data from the call, so kill the object now
msg.cmd->deleteThis();
}
}
else
{
// nothing in incoming queue, so wait until we get the signal
::WaitForSingleObject(m_hEvent, INFINITE);
}
// check the size of the outqueue; if it's getting too big, sleep to let the main thread catch up
if (m_OutQueue.Count() > 50)
{
::Sleep(2);
}
}
::LeaveCriticalSection(&m_csThread);
}
//-----------------------------------------------------------------------------
// Purpose: Adds a database command to the queue, and wakes the db thread
//-----------------------------------------------------------------------------
void CMySqlDatabase::AddCommandToQueue(ISQLDBCommand *cmd, ISQLDBReplyTarget *replyTarget, int returnState)
{
::EnterCriticalSection(&m_csInQueue);
// add to the queue
msg_t msg = { cmd, replyTarget, 0, returnState };
m_InQueue.AddToTail(msg);
::LeaveCriticalSection(&m_csInQueue);
// signal the thread to start running
::SetEvent(m_hEvent);
}
//-----------------------------------------------------------------------------
// Purpose: Dispatches responses to SQLDB queries
//-----------------------------------------------------------------------------
bool CMySqlDatabase::RunFrame()
{
bool doneWork = false;
while (m_OutQueue.Count() > 0)
{
::EnterCriticalSection(&m_csOutQueue);
// pop the first item in the queue
int headIndex = m_OutQueue.Head();
msg_t msg = m_OutQueue[headIndex];
m_OutQueue.Remove(headIndex);
::LeaveCriticalSection(&m_csOutQueue);
// run result
if (msg.replyTarget)
{
msg.replyTarget->SQLDBResponse(msg.cmd->GetID(), msg.returnState, msg.result, msg.cmd->GetReturnData());
// kill command
// it would be a good optimization to be able to reuse these
msg.cmd->deleteThis();
}
doneWork = true;
}
return doneWork;
}
//-----------------------------------------------------------------------------
// Purpose: load info - returns the number of sql db queries waiting to be processed
//-----------------------------------------------------------------------------
int CMySqlDatabase::QueriesInOutQueue()
{
// the queue names are from the DB point of view, not the server - thus the reversal
return m_InQueue.Count();
}
//-----------------------------------------------------------------------------
// Purpose: number of queries finished processing, waiting to be responded to
//-----------------------------------------------------------------------------
int CMySqlDatabase::QueriesInFinishedQueue()
{
return m_OutQueue.Count();
}