Parcourir la source

[4047] Instance of the TimerMgr is now shared pointer.

Marcin Siodelski il y a 9 ans
Parent
commit
c99f6c0233

+ 73 - 72
src/lib/dhcpsrv/tests/timer_mgr_unittest.cc

@@ -113,27 +113,27 @@ TimerMgrTest::SetUp() {
     calls_count_.clear();
     timeout_ = false;
     // Make sure there are no dangling threads.
-    TimerMgr::instance().stopThread();
+    TimerMgr::instance()->stopThread();
 }
 
 void
 TimerMgrTest::TearDown() {
     // Make sure there are no dangling threads.
-    TimerMgr::instance().stopThread();
+    TimerMgr::instance()->stopThread();
     // Remove all timers.
-    TimerMgr::instance().unregisterTimers();
+    TimerMgr::instance()->unregisterTimers();
 }
 
 void
 TimerMgrTest::registerTimer(const std::string& timer_name, const long timer_interval,
                             const IntervalTimer::Mode& timer_mode) {
-    TimerMgr& timer_mgr = TimerMgr::instance();
+    const TimerMgrPtr& timer_mgr = TimerMgr::instance();
 
     // Register the timer with the generic callback that counts the
     // number of callback invocations.
     ASSERT_NO_THROW(
-        timer_mgr.registerTimer(timer_name, makeCallback(timer_name), timer_interval,
-                                timer_mode)
+        timer_mgr->registerTimer(timer_name, makeCallback(timer_name), timer_interval,
+                                 timer_mode)
     );
 
     calls_count_[timer_name] = 0;
@@ -151,7 +151,7 @@ TimerMgrTest::doWait(const long timeout, const bool call_receive) {
     while (!timeout_) {
         if (call_receive) {
             // Block for one 1 millisecond.
-            IfaceMgr::instance().receive6(0, 1000);
+            IfaceMgr::instancePtr()->receive6(0, 1000);
         }
         // Run ready handlers from the local IO service to execute
         // the timeout callback if necessary.
@@ -168,7 +168,7 @@ TimerMgrTest::timerCallback(const std::string& timer_name) {
 
     // The timer installed is the ONE_SHOT timer, so we have
     // to reschedule the timer.
-    TimerMgr::instance().setup(timer_name);
+    TimerMgr::instance()->setup(timer_name);
 }
 
 void
@@ -200,52 +200,52 @@ TimerMgrTest::timeoutCallback() {
 // parameters are specified when registering a timer, or when
 // the registration can't be made.
 TEST_F(TimerMgrTest, registerTimer) {
-    TimerMgr& timer_mgr = TimerMgr::instance();
+    const TimerMgrPtr& timer_mgr = TimerMgr::instance();
 
     // Empty timer name is not allowed.
-    ASSERT_THROW(timer_mgr.registerTimer("", makeCallback("timer1"), 1,
-                                         IntervalTimer::ONE_SHOT),
+    ASSERT_THROW(timer_mgr->registerTimer("", makeCallback("timer1"), 1,
+                                          IntervalTimer::ONE_SHOT),
                  BadValue);
 
     // Add a timer with a correct name.
-    ASSERT_NO_THROW(timer_mgr.registerTimer("timer2", makeCallback("timer2"), 1,
-                                         IntervalTimer::ONE_SHOT));
+    ASSERT_NO_THROW(timer_mgr->registerTimer("timer2", makeCallback("timer2"), 1,
+                                             IntervalTimer::ONE_SHOT));
     // Adding the timer with the same name as the existing timer is not
     // allowed.
-    ASSERT_THROW(timer_mgr.registerTimer("timer2", makeCallback("timer2"), 1,
-                                         IntervalTimer::ONE_SHOT),
+    ASSERT_THROW(timer_mgr->registerTimer("timer2", makeCallback("timer2"), 1,
+                                          IntervalTimer::ONE_SHOT),
                  BadValue);
 
     // Start worker thread.
-    ASSERT_NO_THROW(timer_mgr.startThread());
+    ASSERT_NO_THROW(timer_mgr->startThread());
 
     // Can't register the timer when the thread is running.
-    ASSERT_THROW(timer_mgr.registerTimer("timer1", makeCallback("timer1"), 1,
-                                         IntervalTimer::ONE_SHOT),
+    ASSERT_THROW(timer_mgr->registerTimer("timer1", makeCallback("timer1"), 1,
+                                          IntervalTimer::ONE_SHOT),
                  InvalidOperation);
 
     // Stop the thread and retry.
-    ASSERT_NO_THROW(timer_mgr.stopThread());
-    EXPECT_NO_THROW(timer_mgr.registerTimer("timer1", makeCallback("timer1"), 1,
-                                            IntervalTimer::ONE_SHOT));
+    ASSERT_NO_THROW(timer_mgr->stopThread());
+    EXPECT_NO_THROW(timer_mgr->registerTimer("timer1", makeCallback("timer1"), 1,
+                                             IntervalTimer::ONE_SHOT));
 
 }
 
 // This test verifies that it is possible to unregister a timer from
 // the TimerMgr.
 TEST_F(TimerMgrTest, unregisterTimer) {
-    TimerMgr& timer_mgr = TimerMgr::instance();
+    const TimerMgrPtr& timer_mgr = TimerMgr::instance();
 
     // Register a timer and start it.
     ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 1));
-    ASSERT_NO_THROW(timer_mgr.setup("timer1"));
-    ASSERT_NO_THROW(timer_mgr.startThread());
+    ASSERT_NO_THROW(timer_mgr->setup("timer1"));
+    ASSERT_NO_THROW(timer_mgr->startThread());
 
     // Wait for the timer to execute several times.
     doWait(100);
 
     // Stop the thread but execute pending callbacks.
-    ASSERT_NO_THROW(timer_mgr.stopThread(true));
+    ASSERT_NO_THROW(timer_mgr->stopThread(true));
 
     // Remember how many times the timer's callback was executed.
     const unsigned int calls_count = calls_count_["timer1"];
@@ -253,15 +253,15 @@ TEST_F(TimerMgrTest, unregisterTimer) {
 
     // Check that an attempt to unregister a non-existing timer would
     // result in exeception.
-    EXPECT_THROW(timer_mgr.unregisterTimer("timer2"), BadValue);
+    EXPECT_THROW(timer_mgr->unregisterTimer("timer2"), BadValue);
 
     // Now unregister the correct one.
-    ASSERT_NO_THROW(timer_mgr.unregisterTimer("timer1"));
+    ASSERT_NO_THROW(timer_mgr->unregisterTimer("timer1"));
 
     // Start the thread again and wait another 100ms.
-    ASSERT_NO_THROW(timer_mgr.startThread());
+    ASSERT_NO_THROW(timer_mgr->startThread());
     doWait(100);
-    ASSERT_NO_THROW(timer_mgr.stopThread(true));
+    ASSERT_NO_THROW(timer_mgr->stopThread(true));
 
     // The number of calls for the timer1 shouldn't change as the
     // timer had been unregistered.
@@ -275,7 +275,7 @@ TEST_F(TimerMgrTest, unregisterTimer) {
 /// solve the problem. See ticket #4009. Until this ticket is
 /// implemented, the test should remain disabled.
 TEST_F(TimerMgrTest, unregisterTimers) {
-    TimerMgr& timer_mgr = TimerMgr::instance();
+    const TimerMgrPtr& timer_mgr = TimerMgr::instance();
 
     // Register 10 timers.
     for (int i = 1; i <= 20; ++i) {
@@ -284,15 +284,15 @@ TEST_F(TimerMgrTest, unregisterTimers) {
         ASSERT_NO_FATAL_FAILURE(registerTimer(s.str(), 1))
             << "fatal failure occurred while registering "
             << s.str();
-        ASSERT_NO_THROW(timer_mgr.setup(s.str()))
+        ASSERT_NO_THROW(timer_mgr->setup(s.str()))
             << "exception thrown while calling setup() for the "
             << s.str();
     }
 
     // Start worker thread and wait for 500ms.
-    ASSERT_NO_THROW(timer_mgr.startThread());
+    ASSERT_NO_THROW(timer_mgr->startThread());
     doWait(500);
-    ASSERT_NO_THROW(timer_mgr.stopThread(true));
+    ASSERT_NO_THROW(timer_mgr->stopThread(true));
 
     // Make sure that all timers have been executed at least once.
     for (CallsCount::iterator it = calls_count_.begin();
@@ -308,12 +308,12 @@ TEST_F(TimerMgrTest, unregisterTimers) {
     CallsCount calls_count(calls_count_);
 
     // Let's unregister all timers.
-    ASSERT_NO_THROW(timer_mgr.unregisterTimers());
+    ASSERT_NO_THROW(timer_mgr->unregisterTimers());
 
     // Start worker thread again and wait for 500ms.
-    ASSERT_NO_THROW(timer_mgr.startThread());
+    ASSERT_NO_THROW(timer_mgr->startThread());
     doWait(500);
-    ASSERT_NO_THROW(timer_mgr.stopThread(true));
+    ASSERT_NO_THROW(timer_mgr->stopThread(true));
 
     // The calls counter shouldn't change because there are
     // no timers registered.
@@ -323,16 +323,16 @@ TEST_F(TimerMgrTest, unregisterTimers) {
 // This test checks that it is not possible to unregister timers
 // while the thread is running.
 TEST_F(TimerMgrTest, unregisterTimerWhileRunning) {
-    TimerMgr& timer_mgr = TimerMgr::instance();
+    const TimerMgrPtr& timer_mgr = TimerMgr::instance();
 
     // Register two timers.
     ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 1));
     ASSERT_NO_FATAL_FAILURE(registerTimer("timer2", 1));
 
     // Start the thread and make sure we can't unregister them.
-    ASSERT_NO_THROW(timer_mgr.startThread());
-    EXPECT_THROW(timer_mgr.unregisterTimer("timer1"), InvalidOperation);
-    EXPECT_THROW(timer_mgr.unregisterTimers(), InvalidOperation);
+    ASSERT_NO_THROW(timer_mgr->startThread());
+    EXPECT_THROW(timer_mgr->unregisterTimer("timer1"), InvalidOperation);
+    EXPECT_THROW(timer_mgr->unregisterTimers(), InvalidOperation);
 
     // No need to stop the thread as it will be stopped by the
     // test fixture destructor.
@@ -340,39 +340,42 @@ TEST_F(TimerMgrTest, unregisterTimerWhileRunning) {
 
 // This test verifies that the timer execution can be cancelled.
 TEST_F(TimerMgrTest, cancel) {
-    TimerMgr& timer_mgr = TimerMgr::instance();
+    const TimerMgrPtr& timer_mgr = TimerMgr::instance();
 
     // Register timer.
     ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 1));
 
-    // We can start the worker thread before we even kick in the timers.
-    ASSERT_NO_THROW(timer_mgr.startThread());
-
     // Kick in the timer and wait for 500ms.
-    ASSERT_NO_THROW(timer_mgr.setup("timer1"));
+    ASSERT_NO_THROW(timer_mgr->setup("timer1"));
+    ASSERT_NO_THROW(timer_mgr->startThread());
     doWait(500);
+    ASSERT_NO_THROW(timer_mgr->stopThread());
 
     // Cancelling non-existing timer should fail.
-    EXPECT_THROW(timer_mgr.cancel("timer2"), BadValue);
+    EXPECT_THROW(timer_mgr->cancel("timer2"), BadValue);
 
     // Cancelling the good one should pass, even when the worker
     // thread is running.
-    ASSERT_NO_THROW(timer_mgr.cancel("timer1"));
+    ASSERT_NO_THROW(timer_mgr->cancel("timer1"));
 
     // Remember how many calls have been invoked and wait for
     // another 500ms.
     unsigned int calls_count = calls_count_["timer1"];
+
+    ASSERT_NO_THROW(timer_mgr->startThread());
     doWait(500);
+    // Stop thread before we setup again.
+    ASSERT_NO_THROW(timer_mgr->stopThread());
 
     // The number of calls shouldn't change because the timer had been
     // cancelled.
     ASSERT_EQ(calls_count, calls_count_["timer1"]);
 
-    TimerMgr::instance().stopThread();
-
     // Setup the timer again.
-    ASSERT_NO_THROW(timer_mgr.setup("timer1"));
-    TimerMgr::instance().startThread();
+    ASSERT_NO_THROW(timer_mgr->setup("timer1"));
+
+    // Restart the thread.
+    ASSERT_NO_THROW(timer_mgr->startThread());
     doWait(500);
 
     // New calls should be recorded.
@@ -382,7 +385,7 @@ TEST_F(TimerMgrTest, cancel) {
 // This test verifies that the callbacks for the scheduled timers are
 // actually called.
 TEST_F(TimerMgrTest, scheduleTimers) {
-    TimerMgr& timer_mgr = TimerMgr::instance();
+    const TimerMgrPtr& timer_mgr = TimerMgr::instance();
 
     // Register two timers: 'timer1' and 'timer2'. The first timer will
     // be executed at the 1ms interval. The second one at the 5ms
@@ -391,12 +394,12 @@ TEST_F(TimerMgrTest, scheduleTimers) {
     ASSERT_NO_FATAL_FAILURE(registerTimer("timer2", 5));
 
     // We can start the worker thread before we even kick in the timers.
-    ASSERT_NO_THROW(timer_mgr.startThread());
+    ASSERT_NO_THROW(timer_mgr->startThread());
 
     // Kick in the timers. The timers have been registered so there
     // should be no exception.
-    ASSERT_NO_THROW(timer_mgr.setup("timer1"));
-    ASSERT_NO_THROW(timer_mgr.setup("timer2"));
+    ASSERT_NO_THROW(timer_mgr->setup("timer1"));
+    ASSERT_NO_THROW(timer_mgr->setup("timer2"));
 
     // Run IfaceMgr::receive6() in the loop for 500ms. This function
     // will read data from the watch sockets created when the timers
@@ -408,7 +411,7 @@ TEST_F(TimerMgrTest, scheduleTimers) {
 
     // Stop the worker thread, which would halt the execution of
     // the timers.
-    timer_mgr.stopThread(true);
+    ASSERT_NO_THROW(timer_mgr->stopThread(true));
 
     // We have been running the timer for 500ms at the interval of
     // 1 ms. The maximum number of callbacks is 500. However, the
@@ -430,10 +433,10 @@ TEST_F(TimerMgrTest, scheduleTimers) {
     unsigned int calls_count_timer2 = calls_count_["timer2"];
 
     // Unregister the 'timer1'.
-    ASSERT_NO_THROW(timer_mgr.unregisterTimer("timer1"));
+    ASSERT_NO_THROW(timer_mgr->unregisterTimer("timer1"));
 
     // Restart the thread.
-    ASSERT_NO_THROW(timer_mgr.startThread());
+    ASSERT_NO_THROW(timer_mgr->startThread());
 
     // Wait another 500ms. The 'timer1' was unregistered so it
     // should not make any more calls. The 'timer2' should still
@@ -449,16 +452,14 @@ TEST_F(TimerMgrTest, scheduleTimers) {
 // This test verifies that it is possible to force that the pending
 // timer callbacks are executed when the worker thread is stopped.
 TEST_F(TimerMgrTest, stopThreadWithRunningHandlers) {
-    TimerMgr& timer_mgr = TimerMgr::instance();
+    const TimerMgrPtr& timer_mgr = TimerMgr::instance();
 
     // Register 'timer1'.
     ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 1));
 
-    // We can start the worker thread before we even kick in the timers.
-    ASSERT_NO_THROW(timer_mgr.startThread());
-
     // Kick in the timer.
-    ASSERT_NO_THROW(timer_mgr.setup("timer1"));
+    ASSERT_NO_THROW(timer_mgr->setup("timer1"));
+    ASSERT_NO_THROW(timer_mgr->startThread());
 
     // Run the thread for 100ms. This should run some timers. The 'false'
     // value indicates that the IfaceMgr::receive6 is not called, so the
@@ -469,13 +470,13 @@ TEST_F(TimerMgrTest, stopThreadWithRunningHandlers) {
     EXPECT_EQ(0, calls_count_["timer1"]);
 
     // Stop the worker thread without completing pending callbacks.
-    ASSERT_NO_THROW(timer_mgr.stopThread(false));
+    ASSERT_NO_THROW(timer_mgr->stopThread(false));
 
     // There should be still not be any calls registered.
     EXPECT_EQ(0, calls_count_["timer1"]);
 
     // We can restart the worker thread before we even kick in the timers.
-    ASSERT_NO_THROW(timer_mgr.startThread());
+    ASSERT_NO_THROW(timer_mgr->startThread());
 
     // Run the thread for 100ms. This should run some timers. The 'false'
     // value indicates that the IfaceMgr::receive6 is not called, so the
@@ -486,7 +487,7 @@ TEST_F(TimerMgrTest, stopThreadWithRunningHandlers) {
     EXPECT_EQ(0, calls_count_["timer1"]);
 
     // Stop the worker thread with completing pending callbacks.
-    ASSERT_NO_THROW(timer_mgr.stopThread(true));
+    ASSERT_NO_THROW(timer_mgr->stopThread(true));
 
     // There should be one call registered.
     EXPECT_EQ(1, calls_count_["timer1"]);
@@ -495,22 +496,22 @@ TEST_F(TimerMgrTest, stopThreadWithRunningHandlers) {
 // This test verifies that exceptions emitted from the callback would
 // be handled by the TimerMgr.
 TEST_F(TimerMgrTest, callbackWithException) {
-    TimerMgr& timer_mgr = TimerMgr::instance();
+    const TimerMgrPtr& timer_mgr = TimerMgr::instance();
 
     // Create timer which will trigger callback generating exception.
     ASSERT_NO_THROW(
-        timer_mgr.registerTimer("timer1", makeCallbackWithException(), 1,
-                                IntervalTimer::ONE_SHOT)
+        timer_mgr->registerTimer("timer1", makeCallbackWithException(), 1,
+                                 IntervalTimer::ONE_SHOT)
     );
 
     // Setup the timer.
-    ASSERT_NO_THROW(timer_mgr.setup("timer1"));
+    ASSERT_NO_THROW(timer_mgr->setup("timer1"));
 
     // Start thread. We hope that exception will be caught by the @c TimerMgr
     // and will not kill the process.
-    ASSERT_NO_THROW(timer_mgr.startThread());
+    ASSERT_NO_THROW(timer_mgr->startThread());
     doWait(500);
-    ASSERT_NO_THROW(timer_mgr.stopThread(true));
+    ASSERT_NO_THROW(timer_mgr->stopThread(true));
 }
 
 } // end of anonymous namespace

+ 2 - 2
src/lib/dhcpsrv/timer_mgr.cc

@@ -603,9 +603,9 @@ TimerMgrImpl::signalSocketClearing() {
     cond_var_.signal();
 }
 
-TimerMgr&
+const TimerMgrPtr&
 TimerMgr::instance() {
-    static TimerMgr timer_mgr;
+    static TimerMgrPtr timer_mgr(new TimerMgr());
     return (timer_mgr);
 }
 

+ 15 - 14
src/lib/dhcpsrv/timer_mgr.h

@@ -17,7 +17,7 @@
 
 #include <asiolink/interval_timer.h>
 #include <boost/noncopyable.hpp>
-#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
 #include <string>
 
 namespace isc {
@@ -26,6 +26,12 @@ namespace dhcp {
 /// @brief Forward declaration of the @c TimerMgr implementation.
 class TimerMgrImpl;
 
+/// @brief Forward declaration of the @c TimerMgr.
+class TimerMgr;
+
+/// @brief Type definition of the shared pointer to @c TimerMgr.
+typedef boost::shared_ptr<TimerMgr> TimerMgrPtr;
+
 /// @brief Manages a pool of asynchronous interval timers for DHCP server.
 ///
 /// This class holds a pool of asynchronous interval timers which are
@@ -97,8 +103,14 @@ class TimerMgrImpl;
 class TimerMgr : public boost::noncopyable {
 public:
 
-    /// @brief Returns sole instance of the @c TimerMgr singleton.
-    static TimerMgr& instance();
+    /// @brief Returns pointer to the sole instance of the @c TimerMgr.
+    static const TimerMgrPtr& instance();
+
+    /// @brief Destructor.
+    ///
+    /// Stops the worker thread if it is running and unregisteres any
+    /// registered timers.
+    ~TimerMgr();
 
     /// @name Registering, unregistering and scheduling the timers.
     //@{
@@ -218,9 +230,6 @@ public:
 
 private:
 
-    /// @name Constructor and destructor.
-    //@{
-    ///
     /// @brief Private default constructor.
     ///
     /// The @c TimerMgr is a singleton class which instance must be created
@@ -228,14 +237,6 @@ private:
     /// construction via @c TimerMgr::instance.
     TimerMgr();
 
-    /// @brief Private destructor.
-    ///
-    /// Stops the worker thread if it is running and unregisteres any
-    /// registered timers.
-    ~TimerMgr();
-
-    //@}
-
     /// @brief Pointer to @c TimerMgr implementation.
     TimerMgrImpl* impl_;