123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #ifndef DATASRC_CLIENTS_MGR_H
- #define DATASRC_CLIENTS_MGR_H 1
- #include <util/threads/thread.h>
- #include <util/threads/lock.h>
- #include <log/logger_support.h>
- #include <log/log_dbglevels.h>
- #include <cc/data.h>
- #include <auth/auth_log.h>
- #include <boost/array.hpp>
- #include <boost/bind.hpp>
- #include <list>
- #include <utility>
- namespace isc {
- namespace auth {
- namespace datasrc_clientmgr_internal {
- // This namespace is essentially private for DataSrcClientsMgr(Base) and
- // DataSrcClientsBuilder(Base). This is exposed in the public header
- // only because these classes are templated (for testing purposes) and
- // class internal has to be defined here.
- /// \brief ID of commands from the DataSrcClientsMgr to DataSrcClientsBuilder.
- enum CommandID {
- NOOP, ///< Do nothing. Only useful for tests; no argument
- SHUTDOWN, ///< Shutdown the builder; no argument
- NUM_COMMANDS
- };
- /// \brief The data type passed from DataSrcClientsMgr to
- /// DataSrcClientsBuilder.
- ///
- /// The first element of the pair is the command ID, and the second element
- /// is its argument. If the command doesn't take an argument it should be
- /// a null pointer.
- typedef std::pair<CommandID, data::ConstElementPtr> Command;
- } // namespace datasrc_clientmgr_internal
- /// \brief Frontend to the manager object for data source clients.
- ///
- /// This class provides interfaces for configuring and updating a set of
- /// data source clients "in the background". The user of this class can
- /// assume any operation on this class can be done effectively non-blocking,
- /// not suspending any delay-sensitive operations such as DNS query
- /// processing. The only exception is the time when this class object
- /// is destroyed (normally as a result of an implicit call to the destructor);
- /// in the current implementation it can take time depending on what is
- /// running "in the background" at the time of the call.
- ///
- /// Internally, an object of this class invokes a separate thread to perform
- /// time consuming operations such as loading large zone data into memory,
- /// but such details are completely hidden from the user of this class.
- ///
- /// This class is templated only so that we can test the class without
- /// involving actual threads or mutex. Normal applications will only
- /// need one specific specialization that has a typedef of
- /// \c DataSrcClientsMgr.
- template <typename ThreadType, typename BuilderType, typename MutexType,
- typename CondVarType>
- class DataSrcClientsMgrBase {
- public:
- /// \brief Constructor.
- ///
- /// It internally invokes a separate thread and waits for further
- /// operations from the user application.
- ///
- /// This method is basically exception free except in case of really
- /// rare system-level errors. When that happens the only reasonable
- /// action that the application can take would be to terminate the program
- /// in practice.
- ///
- /// \throw std::bad_alloc internal memory allocation failure.
- /// \throw isc::Unexpected general unexpected system errors.
- DataSrcClientsMgrBase() :
- builder_(&command_queue_, &cond_, &queue_mutex_),
- builder_thread_(boost::bind(&BuilderType::run, &builder_))
- {}
- /// \brief The destructor.
- ///
- /// It tells the internal thread to stop and waits for it completion.
- /// In the current implementation, it can block for some unpredictably
- /// long period depending on what the thread is doing at that time
- /// (in future we may want to implement a rapid way of killing the thread
- /// and/or provide a separate interface for waiting so that the application
- /// can choose the timing).
- ///
- /// The waiting operation can result in an exception, but this method
- /// catches any of them so this method itself is exception free.
- ~DataSrcClientsMgrBase() {
- // We share class member variables with the builder, which will be
- // invalidated after the call to the destructor, so we need to make
- // sure the builder thread is terminated. Depending on the timing
- // this could take a long time; if we don't want that to happen in
- // this context, we may want to introduce a separate 'shutdown()'
- // method.
- // Also, since we don't want to propagate exceptions from a destructor,
- // we catch any possible ones. In fact the only really expected one
- // is Thread::UncaughtException when the builder thread died due to
- // an exception. We specifically log it and just ignore others.
- try {
- sendCommand(datasrc_clientmgr_internal::SHUTDOWN,
- data::ConstElementPtr());
- builder_thread_.wait();
- } catch (const util::thread::Thread::UncaughtException& ex) {
- // technically, logging this could throw, which will be propagated.
- // But such an exception would be a fatal one anyway, so we
- // simply let it go through.
- LOG_ERROR(auth_logger, AUTH_DATASRC_CLIENTS_SHUTDOWN_ERROR).
- arg(ex.what());
- } catch (...) {
- LOG_ERROR(auth_logger,
- AUTH_DATASRC_CLIENTS_SHUTDOWN_UNEXPECTED_ERROR);
- }
- cleanup(); // see below
- }
- private:
- // This is expected to be called at the end of the destructor. It
- // actually does nothing, but provides a customization point for
- // specialized class for tests so that the tests can inspect the last
- // state of the class.
- void cleanup() {}
- void sendCommand(datasrc_clientmgr_internal::CommandID command,
- data::ConstElementPtr arg) {
- {
- typename MutexType::Locker locker(queue_mutex_);
- command_queue_.push_back(
- datasrc_clientmgr_internal::Command(command, arg));
- }
- cond_.signal();
- }
- //
- // The following are shared with the builder.
- //
- // The list is used as a one-way queue: back-in, front-out
- std::list<datasrc_clientmgr_internal::Command> command_queue_;
- CondVarType cond_; // condition variable for queue operations
- MutexType queue_mutex_; // mutex to protect the queue
- #ifdef notyet // until #2210 or #2212
- boost::shared_ptr<DataSrcClientListMap> clients_map_;
- MutexType map_mutex_;
- #endif
- BuilderType builder_;
- ThreadType builder_thread_; // for safety this should be placed last
- };
- namespace datasrc_clientmgr_internal {
- /// \brief A class that maintains a set of data source clients.
- ///
- /// An object of this class is supposed to run on a dedicated thread, whose
- /// main function is a call to its \c run() method. It runs in a loop
- /// waiting for commands from the manager and handles each command (including
- /// reloading a new version of zone data into memory or fully reconfiguration
- /// of specific set of data source clients). When it receives a SHUTDOWN
- /// command, it exits from the loop, which will terminate the thread.
- ///
- /// This class is a server of \c DataSrcClientsMgr. Except for tests,
- /// applications should not directly access to this class.
- ///
- /// This class is templated so that we can test it without involving actual
- /// threads or locks.
- template <typename MutexType, typename CondVarType>
- class DataSrcClientsBuilderBase {
- public:
- /// \brief Constructor.
- ///
- /// It simply sets up a local copy of shared data with the manager.
- ///
- /// Note: this will take actual set (map) of data source clients and
- /// a mutex object for it in #2210 or #2212.
- ///
- /// \throw None
- DataSrcClientsBuilderBase(std::list<Command>* command_queue,
- CondVarType* cond, MutexType* queue_mutex
- #ifdef notyet
- // In #2210 or #2212 we pass other data
- #endif
- ) :
- command_queue_(command_queue), cond_(cond), queue_mutex_(queue_mutex)
- {}
- /// \brief The main loop.
- void run();
- /// \brief Handle one command from the manager.
- ///
- /// This is a dedicated subroutine of run() and is essentially private,
- /// but is defined as a separate public method so we can test each
- /// command test individually. In any case, this class itself is
- /// generally considered private.
- ///
- /// \return true if the builder should keep running; false otherwise.
- bool handleCommand(const Command& command);
- private:
- // NOOP command handler. We use this so tests can override it; the default
- // implementation really does nothing.
- void doNoop() {}
- // The following are shared with the manager
- std::list<Command>* command_queue_;
- CondVarType* cond_;
- MutexType* queue_mutex_;
- };
- // Shortcut typedef for normal use
- typedef DataSrcClientsBuilderBase<util::thread::Mutex, util::thread::CondVar>
- DataSrcClientsBuilder;
- template <typename MutexType, typename CondVarType>
- void
- DataSrcClientsBuilderBase<MutexType, CondVarType>::run() {
- LOG_INFO(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_STARTED);
- try {
- bool keep_running = true;
- while (keep_running) {
- std::list<Command> current_commands;
- {
- // Move all new commands to local queue under the protection of
- // queue_mutex_. Note that list::splice() should never throw.
- typename MutexType::Locker locker(*queue_mutex_);
- while (command_queue_->empty()) {
- cond_->wait(*queue_mutex_);
- }
- current_commands.splice(current_commands.end(),
- *command_queue_);
- } // the lock is release here.
- while (keep_running && !current_commands.empty()) {
- keep_running = handleCommand(current_commands.front());
- current_commands.pop_front();
- }
- }
- LOG_INFO(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_STOPPED);
- } catch (const std::exception& ex) {
- // We explicitly catch exceptions so we can log it as soon as possible.
- LOG_ERROR(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_FAILED).
- arg(ex.what());
- throw;
- } catch (...) {
- LOG_ERROR(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_FAILED_UNEXPECTED);
- throw;
- }
- }
- template <typename MutexType, typename CondVarType>
- bool
- DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
- const Command& command)
- {
- const CommandID cid = command.first;
- if (cid >= NUM_COMMANDS) {
- // This shouldn't happen except for a bug within this file.
- isc_throw(Unexpected, "internal bug: invalid command, ID: " << cid);
- }
- const boost::array<const char*, NUM_COMMANDS> command_desc = {
- {"NOOP", "SHUTDOWN"}
- };
- LOG_DEBUG(auth_logger, DBGLVL_TRACE_BASIC,
- AUTH_DATASRC_CLIENTS_BUILDER_COMMAND).arg(command_desc.at(cid));
- switch (command.first) {
- case SHUTDOWN:
- return (false);
- case NOOP:
- doNoop();
- break;
- case NUM_COMMANDS:
- assert(false); // we rejected this case above
- }
- return (true);
- }
- } // namespace datasrc_clientmgr_internal
- /// \brief Shortcut type for normal data source clients manager.
- ///
- /// In fact, for non test applications this is the only type of this kind
- /// to be considered.
- typedef DataSrcClientsMgrBase<
- util::thread::Thread,
- datasrc_clientmgr_internal::DataSrcClientsBuilder,
- util::thread::Mutex, util::thread::CondVar> DataSrcClientsMgr;
- } // namespace auth
- } // namespace isc
- #endif // DATASRC_CLIENTS_MGR_H
- // Local Variables:
- // mode: c++
- // End:
|