PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/mordor/timer.cpp

http://github.com/mozy/mordor
C++ | 346 lines | 297 code | 31 blank | 18 comment | 50 complexity | d912f72972613248b79c02403e38dfaf MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "timer.h"
  3. #include <algorithm>
  4. #include <vector>
  5. #include "assert.h"
  6. #include "atomic.h"
  7. #include "exception.h"
  8. #include "log.h"
  9. #include "version.h"
  10. #include "util.h"
  11. #include "mordor/config.h"
  12. #ifdef OSX
  13. #include <mach/mach_time.h>
  14. #elif defined(WINDOWS)
  15. #include <windows.h> // for LARGE_INTEGER, QueryPerformanceFrequency()
  16. #else
  17. #include <sys/time.h>
  18. #include <time.h>
  19. #endif
  20. namespace Mordor {
  21. boost::function<unsigned long long ()> TimerManager::ms_clockDg;
  22. static Logger::ptr g_log = Log::lookup("mordor:timer");
  23. // now() doesn't roll over at 0xffff...., because underlying hardware timers
  24. // do not count in microseconds; also, prevent clock anomalies from causing
  25. // timers that will never expire
  26. static ConfigVar<unsigned long long>::ptr g_clockRolloverThreshold =
  27. Config::lookup<unsigned long long>("timer.clockrolloverthreshold", 5000000ULL,
  28. "Expire all timers if the clock goes backward by >= this amount");
  29. static void
  30. stubOnTimer(boost::weak_ptr<void> weakCond, boost::function<void ()> dg);
  31. #ifdef WINDOWS
  32. static unsigned long long queryFrequency()
  33. {
  34. LARGE_INTEGER frequency;
  35. BOOL bRet = QueryPerformanceFrequency(&frequency);
  36. MORDOR_ASSERT(bRet);
  37. return (unsigned long long)frequency.QuadPart;
  38. }
  39. unsigned long long g_frequency = queryFrequency();
  40. #elif defined (OSX)
  41. static mach_timebase_info_data_t queryTimebase()
  42. {
  43. mach_timebase_info_data_t timebase;
  44. mach_timebase_info(&timebase);
  45. return timebase;
  46. }
  47. mach_timebase_info_data_t g_timebase = queryTimebase();
  48. #endif
  49. unsigned long long
  50. TimerManager::now()
  51. {
  52. if (ms_clockDg)
  53. return ms_clockDg();
  54. #ifdef WINDOWS
  55. LARGE_INTEGER count;
  56. if (!QueryPerformanceCounter(&count))
  57. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("QueryPerformanceCounter");
  58. unsigned long long countUll = (unsigned long long)count.QuadPart;
  59. if (g_frequency == 0)
  60. g_frequency = queryFrequency();
  61. return muldiv64(countUll, 1000000, g_frequency);
  62. #elif defined(OSX)
  63. unsigned long long absoluteTime = mach_absolute_time();
  64. if (g_timebase.denom == 0)
  65. g_timebase = queryTimebase();
  66. return muldiv64(absoluteTime, g_timebase.numer, (uint64_t)g_timebase.denom * 1000);
  67. #else
  68. struct timespec ts;
  69. if (clock_gettime(CLOCK_MONOTONIC, &ts))
  70. MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("clock_gettime");
  71. return ts.tv_sec * 1000000ull + ts.tv_nsec / 1000;
  72. #endif
  73. }
  74. Timer::Timer(unsigned long long us, boost::function<void ()> dg, bool recurring,
  75. TimerManager *manager)
  76. : m_recurring(recurring),
  77. m_us(us),
  78. m_dg(dg),
  79. m_manager(manager)
  80. {
  81. MORDOR_ASSERT(m_dg);
  82. m_next = TimerManager::now() + m_us;
  83. }
  84. Timer::Timer(unsigned long long next)
  85. : m_next(next)
  86. {}
  87. bool
  88. Timer::cancel()
  89. {
  90. MORDOR_LOG_DEBUG(g_log) << this << " cancel";
  91. boost::mutex::scoped_lock lock(m_manager->m_mutex);
  92. if (m_dg) {
  93. m_dg = NULL;
  94. std::set<Timer::ptr, Timer::Comparator>::iterator it =
  95. m_manager->m_timers.find(shared_from_this());
  96. MORDOR_ASSERT(it != m_manager->m_timers.end());
  97. m_manager->m_timers.erase(it);
  98. return true;
  99. }
  100. return false;
  101. }
  102. bool
  103. Timer::refresh()
  104. {
  105. boost::mutex::scoped_lock lock(m_manager->m_mutex);
  106. if (!m_dg)
  107. return false;
  108. std::set<Timer::ptr, Timer::Comparator>::iterator it =
  109. m_manager->m_timers.find(shared_from_this());
  110. MORDOR_ASSERT(it != m_manager->m_timers.end());
  111. m_manager->m_timers.erase(it);
  112. m_next = TimerManager::now() + m_us;
  113. m_manager->m_timers.insert(shared_from_this());
  114. lock.unlock();
  115. MORDOR_LOG_DEBUG(g_log) << this << " refresh";
  116. return true;
  117. }
  118. bool
  119. Timer::reset(unsigned long long us, bool fromNow)
  120. {
  121. boost::mutex::scoped_lock lock(m_manager->m_mutex);
  122. if (!m_dg)
  123. return false;
  124. // No change
  125. if (us == m_us && !fromNow)
  126. return true;
  127. std::set<Timer::ptr, Timer::Comparator>::iterator it =
  128. m_manager->m_timers.find(shared_from_this());
  129. MORDOR_ASSERT(it != m_manager->m_timers.end());
  130. m_manager->m_timers.erase(it);
  131. unsigned long long start;
  132. if (fromNow)
  133. start = TimerManager::now();
  134. else
  135. start = m_next - m_us;
  136. m_us = us;
  137. m_next = start + m_us;
  138. it = m_manager->m_timers.insert(shared_from_this()).first;
  139. bool atFront = (it == m_manager->m_timers.begin()) && !m_manager->m_tickled;
  140. if (atFront)
  141. m_manager->m_tickled = true;
  142. lock.unlock();
  143. MORDOR_LOG_DEBUG(g_log) << this << " reset to " << m_us;
  144. if (atFront)
  145. m_manager->onTimerInsertedAtFront();
  146. return true;
  147. }
  148. TimerManager::TimerManager()
  149. : m_tickled(false),
  150. m_previousTime(0ull)
  151. {}
  152. TimerManager::~TimerManager()
  153. {
  154. #ifndef NDEBUG
  155. boost::mutex::scoped_lock lock(m_mutex);
  156. MORDOR_NOTHROW_ASSERT(m_timers.empty());
  157. #endif
  158. }
  159. Timer::ptr
  160. TimerManager::registerTimer(unsigned long long us, boost::function<void ()> dg,
  161. bool recurring)
  162. {
  163. MORDOR_ASSERT(dg);
  164. Timer::ptr result(new Timer(us, dg, recurring, this));
  165. boost::mutex::scoped_lock lock(m_mutex);
  166. std::set<Timer::ptr, Timer::Comparator>::iterator it =
  167. m_timers.insert(result).first;
  168. bool atFront = (it == m_timers.begin()) && !m_tickled;
  169. if (atFront)
  170. m_tickled = true;
  171. lock.unlock();
  172. MORDOR_LOG_DEBUG(g_log) << result.get() << " registerTimer(" << us
  173. << ", " << recurring << "): " << atFront;
  174. if (atFront)
  175. onTimerInsertedAtFront();
  176. return result;
  177. }
  178. Timer::ptr
  179. TimerManager::registerConditionTimer(unsigned long long us,
  180. boost::function<void ()> dg,
  181. boost::weak_ptr<void> weakCond,
  182. bool recurring)
  183. {
  184. return registerTimer(us,
  185. boost::bind(stubOnTimer, weakCond, dg),
  186. recurring);
  187. }
  188. static void
  189. stubOnTimer(
  190. boost::weak_ptr<void> weakCond, boost::function<void ()> dg)
  191. {
  192. boost::shared_ptr<void> temp = weakCond.lock();
  193. if (temp) {
  194. dg();
  195. } else {
  196. MORDOR_LOG_DEBUG(g_log) << " Conditionally skip in stubOnTimer!";
  197. }
  198. }
  199. unsigned long long
  200. TimerManager::nextTimer()
  201. {
  202. boost::mutex::scoped_lock lock(m_mutex);
  203. m_tickled = false;
  204. if (m_timers.empty()) {
  205. MORDOR_LOG_DEBUG(g_log) << this << " nextTimer(): ~0ull";
  206. return ~0ull;
  207. }
  208. const Timer::ptr &next = *m_timers.begin();
  209. unsigned long long nowUs = now();
  210. unsigned long long result;
  211. if (nowUs >= next->m_next)
  212. result = 0;
  213. else
  214. result = next->m_next - nowUs;
  215. MORDOR_LOG_DEBUG(g_log) << this << " nextTimer(): " << result;
  216. return result;
  217. }
  218. bool
  219. TimerManager::detectClockRollover(unsigned long long nowUs)
  220. {
  221. // If the time jumps backward, expire timers (rather than have them
  222. // expire in the distant future or not at all).
  223. // We check this way because now() will not roll from 0xffff... to zero
  224. // since the underlying hardware counter doesn't count microseconds.
  225. // Use a threshold value so we don't overreact to minor clock jitter.
  226. bool rollover = false;
  227. if (nowUs < m_previousTime && // check first in case the next line would underflow
  228. nowUs < m_previousTime - g_clockRolloverThreshold->val())
  229. {
  230. MORDOR_LOG_ERROR(g_log) << this << " clock has rolled back from "
  231. << m_previousTime << " to " << nowUs << "; expiring all timers";
  232. rollover = true;
  233. }
  234. m_previousTime = nowUs;
  235. return rollover;
  236. }
  237. std::vector<boost::function<void ()> >
  238. TimerManager::processTimers()
  239. {
  240. std::vector<Timer::ptr> expired;
  241. std::vector<boost::function<void ()> > result;
  242. unsigned long long nowUs = now();
  243. {
  244. boost::mutex::scoped_lock lock(m_mutex);
  245. if (m_timers.empty())
  246. return result;
  247. bool rollover = detectClockRollover(nowUs);
  248. if (!rollover && (*m_timers.begin())->m_next > nowUs)
  249. return result;
  250. Timer nowTimer(nowUs);
  251. Timer::ptr nowTimerPtr(&nowTimer, &nop<Timer *>);
  252. // Find all timers that are expired
  253. std::set<Timer::ptr, Timer::Comparator>::iterator it =
  254. rollover ? m_timers.end() : m_timers.lower_bound(nowTimerPtr);
  255. while (it != m_timers.end() && (*it)->m_next == nowUs ) ++it;
  256. // Copy to expired, remove from m_timers;
  257. expired.insert(expired.begin(), m_timers.begin(), it);
  258. m_timers.erase(m_timers.begin(), it);
  259. result.reserve(expired.size());
  260. // Look at expired timers and re-register recurring timers
  261. // (while under the same lock)
  262. for (std::vector<Timer::ptr>::iterator it2(expired.begin());
  263. it2 != expired.end();
  264. ++it2) {
  265. Timer::ptr &timer = *it2;
  266. MORDOR_ASSERT(timer->m_dg);
  267. result.push_back(timer->m_dg);
  268. if (timer->m_recurring) {
  269. MORDOR_LOG_TRACE(g_log) << timer << " expired and refreshed";
  270. timer->m_next = nowUs + timer->m_us;
  271. m_timers.insert(timer);
  272. } else {
  273. MORDOR_LOG_TRACE(g_log) << timer << " expired";
  274. timer->m_dg = NULL;
  275. }
  276. }
  277. }
  278. return result;
  279. }
  280. void
  281. TimerManager::executeTimers()
  282. {
  283. std::vector<boost::function<void ()> > expired = processTimers();
  284. // Run the callbacks for each expired timer (not under a lock)
  285. for (std::vector<boost::function<void ()> >::iterator it(expired.begin());
  286. it != expired.end();
  287. ++it) {
  288. (*it)();
  289. }
  290. }
  291. void
  292. TimerManager::setClock(boost::function<unsigned long long()> dg)
  293. {
  294. ms_clockDg = dg;
  295. }
  296. bool
  297. Timer::Comparator::operator()(const Timer::ptr &lhs,
  298. const Timer::ptr &rhs) const
  299. {
  300. // Order NULL before everything else
  301. if (!lhs && !rhs)
  302. return false;
  303. if (!lhs)
  304. return true;
  305. if (!rhs)
  306. return false;
  307. // Order primarily on m_next
  308. if (lhs->m_next < rhs->m_next)
  309. return true;
  310. if (rhs->m_next < lhs->m_next)
  311. return false;
  312. // Order by raw pointer for equivalent timeout values
  313. return lhs.get() < rhs.get();
  314. }
  315. }