summaryrefslogtreecommitdiffstats
path: root/src/lib/util/multi_threading_mgr.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/multi_threading_mgr.h')
-rw-r--r--src/lib/util/multi_threading_mgr.h374
1 files changed, 374 insertions, 0 deletions
diff --git a/src/lib/util/multi_threading_mgr.h b/src/lib/util/multi_threading_mgr.h
new file mode 100644
index 0000000..e86c488
--- /dev/null
+++ b/src/lib/util/multi_threading_mgr.h
@@ -0,0 +1,374 @@
+// Copyright (C) 2019-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef MULTI_THREADING_MGR_H
+#define MULTI_THREADING_MGR_H
+
+#include <util/thread_pool.h>
+#include <functional>
+#include <list>
+
+#include <boost/noncopyable.hpp>
+
+#include <stdint.h>
+
+namespace isc {
+namespace util {
+
+
+/// @brief Embodies a named set of CriticalSection callbacks.
+///
+/// This class associates with a name, a set of callbacks, one to be invoked
+/// before CriticalSection entry and exit callbacks to check current thread
+/// permissions to perform such actions, one to be invoked upon CriticalSection
+/// entry and one to be invoked upon CriticalSection exit.
+/// The name allows the set to be uniquely identified such that they can be
+/// added and removed as needed.
+/// The check current thread permissions callback needs to throw
+/// @ref MultiThreadingInvalidOperation if the thread is not allowed to perform
+/// CriticalSection entry and exit. Any other exception thrown by the check
+/// permission callbacks will be silently ignored.
+/// The CriticalSection entry and exit callbacks exceptions will be silently
+/// ignored.
+struct CSCallbackSet {
+ /// @brief Defines a callback as a simple void() functor.
+ typedef std::function<void()> Callback;
+
+ /// @brief Constructor
+ ///
+ /// @param name Name by which the callbacks can be found.
+ /// @param check_cb Callback to check current thread permissions to call
+ /// the CriticalSection entry and exit callbacks.
+ /// @param entry_cb Callback to invoke upon CriticalSection entry.
+ /// @param exit_cb Callback to invoke upon CriticalSection exit.
+ CSCallbackSet(const std::string& name, const Callback& check_cb,
+ const Callback& entry_cb, const Callback& exit_cb)
+ : name_(name), check_cb_(check_cb), entry_cb_(entry_cb),
+ exit_cb_(exit_cb) {}
+
+ /// @brief Name by which the callback can be found.
+ std::string name_;
+
+ /// @brief Check permissions callback associated with name.
+ Callback check_cb_;
+
+ /// @brief Entry point callback associated with name.
+ Callback entry_cb_;
+
+ /// @brief Exit point callback associated with name.
+ Callback exit_cb_;
+};
+
+/// @brief Maintains list of unique CSCallbackSets.
+///
+/// The list emphasizes iteration order and speed over
+/// retrieval by name. When iterating over the list of
+/// callback sets, they are returned in the order they were
+/// added, not by name.
+class CSCallbackSetList {
+public:
+ /// @brief Constructor.
+ CSCallbackSetList() {}
+
+ /// @brief Adds a callback set to the list.
+ ///
+ /// @param name Name of the callback to add.
+ /// @param check_cb The check permissions callback to add.
+ /// @param entry_cb The CriticalSection entry callback to add.
+ /// @param exit_cb The CriticalSection exit callback to add.
+ ///
+ /// @throw BadValue if the name is already in the list,
+ /// the name is blank, or either callback is empty.
+ void addCallbackSet(const std::string& name,
+ const CSCallbackSet::Callback& check_cb,
+ const CSCallbackSet::Callback& entry_cb,
+ const CSCallbackSet::Callback& exit_cb);
+
+ /// @brief Removes a callback set from the list.
+ ///
+ /// @param name Name of the callback to remove.
+ /// If no such callback exists, it simply returns.
+ void removeCallbackSet(const std::string& name);
+
+ /// @brief Removes all callbacks from the list.
+ void removeAll();
+
+ /// @brief Fetches the list of callback sets.
+ const std::list<CSCallbackSet>& getCallbackSets();
+
+private:
+ /// @brief The list of callback sets.
+ std::list<CSCallbackSet> cb_sets_;
+};
+
+/// @brief Multi Threading Manager.
+///
+/// This singleton class holds the multi-threading mode.
+///
+/// See the @ref MultiThreadingLock class for a standard way of protecting code
+/// with a mutex. Or if you want to make it look like you're writing more code:
+/// @code
+/// if (MultiThreadingMgr::instance().getMode()) {
+/// multi-threaded code
+/// } else {
+/// single-threaded code
+/// }
+/// @endcode
+///
+/// For instance for a class protected by its mutex:
+/// @code
+/// namespace locked {
+/// void foo() { ... }
+/// } // end of locked namespace
+///
+/// void foo() {
+/// if (MultiThreadingMgr::instance().getMode()) {
+/// lock_guard<mutex> lock(mutex_);
+/// locked::foo();
+/// } else {
+/// locked::foo();
+/// }
+/// }
+/// @endcode
+class MultiThreadingMgr : public boost::noncopyable {
+public:
+ /// @brief Returns a single instance of Multi Threading Manager.
+ ///
+ /// MultiThreadingMgr is a singleton and this method is the only way
+ /// of accessing it.
+ ///
+ /// @return the single instance.
+ static MultiThreadingMgr& instance();
+
+ /// @brief Get the multi-threading mode.
+ ///
+ /// @return The current multi-threading mode: true if multi-threading is
+ /// enabled, false otherwise.
+ bool getMode() const;
+
+ /// @brief Set the multi-threading mode.
+ ///
+ /// @param enabled The new mode.
+ void setMode(bool enabled);
+
+ /// @brief Enter critical section.
+ ///
+ /// When entering @ref MultiThreadingCriticalSection, increment internal
+ /// counter so that any configuration change that might start the packet
+ /// thread pool is delayed until exiting the respective section.
+ /// If the internal counter is 0, then stop the thread pool.
+ ///
+ /// Invokes all CriticalSection entry callbacks. Has no effect in
+ /// single-threaded mode.
+ ///
+ /// @note This function swallows exceptions thrown by all entry callbacks
+ /// without logging to avoid breaking the CS chain.
+ void enterCriticalSection();
+
+ /// @brief Exit critical section.
+ ///
+ /// When exiting @ref MultiThreadingCriticalSection, decrement internal
+ /// counter so that the dhcp thread pool can be started according to the
+ /// new configuration.
+ /// If the internal counter is 0, then start the thread pool.
+ ///
+ /// Invokes all CriticalSection exit callbacks. Has no effect in
+ /// single-threaded mode.
+ ///
+ /// @note This function swallows exceptions thrown by all exit callbacks
+ /// without logging to avoid breaking the CS chain.
+ void exitCriticalSection();
+
+ /// @brief Is in critical section flag.
+ ///
+ /// @return The critical section flag.
+ bool isInCriticalSection();
+
+ /// @brief Get the dhcp thread pool.
+ ///
+ /// @return The dhcp thread pool.
+ ThreadPool<std::function<void()>>& getThreadPool();
+
+ /// @brief Get the configured dhcp thread pool size.
+ ///
+ /// @return The dhcp thread pool size.
+ uint32_t getThreadPoolSize() const;
+
+ /// @brief Set the configured dhcp thread pool size.
+ ///
+ /// @param size The dhcp thread pool size.
+ void setThreadPoolSize(uint32_t size);
+
+ /// @brief Get the configured dhcp packet queue size.
+ ///
+ /// @return The dhcp packet queue size.
+ uint32_t getPacketQueueSize();
+
+ /// @brief Set the configured dhcp packet queue size.
+ ///
+ /// @param size The dhcp packet queue size.
+ void setPacketQueueSize(uint32_t size);
+
+ /// @brief The system current detected hardware concurrency thread count.
+ ///
+ /// This function will return 0 if the value can not be determined.
+ ///
+ /// @return The thread count.
+ static uint32_t detectThreadCount();
+
+ /// @brief Apply the multi-threading related settings.
+ ///
+ /// This function should usually be called after constructing a
+ /// @ref MultiThreadingCriticalSection so that all thread pool parameters
+ /// can be safely applied.
+ ///
+ /// @param enabled The enabled flag: true if multi-threading is enabled,
+ /// false otherwise.
+ /// @param thread_count The desired number of threads: non 0 if explicitly
+ /// configured, 0 if auto scaling is desired
+ /// @param queue_size The desired thread queue size: non 0 if explicitly
+ /// configured, 0 for unlimited size
+ void apply(bool enabled, uint32_t thread_count, uint32_t queue_size);
+
+ /// @brief Adds a set of callbacks to the list of CriticalSection callbacks.
+ ///
+ /// @note Callbacks must be exception-safe, handling any errors themselves.
+ ///
+ /// @param name Name of the set of callbacks. This value is used by the
+ /// callback owner to add and remove them. Duplicates are not allowed.
+ /// @param check_cb Callback to check current thread permissions to call
+ /// the CriticalSection entry and exit callbacks.
+ /// @param entry_cb Callback to invoke upon CriticalSection entry. Cannot be
+ /// empty.
+ /// @param exit_cb Callback to invoke upon CriticalSection exit. Cannot be
+ /// empty.
+ void addCriticalSectionCallbacks(const std::string& name,
+ const CSCallbackSet::Callback& check_cb,
+ const CSCallbackSet::Callback& entry_cb,
+ const CSCallbackSet::Callback& exit_cb);
+
+ /// @brief Removes the set of callbacks associated with a given name
+ /// from the list of CriticalSection callbacks.
+ ///
+ /// If the name is not found in the list, it simply returns, otherwise
+ /// both callbacks registered under the name are removed.
+ ///
+ /// @param name Name of the set of callbacks to remove.
+ void removeCriticalSectionCallbacks(const std::string& name);
+
+ /// @brief Removes all callbacks in the list of CriticalSection callbacks.
+ void removeAllCriticalSectionCallbacks();
+
+protected:
+
+ /// @brief Constructor.
+ MultiThreadingMgr();
+
+ /// @brief Destructor.
+ virtual ~MultiThreadingMgr();
+
+private:
+
+ /// @brief Class method tests if current thread is allowed to enter the
+ /// @ref MultiThreadingCriticalSection and to invoke the entry and exit
+ /// callbacks.
+ ///
+ /// Has no effect in single-threaded mode.
+ ///
+ /// @note This function swallows exceptions thrown by all check permission
+ /// callbacks without logging to avoid breaking the CS chain, except for the
+ /// @ref MultiThreadingInvalidOperation which needs to be propagated to the
+ /// scope of the @ref MultiThreadingCriticalSection constructor.
+ /// @throw MultiThreadingInvalidOperation if current thread has no
+ /// permission to enter CriticalSection.
+ void checkCallbacksPermissions();
+
+ /// @brief Class method which invokes CriticalSection entry callbacks.
+ ///
+ /// Has no effect in single-threaded mode.
+ ///
+ /// @note This function swallows exceptions thrown by all entry callbacks
+ /// without logging to avoid breaking the CS chain.
+ void callEntryCallbacks();
+
+ /// @brief Class method which invokes CriticalSection exit callbacks.
+ ///
+ /// Has no effect in single-threaded mode.
+ ///
+ /// @note This function swallows exceptions thrown by all exit callbacks
+ /// without logging to avoid breaking the CS chain.
+ void callExitCallbacks();
+
+ /// @brief The current multi-threading mode.
+ ///
+ /// The multi-threading flag: true if multi-threading is enabled, false
+ /// otherwise.
+ bool enabled_;
+
+ /// @brief The critical section count.
+ ///
+ /// In case the configuration is applied within a
+ /// @ref MultiThreadingCriticalSection, the thread pool should not be
+ /// started until leaving the respective section.
+ /// This handles multiple interleaved sections.
+ uint32_t critical_section_count_;
+
+ /// @brief The configured size of the dhcp thread pool.
+ uint32_t thread_pool_size_;
+
+ /// @brief Packet processing thread pool.
+ ThreadPool<std::function<void()>> thread_pool_;
+
+ /// @brief List of CriticalSection entry and exit callback sets.
+ CSCallbackSetList cs_callbacks_;
+};
+
+/// @brief RAII lock object to protect the code in the same scope with a mutex
+struct MultiThreadingLock {
+ /// @brief Constructor locks the mutex if multi-threading is enabled.
+ ///
+ /// The lock is automatically unlocked in the default destructor.
+ ///
+ /// @param mutex the mutex to be locked
+ MultiThreadingLock(std::mutex& mutex);
+
+private:
+ /// @brief object keeping the mutex locked for its entire lifetime
+ std::unique_lock<std::mutex> lock_;
+};
+
+/// @note: everything here MUST be used ONLY from the main thread.
+/// When called from a thread of the pool it can deadlock.
+
+/// @brief RAII class creating a critical section.
+///
+/// @note: the multi-threading mode MUST NOT be changed in the RAII
+/// @c MultiThreadingCriticalSection body.
+/// @note: starting and stopping the dhcp thread pool should be handled
+/// in the main thread, if done on one of the processing threads will cause a
+/// deadlock.
+/// This is mainly useful in hook commands which handle configuration
+/// changes.
+class MultiThreadingCriticalSection : public boost::noncopyable {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Entering the critical section. The dhcp thread pool instance will be
+ /// stopped so that all configuration changes can be safely applied.
+ MultiThreadingCriticalSection();
+
+ /// @brief Destructor.
+ ///
+ /// Leaving the critical section. The dhcp thread pool instance will be
+ /// started according to the new configuration.
+ virtual ~MultiThreadingCriticalSection();
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // MULTI_THREADING_MGR_H