//========= 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(); }