#include <asiolink/io_service.h>
#include <dhcpsrv/timer_mgr.h>
#include <exceptions/exceptions.h>
+#include <testutils/multi_threading_utils.h>
#include <gtest/gtest.h>
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;
+using namespace isc::test;
namespace {
/// @c timeout_ member.
void timeoutCallback();
+ // @brief This test checks that certain errors are returned when invalid
+ // parameters are specified when registering a timer, or when
+ // the registration can't be made.
+ void testRegisterTimer();
+
+ /// @brief This test verifies that it is possible to unregister a timer from
+ /// the TimerMgr.
+ void testUnregisterTimer();
+
+ /// @brief This test verifies that it is possible to unregister all timers.
+ void testUnregisterTimers();
+
+ /// @brief This test verifies that the timer execution can be cancelled.
+ void testCancel();
+
+ /// @brief This test verifies that the callbacks for the scheduled timers
+ /// are actually called.
+ void testScheduleTimers();
+
+ /// @brief This test verifies that exceptions emitted from the callback
+ /// would be handled by the TimerMgr.
+ void testCallbackWithException();
+
/// @brief Type definition for a map holding calls counters for
/// timers.
typedef std::map<std::string, unsigned int> CallsCount;
// This test checks that certain errors are returned when invalid
// parameters are specified when registering a timer, or when
// the registration can't be made.
-TEST_F(TimerMgrTest, registerTimer) {
+void
+TimerMgrTest::testRegisterTimer() {
// Empty timer name is not allowed.
ASSERT_THROW(timer_mgr_->registerTimer("", makeCallback("timer1"), 1,
IntervalTimer::ONE_SHOT),
BadValue);
}
-// This test verifies that it is possible to unregister a timer from
-// the TimerMgr.
-TEST_F(TimerMgrTest, unregisterTimer) {
+TEST_F(TimerMgrTest, registerTimer) {
+ // Disable Multi-Threading.
+ MultiThreadingTest mt(false);
+ testRegisterTimer();
+}
+
+TEST_F(TimerMgrTest, registerTimerMultiThreading) {
+ // Enable Multi-Threading.
+ MultiThreadingTest mt(true);
+ testRegisterTimer();
+}
+
+void
+TimerMgrTest::testUnregisterTimer() {
// Register a timer and start it.
ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 1));
ASSERT_EQ(1, timer_mgr_->timersCount());
EXPECT_EQ(calls_count_["timer1"], calls_count);
}
-// This test verifies taht it is possible to unregister all timers.
-TEST_F(TimerMgrTest, unregisterTimers) {
+TEST_F(TimerMgrTest, unregisterTimer) {
+ // Disable Multi-Threading.
+ MultiThreadingTest mt(false);
+ testUnregisterTimer();
+}
+
+TEST_F(TimerMgrTest, unregisterTimerMultiThreading) {
+ // Enable Multi-Threading.
+ MultiThreadingTest mt(true);
+ testUnregisterTimer();
+}
+
+void
+TimerMgrTest::testUnregisterTimers() {
// Register 10 timers.
for (int i = 1; i <= 20; ++i) {
std::ostringstream s;
EXPECT_TRUE(calls_count == calls_count_);
}
-// This test verifies that the timer execution can be cancelled.
-TEST_F(TimerMgrTest, cancel) {
+TEST_F(TimerMgrTest, unregisterTimers) {
+ // Disable Multi-Threading.
+ MultiThreadingTest mt(false);
+ testUnregisterTimers();
+}
+
+TEST_F(TimerMgrTest, unregisterTimersMultiThreading) {
+ // Enable Multi-Threading.
+ MultiThreadingTest mt(true);
+ testUnregisterTimers();
+}
+
+void
+TimerMgrTest::testCancel() {
// Register timer.
ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 1));
EXPECT_GT(calls_count_["timer1"], calls_count);
}
-// This test verifies that the callbacks for the scheduled timers are
-// actually called.
-TEST_F(TimerMgrTest, scheduleTimers) {
+TEST_F(TimerMgrTest, cancel) {
+ // Disable Multi-Threading.
+ MultiThreadingTest mt(false);
+ testCancel();
+}
+
+TEST_F(TimerMgrTest, cancelMultiThreading) {
+ // Enable Multi-Threading.
+ MultiThreadingTest mt(true);
+ testCancel();
+}
+
+void
+TimerMgrTest::testScheduleTimers() {
// Register two timers: 'timer1' and 'timer2'. The first timer will
// be executed at the 50ms interval. The second one at the 100ms
// interval.
EXPECT_GT(calls_count_["timer2"], calls_count_timer2);
}
-// This test verifies that exceptions emitted from the callback would
-// be handled by the TimerMgr.
-TEST_F(TimerMgrTest, callbackWithException) {
+TEST_F(TimerMgrTest, scheduleTimers) {
+ // Disable Multi-Threading.
+ MultiThreadingTest mt(false);
+ testScheduleTimers();
+}
+
+TEST_F(TimerMgrTest, scheduleTimersMultiThreading) {
+ // Enable Multi-Threading.
+ MultiThreadingTest mt(true);
+ testScheduleTimers();
+}
+
+void
+TimerMgrTest::testCallbackWithException() {
// Create timer which will trigger callback generating exception.
ASSERT_NO_THROW(
timer_mgr_->registerTimer("timer1", makeCallbackWithException(), 1,
doWait(500);
}
+TEST_F(TimerMgrTest, callbackWithException) {
+ // Disable Multi-Threading.
+ MultiThreadingTest mt(false);
+ testCallbackWithException();
+}
+
+TEST_F(TimerMgrTest, callbackWithExceptionMultiThreading) {
+ // Enable Multi-Threading.
+ MultiThreadingTest mt(true);
+ testCallbackWithException();
+}
+
// This test verifies that IO service specified for the TimerMgr
// must not be null.
TEST_F(TimerMgrTest, setIOService) {
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/timer_mgr.h>
#include <exceptions/exceptions.h>
+#include <util/multi_threading_mgr.h>
+
+#include <boost/scoped_ptr.hpp>
#include <functional>
#include <utility>
using namespace isc;
using namespace isc::asiolink;
+using namespace isc::util;
namespace {
/// @brief A type definition for the map holding timers configuration.
typedef std::map<std::string, TimerInfoPtr> TimerInfoMap;
-
/// @brief Implementation of the @c TimerMgr
class TimerMgrImpl {
public:
private:
+ /// @name Internal methods called holding the mutex in multi threading
+ /// mode.
+
+ /// @brief Registers new timer in the @c TimerMgr.
+ ///
+ /// @param timer_name Unique name for the timer.
+ /// @param callback Pointer to the callback function to be invoked
+ /// when the timer elapses, e.g. function processing expired leases
+ /// in the DHCP server.
+ /// @param interval Timer interval in milliseconds.
+ /// @param scheduling_mode Scheduling mode of the timer as described in
+ /// @c asiolink::IntervalTimer::Mode.
+ ///
+ /// @throw BadValue if the timer name is invalid or duplicate.
+ void registerTimerInternal(const std::string& timer_name,
+ const asiolink::IntervalTimer::Callback& callback,
+ const long interval,
+ const asiolink::IntervalTimer::Mode& scheduling_mode);
+
+
+ /// @brief Unregisters specified timer.
+ ///
+ /// This method cancels the timer if it is setup and removes the timer
+ /// from the internal collection of timers.
+ ///
+ /// @param timer_name Name of the timer to be unregistered.
+ ///
+ /// @throw BadValue if the specified timer hasn't been registered.
+ void unregisterTimerInternal(const std::string& timer_name);
+
+ /// @brief Unregisters all timers.
+ ///
+ /// This method must be explicitly called prior to termination of the
+ /// process.
+ void unregisterTimersInternal();
+
+ /// @brief Schedules the execution of the interval timer.
+ ///
+ /// This method schedules the timer, i.e. the callback will be executed
+ /// after specified interval elapses. The interval has been specified
+ /// during timer registration. Depending on the mode selected during the
+ /// timer registration, the callback will be executed once after it has
+ /// been scheduled or until it is cancelled. Though, in the former case
+ /// the timer can be re-scheduled in the callback function.
+ ///
+ /// @param timer_name Unique timer name.
+ ///
+ /// @throw BadValue if the timer hasn't been registered.
+ void setupInternal(const std::string& timer_name);
+
+ /// @brief Cancels the execution of the interval timer.
+ ///
+ /// @param timer_name Unique timer name.
+ ///
+ /// @throw BadValue if the timer hasn't been registered.
+ void cancelInternal(const std::string& timer_name);
+
/// @brief Callback function to be executed for each interval timer when
/// its scheduled interval elapses.
///
/// @brief Holds mapping of the timer name to timer instance and other
/// parameters pertaining to the timer.
TimerInfoMap registered_timers_;
+
+ /// @brief The mutex to protect the timer manager.
+ boost::scoped_ptr<std::mutex> mutex_;
};
-TimerMgrImpl::TimerMgrImpl() :
- io_service_(new IOService()), registered_timers_() {
+TimerMgrImpl::TimerMgrImpl() : io_service_(new IOService()),
+ registered_timers_(), mutex_(new std::mutex) {
}
void
if (!io_service) {
isc_throw(BadValue, "IO service object must not be null for TimerMgr");
}
+
io_service_ = io_service;
}
const IntervalTimer::Callback& callback,
const long interval,
const IntervalTimer::Mode& scheduling_mode) {
+ if (MultiThreadingMgr::instance().getMode()) {
+ std::lock_guard<std::mutex> lock(*mutex_);
+ registerTimerInternal(timer_name, callback, interval, scheduling_mode);
+ } else {
+ registerTimerInternal(timer_name, callback, interval, scheduling_mode);
+ }
+}
+void
+TimerMgrImpl::registerTimerInternal(const std::string& timer_name,
+ const IntervalTimer::Callback& callback,
+ const long interval,
+ const IntervalTimer::Mode& scheduling_mode) {
// Timer name must not be empty.
if (timer_name.empty()) {
isc_throw(BadValue, "registered timer name must not be empty");
void
TimerMgrImpl::unregisterTimer(const std::string& timer_name) {
+ if (MultiThreadingMgr::instance().getMode()) {
+ std::lock_guard<std::mutex> lock(*mutex_);
+ unregisterTimerInternal(timer_name);
+ } else {
+ unregisterTimerInternal(timer_name);
+ }
+}
+void
+TimerMgrImpl::unregisterTimerInternal(const std::string& timer_name) {
// Find the timer with specified name.
TimerInfoMap::iterator timer_info_it = registered_timers_.find(timer_name);
}
// Cancel any pending asynchronous operation and stop the timer.
- cancel(timer_name);
+ cancelInternal(timer_name);
// Remove the timer.
registered_timers_.erase(timer_info_it);
void
TimerMgrImpl::unregisterTimers() {
+ if (MultiThreadingMgr::instance().getMode()) {
+ std::lock_guard<std::mutex> lock(*mutex_);
+ unregisterTimersInternal();
+ } else {
+ unregisterTimersInternal();
+ }
+}
+
+void
+TimerMgrImpl::unregisterTimersInternal() {
// Copy the map holding timers configuration. This is required so as
// we don't cut the branch which we're sitting on when we will be
// erasing the timers. We're going to iterate over the register timers
// Iterate over the existing timers and unregister them.
for (TimerInfoMap::iterator timer_info_it = registered_timers_copy.begin();
timer_info_it != registered_timers_copy.end(); ++timer_info_it) {
- unregisterTimer(timer_info_it->first);
+ unregisterTimerInternal(timer_info_it->first);
}
}
bool
TimerMgrImpl::isTimerRegistered(const std::string& timer_name) {
- return (registered_timers_.find(timer_name) != registered_timers_.end());
+ if (MultiThreadingMgr::instance().getMode()) {
+ std::lock_guard<std::mutex> lock(*mutex_);
+ return (registered_timers_.find(timer_name) != registered_timers_.end());
+ } else {
+ return (registered_timers_.find(timer_name) != registered_timers_.end());
+ }
}
size_t
TimerMgrImpl::timersCount() const {
- return (registered_timers_.size());
+ if (MultiThreadingMgr::instance().getMode()) {
+ std::lock_guard<std::mutex> lock(*mutex_);
+ return (registered_timers_.size());
+ } else {
+ return (registered_timers_.size());
+ }
}
void
TimerMgrImpl::setup(const std::string& timer_name) {
+ if (MultiThreadingMgr::instance().getMode()) {
+ std::lock_guard<std::mutex> lock(*mutex_);
+ setupInternal(timer_name);
+ } else {
+ setupInternal(timer_name);
+ }
+}
+void
+TimerMgrImpl::setupInternal(const std::string& timer_name) {
// Check if the specified timer exists.
TimerInfoMap::const_iterator timer_info_it = registered_timers_.find(timer_name);
if (timer_info_it == registered_timers_.end()) {
void
TimerMgrImpl::cancel(const std::string& timer_name) {
+ if (MultiThreadingMgr::instance().getMode()) {
+ std::lock_guard<std::mutex> lock(*mutex_);
+ cancelInternal(timer_name);
+ } else {
+ cancelInternal(timer_name);
+ }
+}
+void
+TimerMgrImpl::cancelInternal(const std::string& timer_name) {
// Find the timer of our interest.
TimerInfoMap::const_iterator timer_info_it = registered_timers_.find(timer_name);
if (timer_info_it == registered_timers_.end()) {
TimerMgr::~TimerMgr() {
impl_->unregisterTimers();
- delete impl_;
}
void
impl_->setIOService(io_service);
}
-
} // end of namespace isc::dhcp
} // end of namespace isc