timer_mgr_unittest.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. // Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <boost/asio.hpp>
  15. #include <config.h>
  16. #include <dhcp/iface_mgr.h>
  17. #include <dhcpsrv/timer_mgr.h>
  18. #include <exceptions/exceptions.h>
  19. #include <util/stopwatch.h>
  20. #include <boost/bind.hpp>
  21. #include <gtest/gtest.h>
  22. #include <sstream>
  23. #include <unistd.h>
  24. using namespace isc;
  25. using namespace isc::dhcp;
  26. using namespace isc::asiolink;
  27. namespace {
  28. /// @brief Test fixture class for @c TimerMgr.
  29. class TimerMgrTest : public ::testing::Test {
  30. private:
  31. /// @brief Prepares the class for a test.
  32. virtual void SetUp();
  33. /// @brief Cleans up after the test.
  34. virtual void TearDown();
  35. public:
  36. /// @brief Wrapper method for registering a new timer.
  37. ///
  38. /// This method registers a new timer in the @c TimerMgr. It associates a
  39. /// @c timerCallback method with a timer. This method registers a number of
  40. /// calls to the particular timer in the @c calls_count_ map.
  41. ///
  42. /// @param timer_name Unique timer name.
  43. /// @param timer_interval Timer interval.
  44. /// @param mode Interval timer mode, which defaults to
  45. /// @c IntervalTimer::ONE_SHOT.
  46. void registerTimer(const std::string& timer_name, const long timer_interval,
  47. const IntervalTimer::Mode& timer_mode = IntervalTimer::ONE_SHOT);
  48. /// @brief Wait for one or many ready handlers.
  49. ///
  50. /// @param timeout Wait timeout in milliseconds.
  51. /// @param call_receive Indicates if the @c IfaceMgr::receive6
  52. /// should be called to run pending callbacks and clear
  53. /// watch sockets.
  54. void doWait(const long timeout, const bool call_receive = true);
  55. /// @brief Generic callback for timers under test.
  56. ///
  57. /// This callback increases the calls count for specified timer name.
  58. ///
  59. /// @param timer_name Name of the timer for which callback counter should
  60. /// be increased.
  61. void timerCallback(const std::string& timer_name);
  62. /// @brief Callback which generates exception.
  63. ///
  64. /// This callback is used to test that the @c TimerMgr can handle
  65. /// the case when the callback generates exceptions.
  66. void timerCallbackWithException();
  67. /// @brief Create a generic callback function for the timer.
  68. ///
  69. /// This is just a wrapped to make it a bit more convenient
  70. /// in the test.
  71. boost::function<void ()> makeCallback(const std::string& timer_name);
  72. /// @brief Create a callback which generates exception.
  73. boost::function<void ()> makeCallbackWithException();
  74. /// @brief Callback for timeout.
  75. ///
  76. /// This callback indicates the test timeout by setting the
  77. /// @c timeout_ member.
  78. void timeoutCallback();
  79. /// @brief Type definition for a map holding calls counters for
  80. /// timers.
  81. typedef std::map<std::string, unsigned int> CallsCount;
  82. /// @brief Holds the calls count for test timers.
  83. ///
  84. /// The key of this map holds the timer names. The value holds the number
  85. /// of calls to the timer handlers.
  86. CallsCount calls_count_;
  87. /// @brief Instance of @c TimerMgr used by the tests.
  88. TimerMgrPtr timer_mgr_;
  89. };
  90. void
  91. TimerMgrTest::SetUp() {
  92. timer_mgr_ = TimerMgr::instance();
  93. calls_count_.clear();
  94. // Make sure there are no dangling threads.
  95. timer_mgr_->stopThread();
  96. }
  97. void
  98. TimerMgrTest::TearDown() {
  99. // Make sure there are no dangling threads.
  100. timer_mgr_->stopThread();
  101. // Remove all timers.
  102. timer_mgr_->unregisterTimers();
  103. }
  104. void
  105. TimerMgrTest::registerTimer(const std::string& timer_name, const long timer_interval,
  106. const IntervalTimer::Mode& timer_mode) {
  107. // Register the timer with the generic callback that counts the
  108. // number of callback invocations.
  109. ASSERT_NO_THROW(
  110. timer_mgr_->registerTimer(timer_name, makeCallback(timer_name), timer_interval,
  111. timer_mode)
  112. );
  113. calls_count_[timer_name] = 0;
  114. }
  115. void
  116. TimerMgrTest::doWait(const long timeout, const bool call_receive) {
  117. util::Stopwatch stopwatch;
  118. while (stopwatch.getTotalMilliseconds() < timeout) {
  119. if (call_receive) {
  120. // Block for one 1 millisecond.
  121. IfaceMgr::instancePtr()->receive6(0, 1000);
  122. }
  123. }
  124. }
  125. void
  126. TimerMgrTest::timerCallback(const std::string& timer_name) {
  127. // Accumulate the number of calls to the timer handler.
  128. ++calls_count_[timer_name];
  129. // The timer installed is the ONE_SHOT timer, so we have
  130. // to reschedule the timer.
  131. timer_mgr_->setup(timer_name);
  132. }
  133. void
  134. TimerMgrTest::timerCallbackWithException() {
  135. isc_throw(Exception, "timerCallbackWithException");
  136. }
  137. boost::function<void ()>
  138. TimerMgrTest::makeCallback(const std::string& timer_name) {
  139. return (boost::bind(&TimerMgrTest::timerCallback, this, timer_name));
  140. }
  141. boost::function<void ()>
  142. TimerMgrTest::makeCallbackWithException() {
  143. return (boost::bind(&TimerMgrTest::timerCallbackWithException, this));
  144. }
  145. // This test checks that certain errors are returned when invalid
  146. // parameters are specified when registering a timer, or when
  147. // the registration can't be made.
  148. TEST_F(TimerMgrTest, registerTimer) {
  149. // Empty timer name is not allowed.
  150. ASSERT_THROW(timer_mgr_->registerTimer("", makeCallback("timer1"), 1,
  151. IntervalTimer::ONE_SHOT),
  152. BadValue);
  153. // Add a timer with a correct name.
  154. ASSERT_NO_THROW(timer_mgr_->registerTimer("timer2", makeCallback("timer2"), 1,
  155. IntervalTimer::ONE_SHOT));
  156. // Adding the timer with the same name as the existing timer is not
  157. // allowed.
  158. ASSERT_THROW(timer_mgr_->registerTimer("timer2", makeCallback("timer2"), 1,
  159. IntervalTimer::ONE_SHOT),
  160. BadValue);
  161. // Start worker thread.
  162. ASSERT_NO_THROW(timer_mgr_->startThread());
  163. // Can't register the timer when the thread is running.
  164. ASSERT_THROW(timer_mgr_->registerTimer("timer1", makeCallback("timer1"), 1,
  165. IntervalTimer::ONE_SHOT),
  166. InvalidOperation);
  167. // Stop the thread and retry.
  168. ASSERT_NO_THROW(timer_mgr_->stopThread());
  169. EXPECT_NO_THROW(timer_mgr_->registerTimer("timer1", makeCallback("timer1"), 1,
  170. IntervalTimer::ONE_SHOT));
  171. }
  172. // This test verifies that it is possible to unregister a timer from
  173. // the TimerMgr.
  174. TEST_F(TimerMgrTest, unregisterTimer) {
  175. // Register a timer and start it.
  176. ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 1));
  177. ASSERT_NO_THROW(timer_mgr_->setup("timer1"));
  178. ASSERT_NO_THROW(timer_mgr_->startThread());
  179. // Wait for the timer to execute several times.
  180. doWait(100);
  181. // Stop the thread but execute pending callbacks.
  182. ASSERT_NO_THROW(timer_mgr_->stopThread(true));
  183. // Remember how many times the timer's callback was executed.
  184. const unsigned int calls_count = calls_count_["timer1"];
  185. ASSERT_GT(calls_count, 0);
  186. // Check that an attempt to unregister a non-existing timer would
  187. // result in exeception.
  188. EXPECT_THROW(timer_mgr_->unregisterTimer("timer2"), BadValue);
  189. // Now unregister the correct one.
  190. ASSERT_NO_THROW(timer_mgr_->unregisterTimer("timer1"));
  191. // Start the thread again and wait another 100ms.
  192. ASSERT_NO_THROW(timer_mgr_->startThread());
  193. doWait(100);
  194. ASSERT_NO_THROW(timer_mgr_->stopThread(true));
  195. // The number of calls for the timer1 shouldn't change as the
  196. // timer had been unregistered.
  197. EXPECT_EQ(calls_count_["timer1"], calls_count);
  198. }
  199. // This test verifies taht it is possible to unregister all timers.
  200. /// @todo This test is disabled because it may occassionally hang
  201. /// due to bug in the ASIO implementation shipped with Kea.
  202. /// Replacing it with the ASIO implementation from BOOST does
  203. /// solve the problem. See ticket #4009. Until this ticket is
  204. /// implemented, the test should remain disabled.
  205. TEST_F(TimerMgrTest, unregisterTimers) {
  206. // Register 10 timers.
  207. for (int i = 1; i <= 20; ++i) {
  208. std::ostringstream s;
  209. s << "timer" << i;
  210. ASSERT_NO_FATAL_FAILURE(registerTimer(s.str(), 1))
  211. << "fatal failure occurred while registering "
  212. << s.str();
  213. ASSERT_NO_THROW(timer_mgr_->setup(s.str()))
  214. << "exception thrown while calling setup() for the "
  215. << s.str();
  216. }
  217. // Start worker thread and wait for 500ms.
  218. ASSERT_NO_THROW(timer_mgr_->startThread());
  219. doWait(500);
  220. ASSERT_NO_THROW(timer_mgr_->stopThread(true));
  221. // Make sure that all timers have been executed at least once.
  222. for (CallsCount::iterator it = calls_count_.begin();
  223. it != calls_count_.end(); ++it) {
  224. unsigned int calls_count = it->second;
  225. ASSERT_GT(calls_count, 0)
  226. << "expected calls counter for timer"
  227. << (std::distance(calls_count_.begin(), it) + 1)
  228. << " greater than 0";
  229. }
  230. // Copy counters for all timers.
  231. CallsCount calls_count(calls_count_);
  232. // Let's unregister all timers.
  233. ASSERT_NO_THROW(timer_mgr_->unregisterTimers());
  234. // Start worker thread again and wait for 500ms.
  235. ASSERT_NO_THROW(timer_mgr_->startThread());
  236. doWait(500);
  237. ASSERT_NO_THROW(timer_mgr_->stopThread(true));
  238. // The calls counter shouldn't change because there are
  239. // no timers registered.
  240. EXPECT_TRUE(calls_count == calls_count_);
  241. }
  242. // This test checks that it is not possible to unregister timers
  243. // while the thread is running.
  244. TEST_F(TimerMgrTest, unregisterTimerWhileRunning) {
  245. // Register two timers.
  246. ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 1));
  247. ASSERT_NO_FATAL_FAILURE(registerTimer("timer2", 1));
  248. // Start the thread and make sure we can't unregister them.
  249. ASSERT_NO_THROW(timer_mgr_->startThread());
  250. EXPECT_THROW(timer_mgr_->unregisterTimer("timer1"), InvalidOperation);
  251. EXPECT_THROW(timer_mgr_->unregisterTimers(), InvalidOperation);
  252. // No need to stop the thread as it will be stopped by the
  253. // test fixture destructor.
  254. }
  255. // This test verifies that the timer execution can be cancelled.
  256. TEST_F(TimerMgrTest, cancel) {
  257. // Register timer.
  258. ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 1));
  259. // Kick in the timer and wait for 500ms.
  260. ASSERT_NO_THROW(timer_mgr_->setup("timer1"));
  261. ASSERT_NO_THROW(timer_mgr_->startThread());
  262. doWait(500);
  263. ASSERT_NO_THROW(timer_mgr_->stopThread());
  264. // Cancelling non-existing timer should fail.
  265. EXPECT_THROW(timer_mgr_->cancel("timer2"), BadValue);
  266. // Cancelling the good one should pass, even when the worker
  267. // thread is running.
  268. ASSERT_NO_THROW(timer_mgr_->cancel("timer1"));
  269. // Remember how many calls have been invoked and wait for
  270. // another 500ms.
  271. unsigned int calls_count = calls_count_["timer1"];
  272. ASSERT_NO_THROW(timer_mgr_->startThread());
  273. doWait(500);
  274. // Stop thread before we setup again.
  275. ASSERT_NO_THROW(timer_mgr_->stopThread());
  276. // The number of calls shouldn't change because the timer had been
  277. // cancelled.
  278. ASSERT_EQ(calls_count, calls_count_["timer1"]);
  279. // Setup the timer again.
  280. ASSERT_NO_THROW(timer_mgr_->setup("timer1"));
  281. // Restart the thread.
  282. ASSERT_NO_THROW(timer_mgr_->startThread());
  283. doWait(500);
  284. // New calls should be recorded.
  285. EXPECT_GT(calls_count_["timer1"], calls_count);
  286. }
  287. // This test verifies that the callbacks for the scheduled timers are
  288. // actually called.
  289. TEST_F(TimerMgrTest, scheduleTimers) {
  290. // Register two timers: 'timer1' and 'timer2'. The first timer will
  291. // be executed at the 50ms interval. The second one at the 100ms
  292. // interval.
  293. ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 50));
  294. ASSERT_NO_FATAL_FAILURE(registerTimer("timer2", 100));
  295. // Kick in the timers.
  296. ASSERT_NO_THROW(timer_mgr_->setup("timer1"));
  297. ASSERT_NO_THROW(timer_mgr_->setup("timer2"));
  298. // We can start the worker thread before we even kick in the timers.
  299. ASSERT_NO_THROW(timer_mgr_->startThread());
  300. // Run IfaceMgr::receive6() in the loop for 1000ms. This function
  301. // will read data from the watch sockets created when the timers
  302. // were registered. The data is delivered to the watch sockets
  303. // at the interval of the timers, which should break the blocking
  304. // call to receive6(). As a result, the callbacks associated
  305. // with the watch sockets should be called.
  306. doWait(1000);
  307. // Stop the worker thread, which would halt the execution of
  308. // the timers.
  309. ASSERT_NO_THROW(timer_mgr_->stopThread(true));
  310. // We have been running the timer for 1000ms at the interval of
  311. // 50ms. The maximum number of callbacks is 20. However, the
  312. // callback itself takes time. Stoping the thread takes time.
  313. // So, the real number differs significantly. We don't know
  314. // exactly how many have been executed. It should be more
  315. // than 10 for sure. But we really made up the numbers here.
  316. EXPECT_GT(calls_count_["timer1"], 10);
  317. // For the second timer it should be more than 5.
  318. EXPECT_GT(calls_count_["timer2"], 5);
  319. // Because the interval of the 'timer1' is lower than the
  320. // interval of the 'timer2' the number of calls should
  321. // be higher for the 'timer1'.
  322. EXPECT_GT(calls_count_["timer1"], calls_count_["timer2"]);
  323. // Remember the number of calls from 'timer1' and 'timer2'.
  324. unsigned int calls_count_timer1 = calls_count_["timer1"];
  325. unsigned int calls_count_timer2 = calls_count_["timer2"];
  326. // Unregister the 'timer1'.
  327. ASSERT_NO_THROW(timer_mgr_->unregisterTimer("timer1"));
  328. // Restart the thread.
  329. ASSERT_NO_THROW(timer_mgr_->startThread());
  330. // Wait another 500ms. The 'timer1' was unregistered so it
  331. // should not make any more calls. The 'timer2' should still
  332. // work as previously.
  333. doWait(500);
  334. // The number of calls shouldn't have changed.
  335. EXPECT_EQ(calls_count_timer1, calls_count_["timer1"]);
  336. // There should be some new calls registered for the 'timer2'.
  337. EXPECT_GT(calls_count_["timer2"], calls_count_timer2);
  338. }
  339. // This test verifies that it is possible to force that the pending
  340. // timer callbacks are executed when the worker thread is stopped.
  341. TEST_F(TimerMgrTest, stopThreadWithRunningHandlers) {
  342. // Register 'timer1'.
  343. ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 1));
  344. // Kick in the timer.
  345. ASSERT_NO_THROW(timer_mgr_->setup("timer1"));
  346. ASSERT_NO_THROW(timer_mgr_->startThread());
  347. // Run the thread for 100ms. This should run some timers. The 'false'
  348. // value indicates that the IfaceMgr::receive6 is not called, so the
  349. // watch socket is never cleared.
  350. doWait(100, false);
  351. // There should be no calls registered for the timer1.
  352. EXPECT_EQ(0, calls_count_["timer1"]);
  353. // Stop the worker thread without completing pending callbacks.
  354. ASSERT_NO_THROW(timer_mgr_->stopThread(false));
  355. // There should be still not be any calls registered.
  356. EXPECT_EQ(0, calls_count_["timer1"]);
  357. // We can restart the worker thread before we even kick in the timers.
  358. ASSERT_NO_THROW(timer_mgr_->startThread());
  359. // Run the thread for 100ms. This should run some timers. The 'false'
  360. // value indicates that the IfaceMgr::receive6 is not called, so the
  361. // watch socket is never cleared.
  362. doWait(100, false);
  363. // There should be no calls registered for the timer1.
  364. EXPECT_EQ(0, calls_count_["timer1"]);
  365. // Stop the worker thread with completing pending callbacks.
  366. ASSERT_NO_THROW(timer_mgr_->stopThread(true));
  367. // There should be one call registered.
  368. EXPECT_EQ(1, calls_count_["timer1"]);
  369. }
  370. // This test verifies that exceptions emitted from the callback would
  371. // be handled by the TimerMgr.
  372. TEST_F(TimerMgrTest, callbackWithException) {
  373. // Create timer which will trigger callback generating exception.
  374. ASSERT_NO_THROW(
  375. timer_mgr_->registerTimer("timer1", makeCallbackWithException(), 1,
  376. IntervalTimer::ONE_SHOT)
  377. );
  378. // Setup the timer.
  379. ASSERT_NO_THROW(timer_mgr_->setup("timer1"));
  380. // Start thread. We hope that exception will be caught by the @c TimerMgr
  381. // and will not kill the process.
  382. ASSERT_NO_THROW(timer_mgr_->startThread());
  383. doWait(500);
  384. ASSERT_NO_THROW(timer_mgr_->stopThread(true));
  385. }
  386. } // end of anonymous namespace