Kea  2.1.7-git
timer_mgr.cc
Go to the documentation of this file.
1 // Copyright (C) 2016-2021 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 
7 #include <config.h>
8 
10 #include <asiolink/io_service.h>
11 #include <dhcpsrv/dhcpsrv_log.h>
12 #include <dhcpsrv/timer_mgr.h>
13 #include <exceptions/exceptions.h>
15 
16 #include <boost/scoped_ptr.hpp>
17 
18 #include <exception>
19 #include <functional>
20 #include <map>
21 #include <mutex>
22 #include <ostream>
23 #include <string>
24 #include <utility>
25 
26 #include <stddef.h>
27 
28 using namespace isc;
29 using namespace isc::asiolink;
30 using namespace isc::util;
31 
32 namespace {
33 
39 struct TimerInfo {
41  asiolink::IntervalTimer interval_timer_;
42 
45  asiolink::IntervalTimer::Callback user_callback_;
46 
48  long interval_;
49 
51  asiolink::IntervalTimer::Mode scheduling_mode_;
52 
61  TimerInfo(asiolink::IOService& io_service,
62  const asiolink::IntervalTimer::Callback& user_callback,
63  const long interval,
65  : interval_timer_(io_service),
66  user_callback_(user_callback),
67  interval_(interval),
68  scheduling_mode_(mode) { };
69 };
70 
71 }
72 
73 namespace isc {
74 namespace dhcp {
75 
77 typedef boost::shared_ptr<TimerInfo> TimerInfoPtr;
78 
80 typedef std::map<std::string, TimerInfoPtr> TimerInfoMap;
81 
83 class TimerMgrImpl {
84 public:
85 
87  TimerMgrImpl();
88 
92  void setIOService(const IOServicePtr& io_service);
93 
105  void registerTimer(const std::string& timer_name,
106  const asiolink::IntervalTimer::Callback& callback,
107  const long interval,
108  const asiolink::IntervalTimer::Mode& scheduling_mode);
109 
110 
119  void unregisterTimer(const std::string& timer_name);
120 
125  void unregisterTimers();
126 
132  bool isTimerRegistered(const std::string& timer_name);
133 
135  size_t timersCount() const;
136 
149  void setup(const std::string& timer_name);
150 
156  void cancel(const std::string& timer_name);
157 
158 private:
159 
162 
174  void registerTimerInternal(const std::string& timer_name,
175  const asiolink::IntervalTimer::Callback& callback,
176  const long interval,
177  const asiolink::IntervalTimer::Mode& scheduling_mode);
178 
179 
188  void unregisterTimerInternal(const std::string& timer_name);
189 
194  void unregisterTimersInternal();
195 
208  void setupInternal(const std::string& timer_name);
209 
215  void cancelInternal(const std::string& timer_name);
216 
221  void timerCallback(const std::string& timer_name);
222 
224  asiolink::IOServicePtr io_service_;
225 
228  TimerInfoMap registered_timers_;
229 
231  boost::scoped_ptr<std::mutex> mutex_;
232 };
233 
234 TimerMgrImpl::TimerMgrImpl() : io_service_(new IOService()),
235  registered_timers_(), mutex_(new std::mutex) {
236 }
237 
238 void
240  if (!io_service) {
241  isc_throw(BadValue, "IO service object must not be null for TimerMgr");
242  }
243 
244  io_service_ = io_service;
245 }
246 
247 void
248 TimerMgrImpl::registerTimer(const std::string& timer_name,
249  const IntervalTimer::Callback& callback,
250  const long interval,
251  const IntervalTimer::Mode& scheduling_mode) {
252  if (MultiThreadingMgr::instance().getMode()) {
253  std::lock_guard<std::mutex> lock(*mutex_);
254  registerTimerInternal(timer_name, callback, interval, scheduling_mode);
255  } else {
256  registerTimerInternal(timer_name, callback, interval, scheduling_mode);
257  }
258 }
259 
260 void
261 TimerMgrImpl::registerTimerInternal(const std::string& timer_name,
262  const IntervalTimer::Callback& callback,
263  const long interval,
264  const IntervalTimer::Mode& scheduling_mode) {
265  // Timer name must not be empty.
266  if (timer_name.empty()) {
267  isc_throw(BadValue, "registered timer name must not be empty");
268  }
269 
270  // Must not register two timers under the same name.
271  if (registered_timers_.find(timer_name) != registered_timers_.end()) {
272  isc_throw(BadValue, "trying to register duplicate timer '"
273  << timer_name << "'");
274  }
275 
276  // Create a structure holding the configuration for the timer. It will
277  // create the instance if the IntervalTimer. It will also hold the
278  // callback, interval and scheduling mode parameters.
279  TimerInfoPtr timer_info(new TimerInfo(*io_service_, callback,
280  interval, scheduling_mode));
281 
282  // Actually register the timer.
283  registered_timers_.insert(std::pair<std::string, TimerInfoPtr>(timer_name,
284  timer_info));
285 }
286 
287 void
288 TimerMgrImpl::unregisterTimer(const std::string& timer_name) {
289  if (MultiThreadingMgr::instance().getMode()) {
290  std::lock_guard<std::mutex> lock(*mutex_);
291  unregisterTimerInternal(timer_name);
292  } else {
293  unregisterTimerInternal(timer_name);
294  }
295 }
296 
297 void
298 TimerMgrImpl::unregisterTimerInternal(const std::string& timer_name) {
299  // Find the timer with specified name.
300  TimerInfoMap::iterator timer_info_it = registered_timers_.find(timer_name);
301 
302  // Check if the timer has been registered.
303  if (timer_info_it == registered_timers_.end()) {
304  isc_throw(BadValue, "unable to unregister non existing timer '"
305  << timer_name << "'");
306  }
307 
308  // Cancel any pending asynchronous operation and stop the timer.
309  cancelInternal(timer_name);
310 
311  // Remove the timer.
312  registered_timers_.erase(timer_info_it);
313 }
314 
315 void
317  if (MultiThreadingMgr::instance().getMode()) {
318  std::lock_guard<std::mutex> lock(*mutex_);
319  unregisterTimersInternal();
320  } else {
321  unregisterTimersInternal();
322  }
323 }
324 
325 void
326 TimerMgrImpl::unregisterTimersInternal() {
327  // Copy the map holding timers configuration. This is required so as
328  // we don't cut the branch which we're sitting on when we will be
329  // erasing the timers. We're going to iterate over the register timers
330  // and remove them with the call to unregisterTimer function. But this
331  // function will remove them from the register_timers_ map. If we
332  // didn't work on the copy here, our iterator would invalidate. The
333  // TimerInfo structure is copyable and since it is using the shared
334  // pointers the copy is not expensive. Also this function is called when
335  // the process terminates so it is not critical for performance.
336  TimerInfoMap registered_timers_copy(registered_timers_);
337 
338  // Iterate over the existing timers and unregister them.
339  for (TimerInfoMap::iterator timer_info_it = registered_timers_copy.begin();
340  timer_info_it != registered_timers_copy.end(); ++timer_info_it) {
341  unregisterTimerInternal(timer_info_it->first);
342  }
343 }
344 
345 bool
346 TimerMgrImpl::isTimerRegistered(const std::string& timer_name) {
347  if (MultiThreadingMgr::instance().getMode()) {
348  std::lock_guard<std::mutex> lock(*mutex_);
349  return (registered_timers_.find(timer_name) != registered_timers_.end());
350  } else {
351  return (registered_timers_.find(timer_name) != registered_timers_.end());
352  }
353 }
354 
355 size_t
357  if (MultiThreadingMgr::instance().getMode()) {
358  std::lock_guard<std::mutex> lock(*mutex_);
359  return (registered_timers_.size());
360  } else {
361  return (registered_timers_.size());
362  }
363 }
364 
365 void
366 TimerMgrImpl::setup(const std::string& timer_name) {
367  if (MultiThreadingMgr::instance().getMode()) {
368  std::lock_guard<std::mutex> lock(*mutex_);
369  setupInternal(timer_name);
370  } else {
371  setupInternal(timer_name);
372  }
373 }
374 
375 void
376 TimerMgrImpl::setupInternal(const std::string& timer_name) {
377  // Check if the specified timer exists.
378  TimerInfoMap::const_iterator timer_info_it = registered_timers_.find(timer_name);
379  if (timer_info_it == registered_timers_.end()) {
380  isc_throw(BadValue, "unable to setup timer '" << timer_name << "': "
381  "no such timer registered");
382  }
383 
384  // Schedule the execution of the timer using the parameters supplied
385  // during the registration.
386  const TimerInfoPtr& timer_info = timer_info_it->second;
387  IntervalTimer::Callback cb = std::bind(&TimerMgrImpl::timerCallback, this,
388  timer_name);
389  timer_info->interval_timer_.setup(cb, timer_info->interval_,
390  timer_info->scheduling_mode_);
391 }
392 
393 void
394 TimerMgrImpl::cancel(const std::string& timer_name) {
395  if (MultiThreadingMgr::instance().getMode()) {
396  std::lock_guard<std::mutex> lock(*mutex_);
397  cancelInternal(timer_name);
398  } else {
399  cancelInternal(timer_name);
400  }
401 }
402 
403 void
404 TimerMgrImpl::cancelInternal(const std::string& timer_name) {
405  // Find the timer of our interest.
406  TimerInfoMap::const_iterator timer_info_it = registered_timers_.find(timer_name);
407  if (timer_info_it == registered_timers_.end()) {
408  isc_throw(BadValue, "unable to cancel timer '" << timer_name << "': "
409  "no such timer registered");
410  }
411  // Cancel the timer.
412  timer_info_it->second->interval_timer_.cancel();
413 }
414 
415 void
416 TimerMgrImpl::timerCallback(const std::string& timer_name) {
417  // Find the specified timer setup.
418  TimerInfoMap::iterator timer_info_it = registered_timers_.find(timer_name);
419  if (timer_info_it != registered_timers_.end()) {
420 
421  // Running user-defined operation for the timer. Logging it
422  // on the slightly lower debug level as there may be many
423  // such traces.
426  .arg(timer_info_it->first);
427 
428  std::string error_string;
429  try {
430  timer_info_it->second->user_callback_();
431 
432  } catch (const std::exception& ex){
433  error_string = ex.what();
434 
435  } catch (...) {
436  error_string = "unknown reason";
437  }
438 
439  // Exception was thrown. Log an error.
440  if (!error_string.empty()) {
442  .arg(timer_info_it->first)
443  .arg(error_string);
444  }
445  }
446 }
447 
448 const TimerMgrPtr&
450  static TimerMgrPtr timer_mgr(new TimerMgr());
451  return (timer_mgr);
452 }
453 
454 TimerMgr::TimerMgr()
455  : impl_(new TimerMgrImpl()) {
456 }
457 
459  impl_->unregisterTimers();
460 }
461 
462 void
463 TimerMgr::registerTimer(const std::string& timer_name,
464  const IntervalTimer::Callback& callback,
465  const long interval,
466  const IntervalTimer::Mode& scheduling_mode) {
467 
470  .arg(timer_name)
471  .arg(interval);
472 
473  impl_->registerTimer(timer_name, callback, interval, scheduling_mode);
474 }
475 
476 void
477 TimerMgr::unregisterTimer(const std::string& timer_name) {
478 
481  .arg(timer_name);
482 
483  impl_->unregisterTimer(timer_name);
484 }
485 
486 void
488 
491 
492  impl_->unregisterTimers();
493 }
494 
495 bool
496 TimerMgr::isTimerRegistered(const std::string& timer_name) {
497  return (impl_->isTimerRegistered(timer_name));
498 }
499 
500 size_t
502  return (impl_->timersCount());
503 }
504 
505 void
506 TimerMgr::setup(const std::string& timer_name) {
507 
510  .arg(timer_name);
511 
512  impl_->setup(timer_name);
513 }
514 
515 void
516 TimerMgr::cancel(const std::string& timer_name) {
517 
520  .arg(timer_name);
521 
522  impl_->cancel(timer_name);
523 }
524 
525 void
527  impl_->setIOService(io_service);
528 }
529 
530 } // end of namespace isc::dhcp
531 } // end of namespace isc
void unregisterTimers()
Unregisters all timers.
Definition: timer_mgr.cc:316
boost::shared_ptr< TimerMgr > TimerMgrPtr
Type definition of the shared pointer to TimerMgr.
Definition: timer_mgr.h:24
const isc::log::MessageID DHCPSRV_TIMERMGR_RUN_TIMER_OPERATION
const isc::log::MessageID DHCPSRV_TIMERMGR_UNREGISTER_ALL_TIMERS
const isc::log::MessageID DHCPSRV_TIMERMGR_CALLBACK_FAILED
Implementation of the TimerMgr.
Definition: timer_mgr.cc:83
bool isTimerRegistered(const std::string &timer_name)
Checks if the timer with a specified name has been registered.
Definition: timer_mgr.cc:346
void registerTimer(const std::string &timer_name, const asiolink::IntervalTimer::Callback &callback, const long interval, const asiolink::IntervalTimer::Mode &scheduling_mode)
Registers new timer in the TimerMgr.
Definition: timer_mgr.cc:248
void cancel(const std::string &timer_name)
Cancels the execution of the interval timer.
Definition: timer_mgr.cc:516
void setIOService(const asiolink::IOServicePtr &io_service)
Sets IO service to be used by the Timer Manager.
Definition: timer_mgr.cc:526
Manages a pool of asynchronous interval timers.
Definition: timer_mgr.h:62
void cancel(const std::string &timer_name)
Cancels the execution of the interval timer.
Definition: timer_mgr.cc:394
TimerMgrImpl()
Constructor.
Definition: timer_mgr.cc:234
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
STL namespace.
bool isTimerRegistered(const std::string &timer_name)
Checks if the timer with a specified name has been registered.
Definition: timer_mgr.cc:496
~TimerMgr()
Destructor.
Definition: timer_mgr.cc:458
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: edns.h:19
void unregisterTimers()
Unregisters all timers.
Definition: timer_mgr.cc:487
size_t timersCount() const
Returns the number of registered timers.
Definition: timer_mgr.cc:356
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
boost::shared_ptr< TimerInfo > TimerInfoPtr
A type definition for the pointer to TimerInfo structure.
Definition: timer_mgr.cc:77
const isc::log::MessageID DHCPSRV_TIMERMGR_UNREGISTER_TIMER
void setup(const std::string &timer_name)
Schedules the execution of the interval timer.
Definition: timer_mgr.cc:366
const isc::log::MessageID DHCPSRV_TIMERMGR_STOP_TIMER
void setIOService(const IOServicePtr &io_service)
Sets IO service to be used by the Timer Manager.
Definition: timer_mgr.cc:239
Defines the logger used by the top-level component of kea-lfc.
const isc::log::MessageID DHCPSRV_TIMERMGR_REGISTER_TIMER
void unregisterTimer(const std::string &timer_name)
Unregisters specified timer.
Definition: timer_mgr.cc:477
void setup(const std::string &timer_name)
Schedules the execution of the interval timer.
Definition: timer_mgr.cc:506
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
void registerTimer(const std::string &timer_name, const asiolink::IntervalTimer::Callback &callback, const long interval, const asiolink::IntervalTimer::Mode &scheduling_mode)
Registers new timer in the TimerMgr.
Definition: timer_mgr.cc:463
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition: dhcpsrv_log.h:26
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
size_t timersCount() const
Returns the number of registered timers.
Definition: timer_mgr.cc:501
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition: timer_mgr.cc:449
const isc::log::MessageID DHCPSRV_TIMERMGR_START_TIMER
std::map< std::string, TimerInfoPtr > TimerInfoMap
A type definition for the map holding timers configuration.
Definition: timer_mgr.cc:80
void unregisterTimer(const std::string &timer_name)
Unregisters specified timer.
Definition: timer_mgr.cc:288