PolledTimer.h 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. /* Copyright The kNet Project.
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License. */
  11. #pragma once
  12. /** @file PolledTimer.h
  13. @brief PolledTimer is a timer object that can be used in "polled" or per-frame executed contexts. */
  14. #include "Clock.h"
  15. namespace kNet
  16. {
  17. class PolledTimer
  18. {
  19. public:
  20. /// The default ctor starts the timer when the object is created.
  21. PolledTimer():enabled(false)
  22. {
  23. startTime = Clock::Tick();
  24. }
  25. explicit PolledTimer(float msecs)
  26. :enabled(false)
  27. {
  28. StartMSecs(msecs);
  29. }
  30. /// Starts the timer in a non-periodic mode, to go off once in the given amount of milliseconds.
  31. void StartMSecs(float msecs)
  32. {
  33. StartTicks((tick_t)(Clock::TicksPerSec() * (msecs / 1000.f)));
  34. }
  35. /// Starts the timer in a non-periodic mode, to go off once in the given amount of high-precision Clock ticks.
  36. void StartTicks(tick_t ticks)
  37. {
  38. startTime = Clock::Tick();
  39. alarmTime = startTime + ticks;
  40. enabled = true;
  41. }
  42. void Stop()
  43. {
  44. enabled = false;
  45. }
  46. void Reset()
  47. {
  48. Stop();
  49. }
  50. /// Returns the amount of time in ticks this timer has been running.
  51. tick_t TicksElapsed() const
  52. {
  53. return Clock::Tick() - startTime;
  54. }
  55. float MSecsElapsed() const
  56. {
  57. return Clock::TicksToMillisecondsF(TicksElapsed());
  58. }
  59. /// Starts the timer from zero to run upwards without a target period, i.e. the timer will never go off,
  60. /// but will only report TicksElapsed/MSecsElapsed values.
  61. void Start()
  62. {
  63. enabled = false; // The target-timer is disabled.
  64. startTime = Clock::Tick(); // Start counting from now.
  65. }
  66. bool Enabled() const
  67. {
  68. return enabled;
  69. }
  70. /// Tests whether the timer has gone off, and resets it as well.
  71. /// Returns true if the timer has elapsed, false otherwise.
  72. bool Test()
  73. {
  74. if (!enabled)
  75. return false;
  76. if (Clock::IsNewer(Clock::Tick(), alarmTime))
  77. {
  78. Reset();
  79. return true;
  80. }
  81. return false;
  82. }
  83. bool TriggeredOrNotRunning()
  84. {
  85. return Test() || !Enabled();
  86. }
  87. /// @return The number of ticks left until the timer elapses, or (tick_t)-1 if the timer is not active.
  88. tick_t TicksLeft() const
  89. {
  90. if (!enabled)
  91. return (tick_t)(-1);
  92. tick_t now = Clock::Tick();
  93. if (Clock::IsNewer(now, alarmTime))
  94. return 0;
  95. else
  96. return Clock::TicksInBetween(alarmTime, now);
  97. }
  98. /// @return The number of msecs left until the timer elapses, 0.f if the timer has elapsed, or
  99. /// -1.0f if the timer is not even active.
  100. float MSecsLeft() const
  101. {
  102. if (!enabled)
  103. return -1.f;
  104. tick_t now = Clock::Tick();
  105. if (Clock::IsNewer(now, alarmTime))
  106. return 0.f;
  107. else
  108. return Clock::TimespanToMillisecondsF(now, alarmTime);
  109. }
  110. /// Waits until the timer elapses. Uses OS Clock::Sleep() to avoid excessive CPU use.
  111. void WaitPrecise()
  112. {
  113. if (!enabled)
  114. return;
  115. tick_t timeLeft = TicksLeft();
  116. while(timeLeft > 0)
  117. {
  118. if (timeLeft > Clock::TicksPerMillisecond())
  119. {
  120. float msecs = Clock::TicksToMillisecondsF(timeLeft);
  121. Clock::Sleep((int)msecs);
  122. }
  123. else // If there's less than one ms left, we use spinwait to return precisely as near to the correct tick as possible.
  124. {
  125. SpinWait();
  126. return;
  127. }
  128. timeLeft = TicksLeft();
  129. }
  130. }
  131. /// Waits in an empty loop until the timer elapses. Will cause the the CPU use to go to max.
  132. void SpinWait()
  133. {
  134. while(enabled && TicksLeft() > 0)
  135. ;
  136. }
  137. private:
  138. bool enabled;
  139. /// The wallclock time when this PolledTimer is supposed to go off.
  140. tick_t alarmTime;
  141. /// The wallclock time when this PolledTimer was last started.
  142. tick_t startTime;
  143. };
  144. } // ~kNet