summaryrefslogtreecommitdiffstats
path: root/src/lib/http/http_thread_pool.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/http/http_thread_pool.h')
-rw-r--r--src/lib/http/http_thread_pool.h265
1 files changed, 265 insertions, 0 deletions
diff --git a/src/lib/http/http_thread_pool.h b/src/lib/http/http_thread_pool.h
new file mode 100644
index 0000000..730f8c9
--- /dev/null
+++ b/src/lib/http/http_thread_pool.h
@@ -0,0 +1,265 @@
+// Copyright (C) 2021-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 HTTP_THREAD_POOL_H
+#define HTTP_THREAD_POOL_H
+
+#include <asiolink/io_service.h>
+#include <util/unlock_guard.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <condition_variable>
+#include <list>
+#include <mutex>
+#include <thread>
+
+namespace isc {
+namespace http {
+
+/// @brief Implements a pausable pool of IOService driven threads.
+class HttpThreadPool {
+public:
+ /// @brief Describes the possible operational state of the thread pool.
+ enum class State {
+ STOPPED, /// Pool is not operational.
+ RUNNING, /// Pool is populated with running threads.
+ PAUSED, /// Pool is populated with threads that are paused.
+ };
+
+ /// @brief Constructor
+ ///
+ /// @param io_service IOService that will drive the pool's IO. If empty, it
+ /// create its own instance.
+ /// @param pool_size Maximum number of threads in the pool. Currently the
+ /// number of threads is fixed at this value.
+ /// @param defer_start If true, creation of the threads is deferred until
+ /// a subsequent call to @ref run(). In this case the pool's operational
+ /// state post construction is STOPPED. If false, the constructor will
+ /// invoke run() to transition the pool into the RUNNING state.
+ HttpThreadPool(asiolink::IOServicePtr io_service, size_t pool_size,
+ bool defer_start = false);
+
+ /// @brief Destructor
+ ///
+ /// Ensures the thread pool is stopped prior to destruction.
+ ~HttpThreadPool();
+
+ /// @brief Transitions the pool from STOPPED or PAUSED to RUNNING.
+ ///
+ /// When called from the STOPPED state, the pool threads are created and
+ /// begin processing events.
+ /// When called from the PAUSED state, the pool threads are released
+ /// from PAUSED and resume processing events.
+ /// Has no effect if the pool is already in the RUNNING state.
+ void run();
+
+ /// @brief Transitions the pool from RUNNING to PAUSED.
+ ///
+ /// Pool threads suspend event processing and pause until they
+ /// are released to either resume running or stop.
+ /// Has no effect if the pool is already in the PAUSED or STOPPED
+ /// state.
+ void pause();
+
+ /// @brief Transitions the pool from RUNNING or PAUSED to STOPPED.
+ ///
+ /// Stops thread event processing and then destroys the pool's threads
+ /// Has no effect if the pool is already in the STOPPED state.
+ void stop();
+
+ /// @brief Check if the thread pool is running.
+ ///
+ /// @return True if the thread pool is running, false otherwise.
+ bool isRunning() {
+ return (getState() == State::RUNNING);
+ }
+
+ /// @brief Check if the thread pool is paused.
+ ///
+ /// @return True if the thread pool is paused, false otherwise.
+ bool isPaused() {
+ return (getState() == State::PAUSED);
+ }
+
+ /// @brief Check if the thread pool is stopped.
+ ///
+ /// @return True if the thread pool is stopped, false otherwise.
+ bool isStopped() {
+ return (getState() == State::STOPPED);
+ }
+
+ /// @brief Check current thread permissions to transition to the new PAUSED
+ /// state.
+ ///
+ /// This function throws @ref MultiThreadingInvalidOperation if the calling
+ /// thread is one of the worker threads. This would prevent a dead-lock if
+ /// the calling thread would try to perform a thread pool state transition
+ /// to PAUSED state.
+ ///
+ /// @throw MultiThreadingInvalidOperation if the state transition is done on
+ /// any of the worker threads.
+ void checkPausePermissions();
+
+private:
+ /// @brief Check current thread permissions to transition to the new state.
+ ///
+ /// This function throws @ref MultiThreadingInvalidOperation if the calling
+ /// thread is one of the worker threads. This would prevent a dead-lock if
+ /// the calling thread would try to perform a thread pool state transition.
+ ///
+ /// @param state The new transition state for the pool.
+ /// @throw MultiThreadingInvalidOperation if the state transition is done on
+ /// any of the worker threads.
+ void checkPermissions(State state);
+
+ /// @brief Check specified thread id against own threads.
+ ///
+ /// @return true if thread is owned, false otherwise.
+ bool checkThreadId(std::thread::id id);
+
+ /// @brief Thread-safe change of the pool's run state.
+ ///
+ /// Transitions a pool from one run state to another:
+ ///
+ /// When moving from STOPPED or PAUSED to RUNNING:
+ /// -# Sets state to RUNNING.
+ /// -# Notifies threads of state change.
+ /// -# Restarts the IOService.
+ /// -# Creates the threads if they do not yet exist (true only
+ /// when transitioning from STOPPED).
+ /// -# Waits until all threads are running.
+ /// -# Sets the count of exited threads to 0.
+ /// -# Returns to caller.
+ ///
+ /// When moving from RUNNING or PAUSED to STOPPED:
+ /// -# Sets state to STOPPED
+ /// -# Notifies threads of state change.
+ /// -# Polls the IOService to flush handlers.
+ /// -# Stops the IOService.
+ /// -# Waits until all threads have exited the work function.
+ /// -# Joins and destroys the threads.
+ /// -# Returns to caller.
+ ///
+ /// When moving from RUNNING to PAUSED:
+ /// -# Sets state to PAUSED
+ /// -# Notifies threads of state change.
+ /// -# Polls the IOService to flush handlers.
+ /// -# Stops the IOService.
+ /// -# Waits until all threads have paused.
+ /// -# Returns to caller.
+ ///
+ /// @param state The new transition state for the pool.
+ /// @throw MultiThreadingInvalidOperation if the state transition is done on
+ /// any of the worker threads.
+ void setState(State state);
+
+ /// @brief Thread-safe fetch of the pool's operational state.
+ ///
+ /// @return Thread pool state.
+ State getState();
+
+ /// @brief Validates whether the pool can change to a given state.
+ ///
+ /// @param state new state for the pool.
+ /// @return true if the change is valid, false otherwise.
+ /// @note Must be called from a thread-safe context.
+ bool validateStateChange(State state) const;
+
+ /// @brief Text representation of a given state.
+ ///
+ /// @param state The state for the pool.
+ /// @return The text representation of a given state.
+ static std::string stateToText(State state);
+
+ /// @brief Work function executed by each thread in the pool.
+ ///
+ /// Implements the run state responsibilities for a given thread.
+ /// It executes a run loop until the pool is stopped. At the top
+ /// of each iteration of the loop the pool's run state is checked
+ /// and when it is:
+ ///
+ /// RUNNING:
+ /// -# The count of threads running is incremented.
+ /// -# If the count has reached the number of threads in pool the
+ /// main thread is notified.
+ /// -# IOService::run() is invoked.
+ /// -# When IOService::run() returns, the count of threads running
+ /// is decremented.
+ ///
+ /// PAUSED:
+ /// -# The count of threads paused is incremented.
+ /// -# If the count has reached the number of threads in pool the
+ /// main thread is notified.
+ /// -# Thread blocks until notified the pool's run state is no
+ /// longer PAUSED.
+ /// -# The count of threads paused is decremented.
+ ///
+ /// STOPPED:
+ /// -# The run loop is exited.
+ /// -# The count of threads exited is incremented.
+ /// -# If the count has reached the number of threads in pool the
+ /// main thread is notified.
+ /// -# The function exits.
+ void threadWork();
+
+public:
+ /// @brief Fetches the IOService that drives the pool.
+ ///
+ /// @return the pointer to the IOService.
+ asiolink::IOServicePtr getIOService() const;
+
+ /// @brief Fetches the maximum size of the thread pool.
+ ///
+ /// @return the maximum size of the thread pool.
+ uint16_t getPoolSize() const;
+
+ /// @brief Fetches the number of threads in the pool.
+ ///
+ /// @return the number of running threads.
+ uint16_t getThreadCount() const;
+
+private:
+ /// @brief Maximum number of threads in the thread pool.
+ size_t pool_size_;
+
+ /// @brief Pointer to private IOService used in multi-threaded mode.
+ asiolink::IOServicePtr io_service_;
+
+ /// @brief Tracks the operational state of the pool.
+ State run_state_;
+
+ /// @brief Mutex to protect the internal state.
+ std::mutex mutex_;
+
+ /// @brief Condition variable used by threads for synchronization.
+ std::condition_variable thread_cv_;
+
+ /// @brief Condition variable used by main thread to wait on threads
+ /// state transitions.
+ std::condition_variable main_cv_;
+
+ /// @brief Number of threads currently paused.
+ size_t paused_;
+
+ /// @brief Number of threads currently running.
+ size_t running_;
+
+ /// @brief Number of threads that have exited the work function.
+ size_t exited_;
+
+ /// @brief Pool of threads used to service connections in multi-threaded
+ /// mode.
+ std::list<boost::shared_ptr<std::thread> > threads_;
+};
+
+/// @brief Defines a pointer to a thread pool.
+typedef boost::shared_ptr<HttpThreadPool> HttpThreadPoolPtr;
+
+} // end of namespace isc::http
+} // end of namespace isc
+
+#endif