interval_timer.cc 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <asiolink/asio_wrapper.h>
  8. #include <asiolink/interval_timer.h>
  9. #include <asiolink/io_service.h>
  10. #include <boost/bind.hpp>
  11. #include <boost/enable_shared_from_this.hpp>
  12. #include <boost/shared_ptr.hpp>
  13. #include <exceptions/exceptions.h>
  14. namespace isc {
  15. namespace asiolink {
  16. /// This class holds a call back function of asynchronous operations.
  17. /// To ensure the object is alive while an asynchronous operation refers
  18. /// to it, we use shared_ptr and enable_shared_from_this.
  19. /// The object will be destructed in case IntervalTimer has been destructed
  20. /// and no asynchronous operation refers to it.
  21. /// Please follow the link to get an example:
  22. /// http://think-async.com/asio/asio-1.4.8/doc/asio/tutorial/tutdaytime3.html#asio.tutorial.tutdaytime3.the_tcp_connection_class
  23. class IntervalTimerImpl :
  24. public boost::enable_shared_from_this<IntervalTimerImpl>
  25. {
  26. private:
  27. // prohibit copy
  28. IntervalTimerImpl(const IntervalTimerImpl& source);
  29. IntervalTimerImpl& operator=(const IntervalTimerImpl& source);
  30. public:
  31. IntervalTimerImpl(IOService& io_service);
  32. ~IntervalTimerImpl();
  33. void setup(const IntervalTimer::Callback& cbfunc, const long interval,
  34. const IntervalTimer::Mode& interval_mode
  35. = IntervalTimer::REPEATING);
  36. void callback(const boost::system::error_code& error);
  37. void cancel() {
  38. timer_.cancel();
  39. interval_ = 0;
  40. }
  41. long getInterval() const { return (interval_); }
  42. private:
  43. // a function to update timer_ when it expires
  44. void update();
  45. // a function to call back when timer_ expires
  46. IntervalTimer::Callback cbfunc_;
  47. // interval in milliseconds
  48. long interval_;
  49. // asio timer
  50. boost::asio::deadline_timer timer_;
  51. // Controls how the timer behaves after expiration.
  52. IntervalTimer::Mode mode_;
  53. // interval_ will be set to this value in destructor in order to detect
  54. // use-after-free type of bugs.
  55. static const long INVALIDATED_INTERVAL = -1;
  56. };
  57. IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
  58. interval_(0), timer_(io_service.get_io_service()),
  59. mode_(IntervalTimer::REPEATING)
  60. {}
  61. IntervalTimerImpl::~IntervalTimerImpl() {
  62. interval_ = INVALIDATED_INTERVAL;
  63. }
  64. void
  65. IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
  66. const long interval,
  67. const IntervalTimer::Mode& mode)
  68. {
  69. // Interval should not be less than or equal to 0.
  70. if (interval <= 0) {
  71. isc_throw(isc::BadValue, "Interval should not be less than or "
  72. "equal to 0");
  73. }
  74. // Call back function should not be empty.
  75. if (cbfunc.empty()) {
  76. isc_throw(isc::InvalidParameter, "Callback function is empty");
  77. }
  78. cbfunc_ = cbfunc;
  79. interval_ = interval;
  80. mode_ = mode;
  81. // Set initial expire time.
  82. // At this point the timer is not running yet and will not expire.
  83. // After calling IOService::run(), the timer will expire.
  84. update();
  85. }
  86. void
  87. IntervalTimerImpl::update() {
  88. try {
  89. // Update expire time to (current time + interval_).
  90. timer_.expires_from_now(boost::posix_time::millisec(interval_));
  91. // Reset timer.
  92. // Pass a function bound with a shared_ptr to this.
  93. timer_.async_wait(boost::bind(&IntervalTimerImpl::callback,
  94. shared_from_this(),
  95. boost::asio::placeholders::error));
  96. } catch (const boost::system::system_error& e) {
  97. isc_throw(isc::Unexpected, "Failed to update timer: " << e.what());
  98. } catch (const boost::bad_weak_ptr&) {
  99. // Can't happen. It means a severe internal bug.
  100. assert(0);
  101. }
  102. }
  103. void
  104. IntervalTimerImpl::callback(const boost::system::error_code& ec) {
  105. assert(interval_ != INVALIDATED_INTERVAL);
  106. if (interval_ == 0 || ec) {
  107. // timer has been canceled. Do nothing.
  108. } else {
  109. // If we should repeat, set next expire time.
  110. if (mode_ == IntervalTimer::REPEATING) {
  111. update();
  112. }
  113. // Invoke the call back function.
  114. cbfunc_();
  115. }
  116. }
  117. IntervalTimer::IntervalTimer(IOService& io_service) :
  118. impl_(new IntervalTimerImpl(io_service))
  119. {}
  120. IntervalTimer::~IntervalTimer() {
  121. // Cancel the timer to make sure cbfunc_() will not be called any more.
  122. cancel();
  123. }
  124. void
  125. IntervalTimer::setup(const Callback& cbfunc, const long interval,
  126. const IntervalTimer::Mode& mode) {
  127. return (impl_->setup(cbfunc, interval, mode));
  128. }
  129. void
  130. IntervalTimer::cancel() {
  131. impl_->cancel();
  132. }
  133. long
  134. IntervalTimer::getInterval() const {
  135. return (impl_->getInterval());
  136. }
  137. } // namespace asiolink
  138. } // namespace isc