From 94c618d1533865e22f977e80a92f7cbc0ba36674 Mon Sep 17 00:00:00 2001 From: Tobias Zwick Date: Wed, 4 Dec 2013 19:35:07 +0700 Subject: [PATCH] add new type C4TimeMilliseconds for time measurements to solve problems when GetTime() overflows (#251) The new type C4TimeMilliseconds behaves for the most part like a uint32_t but is overflow-proof in comparisons. In some places, a 0-value (or uint_max) of the variable storing the time had the special meaning "not set yet". This has been resolved by having it as a pointer to C4TimeMilliseconds with NULL meaning that it has not been set yet. --- CMakeLists.txt | 1 + src/C4Include.h | 2 + src/control/C4PlayerControl.cpp | 6 +-- src/control/C4PlayerControl.h | 5 +- src/game/C4Application.cpp | 2 +- src/game/C4Application.h | 2 +- src/gamescript/C4GameScript.cpp | 2 +- src/gui/C4Gui.h | 16 +++--- src/gui/C4GuiLabels.cpp | 17 +++++-- src/landscape/C4LandscapeRender.cpp | 2 +- src/lib/C4Stat.h | 4 +- src/network/C4GameControlNetwork.cpp | 25 +++++++--- src/network/C4GameControlNetwork.h | 11 ++-- src/network/C4InteractiveThread.h | 2 +- src/network/C4NetIO.cpp | 66 ++++++++++++++++-------- src/network/C4NetIO.h | 16 +++--- src/network/C4Network2.cpp | 29 ++++++++--- src/network/C4Network2.h | 4 +- src/network/C4Network2IO.cpp | 58 ++++++++++++--------- src/network/C4Network2IO.h | 14 +++--- src/network/C4Network2Reference.cpp | 10 ++-- src/network/C4Network2Reference.h | 2 +- src/network/C4Packet2.cpp | 6 +-- src/platform/C4SoundSystem.h | 2 +- src/platform/C4TimeMilliseconds.h | 75 ++++++++++++++++++++++++++++ src/platform/GetTime.cpp | 12 ++--- src/platform/PlatformAbstraction.h | 4 +- src/platform/StdScheduler.cpp | 16 +++--- src/platform/StdScheduler.h | 36 +++++++++---- src/script/C4Aul.h | 8 +-- src/script/C4AulExec.cpp | 10 ++-- src/script/C4AulExec.h | 3 +- 32 files changed, 320 insertions(+), 148 deletions(-) create mode 100644 src/platform/C4TimeMilliseconds.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fcf529a2f..4d6fd6681 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -456,6 +456,7 @@ set(OC_CLONK_SOURCES src/platform/C4SoundLoaders.h src/platform/C4SoundSystem.cpp src/platform/C4SoundSystem.h + src/platform/C4TimeMilliseconds.h src/platform/C4StdInProc.cpp src/platform/C4StdInProc.h src/platform/C4Video.cpp diff --git a/src/C4Include.h b/src/C4Include.h index 494f801ee..fd24dbbee 100644 --- a/src/C4Include.h +++ b/src/C4Include.h @@ -108,4 +108,6 @@ inline void operator delete(void *p, const char *, long) #include "C4Game.h" +#include "C4TimeMilliseconds.h" + #endif // INC_C4Include diff --git a/src/control/C4PlayerControl.cpp b/src/control/C4PlayerControl.cpp index 3d36b7688..81389fcf2 100644 --- a/src/control/C4PlayerControl.cpp +++ b/src/control/C4PlayerControl.cpp @@ -362,7 +362,7 @@ bool C4PlayerControlAssignment::IsComboMatched(const C4PlayerControlRecentKeyLis // check if combo is currently fulfilled (assuming TriggerKey is already matched) if (fComboIsSequence) { - time_t tKeyLast = GetTime(); + C4TimeMilliseconds tKeyLast = GetTime(); // combo is a sequence: The last keys of RecentKeys must match the sequence // the last ComboKey is the TriggerKey, which is omitted because it has already been matched and is not to be found in RecentKeys yet KeyComboVec::const_reverse_iterator i = KeyCombo.rbegin()+1; @@ -372,7 +372,7 @@ bool C4PlayerControlAssignment::IsComboMatched(const C4PlayerControlRecentKeyLis if (ri == RecentKeys.rend()) return false; const C4PlayerControlRecentKey &rk = *ri; // user waited for too long? - time_t tKeyRecent = rk.tTime; + C4TimeMilliseconds tKeyRecent = rk.tTime; if (tKeyLast - tKeyRecent > C4PlayerControl::MaxSequenceKeyDelay) return false; // key doesn't match? const KeyComboItem &k = *i; @@ -1288,8 +1288,8 @@ void C4PlayerControl::Execute() } } // cleanup old recent keys + C4TimeMilliseconds tNow = GetTime(); C4PlayerControlRecentKeyList::iterator irk; - time_t tNow = GetTime(); for (irk = RecentKeys.begin(); irk != RecentKeys.end(); ++irk) { C4PlayerControlRecentKey &rk = *irk; diff --git a/src/control/C4PlayerControl.h b/src/control/C4PlayerControl.h index 7159d2e07..62ca3e6ed 100644 --- a/src/control/C4PlayerControl.h +++ b/src/control/C4PlayerControl.h @@ -25,6 +25,7 @@ #include "C4KeyboardInput.h" #include "C4LangStringTable.h" #include "C4Id.h" +#include "C4TimeMilliseconds.h" #include @@ -134,8 +135,8 @@ public: struct C4PlayerControlRecentKey { C4KeyCodeEx pressed_key, matched_key; - time_t tTime; - C4PlayerControlRecentKey(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key, time_t tTime) : pressed_key(pressed_key), matched_key(matched_key), tTime(tTime) {} + C4TimeMilliseconds tTime; + C4PlayerControlRecentKey(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key, C4TimeMilliseconds tTime) : pressed_key(pressed_key), matched_key(matched_key), tTime(tTime) {} bool operator ==(const C4KeyCodeEx &cmp) { return pressed_key==cmp; } // comparison op for finding items in lists: Search for the pressed key only }; diff --git a/src/game/C4Application.cpp b/src/game/C4Application.cpp index a61c49ad8..159823816 100644 --- a/src/game/C4Application.cpp +++ b/src/game/C4Application.cpp @@ -868,7 +868,7 @@ bool C4ApplicationGameTimer::Execute(int iTimeout, pollfd *) { // Check timer and reset if (!CheckAndReset()) return true; - time_t tNow = GetTime(); + C4TimeMilliseconds tNow = GetTime(); // Execute if (tNow >= tLastGameTick + iGameTickDelay || Game.GameGo) { diff --git a/src/game/C4Application.h b/src/game/C4Application.h index 4bf495856..d74ce9818 100644 --- a/src/game/C4Application.h +++ b/src/game/C4Application.h @@ -112,7 +112,7 @@ class C4ApplicationGameTimer : public CStdMultimediaTimerProc public: C4ApplicationGameTimer(); private: - time_t tLastGameTick; + C4TimeMilliseconds tLastGameTick; unsigned int iGameTickDelay; public: void SetGameTickDelay(uint32_t iDelay); diff --git a/src/gamescript/C4GameScript.cpp b/src/gamescript/C4GameScript.cpp index 472f793bb..59625bc8f 100644 --- a/src/gamescript/C4GameScript.cpp +++ b/src/gamescript/C4GameScript.cpp @@ -1479,7 +1479,7 @@ static long FnGetTime(C4PropList * _this) { // check network, record, etc if (::Control.SyncMode()) return 0; - return GetTime(); + return GetTime().AsInt(); } static C4Value FnSetPlrExtraData(C4PropList * _this, int iPlayer, C4String * DataName, const C4Value & Data) diff --git a/src/gui/C4Gui.h b/src/gui/C4Gui.h index 98ea42227..4709589f1 100644 --- a/src/gui/C4Gui.h +++ b/src/gui/C4Gui.h @@ -524,7 +524,9 @@ namespace C4GUI { private: time_t tAutoScrollDelay; // if set and text is longer than would fit, the label will automatically start moving if not changed and displayed for a while - time_t tLastChangeTime; // time when the label text was changed last. 0 if not initialized; set upon first drawing + + // Time when the label text was changed last. NULL if not initialized; set upon first drawing + C4TimeMilliseconds *tLastChangeTime; int32_t iScrollPos, iScrollDir; int32_t iRightIndent; protected: @@ -537,14 +539,16 @@ namespace C4GUI public: WoodenLabel(const char *szLblText, const C4Rect &rcBounds, DWORD dwFClr=0xffffffff, CStdFont *pFont=NULL, int32_t iAlign=ACenter, bool fMarkup=true) // ctor - : Label(szLblText, rcBounds, iAlign, dwFClr, pFont, true, true, fMarkup), tAutoScrollDelay(0), tLastChangeTime(0), iScrollPos(0), iScrollDir(0), iRightIndent(0) + : Label(szLblText, rcBounds, iAlign, dwFClr, pFont, true, true, fMarkup), tAutoScrollDelay(0), tLastChangeTime(NULL), iScrollPos(0), iScrollDir(0), iRightIndent(0) { SetAutosize(false); this->rcBounds=rcBounds; }// ctor - re-sets bounds after SetText + ~WoodenLabel() { delete tLastChangeTime; } static int32_t GetDefaultHeight(CStdFont *pUseFont=NULL); void SetIcon(const C4Facet &rfctIcon); void SetAutoScrollTime(time_t tDelay) { tAutoScrollDelay=tDelay; ResetAutoScroll(); } - void ResetAutoScroll() { tLastChangeTime=0; iScrollPos=iScrollDir=0; } + void ResetAutoScroll(); + void SetRightIndent(int32_t iNewIndent) { iRightIndent = iNewIndent; } }; @@ -1233,7 +1237,7 @@ namespace C4GUI int32_t iCursorPos; // cursor position: char, before which the cursor is located int32_t iSelectionStart, iSelectionEnd; // selection range (start may be larger than end) int32_t iMaxTextLength; // maximum number of characters to be input here - time_t tLastInputTime; // time of last input (for cursor flashing) + C4TimeMilliseconds tLastInputTime; // time of last input (for cursor flashing) int32_t iXScroll; // horizontal scrolling char cPasswordMask; // character to be used for masking the contents. 0 for none. @@ -1567,7 +1571,7 @@ namespace C4GUI int32_t iSheetSpacing, iSheetOff; // distances of sheet captions int32_t iCaptionLengthTotal, iCaptionScrollPos; // scrolling in captions (top only) bool fScrollingLeft, fScrollingRight, fScrollingLeftDown, fScrollingRightDown; // scrolling in captions (top only) - time_t tLastScrollTime; // set when fScrollingLeftDown or fScrollingRightDown are true: Time for next scrolling if mouse is held down + C4TimeMilliseconds tLastScrollTime; // set when fScrollingLeftDown or fScrollingRightDown are true: Time for next scrolling if mouse is held down int iSheetMargin; bool fDrawSelf; // if border and bg shall be drawn @@ -2448,7 +2452,7 @@ namespace C4GUI int32_t LDownX, LDownY; // position where left button was pressed last DWORD dwKeys; // shift, ctrl, etc. bool fActive; - time_t tLastMovementTime; // GetTime() when the mouse pos changed last + C4TimeMilliseconds tLastMovementTime; // GetTime() when the mouse pos changed last // whether last input was done by mouse // set to true whenever mouse pos changes or buttons are pressed diff --git a/src/gui/C4GuiLabels.cpp b/src/gui/C4GuiLabels.cpp index 52b0bedec..8ee58fd8b 100644 --- a/src/gui/C4GuiLabels.cpp +++ b/src/gui/C4GuiLabels.cpp @@ -153,10 +153,10 @@ namespace C4GUI if (iAlign == ALeft) iXOff += 5; if (tAutoScrollDelay) { - time_t tNow = GetTime(); + C4TimeMilliseconds tNow = GetTime(); if (!tLastChangeTime) - tLastChangeTime = tNow; - else if (tNow - tLastChangeTime >= tAutoScrollDelay) + tLastChangeTime = new C4TimeMilliseconds(tNow); + else if (tNow - *tLastChangeTime >= tAutoScrollDelay) { if (!iScrollDir) iScrollDir=1; int32_t iMaxScroll = Max(pFont->GetTextWidth(sText.getData(), true) + (x0 - rcBounds.x) + iXOff + GetRightIndent() - rcBounds.Wdt, 0); @@ -167,7 +167,7 @@ namespace C4GUI { iScrollDir = -iScrollDir; iScrollPos += iScrollDir; - tLastChangeTime = tNow; + *tLastChangeTime = tNow; } } } @@ -198,6 +198,15 @@ namespace C4GUI UpdateOwnPos(); } + void WoodenLabel::ResetAutoScroll() + { + if(tLastChangeTime) + { + delete tLastChangeTime; + tLastChangeTime=NULL; + } + iScrollPos = iScrollDir = 0; + } // -------------------------------------------------- // MultilineLabel diff --git a/src/landscape/C4LandscapeRender.cpp b/src/landscape/C4LandscapeRender.cpp index dbe0c6d0e..13b4c0c35 100644 --- a/src/landscape/C4LandscapeRender.cpp +++ b/src/landscape/C4LandscapeRender.cpp @@ -929,7 +929,7 @@ void C4LandscapeRenderGL::BuildMatMap(GLfloat *pFMap, GLubyte *pIMap) while(p = strchr(p, '-')) { p++; iPhases++; } // Hard-coded hack. Fix me! const int iPhaseLength = 300; - float phase = (iPhases == 1 ? 0 : float(GetTime() % (iPhases * iPhaseLength)) / iPhaseLength); + float phase = (iPhases == 1 ? 0 : float(GetTime().AsInt() % (iPhases * iPhaseLength)) / iPhaseLength); // Find our transition const char *pFrom = pEntry->GetTextureName(); diff --git a/src/lib/C4Stat.h b/src/lib/C4Stat.h index f7a7358b7..0b48d7fa2 100644 --- a/src/lib/C4Stat.h +++ b/src/lib/C4Stat.h @@ -71,7 +71,7 @@ public: iStartCalled --; if (!iStartCalled && iCount >= 100) { - time_t tTime = GetTime() - tStartTime; + uint32_t tTime = GetTime() - tStartTime; tTimeSum += tTime; tTimeSumPart += tTime; @@ -89,7 +89,7 @@ protected: C4Stat* pNext; C4Stat* pPrev; - time_t tStartTime; + C4TimeMilliseconds tStartTime; // start-call depth unsigned int iStartCalled; diff --git a/src/network/C4GameControlNetwork.cpp b/src/network/C4GameControlNetwork.cpp index feb66f44b..8b729fbcc 100644 --- a/src/network/C4GameControlNetwork.cpp +++ b/src/network/C4GameControlNetwork.cpp @@ -33,7 +33,7 @@ C4GameControlNetwork::C4GameControlNetwork(C4GameControl *pnParent) : fEnabled(false), fRunning(false), iClientID(C4ClientIDUnknown), fActivated(false), iTargetTick(-1), - iControlPreSend(1), tWaitStart(0), iAvgControlSendTime(0), iTargetFPS(38), + iControlPreSend(1), tWaitStart(NULL), iAvgControlSendTime(0), iTargetFPS(38), iControlSent(0), iControlReady(0), pCtrlStack(NULL), tNextControlRequest(0), @@ -64,6 +64,11 @@ bool C4GameControlNetwork::Init(int32_t inClientID, bool fnHost, int32_t iStartT fEnabled = true; fRunning = false; iTargetFPS = 38; tNextControlRequest = GetTime() + C4ControlRequestInterval; + if(tWaitStart) + { + delete tWaitStart; + tWaitStart = NULL; + } return true; } @@ -90,7 +95,7 @@ void C4GameControlNetwork::Execute() // by main thread // Save time the control tick was reached if (!tWaitStart) - tWaitStart = GetTime(); + tWaitStart = new C4TimeMilliseconds(GetTime()); // Execute any queued sync control ExecQueuedSyncCtrl(); @@ -117,7 +122,11 @@ bool C4GameControlNetwork::GetControl(C4Control *pCtrl, int32_t iTick) // by mai pCtrl->Append(pPkt->getControl()); // calc performance CalcPerformance(iTick); - tWaitStart = 0; + if(tWaitStart) + { + delete tWaitStart; + tWaitStart = NULL; + } // ok return true; } @@ -420,9 +429,9 @@ void C4GameControlNetwork::CalcPerformance(int32_t iCtrlTick) // Performance statistics // find control (may not be found, if we only got the complete ctrl) C4GameControlPacket *pCtrl = getCtrl(pClient->getClientID(), iCtrlTick); - if (!pCtrl) continue; + if (!pCtrl || !tWaitStart) continue; // calc stats - pClient->AddPerf(pCtrl->getTime() - tWaitStart); + pClient->AddPerf(pCtrl->getTime() - *tWaitStart); } // Now do PreSend-calcs based on ping times int32_t iControlSendTime; @@ -765,7 +774,7 @@ C4GameControlPacket *C4GameControlNetwork::PackCompleteCtrl(int32_t iTick) { // async mode: wait n extra frames for slow clients const int iMaxWait = (Config.Network.AsyncMaxWait * 1000) / iTargetFPS; - if (eMode != CNM_Async || !tWaitStart || GetTime() <= tWaitStart + iMaxWait) + if (eMode != CNM_Async || !tWaitStart || GetTime() <= *tWaitStart + iMaxWait) return NULL; } @@ -795,8 +804,8 @@ C4GameControlPacket *C4GameControlNetwork::PackCompleteCtrl(int32_t iTick) if (eMode != CNM_Decentral) ::Network.Clients.BroadcastMsgToConnClients(MkC4NetIOPacket(PID_Control, *pComplete)); - // advance control request time (note: this is buggy if time_t values overflow after ~1 month on 32Bit Windows) - tNextControlRequest = Max(tNextControlRequest, time_t(GetTime() + C4ControlRequestInterval)); + // advance control request time + tNextControlRequest = Max(tNextControlRequest, GetTime() + C4ControlRequestInterval); // return return pComplete; diff --git a/src/network/C4GameControlNetwork.h b/src/network/C4GameControlNetwork.h index ed302ca29..0e9395f1c 100644 --- a/src/network/C4GameControlNetwork.h +++ b/src/network/C4GameControlNetwork.h @@ -64,7 +64,10 @@ protected: volatile int32_t iControlPreSend; // statistics - time_t tWaitStart; + + + // time started to wait. NULL if not set yet + C4TimeMilliseconds *tWaitStart; int32_t iAvgControlSendTime; int32_t iTargetFPS; // used for PreSend-colculation @@ -85,7 +88,7 @@ protected: C4GameControlPacket *pSyncCtrlQueue; // control request timing - time_t tNextControlRequest; + C4TimeMilliseconds tNextControlRequest; // links C4GameControl *const pParent; @@ -182,7 +185,7 @@ public: protected: // header int32_t iClientID, iCtrlTick; - time_t tTime; + C4TimeMilliseconds tTime; // data C4Control Ctrl; @@ -193,7 +196,7 @@ protected: public: int32_t getClientID() const { return iClientID; } int32_t getCtrlTick() const { return iCtrlTick; } - time_t getTime() const { return tTime; } + C4TimeMilliseconds getTime() const { return tTime; } const C4Control &getControl() const { return Ctrl; } void Set(int32_t iClientID, int32_t iCtrlTick); diff --git a/src/network/C4InteractiveThread.h b/src/network/C4InteractiveThread.h index 115529373..ebc114787 100644 --- a/src/network/C4InteractiveThread.h +++ b/src/network/C4InteractiveThread.h @@ -85,7 +85,7 @@ private: C4InteractiveEventType Type; void *Data; #ifdef _DEBUG - time_t Time; + C4TimeMilliseconds Time; #endif Event *Next; }; diff --git a/src/network/C4NetIO.cpp b/src/network/C4NetIO.cpp index a58248efc..3a8b880d8 100644 --- a/src/network/C4NetIO.cpp +++ b/src/network/C4NetIO.cpp @@ -1795,7 +1795,7 @@ C4NetIOUDP::C4NetIOUDP() pPeerList(NULL), fSavePacket(false), fDelayedLoopbackTest(false), - tNextCheck(0), + tNextCheck(NULL), OPackets(iMaxOPacketBacklog), iOPacketCounter(0), iBroadcastRate(0) @@ -1805,6 +1805,7 @@ C4NetIOUDP::C4NetIOUDP() C4NetIOUDP::~C4NetIOUDP() { + delete tNextCheck; Close(); } @@ -1829,7 +1830,9 @@ bool C4NetIOUDP::Init(uint16_t inPort) // set flags fInit = true; fMultiCast = false; - tNextCheck = GetTime() + iCheckInterval; + + if(!tNextCheck) tNextCheck = new C4TimeMilliseconds(); + *tNextCheck = GetTime() + iCheckInterval; // ok, that's all for now. // call InitBroadcast for more initialization fun @@ -2001,17 +2004,18 @@ bool C4NetIOUDP::Execute(int iMaxTime, pollfd *) // (mt-safe) ResetError(); // adjust maximum block time - time_t tNow = GetTime(); - int iMaxBlock = GetNextTick(tNow) - tNow; - if (iMaxTime == TO_INF || iMaxTime > iMaxBlock) iMaxTime = iMaxBlock; + C4TimeMilliseconds tNow = GetTime(); + uint32_t iMaxBlock = GetNextTick(tNow) - tNow; + if (iMaxTime == TO_INF || iMaxTime > (int) iMaxBlock) iMaxTime = iMaxBlock; // execute subclass if (!C4NetIOSimpleUDP::Execute(iMaxTime)) return false; // connection check needed? - if (tNextCheck <= GetTime()) - DoCheck(); + if (tNextCheck) + if (*tNextCheck <= GetTime()) + DoCheck(); // client timeout? for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next) if (!pPeer->Closed()) @@ -2095,16 +2099,17 @@ bool C4NetIOUDP::SetBroadcast(const addr_t &addr, bool fSet) // (mt-safe) return true; } -time_t C4NetIOUDP::GetNextTick(time_t tNow) // (mt-safe) +C4TimeMilliseconds C4NetIOUDP::GetNextTick(C4TimeMilliseconds tNow) // (mt-safe) { // maximum time: check interval - time_t tTiming = Max(tNow, tNextCheck); + C4TimeMilliseconds tTiming = tNextCheck ? Max(tNow, *tNextCheck): tNow; + // client timeouts (e.g. connection timeout) CStdShareLock PeerListLock(&PeerListCSec); for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next) if (!pPeer->Closed()) - if (pPeer->GetTimeout() > 0) - tTiming = Min(tTiming, pPeer->GetTimeout()); + if (pPeer->GetTimeout()) + tTiming = Min(tTiming, *pPeer->GetTimeout()); // return timing value return tTiming; } @@ -2485,7 +2490,7 @@ C4NetIOUDP::Peer::Peer(const sockaddr_in &naddr, C4NetIOUDP *pnParent) iIPacketCounter(0), iRIPacketCounter(0), iIMCPacketCounter(0), iRIMCPacketCounter(0), iMCAckPacketCounter(0), - tNextReCheck(0), + tNextReCheck(NULL), iIRate(0), iORate(0), iLoss(0) { ZeroMem(&addr2, sizeof(addr2)); @@ -2494,6 +2499,8 @@ C4NetIOUDP::Peer::Peer(const sockaddr_in &naddr, C4NetIOUDP *pnParent) C4NetIOUDP::Peer::~Peer() { + delete tNextReCheck; + delete tTimeout; // send close-packet Close("deleted"); } @@ -2533,7 +2540,7 @@ bool C4NetIOUDP::Peer::Check(bool fForceCheck) if (eStatus != CS_Works) return true; // prevent re-check (check floods) // instead, ask for other packets that are missing until recheck is allowed - bool fNoReCheck = !!tNextReCheck && tNextReCheck > GetTime(); + bool fNoReCheck = !!tNextReCheck && *tNextReCheck > GetTime(); if (!fNoReCheck) iLastPacketAsked = iLastMCPacketAsked = 0; unsigned int iStartAt = fNoReCheck ? Max(iLastPacketAsked + 1, iIPacketCounter) : iIPacketCounter; unsigned int iStartAtMC = fNoReCheck ? Max(iLastMCPacketAsked + 1, iIMCPacketCounter) : iIMCPacketCounter; @@ -2551,7 +2558,10 @@ bool C4NetIOUDP::Peer::Check(bool fForceCheck) int iEAskCnt = iAskCnt + iMCAskCnt; // no re-check limit? set it if (!fNoReCheck) - tNextReCheck = iEAskCnt ? GetTime() + iReCheckInterval : 0; + { + if(!tNextReCheck) tNextReCheck = new C4TimeMilliseconds(); + *tNextReCheck = iEAskCnt ? GetTime() + iReCheckInterval : 0; + } // something to ask for? (or check forced?) if (iEAskCnt || fForceCheck) return DoCheck(iAskCnt, iMCAskCnt, iAskList); @@ -2616,7 +2626,15 @@ void C4NetIOUDP::Peer::OnRecv(const C4NetIOPacket &rPacket) // (mt-safe) else iRIPacketCounter = iIPacketCounter = pPkt->Nr; // clear incoming packets - IPackets.Clear(); IMCPackets.Clear(); tNextReCheck = 0; + IPackets.Clear(); + IMCPackets.Clear(); + + if(tNextReCheck) + { + delete tNextReCheck; + tNextReCheck = NULL; + } + iLastPacketAsked = iLastMCPacketAsked = 0; // Activate Multicast? if (!pParent->fMultiCast) @@ -2777,7 +2795,7 @@ void C4NetIOUDP::Peer::CheckTimeout() // timeout set? if (!tTimeout) return; // check - if (GetTime() > tTimeout) + if (GetTime() > *tTimeout) OnTimeout(); } @@ -2926,9 +2944,15 @@ void C4NetIOUDP::Peer::CheckCompleteIPackets() void C4NetIOUDP::Peer::SetTimeout(int iLength, int iRetryCnt) // (mt-safe) { if (iLength != TO_INF) - tTimeout = GetTime() + iLength; + { + if(!tTimeout) tTimeout = new C4TimeMilliseconds(); + *tTimeout = GetTime() + iLength; + } else - tTimeout = 0; + { + delete tTimeout; + tTimeout = NULL; + } iRetries = iRetryCnt; } @@ -3144,8 +3168,10 @@ void C4NetIOUDP::DoCheck() // (mt-safe) for (Peer *pPeer = pPeerList; pPeer; pPeer = pPeer->Next) if (pPeer->Open()) pPeer->Check(); + // set time for next check - tNextCheck = GetTime() + iCheckInterval; + if(!tNextCheck) tNextCheck = new C4TimeMilliseconds(); + *tNextCheck = GetTime() + iCheckInterval; } // debug @@ -3193,7 +3219,7 @@ void C4NetIOUDP::CloseDebugLog() void C4NetIOUDP::DebugLogPkt(bool fOut, const C4NetIOPacket &Pkt) { StdStrBuf O; - time_t tTime = GetTime(); + uint32_t tTime = GetTime().AsInt(); O.Format("%s %u:%02u:%02u:%03u %s:%d:", fOut ? "out" : "in ", (tTime / 1000 / 60 / 60), (tTime / 1000 / 60) % 60, (tTime / 1000) % 60, tTime % 1000, inet_ntoa(Pkt.getAddr().sin_addr), htons(Pkt.getAddr().sin_port)); diff --git a/src/network/C4NetIO.h b/src/network/C4NetIO.h index ebd9fe3f8..96bf052f5 100644 --- a/src/network/C4NetIO.h +++ b/src/network/C4NetIO.h @@ -458,7 +458,7 @@ public: virtual bool Broadcast(const C4NetIOPacket &rPacket); virtual bool SetBroadcast(const addr_t &addr, bool fSet = true); - virtual time_t GetNextTick(time_t tNow); + virtual C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow); virtual bool IsScheduledExecution() { return true; } virtual bool GetStatistic(int *pBroadcastRate); @@ -620,12 +620,12 @@ protected: // output critical section CStdCSec OutCSec; - // connection check time limit - time_t tNextReCheck; + // connection check time limit. NULL if no recheck time yet + C4TimeMilliseconds *tNextReCheck; unsigned int iLastPacketAsked, iLastMCPacketAsked; - // timeout - time_t tTimeout; + // timeout time. NULL if no timeout time set yet + C4TimeMilliseconds *tTimeout; unsigned int iRetries; // statistics @@ -661,7 +661,7 @@ protected: unsigned int GetMCAckPacketCounter() const { return iMCAckPacketCounter; } // timeout checking - time_t GetTimeout() { return tTimeout; } + C4TimeMilliseconds *GetTimeout() { return tTimeout; } void CheckTimeout(); // selected for broadcast? @@ -726,8 +726,8 @@ protected: addr_t MCLoopbackAddr; bool fDelayedLoopbackTest; - // check timing - time_t tNextCheck; + // check timing. NULL if no next check yet + C4TimeMilliseconds *tNextCheck; // outgoing packet list (for multicast) PacketList OPackets; diff --git a/src/network/C4Network2.cpp b/src/network/C4Network2.cpp index c033f8ed5..ace089e57 100644 --- a/src/network/C4Network2.cpp +++ b/src/network/C4Network2.cpp @@ -146,7 +146,7 @@ C4Network2::C4Network2() pLobby(NULL), fLobbyRunning(false), pLobbyCountdown(NULL), iNextClientID(0), iLastChaseTargetUpdate(0), - tLastActivateRequest(0), + tLastActivateRequest(NULL), iLastReferenceUpdate(0), iLastLeagueUpdate(0), pLeagueClient(NULL), @@ -664,7 +664,12 @@ void C4Network2::Clear() // stuff fAllowJoin = false; iDynamicTick = -1; fDynamicNeeded = false; - tLastActivateRequest = iLastChaseTargetUpdate = iLastReferenceUpdate = iLastLeagueUpdate = 0; + if(tLastActivateRequest) + { + delete tLastActivateRequest; + tLastActivateRequest = NULL; + } + iLastChaseTargetUpdate = iLastReferenceUpdate = iLastLeagueUpdate = 0; fDelayedActivateReq = false; delete pVoteDialog; pVoteDialog = NULL; fPausedForVote = false; @@ -1503,7 +1508,7 @@ C4Network2Res::Ref C4Network2::RetrieveRes(const C4Network2ResCore &Core, int32_ C4GUI::ProgressDialog *pDlg = NULL; bool fLog = false; int32_t iProcess = -1; - time_t tTimeout = GetTime() + iTimeoutLen; + C4TimeMilliseconds tTimeout = GetTime() + iTimeoutLen; // wait for resource while (isEnabled()) { @@ -1511,6 +1516,7 @@ C4Network2Res::Ref C4Network2::RetrieveRes(const C4Network2ResCore &Core, int32_ C4Network2Res::Ref pRes = ResList.getRefRes(Core.getID()); // res not found? if (!pRes) + { if (Core.isNull()) { // should wait for core? @@ -1521,6 +1527,7 @@ C4Network2Res::Ref C4Network2::RetrieveRes(const C4Network2ResCore &Core, int32_ // start loading pRes = ResList.AddByCore(Core); } + } // res found and loaded completely else if (!pRes->isLoading()) { @@ -1762,7 +1769,11 @@ void C4Network2::RequestActivate() // neither observer nor activated? if (Game.Clients.getLocal()->isObserver() || Game.Clients.getLocal()->isActivated()) { - tLastActivateRequest = 0; + if(tLastActivateRequest) + { + delete tLastActivateRequest; + tLastActivateRequest = NULL; + } return; } // host? just do it @@ -1775,8 +1786,9 @@ void C4Network2::RequestActivate() return; } // ensure interval - if (tLastActivateRequest && GetTime() < tLastActivateRequest + C4NetActivationReqInterval) - return; + if (tLastActivateRequest) + if(GetTime() < *tLastActivateRequest + C4NetActivationReqInterval) + return; // status not reached yet? May be chasing, let's delay this. if (!fStatusReached) { @@ -1786,7 +1798,10 @@ void C4Network2::RequestActivate() // request Clients.SendMsgToHost(MkC4NetIOPacket(PID_ClientActReq, C4PacketActivateReq(Game.FrameCounter))); // store time - tLastActivateRequest = GetTime(); + if(!tLastActivateRequest) + tLastActivateRequest = new C4TimeMilliseconds(GetTime()); + else + *tLastActivateRequest = GetTime(); } void C4Network2::DeactivateInactiveClients() diff --git a/src/network/C4Network2.h b/src/network/C4Network2.h index 5269cbffc..8da0b8b0f 100644 --- a/src/network/C4Network2.h +++ b/src/network/C4Network2.h @@ -166,8 +166,8 @@ protected: // chase uint32_t iLastChaseTargetUpdate; - // activation - time_t tLastActivateRequest; + // time of last activation request. NULL if no last request + C4TimeMilliseconds *tLastActivateRequest; // reference uint32_t iLastReferenceUpdate; diff --git a/src/network/C4Network2IO.cpp b/src/network/C4Network2IO.cpp index 857636a63..c41e61a34 100644 --- a/src/network/C4Network2IO.cpp +++ b/src/network/C4Network2IO.cpp @@ -506,7 +506,7 @@ bool C4Network2IO::OnConn(const C4NetIO::addr_t &PeerAddr, const C4NetIO::addr_t return false; } #if(C4NET2IO_DUMP_LEVEL > 1) - time_t tTime = GetTime(); + uint32_t tTime = GetTime().AsInt(); ThreadLogS("OnConn: %u:%02u:%02u:%03u: %s", (tTime / 1000 / 60 / 60), (tTime / 1000 / 60) % 60, (tTime / 1000) % 60, tTime % 1000, getNetIOName(pNetIO)); @@ -561,7 +561,7 @@ void C4Network2IO::OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const return; } #if(C4NET2IO_DUMP_LEVEL > 1) - time_t tTime = GetTime(); + uint32_t tTime = GetTime().AsInt(); ThreadLogS("OnDisconn: %u:%02u:%02u:%03u: %s", (tTime / 1000 / 60 / 60), (tTime / 1000 / 60) % 60, (tTime / 1000) % 60, tTime % 1000, getNetIOName(pNetIO)); @@ -591,7 +591,7 @@ void C4Network2IO::OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const void C4Network2IO::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO) { #if(C4NET2IO_DUMP_LEVEL > 1) - time_t tTime = GetTime(); + uint32_t tTime = GetTime().AsInt(); ThreadLogS("OnPacket: %u:%02u:%02u:%03u: status %02x %s", (tTime / 1000 / 60 / 60), (tTime / 1000 / 60) % 60, (tTime / 1000) % 60, tTime % 1000, rPacket.getStatus(), getNetIOName(pNetIO)); @@ -629,14 +629,14 @@ bool C4Network2IO::Execute(int iTimeout, pollfd *) CheckTimeout(); // ping all open connections - if (!Inside(tLastPing, GetTime() - C4NetPingFreq, GetTime())) + if (!Inside(tLastPing, tLastExecute - C4NetPingFreq, tLastExecute)) { Ping(); tLastPing = tLastExecute; } // do statistics - if (!Inside(tLastStatistic, GetTime() - C4NetStatisticsFreq, GetTime())) + if (!Inside(tLastStatistic, tLastExecute - C4NetStatisticsFreq, tLastExecute)) { GenerateStatistics(tLastExecute - tLastStatistic); tLastStatistic = tLastExecute; @@ -649,7 +649,7 @@ bool C4Network2IO::Execute(int iTimeout, pollfd *) return true; } -time_t C4Network2IO::GetNextTick(time_t tNow) +C4TimeMilliseconds C4Network2IO::GetNextTick(C4TimeMilliseconds tNow) { return tLastExecute + C4NetTimer; } @@ -845,7 +845,7 @@ bool C4Network2IO::HandlePacket(const C4NetIOPacket &rPacket, C4Network2IOConnec #if(C4NET2IO_DUMP_LEVEL > 0) if (fThread && Pkt.getPktType() != PID_Ping && Pkt.getPktType() != PID_Pong && Pkt.getPktType() != PID_NetResData) { - time_t tTime = GetTime(); + uint32_t tTime = GetTime().AsInt(); // StdStrBuf PacketDump = DecompileToBuf(mkNamingAdaptrPacket); StdStrBuf PacketHeader = FormatString("HandlePacket: %u:%02u:%02u:%03u by %s:%d (%lu bytes, counter %d)", tTime / 1000 / 60 / 60, (tTime / 1000 / 60) % 60, (tTime / 1000) % 60, tTime % 1000, @@ -870,15 +870,18 @@ bool C4Network2IO::HandlePacket(const C4NetIOPacket &rPacket, C4Network2IOConnec { fHandled = true; #if(C4NET2IO_DUMP_LEVEL > 2) - time_t tStart = GetTime(); + C4TimeMilliseconds tStart = GetTime(); #endif // call handler(s) CallHandlers(pHData->HandlerID, &Pkt, pConn, fThread); #if(C4NET2IO_DUMP_LEVEL > 2) - if (fThread && GetTime() - tStart > 100) - ThreadLogS("HandlePacket: ... blocked for %d ms!", GetTime() - tStart); + uint32_t iBlockedTime = GetTime() - tStart; + if (fThread && iBlockedTime > 100) + { + ThreadLogS("HandlePacket: ... blocked for %u ms!", iBlockedTime); + } #endif } @@ -1209,8 +1212,7 @@ void C4Network2IO::CheckTimeout() void C4Network2IO::GenerateStatistics(int iInterval) { - int iTCPIRateSum = 0, iTCPORateSum = 0, - iUDPIRateSum = 0, iUDPORateSum = 0; + int iTCPIRateSum = 0, iTCPORateSum = 0, iUDPIRateSum = 0, iUDPORateSum = 0; // acquire lock, get connection statistics CStdLock ConnListLock(&ConnListCSec); @@ -1303,7 +1305,7 @@ C4Network2IOConnection::C4Network2IOConnection() fBroadcastTarget(false), iTimestamp(0), iPingTime(-1), - tLastPing(ULONG_MAX), tLastPong(ULONG_MAX), + tLastPing(NULL), tLastPong(NULL), fConnSent(false), fPostMortemSent(false), iOutPacketCounter(0), iInPacketCounter(0), @@ -1320,17 +1322,23 @@ C4Network2IOConnection::~C4Network2IOConnection() if (pNetClass && !isClosed()) Close(); // clear the packet log ClearPacketLog(); + + delete tLastPing; + delete tLastPong; } int C4Network2IOConnection::getLag() const { - // Last ping not answered yet? - if (iPingTime != -1 && tLastPing != ULONG_MAX && (tLastPong == ~0u || tLastPing > tLastPong)) + if (iPingTime != -1) { - int iPingLag = GetTime() - tLastPing; - // Use it for lag measurement once it's larger then the last ping time - // (the ping time won't be better than this anyway once the pong's here) - return Max(iPingLag, iPingTime); + // Last ping not answered yet? + if(tLastPing && (!tLastPong || *tLastPing > *tLastPong)) + { + int iPingLag = GetTime() - *tLastPing; + // Use it for lag measurement once it's larger then the last ping time + // (the ping time won't be better than this anyway once the pong's here) + return Max(iPingLag, iPingTime); + } } // Last ping result return iPingTime; @@ -1363,10 +1371,13 @@ void C4Network2IOConnection::SetPeerAddr(const C4NetIO::addr_t &nPeerAddr) void C4Network2IOConnection::OnPing() { // Still no pong for the last ping? - if (tLastPong < tLastPing) - return; + if(tLastPing && tLastPong) + if (*tLastPong < *tLastPing) + return; + // Save time - tLastPing = GetTime(); + if(!tLastPing) tLastPing = new C4TimeMilliseconds(); + *tLastPing = GetTime(); } void C4Network2IOConnection::SetPingTime(int inPingTime) @@ -1374,7 +1385,8 @@ void C4Network2IOConnection::SetPingTime(int inPingTime) // save it iPingTime = inPingTime; // pong received - save timestamp - tLastPong = GetTime(); + if(!tLastPong) tLastPong = new C4TimeMilliseconds(); + *tLastPong = GetTime(); } void C4Network2IOConnection::SetStatus(C4Network2IOConnStatus nStatus) diff --git a/src/network/C4Network2IO.h b/src/network/C4Network2IO.h index 04238a35a..4a45016b7 100644 --- a/src/network/C4Network2IO.h +++ b/src/network/C4Network2IO.h @@ -89,11 +89,11 @@ protected: bool fExclusiveConn; // timer & ping - time_t tLastExecute; - time_t tLastPing; + C4TimeMilliseconds tLastExecute; + C4TimeMilliseconds tLastPing; // statistics - time_t tLastStatistic; + C4TimeMilliseconds tLastStatistic; int iTCPIRate, iTCPORate, iTCPBCRate, iUDPIRate, iUDPORate, iUDPBCRate; @@ -163,7 +163,7 @@ protected: virtual void OnError(const char *strError, C4NetIO *pNetIO); // StdSchedulerProc virtual bool Execute(int iTimeout, pollfd *); - virtual time_t GetNextTick(time_t Now); + virtual C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow); virtual bool IsScheduledExecution() { return true; } // Event callback by C4InteractiveThread void OnThreadEvent(C4InteractiveEventType eEvent, void *pEventData); // by main thread @@ -231,8 +231,8 @@ protected: bool fBroadcastTarget; // broadcast target? time_t iTimestamp; // timestamp of last status change int iPingTime; // ping - time_t tLastPing; // if > iLastPong, it's the first ping that hasn't been answered yet - time_t tLastPong; // last pong received + C4TimeMilliseconds *tLastPing; // if > iLastPong, it's the first ping that hasn't been answered yet, NULL if no ping received yet + C4TimeMilliseconds *tLastPong; // last pong received, NULL if no pong received yet C4ClientCore CCore; // client core (>= CS_HalfAccepted) CStdCSec CCoreCSec; int iIRate, iORate; // input/output rates (by C4NetIO, in b/s) @@ -335,7 +335,7 @@ public: C4PacketPing(uint32_t iPacketCounter = 0, uint32_t iRemotePacketCounter = 0); protected: - time_t tTime; + C4TimeMilliseconds tTime; uint32_t iPacketCounter; public: diff --git a/src/network/C4Network2Reference.cpp b/src/network/C4Network2Reference.cpp index 8e1ddf056..dcab62aaf 100644 --- a/src/network/C4Network2Reference.cpp +++ b/src/network/C4Network2Reference.cpp @@ -430,15 +430,15 @@ bool C4Network2HTTPClient::Execute(int iMaxTime) return C4NetIOTCP::Execute(iMaxTime); } -time_t C4Network2HTTPClient::GetNextTick(time_t tNow) +C4TimeMilliseconds C4Network2HTTPClient::GetNextTick(C4TimeMilliseconds tNow) { - time_t iNetIOTCPTick = C4NetIOTCP::GetNextTick(tNow); + C4TimeMilliseconds tNetIOTCPTick = C4NetIOTCP::GetNextTick(tNow); if (!fBusy) - return iNetIOTCPTick; + return tNetIOTCPTick; - time_t iHTTPClientTick = tNow + 1000 * Max(iRequestTimeout - time(NULL), 0); + C4TimeMilliseconds tHTTPClientTick = tNow + 1000 * Max(iRequestTimeout - time(NULL), 0); - return Max(iNetIOTCPTick, iHTTPClientTick); + return Max(tNetIOTCPTick, tHTTPClientTick); } bool C4Network2HTTPClient::IsScheduledExecution() diff --git a/src/network/C4Network2Reference.h b/src/network/C4Network2Reference.h index 37328e20a..72df28446 100644 --- a/src/network/C4Network2Reference.h +++ b/src/network/C4Network2Reference.h @@ -181,7 +181,7 @@ public: // Overridden virtual bool Execute(int iMaxTime, pollfd * readyfds) { return Execute(iMaxTime); } virtual bool Execute(int iMaxTime = TO_INF); - virtual time_t GetNextTick(time_t tNow); + virtual C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow); virtual bool IsScheduledExecution(); private: diff --git a/src/network/C4Packet2.cpp b/src/network/C4Packet2.cpp index 501d47909..a5d517d4b 100644 --- a/src/network/C4Packet2.cpp +++ b/src/network/C4Packet2.cpp @@ -525,11 +525,9 @@ uint32_t C4PacketPing::getTravelTime() const void C4PacketPing::CompileFunc(StdCompiler *pComp) { - // FIXME: the compiler can't compile 64bit integers (yet), the ping will - // return wrong times if GetTime() returns integers too large for uint32 - uint32_t time = tTime; + uint32_t time = tTime.AsInt(); pComp->Value(mkNamingAdapt(time, "Time", 0U)); - tTime = time; + tTime = C4TimeMilliseconds(time); pComp->Value(mkNamingAdapt(iPacketCounter, "PacketCounter", 0U)); } diff --git a/src/platform/C4SoundSystem.h b/src/platform/C4SoundSystem.h index 6e9521d14..f805b6422 100644 --- a/src/platform/C4SoundSystem.h +++ b/src/platform/C4SoundSystem.h @@ -90,7 +90,7 @@ public: protected: C4SoundEffect *pEffect; int32_t iVolume, iPan, iChannel; - time_t tStarted; + C4TimeMilliseconds tStarted; int32_t iNearInstanceMax; bool fLooping; C4Object *pObj; diff --git a/src/platform/C4TimeMilliseconds.h b/src/platform/C4TimeMilliseconds.h new file mode 100644 index 000000000..e45c0d34e --- /dev/null +++ b/src/platform/C4TimeMilliseconds.h @@ -0,0 +1,75 @@ +/* + * OpenClonk, http://www.openclonk.org + * + * Copyright (c) 2013 Tobias Zwick + * + * Portions might be copyrighted by other authors who have contributed + * to OpenClonk. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * See isc_license.txt for full license and disclaimer. + * + * "Clonk" is a registered trademark of Matthes Bender. + * See clonk_trademark_license.txt for full license. + */ +#ifndef INC_C4TimeMilliseconds +#define INC_C4TimeMilliseconds + +#include "PlatformAbstraction.h" + +/* Class to store times in milliseconds for measurement purposes. + + This class behaves for the most part like an unsigned integer, with the + difference that it handles an overflow correctly. For example: + C4TimeMilliseconds start = UINT32_MAX-10; + C4TimeMilliseconds stop = 10; + start < stop returns true. stop - start is 20. + + A commonly used operation is to measure the time difference between two + C4TimeMilliseconds, that is why if two C4TimeMilliseconds are subtracted from another, + the return value is of the type uint32_t. + + Otherwise, there should be no use case other than for printing, or packing/ + unpacking for network to have a uint32_t representation of this. You can use + AsInt() for that. */ +class C4TimeMilliseconds +{ +private: + uint32_t time; + +public: + C4TimeMilliseconds() : time(0) { } + C4TimeMilliseconds(uint32_t millis) : time(millis) { } + C4TimeMilliseconds(const C4TimeMilliseconds& rhs) : time(rhs.time) { } + ~C4TimeMilliseconds() { } + + uint32_t AsInt() const { return time; } + + C4TimeMilliseconds& operator=(const C4TimeMilliseconds& rhs) { time = rhs.time; return *this; } + + inline C4TimeMilliseconds& operator-=(const uint32_t& rhs) { time -= rhs; return *this; } + inline C4TimeMilliseconds& operator+=(const uint32_t& rhs) { time += rhs; return *this; } + +}; + +inline bool operator==( const C4TimeMilliseconds& lhs, const C4TimeMilliseconds& rhs ) { return lhs.AsInt() == rhs.AsInt(); } +inline bool operator!=( const C4TimeMilliseconds& lhs, const C4TimeMilliseconds& rhs ) { return !(lhs == rhs); } +inline bool operator<( const C4TimeMilliseconds& lhs, const C4TimeMilliseconds& rhs ) { return int32_t(lhs.AsInt() - rhs.AsInt()) < 0; } +inline bool operator>( const C4TimeMilliseconds& lhs, const C4TimeMilliseconds& rhs ) { return rhs < lhs; } +inline bool operator<=( const C4TimeMilliseconds& lhs, const C4TimeMilliseconds& rhs ) { return !(lhs > rhs); } +inline bool operator>=( const C4TimeMilliseconds& lhs, const C4TimeMilliseconds& rhs ) { return !(lhs < rhs); } + +inline uint32_t operator-(const C4TimeMilliseconds& lhs, const C4TimeMilliseconds& rhs) { return lhs.AsInt() - rhs.AsInt(); } + +inline C4TimeMilliseconds operator+(C4TimeMilliseconds lhs, const uint32_t& rhs) { lhs += rhs; return lhs; } +inline C4TimeMilliseconds operator-(C4TimeMilliseconds lhs, const uint32_t& rhs) { lhs -= rhs; return lhs; } + +/* the following operations make no sense and should rather be not defined to + throw a compiler error than being defined for the sake of it */ + +//inline uint32_t operator+(const C4TimeMilliseconds& lhs, const C4TimeMilliseconds& rhs) { return lhs.AsInt() + rhs.AsInt(); } + + +#endif diff --git a/src/platform/GetTime.cpp b/src/platform/GetTime.cpp index 904e446c6..4370d7644 100644 --- a/src/platform/GetTime.cpp +++ b/src/platform/GetTime.cpp @@ -18,15 +18,16 @@ */ #include "C4Include.h" +#include "C4TimeMilliseconds.h" #ifdef _WIN32 #include #include -time_t GetTime() +C4TimeMilliseconds GetTime() { - return timeGetTime(); + return C4TimeMilliseconds(timeGetTime()); } #else @@ -37,21 +38,20 @@ time_t GetTime() #include #endif -time_t GetTime() +C4TimeMilliseconds GetTime() { #ifdef __APPLE__ static time_t sec_offset; timeval tv; gettimeofday(&tv, 0); if (!sec_offset) sec_offset = tv.tv_sec; - return (tv.tv_sec - sec_offset) * 1000 + tv.tv_usec / 1000; + return C4TimeMilliseconds((tv.tv_sec - sec_offset) * 1000 + tv.tv_usec / 1000); #else timespec tv; clock_gettime(CLOCK_MONOTONIC, &tv); static time_t sec_offset = tv.tv_sec; - return (tv.tv_sec - sec_offset) * 1000 + tv.tv_nsec / 1000000; + return C4TimeMilliseconds((tv.tv_sec - sec_offset) * 1000 + tv.tv_nsec / 1000000); #endif } #endif - diff --git a/src/platform/PlatformAbstraction.h b/src/platform/PlatformAbstraction.h index 9fb9f76e9..af0bfadf3 100644 --- a/src/platform/PlatformAbstraction.h +++ b/src/platform/PlatformAbstraction.h @@ -230,8 +230,8 @@ bool IsGermanSystem(); bool OpenURL(const char* szURL); // Get a monotonically increasing timestamp in milliseconds -#include -time_t GetTime(); +class C4TimeMilliseconds; +C4TimeMilliseconds GetTime(); #ifdef _WIN32 #include diff --git a/src/platform/StdScheduler.cpp b/src/platform/StdScheduler.cpp index 22da28e6f..776cc9827 100644 --- a/src/platform/StdScheduler.cpp +++ b/src/platform/StdScheduler.cpp @@ -65,17 +65,17 @@ bool StdSchedulerProc::ExecuteUntil(int iTimeout) if (!Execute()) return false; // Calculate endpoint - time_t tStopTime = GetTime() + iTimeout; + C4TimeMilliseconds tStopTime = GetTime() + iTimeout; for (;;) { // Call execute with given timeout if (!Execute(Max(iTimeout, 0))) return false; // Calculate timeout - time_t tTime = GetTime(); + C4TimeMilliseconds tTime = GetTime(); if (tTime >= tStopTime) break; - iTimeout = int(tStopTime - tTime); + iTimeout = tStopTime - tTime; } // All ok. return true; @@ -176,15 +176,17 @@ bool StdScheduler::ScheduleProcs(int iTimeout) // Get timeout int i; - time_t tProcTick; - time_t tNow = GetTime(); + C4TimeMilliseconds tProcTick; + C4TimeMilliseconds tNow = GetTime(); for (i = 0; i < iProcCnt; i++) { if(ppProcs[i]->IsScheduledExecution()) { tProcTick = ppProcs[i]->GetNextTick(tNow); - if (iTimeout == -1 || iTimeout + tNow > tProcTick) - iTimeout = Max(tProcTick - tNow, 0); + if (iTimeout == -1 || tNow + iTimeout > tProcTick) + { + iTimeout = Max(tProcTick - tNow, 0); + } } } diff --git a/src/platform/StdScheduler.h b/src/platform/StdScheduler.h index efe04363a..550c89951 100644 --- a/src/platform/StdScheduler.h +++ b/src/platform/StdScheduler.h @@ -70,7 +70,7 @@ public: #endif // Call Execute() after this time has elapsed - virtual time_t GetNextTick(time_t tNow) { return 0; }; + virtual C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow) { return 0; }; virtual bool IsScheduledExecution() { return false; } @@ -86,30 +86,44 @@ public: class CStdTimerProc : public StdSchedulerProc { public: - CStdTimerProc(uint32_t iDelay) : tLastTimer(0), iDelay(iDelay) { } - ~CStdTimerProc() { } + CStdTimerProc(uint32_t iDelay) : tLastTimer(NULL), iDelay(iDelay) { } + ~CStdTimerProc() { Set(); } private: - time_t tLastTimer; + C4TimeMilliseconds *tLastTimer; uint32_t iDelay; public: - void Set() { tLastTimer = 0; } + void Set() + { + delete tLastTimer; + tLastTimer = NULL; + } void SetDelay(uint32_t inDelay) { iDelay = inDelay; } bool CheckAndReset() { - if (GetTime() < tLastTimer + iDelay) return false; + C4TimeMilliseconds tTime = GetTime(); + // first execution + if(!tLastTimer) + { + tLastTimer = new C4TimeMilliseconds(tTime - iDelay / 2); + return true; + } + // too early to execute + if (tTime < *tLastTimer + iDelay) return false; // Compensate light drifting - time_t tTime = GetTime(); - uint32_t iDrift = tTime - tLastTimer - iDelay; // >= 0 because of Check() - tLastTimer = tTime - Min(iDrift, iDelay / 2); + uint32_t iDrift = tTime - (*tLastTimer + iDelay); // a positive time difference because of above check + *tLastTimer = tTime - Min(iDrift, iDelay / 2); return true; } // StdSchedulerProc override - virtual time_t GetNextTick(time_t tNow) + virtual C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow) { - return tLastTimer + iDelay; + // never executed yet? Execute now + if(!tLastTimer) return tNow; + // otherwise, last time executed plus specified delay + return *tLastTimer + iDelay; } virtual bool IsScheduledExecution() { return true; } diff --git a/src/script/C4Aul.h b/src/script/C4Aul.h index 5da677031..769397446 100644 --- a/src/script/C4Aul.h +++ b/src/script/C4Aul.h @@ -174,7 +174,7 @@ struct C4AulScriptContext C4Value *Vars; C4AulScriptFunc *Func; C4AulBCC *CPos; - time_t tTime; // initialized only by profiler if active + C4TimeMilliseconds tTime; // initialized only by profiler if active void dump(StdStrBuf Dump = StdStrBuf("")); StdStrBuf ReturnDump(StdStrBuf Dump = StdStrBuf("")); @@ -227,7 +227,7 @@ public: int GetLineOfCode(C4AulBCC * bcc); C4AulBCC * GetCode(); - time_t tProfileTime; // internally set by profiler + uint32_t tProfileTime; // internally set by profiler friend class C4AulParse; friend class C4ScriptHost; @@ -272,7 +272,7 @@ private: struct Entry { C4AulScriptFunc *pFunc; - time_t tProfileTime; + uint32_t tProfileTime; bool operator < (const Entry &e2) const { return tProfileTime < e2.tProfileTime ; } }; @@ -281,7 +281,7 @@ private: std::vector Times; public: - void CollectEntry(C4AulScriptFunc *pFunc, time_t tProfileTime); + void CollectEntry(C4AulScriptFunc *pFunc, uint32_t tProfileTime); void Show(); static void Abort(); diff --git a/src/script/C4AulExec.cpp b/src/script/C4AulExec.cpp index ff3a3bdd2..2b802316b 100644 --- a/src/script/C4AulExec.cpp +++ b/src/script/C4AulExec.cpp @@ -941,7 +941,7 @@ void C4AulExec::StartProfiling(C4AulScript *pProfiledScript) fProfiling = true; // resets profling times and starts recording the times this->pProfiledScript = pProfiledScript; - time_t tNow = GetTime(); + C4TimeMilliseconds tNow = GetTime(); tDirectExecStart = tNow; // in case profiling is started from DirectExec tDirectExecTotal = 0; pProfiledScript->ResetProfilerTimes(); @@ -984,8 +984,8 @@ void C4AulExec::PopContext() // Profiler adding up times if (fProfiling) { - time_t dt = GetTime() - pCurCtx->tTime; - if (dt && pCurCtx->Func) + uint32_t dt = GetTime() - pCurCtx->tTime; + if (pCurCtx->Func) pCurCtx->Func->tProfileTime += dt; } // Trace done? @@ -1014,7 +1014,7 @@ void C4AulProfiler::Abort() AulExec.AbortProfiling(); } -void C4AulProfiler::CollectEntry(C4AulScriptFunc *pFunc, time_t tProfileTime) +void C4AulProfiler::CollectEntry(C4AulScriptFunc *pFunc, uint32_t tProfileTime) { // zero entries are not collected to have a cleaner list if (!tProfileTime) return; @@ -1036,7 +1036,7 @@ void C4AulProfiler::Show() for (EntryList::iterator i = Times.begin(); i!=Times.end(); ++i) { Entry &e = (*i); - LogF("%05dms\t%s", (int) e.tProfileTime, e.pFunc ? (e.pFunc->GetFullName().getData()) : "Direct exec"); + LogF("%05ums\t%s", e.tProfileTime, e.pFunc ? (e.pFunc->GetFullName().getData()) : "Direct exec"); } Log("=============================="); // done! diff --git a/src/script/C4AulExec.h b/src/script/C4AulExec.h index 9eacd3518..d448187db 100644 --- a/src/script/C4AulExec.h +++ b/src/script/C4AulExec.h @@ -50,7 +50,8 @@ private: int iTraceStart; bool fProfiling; - time_t tDirectExecStart, tDirectExecTotal; // profiler time for DirectExec + C4TimeMilliseconds tDirectExecStart; + uint32_t tDirectExecTotal; // profiler time for DirectExec C4AulScript *pProfiledScript; C4AulScriptContext Contexts[MAX_CONTEXT_STACK];