Commit 811a19c9 authored by Marcin Siodelski's avatar Marcin Siodelski

[3970] Basic implementation of the TimerMgr singleton.

parent c3a1e566
......@@ -120,6 +120,7 @@ libkea_dhcpsrv_la_SOURCES += srv_config.cc srv_config.h
libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h
libkea_dhcpsrv_la_SOURCES += subnet_id.h
libkea_dhcpsrv_la_SOURCES += subnet_selector.h
libkea_dhcpsrv_la_SOURCES += timer_mgr.cc timer_mgr.h
libkea_dhcpsrv_la_SOURCES += triplet.h
libkea_dhcpsrv_la_SOURCES += utils.h
libkea_dhcpsrv_la_SOURCES += writable_host_data_source.h
......
......@@ -103,6 +103,7 @@ libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
libdhcpsrv_unittests_SOURCES += test_utils.cc test_utils.h
libdhcpsrv_unittests_SOURCES += timer_mgr_unittest.cc
libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
if HAVE_MYSQL
......
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <dhcpsrv/timer_mgr.h>
#include <boost/bind.hpp>
#include <gtest/gtest.h>
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;
namespace {
/// @brief Test fixture class for @c TimerMgr.
class TimerMgrTest : public ::testing::Test {
private:
/// @brief Prepares the class for a test.
virtual void SetUp();
/// @brief Cleans up after the test.
virtual void TearDown();
public:
/// @brief Wrapper method for registering a new timer.
///
/// This method registers a new timer in the @c TimerMgr. It associates a
/// @c timerCallback method with a timer. This method registers a number of
/// calls to the particular timer in the @c calls_count_ map.
///
/// @param timer_name Unique timer name.
/// @param timer_interval Timer interval.
/// @param mode Interval timer mode, which defaults to
/// @c IntervalTimer::ONE_SHOT.
void registerTimer(const std::string& timer_name, const long timer_interval,
const IntervalTimer::Mode& timer_mode = IntervalTimer::ONE_SHOT);
/// @brief Waits for one ready handler to be executed.
///
/// @param timeout Wait timeout.
/// @return false if the timeout has occurred, true otherwise. The returned
/// value of true indicates that the test was successful, i.e. the timer
/// ready handler had been executed before the timeout occurred.
bool waitForOne(const long timeout);
/// @brief Waits for a specified amount of time to execute ready handlers.
///
/// This method waits for exactly @c timeout amount of time for all ready
/// handlers to be executed. A caller can determine whether the expected
/// handlers have been executed by checking the @c calls_count_ entries.
///
/// @param timeout Wait timeout.
void waitForMany(const long timeout);
private:
/// @brief Wait for one or many ready handlers.
///
/// This method is called internally by the public methods
/// @c waitForOne and @c waitForMany.
///
/// @param timeout Wait timeout.
/// @param wait_for_many A boolean flag indicating if the method should
/// wait for the specified amount of time to execute all handlers (if true)
/// or should wait for one ready handlers (if false).
///
/// @return false if the timeout has occurred, true otherwise.
bool doWait(const long timeout, const bool wait_for_many);
/// @brief Generic callback for timers under test.
///
/// This callback increases the calls count for specified timer name.
///
/// @param timer_name Name of the timer for which callback counter should
/// be increased.
void timerCallback(const std::string& timer_name);
/// @brief Callback for timeout.
///
/// This callback is installed when the @c waitForOne or @c waitForMany
/// is executed to stop waiting after a given amount of time. It stops
/// the io service in the @c TimerMgr.
void timeoutCallback(asiolink::IOService& io_service);
/// @brief Internal flag indicating if test timeout occurred.
///
/// This flag is set by the @c timeoutCallback function when the timeout
/// has occurred. The @c waitWithTimeout returns 'false' if this value
/// is 'true', and 'true' if this value is 'false'.
bool timeout_occurred_;
public:
/// @brief Holds the calls count for test timers.
///
/// The key of this map holds the timer names. The value holds the number
/// of calls to the timer handlers.
std::map<std::string, unsigned int> calls_count_;
};
void
TimerMgrTest::SetUp() {
calls_count_.clear();
timeout_occurred_ = false;
}
void
TimerMgrTest::TearDown() {
}
void
TimerMgrTest::registerTimer(const std::string& timer_name, const long timer_interval,
const IntervalTimer::Mode& timer_mode) {
TimerMgr& timer_mgr = TimerMgr::instance();
ASSERT_NO_THROW(
timer_mgr.registerTimer(timer_name, boost::bind(&TimerMgrTest::timerCallback,
this, timer_name),
timer_interval, timer_mode)
);
calls_count_[timer_name] = 0;
}
bool
TimerMgrTest::waitForOne(const long timeout) {
return (doWait(timeout, false));
}
void
TimerMgrTest::waitForMany(const long timeout) {
static_cast<void>(doWait(timeout, true));
}
bool
TimerMgrTest::doWait(const long timeout, const bool wait_for_many) {
IOService& io_service = TimerMgr::instance().getIOService();
IntervalTimer timeout_timer(io_service);
timeout_timer.setup(boost::bind(&TimerMgrTest::timeoutCallback, this,
boost::ref(io_service)), timeout,
IntervalTimer::ONE_SHOT);
if (wait_for_many) {
io_service.run();
} else {
io_service.run_one();
}
if (timeout_occurred_) {
// Reset the flag so as it is set to false for another test.
timeout_occurred_ = false;
return (false);
}
// No timeout, some ready handlers have been executed.
return (true);
}
void
TimerMgrTest::timerCallback(const std::string& timer_name) {
// Accumulate the number of calls to the timer handler.
++calls_count_[timer_name];
}
void
TimerMgrTest::timeoutCallback(asiolink::IOService& io_service) {
// Timeout has occurred. Stop the io service to stop waiting for
// ready handlers.
io_service.stop();
// Indicate that we hit the timeout.
timeout_occurred_ = true;
}
TEST_F(TimerMgrTest, registerTimer) {
TimerMgr& timer_mgr = TimerMgr::instance();
ASSERT_NO_FATAL_FAILURE(registerTimer("timer1", 1));
TimerMgr::instance().setup("timer1");
EXPECT_TRUE(waitForOne(5));
EXPECT_EQ(1, calls_count_["timer1"]);
}
} // end of anonymous namespace
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcpsrv/timer_mgr.h>
#include <exceptions/exceptions.h>
using namespace isc;
using namespace isc::asiolink;
using namespace isc::util;
namespace isc {
namespace dhcp {
TimerMgr&
TimerMgr::instance() {
static TimerMgr timer_mgr;
return (timer_mgr);
}
TimerMgr::TimerMgr()
: io_service_(new IOService()) {
}
void
TimerMgr::registerTimer(const std::string& timer_name,
const IntervalTimer::Callback& callback,
const long interval,
const IntervalTimer::Mode& scheduling_mode) {
if (timer_name.empty()) {
isc_throw(BadValue, "registered timer name must not be empty");
}
if (registered_timers_.find(timer_name) != registered_timers_.end()) {
isc_throw(BadValue, "trying to register duplicate timer '"
<< timer_name << "'");
}
WatchSocket watch_socket;
IntervalTimerPtr interval_timer(new IntervalTimer(getIOService()));
TimerInfo timer_info(watch_socket, interval_timer, callback, interval,
scheduling_mode);
registered_timers_.insert(std::pair<std::string, TimerInfo>(timer_name, timer_info));
}
void
TimerMgr::deregisterTimer(const std::string& timer_name) {
}
void
TimerMgr::setup(const std::string& timer_name) {
TimerInfoMap::const_iterator timer_info_it = registered_timers_.find(timer_name);
if (timer_info_it == registered_timers_.end()) {
isc_throw(BadValue, "unable to setup timer '" << timer_name << "': "
"no such timer registered");
}
const TimerInfo& timer_info = timer_info_it->second;
timer_info.interval_timer_->setup(timer_info.callback_, timer_info.interval_,
timer_info.scheduling_mode_);
}
void
TimerMgr::localCallback(const std::string& timer_name) const {
}
} // end of namespace isc::dhcp
} // end of namespace isc
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef TIMER_MGR_H
#define TIMER_MGR_H
#include <asiolink/interval_timer.h>
#include <asiolink/io_service.h>
#include <util/watch_socket.h>
#include <boost/noncopyable.hpp>
#include <map>
#include <string>
namespace isc {
namespace dhcp {
class TimerMgr : public boost::noncopyable {
public:
static TimerMgr& instance();
void registerTimer(const std::string& timer_name,
const asiolink::IntervalTimer::Callback& callback,
const long interval,
const asiolink::IntervalTimer::Mode& scheduling_mode);
void deregisterTimer(const std::string& timer_name);
void setup(const std::string& timer_name);
asiolink::IOService& getIOService() const {
return (*io_service_);
}
private:
/// @brief Private default constructor.
///
/// The @c TimerMgr is a singleton class which instance must be created
/// using the @c TimerMgr::instance method. Private constructor enforces
/// construction via @c TimerMgr::instance.
TimerMgr();
/// @brief Callback function to be executed for each interval timer when
/// its scheduled interval elapses.
///
/// @param timer_name Unique timer name to be passed to the callback.
void localCallback(const std::string& timer_name) const;
/// @brief Holds the pointer to the io service object.
asiolink::IOServicePtr io_service_;
/// @brief Structure holding information for a single timer.
///
/// This structure holds the instance of the watch socket being used to
/// signal that the timer is "ready". It also holds the instance of the
/// interval timer.
struct TimerInfo {
/// @brief Instance of the watch socket.
util::WatchSocket watch_socket_;
/// @brief Instance of the interval timer.
asiolink::IntervalTimerPtr interval_timer_;
asiolink::IntervalTimer::Callback callback_;
long interval_;
asiolink::IntervalTimer::Mode scheduling_mode_;
TimerInfo(const util::WatchSocket& watch_socket,
const asiolink::IntervalTimerPtr& interval_timer,
const asiolink::IntervalTimer::Callback& callback,
const long interval,
const asiolink::IntervalTimer::Mode& mode)
: watch_socket_(watch_socket),
interval_timer_(interval_timer),
callback_(callback),
interval_(interval),
scheduling_mode_(mode) { };
};
typedef std::map<std::string, TimerInfo> TimerInfoMap;
/// @brief Holds mapping of the timer name to the watch socket and timer
/// instance.
///
/// Each registered timer has a unique name which is used as a key to
/// the map. The timer is associated with an instance of the @c WatchSocket
/// which is marked ready when the interval for the particular elapses.
std::map<std::string, TimerInfo> registered_timers_;
};
} // end of namespace isc::dhcp
} // end of namespace isc
#endif // TIMER_MGR_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment