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();
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() {
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) {
}
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.
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_();
}
}
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
/// 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
/// \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
///
/// \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
/// \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.
///
boost::shared_ptr<IntervalTimerImpl> impl_;
};
+typedef boost::shared_ptr<isc::asiolink::IntervalTimer> IntervalTimerPtr;
+
} // namespace asiolink
} // namespace isc
#endif // ASIOLINK_INTERVAL_TIMER_H
}
void operator()() {
++counter_;
+ std::cout << "inside counter cb: " << counter_ << std::endl;
return;
}
int counter_;
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_;
// 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);
+}