datasrc_clients_mgr.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef DATASRC_CLIENTS_MGR_H
  15. #define DATASRC_CLIENTS_MGR_H 1
  16. #include <util/threads/thread.h>
  17. #include <util/threads/lock.h>
  18. #include <log/logger_support.h>
  19. #include <log/log_dbglevels.h>
  20. #include <cc/data.h>
  21. #include <auth/auth_log.h>
  22. #include <boost/array.hpp>
  23. #include <boost/bind.hpp>
  24. #include <list>
  25. #include <utility>
  26. namespace isc {
  27. namespace auth {
  28. namespace datasrc_clientmgr_internal {
  29. // This namespace is essentially private for DataSrcClientsMgr(Base) and
  30. // DataSrcClientsBuilder(Base). This is exposed in the public header
  31. // only because these classes are templated (for testing purposes) and
  32. // class internal has to be defined here.
  33. /// \brief ID of commands from the DataSrcClientsMgr to DataSrcClientsBuilder.
  34. enum CommandID {
  35. NOOP, ///< Do nothing. Only useful for tests; no argument
  36. SHUTDOWN, ///< Shutdown the builder; no argument
  37. NUM_COMMANDS
  38. };
  39. /// \brief The data type passed from DataSrcClientsMgr to
  40. /// DataSrcClientsBuilder.
  41. ///
  42. /// The first element of the pair is the command ID, and the second element
  43. /// is its argument. If the command doesn't take an argument it should be
  44. /// a null pointer.
  45. typedef std::pair<CommandID, data::ConstElementPtr> Command;
  46. } // namespace datasrc_clientmgr_internal
  47. /// \brief Frontend to the manager object for data source clients.
  48. ///
  49. /// This class provides interfaces for configuring and updating a set of
  50. /// data source clients "in the background". The user of this class can
  51. /// assume any operation on this class can be done effectively non-blocking,
  52. /// not suspending any delay-sensitive operations such as DNS query
  53. /// processing. The only exception is the time when this class object
  54. /// is destroyed (normally as a result of an implicit call to the destructor);
  55. /// in the current implementation it can take time depending on what is
  56. /// running "in the background" at the time of the call.
  57. ///
  58. /// Internally, an object of this class invokes a separate thread to perform
  59. /// time consuming operations such as loading large zone data into memory,
  60. /// but such details are completely hidden from the user of this class.
  61. ///
  62. /// This class is templated only so that we can test the class without
  63. /// involving actual threads or mutex. Normal applications will only
  64. /// need one specific specialization that has a typedef of
  65. /// \c DataSrcClientsMgr.
  66. template <typename ThreadType, typename BuilderType, typename MutexType,
  67. typename CondVarType>
  68. class DataSrcClientsMgrBase {
  69. public:
  70. /// \brief Constructor.
  71. ///
  72. /// It internally invokes a separate thread and waits for further
  73. /// operations from the user application.
  74. ///
  75. /// This method is basically exception free except in case of really
  76. /// rare system-level errors. When that happens the only reasonable
  77. /// action that the application can take would be to terminate the program
  78. /// in practice.
  79. ///
  80. /// \throw std::bad_alloc internal memory allocation failure.
  81. /// \throw isc::Unexpected general unexpected system errors.
  82. DataSrcClientsMgrBase() :
  83. builder_(&command_queue_, &cond_, &queue_mutex_),
  84. builder_thread_(boost::bind(&BuilderType::run, &builder_))
  85. {}
  86. /// \brief The destructor.
  87. ///
  88. /// It tells the internal thread to stop and waits for it completion.
  89. /// In the current implementation, it can block for some unpredictably
  90. /// long period depending on what the thread is doing at that time
  91. /// (in future we may want to implement a rapid way of killing the thread
  92. /// and/or provide a separate interface for waiting so that the application
  93. /// can choose the timing).
  94. ///
  95. /// The waiting operation can result in an exception, but this method
  96. /// catches any of them so this method itself is exception free.
  97. ~DataSrcClientsMgrBase() {
  98. // We share class member variables with the builder, which will be
  99. // invalidated after the call to the destructor, so we need to make
  100. // sure the builder thread is terminated. Depending on the timing
  101. // this could take a long time; if we don't want that to happen in
  102. // this context, we may want to introduce a separate 'shutdown()'
  103. // method.
  104. // Also, since we don't want to propagate exceptions from a destructor,
  105. // we catch any possible ones. In fact the only really expected one
  106. // is Thread::UncaughtException when the builder thread died due to
  107. // an exception. We specifically log it and just ignore others.
  108. try {
  109. sendCommand(datasrc_clientmgr_internal::SHUTDOWN,
  110. data::ConstElementPtr());
  111. builder_thread_.wait();
  112. } catch (const util::thread::Thread::UncaughtException& ex) {
  113. // technically, logging this could throw, which will be propagated.
  114. // But such an exception would be a fatal one anyway, so we
  115. // simply let it go through.
  116. LOG_ERROR(auth_logger, AUTH_DATASRC_CLIENTS_SHUTDOWN_ERROR).
  117. arg(ex.what());
  118. } catch (...) {
  119. LOG_ERROR(auth_logger,
  120. AUTH_DATASRC_CLIENTS_SHUTDOWN_UNEXPECTED_ERROR);
  121. }
  122. cleanup(); // see below
  123. }
  124. private:
  125. // This is expected to be called at the end of the destructor. It
  126. // actually does nothing, but provides a customization point for
  127. // specialized class for tests so that the tests can inspect the last
  128. // state of the class.
  129. void cleanup() {}
  130. void sendCommand(datasrc_clientmgr_internal::CommandID command,
  131. data::ConstElementPtr arg) {
  132. {
  133. typename MutexType::Locker locker(queue_mutex_);
  134. command_queue_.push_back(
  135. datasrc_clientmgr_internal::Command(command, arg));
  136. }
  137. cond_.signal();
  138. }
  139. //
  140. // The following are shared with the builder.
  141. //
  142. // The list is used as a one-way queue: back-in, front-out
  143. std::list<datasrc_clientmgr_internal::Command> command_queue_;
  144. CondVarType cond_; // condition variable for queue operations
  145. MutexType queue_mutex_; // mutex to protect the queue
  146. #ifdef notyet // until #2210 or #2212
  147. boost::shared_ptr<DataSrcClientListMap> clients_map_;
  148. MutexType map_mutex_;
  149. #endif
  150. BuilderType builder_;
  151. ThreadType builder_thread_; // for safety this should be placed last
  152. };
  153. namespace datasrc_clientmgr_internal {
  154. /// \brief A class that maintains a set of data source clients.
  155. ///
  156. /// An object of this class is supposed to run on a dedicated thread, whose
  157. /// main function is a call to its \c run() method. It runs in a loop
  158. /// waiting for commands from the manager and handles each command (including
  159. /// reloading a new version of zone data into memory or fully reconfiguration
  160. /// of specific set of data source clients). When it receives a SHUTDOWN
  161. /// command, it exits from the loop, which will terminate the thread.
  162. ///
  163. /// This class is a server of \c DataSrcClientsMgr. Except for tests,
  164. /// applications should not directly access to this class.
  165. ///
  166. /// This class is templated so that we can test it without involving actual
  167. /// threads or locks.
  168. template <typename MutexType, typename CondVarType>
  169. class DataSrcClientsBuilderBase {
  170. public:
  171. /// \brief Constructor.
  172. ///
  173. /// It simply sets up a local copy of shared data with the manager.
  174. ///
  175. /// Note: this will take actual set (map) of data source clients and
  176. /// a mutex object for it in #2210 or #2212.
  177. ///
  178. /// \throw None
  179. DataSrcClientsBuilderBase(std::list<Command>* command_queue,
  180. CondVarType* cond, MutexType* queue_mutex
  181. #ifdef notyet
  182. // In #2210 or #2212 we pass other data
  183. #endif
  184. ) :
  185. command_queue_(command_queue), cond_(cond), queue_mutex_(queue_mutex)
  186. {}
  187. /// \brief The main loop.
  188. void run();
  189. /// \brief Handle one command from the manager.
  190. ///
  191. /// This is a dedicated subroutine of run() and is essentially private,
  192. /// but is defined as a separate public method so we can test each
  193. /// command test individually. In any case, this class itself is
  194. /// generally considered private.
  195. ///
  196. /// \return true if the builder should keep running; false otherwise.
  197. bool handleCommand(const Command& command);
  198. private:
  199. // NOOP command handler. We use this so tests can override it; the default
  200. // implementation really does nothing.
  201. void doNoop() {}
  202. // The following are shared with the manager
  203. std::list<Command>* command_queue_;
  204. CondVarType* cond_;
  205. MutexType* queue_mutex_;
  206. };
  207. // Shortcut typedef for normal use
  208. typedef DataSrcClientsBuilderBase<util::thread::Mutex, util::thread::CondVar>
  209. DataSrcClientsBuilder;
  210. template <typename MutexType, typename CondVarType>
  211. void
  212. DataSrcClientsBuilderBase<MutexType, CondVarType>::run() {
  213. LOG_INFO(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_STARTED);
  214. try {
  215. bool keep_running = true;
  216. while (keep_running) {
  217. std::list<Command> current_commands;
  218. {
  219. // Move all new commands to local queue under the protection of
  220. // queue_mutex_. Note that list::splice() should never throw.
  221. typename MutexType::Locker locker(*queue_mutex_);
  222. while (command_queue_->empty()) {
  223. cond_->wait(*queue_mutex_);
  224. }
  225. current_commands.splice(current_commands.end(),
  226. *command_queue_);
  227. } // the lock is release here.
  228. while (keep_running && !current_commands.empty()) {
  229. keep_running = handleCommand(current_commands.front());
  230. current_commands.pop_front();
  231. }
  232. }
  233. LOG_INFO(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_STOPPED);
  234. } catch (const std::exception& ex) {
  235. // We explicitly catch exceptions so we can log it as soon as possible.
  236. LOG_ERROR(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_FAILED).
  237. arg(ex.what());
  238. throw;
  239. } catch (...) {
  240. LOG_ERROR(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_FAILED_UNEXPECTED);
  241. throw;
  242. }
  243. }
  244. template <typename MutexType, typename CondVarType>
  245. bool
  246. DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
  247. const Command& command)
  248. {
  249. const CommandID cid = command.first;
  250. if (cid >= NUM_COMMANDS) {
  251. // This shouldn't happen except for a bug within this file.
  252. isc_throw(Unexpected, "internal bug: invalid command, ID: " << cid);
  253. }
  254. const boost::array<const char*, NUM_COMMANDS> command_desc = {
  255. {"NOOP", "SHUTDOWN"}
  256. };
  257. LOG_DEBUG(auth_logger, DBGLVL_TRACE_BASIC,
  258. AUTH_DATASRC_CLIENTS_BUILDER_COMMAND).arg(command_desc.at(cid));
  259. switch (command.first) {
  260. case SHUTDOWN:
  261. return (false);
  262. case NOOP:
  263. doNoop();
  264. break;
  265. case NUM_COMMANDS:
  266. assert(false); // we rejected this case above
  267. }
  268. return (true);
  269. }
  270. } // namespace datasrc_clientmgr_internal
  271. /// \brief Shortcut type for normal data source clients manager.
  272. ///
  273. /// In fact, for non test applications this is the only type of this kind
  274. /// to be considered.
  275. typedef DataSrcClientsMgrBase<
  276. util::thread::Thread,
  277. datasrc_clientmgr_internal::DataSrcClientsBuilder,
  278. util::thread::Mutex, util::thread::CondVar> DataSrcClientsMgr;
  279. } // namespace auth
  280. } // namespace isc
  281. #endif // DATASRC_CLIENTS_MGR_H
  282. // Local Variables:
  283. // mode: c++
  284. // End: