mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
246 lines
5.4 KiB
C++
246 lines
5.4 KiB
C++
#include "ht.h"
|
|
#include "base/messagequeue.h"
|
|
|
|
namespace wi {
|
|
|
|
//#define TIME_STEPPER
|
|
|
|
#ifdef TIME_STEPPER
|
|
// This makes time "stop" when in the debugger
|
|
class TimeStepper : public base::MessageHandler {
|
|
public:
|
|
TimeStepper() : m_ct(0), m_fInitialized(false) {}
|
|
|
|
long GetTickCount() {
|
|
if (!m_fInitialized) {
|
|
OneShot();
|
|
m_fInitialized = true;
|
|
}
|
|
return m_ct;
|
|
}
|
|
|
|
private:
|
|
void OneShot() {
|
|
base::Message msg;
|
|
msg.handler = this;
|
|
thread_.PostDelayed(&msg, 1);
|
|
}
|
|
|
|
virtual void OnMessage(base::Message *pmsg) {
|
|
m_ct++;
|
|
OneShot();
|
|
}
|
|
|
|
long m_ct;
|
|
bool m_fInitialized;
|
|
};
|
|
#endif
|
|
|
|
TimerMgr::TimerMgr()
|
|
{
|
|
m_fEnabled = true;
|
|
m_ptmrFirst = NULL;
|
|
m_ptmrNotifying = NULL;
|
|
m_ptmrTriggerNext = NULL;
|
|
m_fForceScan = true;
|
|
}
|
|
|
|
TimerMgr::~TimerMgr()
|
|
{
|
|
Assert(m_ptmrFirst == NULL);
|
|
}
|
|
|
|
long TimerMgr::GetTickCount()
|
|
{
|
|
#ifdef TIME_STEPPER
|
|
// Makes time stop while in the debugger, for easier debugging
|
|
|
|
static TimeStepper stepper;
|
|
return stepper.GetTickCount();
|
|
#else
|
|
return HostGetTickCount();
|
|
#endif
|
|
}
|
|
|
|
long TimerMgr::ScanDispatch(long tCurrent)
|
|
{
|
|
if (!m_fEnabled)
|
|
return -1;
|
|
|
|
// If currently scanning and being re-entered, disallow it.
|
|
|
|
Assert(m_ptmrNotifying == NULL);
|
|
if (m_ptmrNotifying != NULL)
|
|
return -1;
|
|
|
|
// If the timer state hasn't changed since the last scan, we can exit if not
|
|
// enough time has elapsed for a timer to go off.
|
|
|
|
if (!m_fForceScan) {
|
|
if (m_ptmrTriggerNext != NULL) {
|
|
long ctT = m_ptmrTriggerNext->m_tTrigger - tCurrent;
|
|
if (ctT > 0)
|
|
return ctT;
|
|
}
|
|
}
|
|
m_fForceScan = false;
|
|
|
|
// Scan timers to look for next timer to go off.
|
|
// Unfortunately these can't be sorted since the firing order is unique for any
|
|
// point in time.
|
|
|
|
Timer *ptmrT;
|
|
for (ptmrT = m_ptmrFirst; ptmrT != NULL; ) {
|
|
|
|
// If the timer has triggered, set its next trigger time to its current
|
|
// trigger time plus its rate. This is an attempt at keeping timing
|
|
// consistent and predictable. However, if this new time is less than
|
|
// or equal to the current real time we set it to the real time so we
|
|
// don't get stuck in a loop only processing timers.
|
|
|
|
bool fRemoved = false;
|
|
Timer *ptmrNext = ptmrT->m_ptmrNext;
|
|
|
|
if (ptmrT->m_tTrigger <= tCurrent) {
|
|
|
|
// Remember that this timer is notifying.
|
|
|
|
m_ptmrNotifying = ptmrT;
|
|
ptmrT->OnTimer(tCurrent);
|
|
m_ptmrNotifying = NULL;
|
|
|
|
fRemoved = ptmrT->m_tTrigger == -1;
|
|
|
|
if (!fRemoved) {
|
|
// Reset the rate after notification in case the rate was
|
|
// changed during notification.
|
|
|
|
ptmrT->m_tTrigger += ptmrT->m_ctRate;
|
|
|
|
// If the timer is falling behind real time catch it up
|
|
|
|
tCurrent = GetTickCount();
|
|
if (ptmrT->m_tTrigger < tCurrent)
|
|
ptmrT->m_tTrigger = tCurrent;
|
|
}
|
|
}
|
|
|
|
ptmrT = ptmrNext;
|
|
}
|
|
|
|
// Determine the next timer to trigger. This is done outside the
|
|
// above loop because timers, including the m_ptmrTriggerNext, may
|
|
// be removed during the OnTimer callback.
|
|
|
|
m_ptmrTriggerNext = m_ptmrFirst;
|
|
for (ptmrT = m_ptmrFirst; ptmrT != NULL; ptmrT = ptmrT->m_ptmrNext) {
|
|
if (ptmrT->m_tTrigger < m_ptmrTriggerNext->m_tTrigger)
|
|
m_ptmrTriggerNext = ptmrT;
|
|
}
|
|
|
|
// Return time till next timer
|
|
|
|
if (m_ptmrTriggerNext == NULL)
|
|
return -1;
|
|
long ct = tCurrent - m_ptmrTriggerNext->m_tTrigger;
|
|
if (ct < 0)
|
|
return 0;
|
|
return ct;
|
|
}
|
|
|
|
void TimerMgr::AddTimer(Timer *ptmr, long ct)
|
|
{
|
|
for (Timer *ptmrT = m_ptmrFirst; ptmrT != NULL; ptmrT = ptmrT->m_ptmrNext) {
|
|
Assert(ptmrT != ptmr, "Timer already added!");
|
|
if (ptmrT == ptmr)
|
|
return;
|
|
}
|
|
|
|
// Link it into the list
|
|
|
|
if (m_ptmrFirst != NULL)
|
|
m_ptmrFirst->m_ptmrPrev = ptmr;
|
|
ptmr->m_ptmrPrev = NULL;
|
|
|
|
ptmr->m_ptmrNext = m_ptmrFirst;
|
|
ptmr->m_ptimm = this;
|
|
m_ptmrFirst = ptmr;
|
|
|
|
SetTimerRate(ptmr, ct);
|
|
}
|
|
|
|
void TimerMgr::RemoveTimer(Timer *ptmr)
|
|
{
|
|
bool fFound = false;
|
|
for (Timer *ptmrT = m_ptmrFirst; ptmrT != NULL; ptmrT = ptmrT->m_ptmrNext) {
|
|
if (ptmrT == ptmr) {
|
|
fFound = true;
|
|
break;
|
|
}
|
|
}
|
|
Assert(fFound, "Timer not on list! (already removed?)");
|
|
if (!fFound)
|
|
return;
|
|
|
|
ptmr->m_tTrigger = -1;
|
|
ptmr->m_ptimm = NULL;
|
|
|
|
if (ptmr->m_ptmrPrev != NULL) {
|
|
ptmr->m_ptmrPrev->m_ptmrNext = ptmr->m_ptmrNext;
|
|
} else {
|
|
m_ptmrFirst = ptmr->m_ptmrNext;
|
|
}
|
|
|
|
if (ptmr->m_ptmrNext != NULL)
|
|
ptmr->m_ptmrNext->m_ptmrPrev = ptmr->m_ptmrPrev;
|
|
|
|
if (m_ptmrNotifying != ptmr && m_ptmrTriggerNext == ptmr) {
|
|
m_ptmrTriggerNext = NULL;
|
|
m_fForceScan = true;
|
|
}
|
|
}
|
|
|
|
void TimerMgr::SetTimerRate(Timer *ptmr, long ct)
|
|
{
|
|
// If the timer rate is decreasing, this may change the time till the next
|
|
// timer goes off, so force a scan next time.
|
|
|
|
ptmr->m_ctRate = ct;
|
|
ptmr->m_tTrigger = GetTickCount() + ct;
|
|
m_fForceScan = true;
|
|
}
|
|
|
|
void TimerMgr::BoostTimer(Timer *ptmr, long ct) {
|
|
ptmr->m_tTrigger += ct;
|
|
m_fForceScan = true;
|
|
}
|
|
|
|
// Force this timer to be dispatched at the next call to TimerMgr::ScanDispatch
|
|
// (presumably by EventMgr::GetEvent)
|
|
|
|
void TimerMgr::TriggerTimer(Timer *ptmr)
|
|
{
|
|
ptmr->m_tTrigger = GetTickCount();
|
|
m_fForceScan = true;
|
|
}
|
|
|
|
bool TimerMgr::IsAdded(Timer *ptmr)
|
|
{
|
|
Timer *ptmrT = m_ptmrFirst;
|
|
while (ptmrT != NULL) {
|
|
if (ptmrT == ptmr) {
|
|
break;
|
|
}
|
|
ptmrT = ptmrT->m_ptmrNext;
|
|
}
|
|
return ptmrT == ptmr;
|
|
}
|
|
|
|
Timer::~Timer() {
|
|
if (m_ptimm != NULL && m_ptimm->IsAdded(this)) {
|
|
Assert("Timer being destroyed, but still in list!");
|
|
}
|
|
}
|
|
|
|
} // namespace wi
|