Commit 91a8eaa5 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge branch 'master' into work/wildcard/main

Conflicts:
	src/lib/datasrc/memory_datasrc.cc
parents 7f570522 47f69905
164. [bug] y-aharen
IntervalTimer: Modified the interface to accept interval in
milliseconds. It shortens the time of the tests of IntervalTimer.
(Trac #452, git c9f6acc81e24c4b8f0eb351123dc7b43f64e0914)
163. [func] vorner
The pimpl design pattern is used in UDPServer, with a shared pointer. This
makes it smaller to copy (which is done a lot as a sideeffect of being
......
......@@ -375,6 +375,10 @@ AC_ARG_WITH([log4cxx],
log4cxx_library_path="${withval}/lib"
])
# This is an urgent fix to avoid regression due to log4cxx on some
# platforms. It should be cleaned up with a better fix.
if test "X$with_log4cxx" != "Xno"; then
# If not specified, try some common paths. These default to
# /usr/include and /usr/lib if not found
......@@ -405,7 +409,10 @@ if test "${log4cxx_library_path}"; then
fi
AC_SUBST(LOG4CXX_LDFLAGS)
# The following two lines are part of the urgent fix, and should be cleaned
# up with a better fix.
fi
AM_CONDITIONAL(USE_LOG4CXX, test "X${with_log4cxx}" != "Xno")
#
# Configure Boost header path
......
......@@ -51,7 +51,6 @@ b10_auth_LDADD += $(top_builddir)/src/lib/cc/libcc.la
b10_auth_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
b10_auth_LDADD += $(top_builddir)/src/lib/log/liblog.la
b10_auth_LDADD += $(SQLITE_LIBS)
# TODO: config.h.in is wrong because doesn't honor pkgdatadir
......
......@@ -354,7 +354,7 @@ AuthSrv::setMemoryDataSrc(const isc::dns::RRClass& rrclass,
uint32_t
AuthSrv::getStatisticsTimerInterval() const {
return (impl_->statistics_timer_.getInterval());
return (impl_->statistics_timer_.getInterval() / 1000);
}
void
......@@ -362,11 +362,17 @@ AuthSrv::setStatisticsTimerInterval(uint32_t interval) {
if (interval == impl_->statistics_timer_.getInterval()) {
return;
}
if (interval > 86400) {
// It can't occur since the value is checked in
// statisticsIntervalConfig::build().
isc_throw(InvalidParameter, "Too long interval: " << interval);
}
if (interval == 0) {
impl_->statistics_timer_.cancel();
} else {
impl_->statistics_timer_.setupTimer(
boost::bind(&AuthSrv::submitStatistics, this), interval);
impl_->statistics_timer_.setup(boost::bind(&AuthSrv::submitStatistics,
this),
interval * 1000);
}
if (impl_->verbose_mode_) {
if (interval == 0) {
......
......@@ -318,7 +318,8 @@ public:
/// If the specified value is non 0, the \c AuthSrv object will submit
/// its statistics to the statistics module every \c interval seconds.
/// If it's 0, and \c AuthSrv currently submits statistics, the submission
/// will be disabled.
/// will be disabled. \c interval must be equal to or shorter than 86400
/// seconds (1 day).
///
/// This method should normally not throw an exception; however, its
/// underlying library routines may involve resource allocation, and
......
......@@ -179,9 +179,14 @@ public:
virtual void build(ConstElementPtr config_value) {
const int32_t config_interval = config_value->intValue();
if (config_interval < 0) {
isc_throw(AuthConfigError, "negative statistics-interval value: "
isc_throw(AuthConfigError, "Negative statistics interval value: "
<< config_interval);
}
if (config_interval > 86400) {
isc_throw(AuthConfigError, "Statistics interval value "
<< config_interval
<< " must be equal to or shorter than 86400");
}
interval_ = config_interval;
}
virtual void commit() {
......
......@@ -98,7 +98,7 @@ AuthConmmandTest::stopServer() {
TEST_F(AuthConmmandTest, shutdown) {
asiolink::IntervalTimer itimer(server.getIOService());
itimer.setupTimer(boost::bind(&AuthConmmandTest::stopServer, this), 1);
itimer.setup(boost::bind(&AuthConmmandTest::stopServer, this), 1);
server.getIOService().run();
EXPECT_EQ(0, rcode);
}
......
......@@ -365,5 +365,9 @@ TEST_F(StatisticsIntervalConfigTest, badInterval) {
EXPECT_THROW(parser->build(Element::fromJSON("2.5")),
isc::data::TypeError);
EXPECT_THROW(parser->build(Element::fromJSON("-1")), AuthConfigError);
// bounds check: interval value must be equal to or shorter than
// 86400 seconds (1 day)
EXPECT_NO_THROW(parser->build(Element::fromJSON("86400")));
EXPECT_THROW(parser->build(Element::fromJSON("86401")), AuthConfigError);
}
}
......@@ -656,21 +656,20 @@ private:
public:
IntervalTimerImpl(IOService& io_service);
~IntervalTimerImpl();
void setupTimer(const IntervalTimer::Callback& cbfunc,
const uint32_t interval);
void setup(const IntervalTimer::Callback& cbfunc, const long interval);
void callback(const asio::error_code& error);
void cancel() {
timer_.cancel();
interval_ = 0;
}
uint32_t getInterval() const { return (interval_); }
long getInterval() const { return (interval_); }
private:
// a function to update timer_ when it expires
void updateTimer();
void update();
// a function to call back when timer_ expires
IntervalTimer::Callback cbfunc_;
// interval in seconds
uint32_t interval_;
// interval in milliseconds
long interval_;
// asio timer
asio::deadline_timer timer_;
};
......@@ -683,12 +682,13 @@ IntervalTimerImpl::~IntervalTimerImpl()
{}
void
IntervalTimerImpl::setupTimer(const IntervalTimer::Callback& cbfunc,
const uint32_t interval)
IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
const long interval)
{
// Interval should not be 0.
if (interval == 0) {
isc_throw(isc::BadValue, "Interval should not be 0");
// Interval should not be less than or equal to 0.
if (interval <= 0) {
isc_throw(isc::BadValue, "Interval should not be less than or "
"equal to 0");
}
// Call back function should not be empty.
if (cbfunc.empty()) {
......@@ -699,19 +699,19 @@ IntervalTimerImpl::setupTimer(const IntervalTimer::Callback& cbfunc,
// Set initial expire time.
// At this point the timer is not running yet and will not expire.
// After calling IOService::run(), the timer will expire.
updateTimer();
update();
return;
}
void
IntervalTimerImpl::updateTimer() {
IntervalTimerImpl::update() {
if (interval_ == 0) {
// timer has been canceled. Do nothing.
return;
}
try {
// Update expire time to (current time + interval_).
timer_.expires_from_now(boost::posix_time::seconds(interval_));
timer_.expires_from_now(boost::posix_time::millisec(interval_));
} catch (const asio::system_error& e) {
isc_throw(isc::Unexpected, "Failed to update timer");
}
......@@ -726,7 +726,7 @@ IntervalTimerImpl::callback(const asio::error_code& cancelled) {
if (!cancelled) {
cbfunc_();
// Set next expire time.
updateTimer();
update();
}
}
......@@ -739,8 +739,8 @@ IntervalTimer::~IntervalTimer() {
}
void
IntervalTimer::setupTimer(const Callback& cbfunc, const uint32_t interval) {
return (impl_->setupTimer(cbfunc, interval));
IntervalTimer::setup(const Callback& cbfunc, const long interval) {
return (impl_->setup(cbfunc, interval));
}
void
......@@ -748,7 +748,7 @@ IntervalTimer::cancel() {
impl_->cancel();
}
uint32_t
long
IntervalTimer::getInterval() const {
return (impl_->getInterval());
}
......
......@@ -613,38 +613,36 @@ private:
/// \brief The \c IntervalTimer class is a wrapper for the ASIO
/// \c asio::deadline_timer class.
///
/// This class is implemented to use \c asio::deadline_timer as
/// interval timer.
/// This class is implemented to use \c asio::deadline_timer as interval
/// timer.
///
/// \c setupTimer() sets a timer to expire on (now + interval) and
/// a call back function.
/// \c setup() sets a timer to expire on (now + interval) and a call back
/// function.
///
/// \c IntervalTimerImpl::callback() is called by the timer when
/// it expires.
/// \c IntervalTimerImpl::callback() is called by the timer when it expires.
///
/// The function calls the call back function set by \c setupTimer()
/// and updates the timer to expire in (now + interval) seconds.
/// The function calls the call back function set by \c setup() and updates
/// the timer to expire in (now + interval) milliseconds.
/// The type of call back function is \c void(void).
///
/// The call back function will not be called if the instance of this
/// class is destructed before the timer is expired.
/// The call back function will not be called if the instance of this class is
/// destroyed before the timer is expired.
///
/// Note: Destruction of an instance of this class while call back
/// is pending causes throwing an exception from \c IOService.
/// Note: Destruction of an instance of this class while call back is pending
/// causes throwing an exception from \c IOService.
///
/// Sample code:
/// \code
/// void function_to_call_back() {
/// // this function will be called periodically
/// }
/// int interval_in_seconds = 1;
/// int interval_in_milliseconds = 1000;
/// IOService io_service;
///
/// IntervalTimer intervalTimer(io_service);
/// intervalTimer.setupTimer(function_to_call_back, interval_in_seconds);
/// intervalTimer.setup(function_to_call_back, interval_in_milliseconds);
/// io_service.run();
/// \endcode
///
class IntervalTimer {
public:
/// \name The type of timer callback function
......@@ -667,7 +665,6 @@ public:
/// This constructor may also throw \c asio::system_error.
///
/// \param io_service A reference to an instance of IOService
///
IntervalTimer(IOService& io_service);
/// \brief The destructor.
......@@ -676,28 +673,26 @@ public:
///
/// On the destruction of this class the timer will be canceled
/// inside \c asio::deadline_timer.
///
~IntervalTimer();
//@}
/// \brief Register timer callback function and interval.
///
/// This function sets callback function and interval in seconds.
/// This function sets callback function and interval in milliseconds.
/// Timer will actually start after calling \c IOService::run().
///
/// \param cbfunc A reference to a function \c void(void) to call back
/// when the timer is expired (should not be an empty functor)
/// \param interval Interval in seconds (greater than 0)
/// \param interval Interval in milliseconds (greater than 0)
///
/// Note: IntervalTimer will not pass \c asio::error_code to
/// call back function. In case the timer is cancelled, the function
/// will not be called.
///
/// \throw isc::InvalidParameter cbfunc is empty
/// \throw isc::BadValue interval is 0
/// \throw isc::BadValue interval is less than or equal to 0
/// \throw isc::Unexpected ASIO library error
///
void setupTimer(const Callback& cbfunc, const uint32_t interval);
void setup(const Callback& cbfunc, const long interval);
/// Cancel the timer.
///
......@@ -711,15 +706,11 @@ public:
/// Return the timer interval.
///
/// This method returns the timer interval in seconds if it's running;
/// This method returns the timer interval in milliseconds if it's running;
/// if the timer has been canceled it returns 0.
///
/// This method never throws an exception.
///
/// Note: We may want to change the granularity of the timer to
/// milliseconds or even finer. If and when this happens the semantics
/// of the return value of this method will be changed accordingly.
uint32_t getInterval() const;
long getInterval() const;
private:
IntervalTimerImpl* impl_;
......
......@@ -1028,13 +1028,12 @@ protected:
++count_;
if (count_ == 1) {
// First time of call back.
// Call setupTimer() to update callback function
// to TimerCallBack.
// Call setup() to update callback function to TimerCallBack.
test_obj_->timer_called_ = false;
timer_.setupTimer(TimerCallBack(test_obj_), 1);
timer_.setup(TimerCallBack(test_obj_), 100);
} else if (count_ == 2) {
// Second time of call back.
// If it reaches here, re-setupTimer() is failed (unexpected).
// If it reaches here, re-setup() is failed (unexpected).
// We should stop here.
test_obj_->io_service_.stop();
}
......@@ -1055,10 +1054,11 @@ TEST_F(IntervalTimerTest, invalidArgumentToIntervalTimer) {
// Create asio_link::IntervalTimer and setup.
IntervalTimer itimer(io_service_);
// expect throw if call back function is empty
EXPECT_THROW(itimer.setupTimer(IntervalTimer::Callback(), 1),
isc::InvalidParameter);
// expect throw if interval is 0
EXPECT_THROW(itimer.setupTimer(TimerCallBack(this), 0), isc::BadValue);
EXPECT_THROW(itimer.setup(IntervalTimer::Callback(), 1),
isc::InvalidParameter);
// expect throw if interval is not greater than 0
EXPECT_THROW(itimer.setup(TimerCallBack(this), 0), isc::BadValue);
EXPECT_THROW(itimer.setup(TimerCallBack(this), -1), isc::BadValue);
}
TEST_F(IntervalTimerTest, startIntervalTimer) {
......@@ -1070,33 +1070,30 @@ TEST_F(IntervalTimerTest, startIntervalTimer) {
boost::posix_time::ptime start;
start = boost::posix_time::microsec_clock::universal_time();
// setup timer
itimer.setupTimer(TimerCallBack(this), 1);
EXPECT_EQ(1, itimer.getInterval());
itimer.setup(TimerCallBack(this), 100);
EXPECT_EQ(100, itimer.getInterval());
io_service_.run();
// reaches here after timer expired
// delta: difference between elapsed time and 1 second
// delta: difference between elapsed time and 100 milliseconds.
boost::posix_time::time_duration delta =
(boost::posix_time::microsec_clock::universal_time() - start)
- boost::posix_time::seconds(1);
- boost::posix_time::millisec(100);
if (delta.is_negative()) {
delta.invert_sign();
}
// expect TimerCallBack is called; timer_called_ is true
EXPECT_TRUE(timer_called_);
// expect interval is 1 second +/- TIMER_MARGIN_MSEC.
// expect interval is 100 milliseconds +/- TIMER_MARGIN_MSEC.
EXPECT_TRUE(delta < TIMER_MARGIN_MSEC);
}
TEST_F(IntervalTimerTest, destructIntervalTimer) {
// Note: This test currently takes 6 seconds. The timer should have
// finer granularity and timer periods in this test should be shorter
// in the future.
// This code isn't exception safe, but we'd rather keep the code
// simpler and more readable as this is only for tests and if it throws
// the program would immediately terminate anyway.
// The call back function will not be called after the timer is
// destructed.
// destroyed.
//
// There are two timers:
// itimer_counter (A)
......@@ -1105,31 +1102,30 @@ TEST_F(IntervalTimerTest, destructIntervalTimer) {
// itimer_canceller (B)
// (Calls TimerCallBackCancelDeleter)
// - first time of callback, it stores the counter value of
// callback_canceller and destructs itimer_counter
// callback_canceller and destroys itimer_counter
// - second time of callback, it compares the counter value of
// callback_canceller with stored value
// if they are same the timer was not called; expected result
// if they are different the timer was called after destructed
//
// 0 1 2 3 4 5 6 (s)
// (A) i-----+--x
// ^
// |destruct itimer_counter
// (B) i--------+--------s
// ^stop io_service
// and test itimer_counter have been stopped
// if they are different the timer was called after destroyed
//
// 0 100 200 300 400 500 600 (ms)
// (A) i--------+----x
// ^
// |destroy itimer_counter
// (B) i-------------+--------------s
// ^stop io_service
// and check if itimer_counter have been
// stopped
// itimer_counter will be deleted in TimerCallBackCancelDeleter
IntervalTimer* itimer_counter = new IntervalTimer(io_service_);
IntervalTimer itimer_canceller(io_service_);
timer_cancel_success_ = false;
TimerCallBackCounter callback_canceller(this);
itimer_counter->setupTimer(callback_canceller, 2);
itimer_canceller.setupTimer(
TimerCallBackCancelDeleter(this, itimer_counter,
callback_canceller),
3);
itimer_counter->setup(callback_canceller, 200);
itimer_canceller.setup(
TimerCallBackCancelDeleter(this, itimer_counter, callback_canceller),
300);
io_service_.run();
EXPECT_TRUE(timer_cancel_success_);
}
......@@ -1140,9 +1136,8 @@ TEST_F(IntervalTimerTest, cancel) {
IntervalTimer itimer_counter(io_service_);
IntervalTimer itimer_watcher(io_service_);
unsigned int counter = 0;
itimer_counter.setupTimer(TimerCallBackCanceller(counter, itimer_counter),
1);
itimer_watcher.setupTimer(TimerCallBack(this), 3);
itimer_counter.setup(TimerCallBackCanceller(counter, itimer_counter), 100);
itimer_watcher.setup(TimerCallBack(this), 200);
io_service_.run();
EXPECT_EQ(1, counter);
EXPECT_EQ(0, itimer_counter.getInterval());
......@@ -1152,36 +1147,31 @@ TEST_F(IntervalTimerTest, cancel) {
}
TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
// Note: This test currently takes 4 seconds. The timer should have
// finer granularity and timer periods in this test should be shorter
// in the future.
// Calling setupTimer() multiple times updates call back function
// and interval.
// Calling setup() multiple times updates call back function and interval.
//
// There are two timers:
// itimer (A)
// (Calls TimerCallBackCounter / TimerCallBack)
// - increments internal counter in callback function
// (TimerCallBackCounter)
// interval: 2 seconds
// interval: 300 milliseconds
// - io_service_.stop() (TimerCallBack)
// interval: 1 second
// interval: 100 milliseconds
// itimer_overwriter (B)
// (Calls TimerCallBackOverwriter)
// - first time of callback, it calls setupTimer() to change
// call back function and interval of itimer to
// TimerCallBack / 1 second
// after 3 + 1 seconds from the beginning of this test,
// - first time of callback, it calls setup() to change call back
// function to TimerCallBack and interval of itimer to 100
// milliseconds
// after 300 + 100 milliseconds from the beginning of this test,
// TimerCallBack() will be called and io_service_ stops.
// - second time of callback, it means the test fails.
//
// 0 1 2 3 4 5 6 (s)
// (A) i-----+--C--s
// ^ ^stop io_service
// |change call back function
// (B) i--------+--------S
// ^(stop io_service on fail)
// 0 100 200 300 400 500 600 700 800 (ms)
// (A) i-------------+----C----s
// ^ ^stop io_service
// |change call back function
// (B) i------------------+-------------------S
// ^(stop io_service on fail)
//
IntervalTimer itimer(io_service_);
......@@ -1189,22 +1179,22 @@ TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
// store start time
boost::posix_time::ptime start;
start = boost::posix_time::microsec_clock::universal_time();
itimer.setupTimer(TimerCallBackCounter(this), 2);
itimer_overwriter.setupTimer(TimerCallBackOverwriter(this, itimer), 3);
itimer.setup(TimerCallBackCounter(this), 300);
itimer_overwriter.setup(TimerCallBackOverwriter(this, itimer), 400);
io_service_.run();
// reaches here after timer expired
// if interval is updated, it takes
// 3 seconds for TimerCallBackOverwriter
// + 1 second for TimerCallBack (stop)
// = 4 seconds.
// 400 milliseconds for TimerCallBackOverwriter
// + 100 milliseconds for TimerCallBack (stop)
// = 500 milliseconds.
// otherwise (test fails), it takes
// 3 seconds for TimerCallBackOverwriter
// + 3 seconds for TimerCallBackOverwriter (stop)
// = 6 seconds.
// delta: difference between elapsed time and 3 + 1 seconds
// 400 milliseconds for TimerCallBackOverwriter
// + 400 milliseconds for TimerCallBackOverwriter (stop)
// = 800 milliseconds.
// delta: difference between elapsed time and 400 + 100 milliseconds
boost::posix_time::time_duration delta =
(boost::posix_time::microsec_clock::universal_time() - start)
- boost::posix_time::seconds(3 + 1);
- boost::posix_time::millisec(400 + 100);
if (delta.is_negative()) {
delta.invert_sign();
}
......
......@@ -35,7 +35,8 @@ namespace datasrc {
struct MemoryZone::MemoryZoneImpl {
// Constructor
MemoryZoneImpl(const RRClass& zone_class, const Name& origin) :
zone_class_(zone_class), origin_(origin), origin_data_(NULL)
zone_class_(zone_class), origin_(origin), origin_data_(NULL),
domains_(true)
{
// We create the node for origin (it needs to exist anyway in future)
domains_.insert(origin, &origin_data_);
......@@ -59,7 +60,7 @@ struct MemoryZone::MemoryZoneImpl {
typedef Domain::value_type DomainPair;
typedef boost::shared_ptr<Domain> DomainPtr;
// The tree stores domains
typedef RBTree<Domain, true> DomainTree;
typedef RBTree<Domain> DomainTree;
typedef RBNode<Domain> DomainNode;
static const DomainNode::Flags DOMAINFLAG_WILD = DomainNode::FLAG_USER1;
......@@ -93,14 +94,13 @@ struct MemoryZone::MemoryZoneImpl {
l > origin_labels;
--l, wname = wname.split(1)) {
if (wname.isWildcard()) {
DomainNode* node;
DomainTree::Result result;
// Ensure a separate level exists for the wildcard name.
// Note: for 'name' itself we do this later anyway, but the
// overhead should be marginal because wildcard names should
// be rare.
result = domains.insert(wname.split(1), &node);
DomainNode* node;
DomainTree::Result result(domains.insert(wname.split(1),
&node));
assert(result == DomainTree::SUCCESS ||
result == DomainTree::ALREADYEXISTS);
node->setFlag(DOMAINFLAG_WILD);
......@@ -359,7 +359,8 @@ struct MemoryZone::MemoryZoneImpl {
// Get the node
DomainNode* node(NULL);
FindState state(options);
switch (domains_.find(name, &node, cutCallback, &state)) {
RBTreeNodeChain<Domain> node_path;
switch (domains_.find(name, &node, node_path, cutCallback, &state)) {
case DomainTree::PARTIALMATCH:
/*
* In fact, we could use a single variable instead of
......@@ -408,10 +409,16 @@ struct MemoryZone::MemoryZoneImpl {
// EXACTMATCH
break;
}
// TODO: we should also cover empty non-terminal cases, which
// will require non trivial code and is deferred for later
// development. For now, we regard any partial match that
// didn't hit a zone cut as "not found".
// If the RBTree search stopped at a node for a super domain
// of the search name, it means the search name exists in
// the zone but is empty. Treat it as NXRRSET.
if (node_path.getLastComparisonResult().getRelation() ==
NameComparisonResult::SUPERDOMAIN) {
return (FindResult(NXRRSET, ConstRRsetPtr()));
}
// fall through
case DomainTree::NOTFOUND:
return (FindResult(NXDOMAIN, ConstRRsetPtr()));
case DomainTree::EXACTMATCH: // This one is OK, handle it
......@@ -419,7 +426,7 @@ struct MemoryZone::MemoryZoneImpl {
default:
assert(0);
}
assert(node);
assert(node != NULL);
// If there is an exact match but the node is empty, it's equivalent
// to NXRRSET.
......
This diff is collapsed.