timer_mgr.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. // Copyright (C) 2016-2017 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/io_service.h>
  9. #include <dhcpsrv/dhcpsrv_log.h>
  10. #include <dhcpsrv/timer_mgr.h>
  11. #include <exceptions/exceptions.h>
  12. #include <boost/bind.hpp>
  13. #include <utility>
  14. using namespace isc;
  15. using namespace isc::asiolink;
  16. namespace {
  17. /// @brief Structure holding information for a single timer.
  18. ///
  19. /// This structure holds the instance of the watch socket being used to
  20. /// signal that the timer is "ready". It also holds the instance of the
  21. /// interval timer and other parameters pertaining to it.
  22. struct TimerInfo {
  23. /// @brief Instance of the interval timer.
  24. asiolink::IntervalTimer interval_timer_;
  25. /// @brief Holds the pointer to the callback supplied when registering
  26. /// the timer.
  27. asiolink::IntervalTimer::Callback user_callback_;
  28. /// @brief Interval timer interval supplied during registration.
  29. long interval_;
  30. /// @brief Interval timer scheduling mode supplied during registration.
  31. asiolink::IntervalTimer::Mode scheduling_mode_;
  32. /// @brief Constructor.
  33. ///
  34. /// @param io_service Reference to the IO service to be used by the
  35. /// interval timer created.
  36. /// @param user_callback Pointer to the callback function supplied
  37. /// during the timer registration.
  38. /// @param interval Timer interval in milliseconds.
  39. /// @param mode Interval timer scheduling mode.
  40. TimerInfo(asiolink::IOService& io_service,
  41. const asiolink::IntervalTimer::Callback& user_callback,
  42. const long interval,
  43. const asiolink::IntervalTimer::Mode& mode)
  44. : interval_timer_(io_service),
  45. user_callback_(user_callback),
  46. interval_(interval),
  47. scheduling_mode_(mode) { };
  48. };
  49. }
  50. namespace isc {
  51. namespace dhcp {
  52. /// @brief A type definition for the pointer to @c TimerInfo structure.
  53. typedef boost::shared_ptr<TimerInfo> TimerInfoPtr;
  54. /// @brief A type definition for the map holding timers configuration.
  55. typedef std::map<std::string, TimerInfoPtr> TimerInfoMap;
  56. /// @brief Implementation of the @c TimerMgr
  57. class TimerMgrImpl {
  58. public:
  59. /// @brief Constructor.
  60. TimerMgrImpl();
  61. /// @brief Sets IO service to be used by the Timer Manager.
  62. ///
  63. /// @param io_service Pointer to the new IO service.
  64. void setIOService(const IOServicePtr& io_service);
  65. /// @brief Registers new timer in the @c TimerMgr.
  66. ///
  67. /// @param timer_name Unique name for the timer.
  68. /// @param callback Pointer to the callback function to be invoked
  69. /// when the timer elapses, e.g. function processing expired leases
  70. /// in the DHCP server.
  71. /// @param interval Timer interval in milliseconds.
  72. /// @param scheduling_mode Scheduling mode of the timer as described in
  73. /// @c asiolink::IntervalTimer::Mode.
  74. ///
  75. /// @throw BadValue if the timer name is invalid or duplicate.
  76. void registerTimer(const std::string& timer_name,
  77. const asiolink::IntervalTimer::Callback& callback,
  78. const long interval,
  79. const asiolink::IntervalTimer::Mode& scheduling_mode);
  80. /// @brief Unregisters specified timer.
  81. ///
  82. /// This method cancels the timer if it is setup and removes the timer
  83. /// from the internal collection of timers.
  84. ///
  85. /// @param timer_name Name of the timer to be unregistered.
  86. ///
  87. /// @throw BadValue if the specified timer hasn't been registered.
  88. void unregisterTimer(const std::string& timer_name);
  89. /// @brief Unregisters all timers.
  90. ///
  91. /// This method must be explicitly called prior to termination of the
  92. /// process.
  93. void unregisterTimers();
  94. /// @brief Returns the number of registered timers.
  95. size_t timersCount() const;
  96. /// @brief Schedules the execution of the interval timer.
  97. ///
  98. /// This method schedules the timer, i.e. the callback will be executed
  99. /// after specified interval elapses. The interval has been specified
  100. /// during timer registration. Depending on the mode selected during the
  101. /// timer registration, the callback will be executed once after it has
  102. /// been scheduled or until it is cancelled. Though, in the former case
  103. /// the timer can be re-scheduled in the callback function.
  104. ///
  105. /// @param timer_name Unique timer name.
  106. ///
  107. /// @throw BadValue if the timer hasn't been registered.
  108. void setup(const std::string& timer_name);
  109. /// @brief Cancels the execution of the interval timer.
  110. ///
  111. /// @param timer_name Unique timer name.
  112. ///
  113. /// @throw BadValue if the timer hasn't been registered.
  114. void cancel(const std::string& timer_name);
  115. private:
  116. /// @brief Callback function to be executed for each interval timer when
  117. /// its scheduled interval elapses.
  118. ///
  119. /// @param timer_name Unique timer name.
  120. void timerCallback(const std::string& timer_name);
  121. /// @brief Pointer to the io service.
  122. asiolink::IOServicePtr io_service_;
  123. /// @brief Holds mapping of the timer name to timer instance and other
  124. /// parameters pertaining to the timer.
  125. TimerInfoMap registered_timers_;
  126. };
  127. TimerMgrImpl::TimerMgrImpl() :
  128. io_service_(new IOService()), registered_timers_() {
  129. }
  130. void
  131. TimerMgrImpl::setIOService(const IOServicePtr& io_service) {
  132. if (!io_service) {
  133. isc_throw(BadValue, "IO service object must not be null for TimerMgr");
  134. }
  135. io_service_ = io_service;
  136. }
  137. void
  138. TimerMgrImpl::registerTimer(const std::string& timer_name,
  139. const IntervalTimer::Callback& callback,
  140. const long interval,
  141. const IntervalTimer::Mode& scheduling_mode) {
  142. // Timer name must not be empty.
  143. if (timer_name.empty()) {
  144. isc_throw(BadValue, "registered timer name must not be empty");
  145. }
  146. // Must not register two timers under the same name.
  147. if (registered_timers_.find(timer_name) != registered_timers_.end()) {
  148. isc_throw(BadValue, "trying to register duplicate timer '"
  149. << timer_name << "'");
  150. }
  151. // Create a structure holding the configuration for the timer. It will
  152. // create the instance if the IntervalTimer. It will also hold the
  153. // callback, interval and scheduling mode parameters.
  154. TimerInfoPtr timer_info(new TimerInfo(*io_service_, callback,
  155. interval, scheduling_mode));
  156. // Actually register the timer.
  157. registered_timers_.insert(std::pair<std::string, TimerInfoPtr>(timer_name,
  158. timer_info));
  159. }
  160. void
  161. TimerMgrImpl::unregisterTimer(const std::string& timer_name) {
  162. // Find the timer with specified name.
  163. TimerInfoMap::iterator timer_info_it = registered_timers_.find(timer_name);
  164. // Check if the timer has been registered.
  165. if (timer_info_it == registered_timers_.end()) {
  166. isc_throw(BadValue, "unable to unregister non existing timer '"
  167. << timer_name << "'");
  168. }
  169. // Cancel any pending asynchronous operation and stop the timer.
  170. cancel(timer_name);
  171. // Remove the timer.
  172. registered_timers_.erase(timer_info_it);
  173. }
  174. void
  175. TimerMgrImpl::unregisterTimers() {
  176. // Copy the map holding timers configuration. This is required so as
  177. // we don't cut the branch which we're sitting on when we will be
  178. // erasing the timers. We're going to iterate over the register timers
  179. // and remove them with the call to unregisterTimer function. But this
  180. // function will remove them from the register_timers_ map. If we
  181. // didn't work on the copy here, our iterator would invalidate. The
  182. // TimerInfo structure is copyable and since it is using the shared
  183. // pointers the copy is not expensive. Also this function is called when
  184. // the process terminates so it is not critical for performance.
  185. TimerInfoMap registered_timers_copy(registered_timers_);
  186. // Iterate over the existing timers and unregister them.
  187. for (TimerInfoMap::iterator timer_info_it = registered_timers_copy.begin();
  188. timer_info_it != registered_timers_copy.end(); ++timer_info_it) {
  189. unregisterTimer(timer_info_it->first);
  190. }
  191. }
  192. size_t
  193. TimerMgrImpl::timersCount() const {
  194. return (registered_timers_.size());
  195. }
  196. void
  197. TimerMgrImpl::setup(const std::string& timer_name) {
  198. // Check if the specified timer exists.
  199. TimerInfoMap::const_iterator timer_info_it = registered_timers_.find(timer_name);
  200. if (timer_info_it == registered_timers_.end()) {
  201. isc_throw(BadValue, "unable to setup timer '" << timer_name << "': "
  202. "no such timer registered");
  203. }
  204. // Schedule the execution of the timer using the parameters supplied
  205. // during the registration.
  206. const TimerInfoPtr& timer_info = timer_info_it->second;
  207. IntervalTimer::Callback cb = boost::bind(&TimerMgrImpl::timerCallback, this,
  208. timer_name);
  209. timer_info->interval_timer_.setup(cb, timer_info->interval_,
  210. timer_info->scheduling_mode_);
  211. }
  212. void
  213. TimerMgrImpl::cancel(const std::string& timer_name) {
  214. // Find the timer of our interest.
  215. TimerInfoMap::const_iterator timer_info_it = registered_timers_.find(timer_name);
  216. if (timer_info_it == registered_timers_.end()) {
  217. isc_throw(BadValue, "unable to cancel timer '" << timer_name << "': "
  218. "no such timer registered");
  219. }
  220. // Cancel the timer.
  221. timer_info_it->second->interval_timer_.cancel();
  222. }
  223. void
  224. TimerMgrImpl::timerCallback(const std::string& timer_name) {
  225. // Find the specified timer setup.
  226. TimerInfoMap::iterator timer_info_it = registered_timers_.find(timer_name);
  227. if (timer_info_it != registered_timers_.end()) {
  228. // Running user-defined operation for the timer. Logging it
  229. // on the slightly lower debug level as there may be many
  230. // such traces.
  231. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  232. DHCPSRV_TIMERMGR_RUN_TIMER_OPERATION)
  233. .arg(timer_info_it->first);
  234. std::string error_string;
  235. try {
  236. timer_info_it->second->user_callback_();
  237. } catch (const std::exception& ex){
  238. error_string = ex.what();
  239. } catch (...) {
  240. error_string = "unknown reason";
  241. }
  242. // Exception was thrown. Log an error.
  243. if (!error_string.empty()) {
  244. LOG_ERROR(dhcpsrv_logger, DHCPSRV_TIMERMGR_CALLBACK_FAILED)
  245. .arg(timer_info_it->first)
  246. .arg(error_string);
  247. }
  248. }
  249. }
  250. const TimerMgrPtr&
  251. TimerMgr::instance() {
  252. static TimerMgrPtr timer_mgr(new TimerMgr());
  253. return (timer_mgr);
  254. }
  255. TimerMgr::TimerMgr()
  256. : impl_(new TimerMgrImpl()) {
  257. }
  258. TimerMgr::~TimerMgr() {
  259. unregisterTimers();
  260. delete impl_;
  261. }
  262. void
  263. TimerMgr::registerTimer(const std::string& timer_name,
  264. const IntervalTimer::Callback& callback,
  265. const long interval,
  266. const IntervalTimer::Mode& scheduling_mode) {
  267. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  268. DHCPSRV_TIMERMGR_REGISTER_TIMER)
  269. .arg(timer_name)
  270. .arg(interval);
  271. impl_->registerTimer(timer_name, callback, interval, scheduling_mode);
  272. }
  273. void
  274. TimerMgr::unregisterTimer(const std::string& timer_name) {
  275. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  276. DHCPSRV_TIMERMGR_UNREGISTER_TIMER)
  277. .arg(timer_name);
  278. impl_->unregisterTimer(timer_name);
  279. }
  280. void
  281. TimerMgr::unregisterTimers() {
  282. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  283. DHCPSRV_TIMERMGR_UNREGISTER_ALL_TIMERS);
  284. impl_->unregisterTimers();
  285. }
  286. size_t
  287. TimerMgr::timersCount() const {
  288. return (impl_->timersCount());
  289. }
  290. void
  291. TimerMgr::setup(const std::string& timer_name) {
  292. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  293. DHCPSRV_TIMERMGR_START_TIMER)
  294. .arg(timer_name);
  295. impl_->setup(timer_name);
  296. }
  297. void
  298. TimerMgr::cancel(const std::string& timer_name) {
  299. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  300. DHCPSRV_TIMERMGR_STOP_TIMER)
  301. .arg(timer_name);
  302. impl_->cancel(timer_name);
  303. }
  304. void
  305. TimerMgr::setIOService(const IOServicePtr& io_service) {
  306. impl_->setIOService(io_service);
  307. }
  308. } // end of namespace isc::dhcp
  309. } // end of namespace isc