Commit 9d712897 authored by Thomas Markwalder's avatar Thomas Markwalder
Browse files

[3407] Added one-shot behavior to set asiolink::IntervalTimer

asiolink::IntervalTimer::setup() has been extended to accept a mode
parameter which determines if the timer will reschedule itself after
each expiration (default behavior), or if it should only run for a
single interval and stop (one-shot).
parent c00d5602
......@@ -44,7 +44,9 @@ private:
public:
IntervalTimerImpl(IOService& io_service);
~IntervalTimerImpl();
void setup(const IntervalTimer::Callback& cbfunc, const long interval);
void setup(const IntervalTimer::Callback& cbfunc, const long interval,
const IntervalTimer::Mode& interval_mode
= IntervalTimer::REPEATING);
void callback(const asio::error_code& error);
void cancel() {
timer_.cancel();
......@@ -60,13 +62,18 @@ private:
long interval_;
// asio timer
asio::deadline_timer timer_;
// Controls how the timer behaves after expiration.
IntervalTimer::Mode mode_;
// interval_ will be set to this value in destructor in order to detect
// use-after-free type of bugs.
static const long INVALIDATED_INTERVAL = -1;
};
IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
interval_(0), timer_(io_service.get_io_service())
interval_(0), timer_(io_service.get_io_service()),
mode_(IntervalTimer::REPEATING)
{}
IntervalTimerImpl::~IntervalTimerImpl() {
......@@ -75,7 +82,8 @@ IntervalTimerImpl::~IntervalTimerImpl() {
void
IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
const long interval)
const long interval,
const IntervalTimer::Mode& mode)
{
// Interval should not be less than or equal to 0.
if (interval <= 0) {
......@@ -88,6 +96,8 @@ IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
}
cbfunc_ = cbfunc;
interval_ = interval;
mode_ = mode;
// 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.
......@@ -118,8 +128,11 @@ IntervalTimerImpl::callback(const asio::error_code& ec) {
if (interval_ == 0 || ec) {
// timer has been canceled. Do nothing.
} else {
// Set next expire time.
update();
// If we should repeat, set next expire time.
if (mode_ == IntervalTimer::REPEATING) {
update();
}
// Invoke the call back function.
cbfunc_();
}
......@@ -135,8 +148,9 @@ IntervalTimer::~IntervalTimer() {
}
void
IntervalTimer::setup(const Callback& cbfunc, const long interval) {
return (impl_->setup(cbfunc, interval));
IntervalTimer::setup(const Callback& cbfunc, const long interval,
const IntervalTimer::Mode& mode) {
return (impl_->setup(cbfunc, interval, mode));
}
void
......
......@@ -31,13 +31,15 @@ class IntervalTimerImpl;
/// This class is implemented to use \c asio::deadline_timer as interval
/// timer.
///
/// \c setup() sets a timer to expire on (now + interval) and a call back
/// function.
/// \c setup() sets a timer to expire on (now + interval), a call back
/// function, and an interval mode.
///
/// \c IntervalTimerImpl::callback() is called by the timer when it expires.
///
/// The function calls the call back function set by \c setup() and updates
/// the timer to expire in (now + interval) milliseconds.
/// The function calls the call back function set by \c setup() and if the
/// the interval mode indicates a repeating interval, will reschedule 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
......@@ -60,6 +62,15 @@ public:
/// \name The type of timer callback function
typedef boost::function<void()> Callback;
/// \brief Defines possible timer modes used to setup a timer.
/// - REPEATING - Timer will reschedule itself after each expiration
/// - ONE_SHOT - Timer will expire after one interval and not reschedule.
enum Mode
{
REPEATING,
ONE_SHOT
};
///
/// \name Constructors and Destructor
///
......@@ -96,6 +107,9 @@ public:
/// \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 milliseconds (greater than 0)
/// \param mode Determines if the timer will automatically reschedule after
/// each expiration (the default) or behave as a one-shot which will run
/// for a single interval and not reschedule.
///
/// Note: IntervalTimer will not pass \c asio::error_code to
/// call back function. In case the timer is canceled, the function
......@@ -104,7 +118,8 @@ public:
/// \throw isc::InvalidParameter cbfunc is empty
/// \throw isc::BadValue interval is less than or equal to 0
/// \throw isc::Unexpected internal runtime error
void setup(const Callback& cbfunc, const long interval);
void setup(const Callback& cbfunc, const long interval,
const Mode& = REPEATING);
/// Cancel the timer.
///
......@@ -128,6 +143,8 @@ private:
boost::shared_ptr<IntervalTimerImpl> impl_;
};
typedef boost::shared_ptr<isc::asiolink::IntervalTimer> IntervalTimerPtr;
} // namespace asiolink
} // namespace isc
#endif // ASIOLINK_INTERVAL_TIMER_H
......@@ -57,6 +57,7 @@ protected:
}
void operator()() {
++counter_;
std::cout << "inside counter cb: " << counter_ << std::endl;
return;
}
int counter_;
......@@ -137,6 +138,20 @@ protected:
IntervalTimer& timer_;
int count_;
};
class TimerCallBackAccumulator: public std::unary_function<void, void> {
public:
TimerCallBackAccumulator(IntervalTimerTest* test_obj, int &counter) :
test_obj_(test_obj), counter_(counter) {
}
void operator()() {
++counter_;
return;
}
private:
IntervalTimerTest* test_obj_;
// Reference to integer accumulator
int& counter_;
};
protected:
IOService io_service_;
bool timer_called_;
......@@ -285,3 +300,70 @@ TEST_F(IntervalTimerTest, overwriteIntervalTimer) {
// Expect interval is updated: return value of getInterval() is updated
EXPECT_EQ(itimer.getInterval(), 100);
}
// This test verifies that timers operate correclty based on their mode.
TEST_F(IntervalTimerTest, intervalModeTest) {
// Create a timer to control the duration of the test.
IntervalTimer test_timer(io_service_);
test_timer.setup(TimerCallBack(this), 550);
// Create an timer which automatically reschedules itself. Use the
// accumulator callback to increment local counter for it.
int repeater_count = 0;
IntervalTimer repeater(io_service_);
repeater.setup(TimerCallBackAccumulator(this, repeater_count), 100);
// Create a one-shot timer. Use the accumulator callback to increment
// local counter variable for it.
int one_shot_count = 0;
IntervalTimer one_shot(io_service_);
one_shot.setup(TimerCallBackAccumulator(this, one_shot_count), 100,
IntervalTimer::ONE_SHOT);
// Run until the test_timer expires.
io_service_.run();
// Verify the repeating timer repeated and the one-shot did not.
EXPECT_EQ(repeater_count, 5);
EXPECT_EQ(one_shot_count, 1);
}
// This test verifies that the same timer can be reused in either mode.
TEST_F(IntervalTimerTest, timerReuseTest) {
// Create a timer to control the duration of the test.
IntervalTimer test_timer(io_service_);
test_timer.setup(TimerCallBack(this), 550);
// Create a one-shot timer. Use the accumulator callback to increment
// local counter variable for it.
int one_shot_count = 0;
IntervalTimer one_shot(io_service_);
TimerCallBackAccumulator callback(this, one_shot_count);
one_shot.setup(callback, 100, IntervalTimer::ONE_SHOT);
// Run until a single event handler executes. This should be our
// one-shot expiring.
io_service_.run_one();
// Verify the timer expired once.
ASSERT_EQ(one_shot_count, 1);
// Setup the one-shot to go again.
one_shot.setup(callback, 100, IntervalTimer::ONE_SHOT);
// Run until a single event handler executes. This should be our
// one-shot expiring.
io_service_.run_one();
// Verify the timer expired once.
ASSERT_EQ(one_shot_count, 2);
// Setup the timer to be repeating.
one_shot.setup(callback, 100, IntervalTimer::REPEATING);
// Run until the test_timer expires.
io_service_.run();
// Verify the timer repeated.
EXPECT_GE(one_shot_count, 4);
}
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