]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1818] Added pause and resume to HAService
authorThomas Markwalder <tmark@isc.org>
Fri, 7 May 2021 16:02:25 +0000 (12:02 -0400)
committerThomas Markwalder <tmark@isc.org>
Mon, 17 May 2021 14:56:49 +0000 (10:56 -0400)
HAService defers client start, and can now pause
and resume client and listener

src/hooks/dhcp/high_availability/ha_service.*
    HAService::HAService() - client start is now deferred
    HAService::startClientAndListener() - now starts client too

    HAService::pauseClientAndListener()
    HAService::resumeClientAndListener() - new methods

src/hooks/dhcp/high_availability/tests/ha_mt_unittest.cc
    TEST_F(HAMtServiceTest, multiThreadingBasics) - new test that
    verifies start,pause,resume and stop

src/lib/config/cmd_http_listener.*
    CmdHttpListener:
        Renamed io_service_ to thread_io_service_
        Added run state convenience methods
        Deleted isListening() method

src/lib/config/tests/cmd_http_listener_unittests.cc
    Updated tests

src/lib/http/client.*
    Clean up
    Added run state convenience methods

src/lib/http/tests/mt_client_unittests.cc
    Updated tests

src/hooks/dhcp/high_availability/ha_service.cc
src/hooks/dhcp/high_availability/ha_service.h
src/hooks/dhcp/high_availability/tests/ha_mt_unittest.cc
src/lib/config/cmd_http_listener.cc
src/lib/config/cmd_http_listener.h
src/lib/config/tests/cmd_http_listener_unittests.cc
src/lib/http/client.cc
src/lib/http/client.h
src/lib/http/tests/mt_client_unittests.cc

index 8536f3cfb631657efc4a5180f9b58f3d42a6bdcf..55f46d1b75fe66fd8c272e84fd1ae42638394660 100644 (file)
@@ -73,9 +73,9 @@ HAService::HAService(const IOServicePtr& io_service, const NetworkStatePtr& netw
         // Not configured for multi-threading, start a client in ST mode.
         client_.reset(new HttpClient(*io_service_, 0));
     } else {
-        // Start a client in MT mode.
+        // Create an MT-mode client.
         client_.reset(new HttpClient(*io_service_,
-                      config_->getHttpClientThreads()));
+                      config_->getHttpClientThreads(), true));
 
         // If we're configured to use our own listener create and start it.
         if (config_->getHttpDedicatedListener()) {
@@ -2815,11 +2815,37 @@ HAService::getPendingRequestInternal(const QueryPtrType& query) {
 
 void
 HAService::startClientAndListener() {
+    if (client_) {
+        client_->start();
+    }
+
     if (listener_) {
         listener_->start();
     }
 }
 
+void
+HAService::pauseClientAndListener() {
+    if (client_) {
+        client_->pause();
+    }
+
+    if (listener_) {
+        listener_->pause();
+    }
+}
+
+void
+HAService::resumeClientAndListener() {
+    if (client_) {
+        client_->resume();
+    }
+
+    if (listener_) {
+        listener_->resume();
+    }
+}
+
 void
 HAService::stopClientAndListener() {
     if (client_) {
index ff91817d29a4c42ad039871f9619a33b3cb29807..9c430056e003a2bb252a373faedb6aa8fa7d6374 100644 (file)
@@ -1004,19 +1004,28 @@ public:
 
     /// @brief Start the client and(or) listener instances.
     ///
-    /// Starts the dedicated listener thread pool, if the listener exists.
-    /// Nothing is required for the client as it does not currently support
-    /// a discrete "start" method, rather it is "started" during its
-    /// construction.
+    /// When HA+Mt is enabled it starts the client's thread pool
+    /// and the dedicated listener thread pool, if the listener exists.
     void startClientAndListener();
 
+    /// @brief Pauses client and(or) listener thread pool operations.
+    ///
+    /// Suspends the client and listener thread pool event processing.
+    /// Has no effect in single-threaded mode or if thread pools are
+    /// not currently running.
+    void pauseClientAndListener();
+
+    /// @brief Resumes client and(or) listener thread pool operations.
+    ///
+    /// Resumes the client and listener thread pool event processing.
+    /// Has no effect in single-threaded mode or if thread pools are
+    /// not currently paused.
+    void resumeClientAndListener();
+
     /// @brief Stop the client and(or) listener instances.
     ///
-    /// Closing connections and stops the thread pools for the client
+    /// Closes all connections and stops the thread pools for the client
     /// and listener, if they exist.
-    ///
-    /// @note Once stopped the service cannot be restarted, it must
-    /// be recreated.
     void stopClientAndListener();
 
 protected:
index 5d798a2148cae77a0bf14ff12ee46c42ded0f1fd..f1f6836cb2ad19e962944795cd4817622dc59900 100644 (file)
@@ -10,6 +10,7 @@
 #include <ha_test.h>
 #include <ha_config.h>
 #include <ha_service.h>
+#include <http/http_thread_pool.h>
 
 #include <util/multi_threading_mgr.h>
 #include <testutils/gtest_utils.h>
@@ -23,6 +24,7 @@ using namespace isc::dhcp;
 
 using namespace isc::ha;
 using namespace isc::ha::test;
+using namespace isc::http;
 using namespace isc::util;
 
 namespace {
@@ -147,8 +149,112 @@ public:
     }
 };
 
+// Verifies HA+MT start, pause, resume, and stop.
+TEST_F(HAMtServiceTest, multiThreadingBasics) {
+
+    // Build the HA JSON configuration.
+    std::stringstream ss;
+    ss <<
+        "["
+        "    {"
+        "        \"this-server-name\": \"server1\","
+        "        \"mode\": \"passive-backup\","
+        "        \"wait-backup-ack\": true,"
+        "        \"peers\": ["
+        "            {"
+        "                \"name\": \"server1\","
+        "                \"url\": \"http://127.0.0.1:8080/\","
+        "                \"role\": \"primary\""
+        "            },"
+        "            {"
+        "                \"name\": \"server2\","
+        "                \"url\": \"http://127.0.0.1:8081/\","
+        "                \"role\": \"backup\""
+        "            }"
+        "        ]";
+
+    // Enable MT, listener, and 3 threads for both client and listener.
+    ss << "," << makeHAMtJson(true, true, 3, 3) << "}]";
+    ConstElementPtr config_json;
+    ASSERT_NO_THROW_LOG(config_json = Element::fromJSON(ss.str()));
+
+    // Enable DHCP multi-threading configuration in CfgMgr with 3 threads.
+    setDHCPMultiThreadingConfig(true, 3);
+
+    /// @todo this is a hack... we have chicken-egg...  CmdHttpListener won't
+    /// start if MT is not enabled BUT that happens after config hook point
+    MultiThreadingMgr::instance().setMode(true);
+
+    // Create the HA configuration
+    HAConfigPtr ha_config(new HAConfig());
+    HAConfigParser parser;
+    ASSERT_NO_THROW_LOG(parser.parse(ha_config, config_json));
+
+    // Instantiate the service.
+    TestHAServicePtr service;
+    ASSERT_NO_THROW_LOG(service.reset(new TestHAService(io_service_, network_state_,
+                                                        ha_config)));
+    // Multi-threading should be enabled.
+    ASSERT_TRUE(ha_config->getEnableMultiThreading());
+
+    // Now we'll start, pause, resume and stop a few times.
+    for (int i = 0; i < 3; ++i) {
+        // Verify we're stopped.
+        // Client should exist but be stopped.
+        ASSERT_TRUE(service->client_);
+        ASSERT_TRUE(service->client_->isStopped());
+
+        // Listener should exist but not be stopped..
+        ASSERT_TRUE(service->listener_);
+        ASSERT_TRUE(service->client_->isStopped());
+
+        // Start client and listener.
+        ASSERT_NO_THROW_LOG(service->startClientAndListener());
+
+        // Verify we've started.
+        // Client should be running.
+        ASSERT_TRUE(service->client_->isRunning());
+        ASSERT_TRUE(service->client_->getThreadIOService());
+        EXPECT_FALSE(service->client_->getThreadIOService()->stopped());
+        EXPECT_EQ(service->client_->getThreadPoolSize(), 3);
+        EXPECT_EQ(service->client_->getThreadCount(), 3);
+
+        // Listener should be running.
+        ASSERT_TRUE(service->listener_->isRunning());
+        ASSERT_TRUE(service->listener_->getThreadIOService());
+        EXPECT_FALSE(service->listener_->getThreadIOService()->stopped());
+        EXPECT_EQ(service->listener_->getThreadPoolSize(), 3);
+        EXPECT_EQ(service->listener_->getThreadCount(), 3);
+
+        // Pause client and listener.
+        ASSERT_NO_THROW_LOG(service->pauseClientAndListener());
+
+        // Client should be paused.
+        ASSERT_TRUE(service->client_->isPaused());
+        EXPECT_TRUE(service->client_->getThreadIOService()->stopped());
+
+        // Listener should be paused.
+        ASSERT_TRUE(service->listener_->isPaused());
+        EXPECT_TRUE(service->listener_->getThreadIOService()->stopped());
+
+        // Now resume client and listener.
+        ASSERT_NO_THROW_LOG(service->resumeClientAndListener());
+
+        // Client should be running.
+        ASSERT_TRUE(service->client_->isRunning());
+        EXPECT_FALSE(service->client_->getThreadIOService()->stopped());
+
+        // Listener should be running.
+        ASSERT_TRUE(service->listener_->isRunning());
+        EXPECT_FALSE(service->listener_->getThreadIOService()->stopped());
+
+        // Stop should succeed.
+        ASSERT_NO_THROW_LOG(service->stopClientAndListener());
+    }
+}
+
 // Verifies permutations of HA+MT configuration and start-up.
-TEST_F(HAMtServiceTest, multiThreadingStartup) {
+TEST_F(HAMtServiceTest, multiThreadingConfigStartup) {
 
     // Structure describing a test scenario.
     struct Scenario {
@@ -273,7 +379,7 @@ TEST_F(HAMtServiceTest, multiThreadingStartup) {
         TestHAServicePtr service;
         ASSERT_NO_THROW_LOG(service.reset(new TestHAService(io_service_, network_state_,
                                                             ha_config)));
-        ASSERT_NO_THROW(service->startClientAndListener());
+        ASSERT_NO_THROW_LOG(service->startClientAndListener());
 
         // Verify the configuration is as expected.
         if (!scenario.exp_ha_mt_enabled_) {
@@ -293,6 +399,7 @@ TEST_F(HAMtServiceTest, multiThreadingStartup) {
 
         // When HA+MT is enabled, client should be multi-threaded.
         ASSERT_TRUE(service->client_);
+        EXPECT_TRUE(service->client_->isRunning());
         EXPECT_TRUE(service->client_->getThreadIOService());
         EXPECT_EQ(service->client_->getThreadPoolSize(), scenario.exp_client_threads_);
         // Currently thread count should be the same as thread pool size.  This might
@@ -305,13 +412,16 @@ TEST_F(HAMtServiceTest, multiThreadingStartup) {
             continue;
         }
 
-        // We should have a listening listener with the expected number of threads.
+        // We should have a running listener with the expected number of threads.
         ASSERT_TRUE(service->listener_);
-        EXPECT_TRUE(service->listener_->isListening());
+        EXPECT_TRUE(service->listener_->isRunning());
         EXPECT_EQ(service->listener_->getThreadPoolSize(), scenario.exp_listener_threads_);
+
         // Currently thread count should be the same as thread pool size.  This might
         // change if we go to so some sort of dynamic thread instance management.
         EXPECT_EQ(service->listener_->getThreadCount(), scenario.exp_listener_threads_);
+
+        ASSERT_NO_THROW_LOG(service->stopClientAndListener());
     }
 }
 
index 528c89cc3de7ada4549ff0f516576336ab7f8c7f..499c3b391f03a09a620e2ba7bb47219934055c0b 100644 (file)
@@ -28,7 +28,7 @@ namespace config {
 
 CmdHttpListener::CmdHttpListener(const IOAddress& address, const uint16_t port,
                                  const uint16_t thread_pool_size /* = 1 */)
-    : address_(address), port_(port), io_service_(), http_listener_(),
+    : address_(address), port_(port), thread_io_service_(), http_listener_(),
       thread_pool_size_(thread_pool_size), threads_() {
 }
 
@@ -44,14 +44,14 @@ CmdHttpListener::start() {
                   " when multi-threading is disabled");
     }
 
-    // Punt if we're already listening.
-    if (isListening()) {
-        isc_throw(InvalidOperation, "CmdHttpListener is already listening!");
+    // Punt if we're already started.
+    if (!isStopped()) {
+        isc_throw(InvalidOperation, "CmdHttpListener already started!");
     }
 
     try {
         // Create a new IOService.
-        io_service_.reset(new IOService());
+        thread_io_service_.reset(new IOService());
 
         // Create the response creator factory first. It will be used to
         // generate response creators. Each response creator will be
@@ -61,13 +61,13 @@ CmdHttpListener::start() {
         // Create the HTTP listener. It will open up a TCP socket and be
         // prepared to accept incoming connections.
         TlsContextPtr tls_context;
-        http_listener_.reset(new HttpListener(*io_service_, address_, port_,
+        http_listener_.reset(new HttpListener(*thread_io_service_, address_, port_,
                                               tls_context, rcf,
                                               HttpListener::RequestTimeout(TIMEOUT_AGENT_RECEIVE_COMMAND),
                                               HttpListener::IdleTimeout(TIMEOUT_AGENT_IDLE_CONNECTION_TIMEOUT)));
 
-        // Create the thread pooli with immediate start.
-        threads_.reset(new HttpThreadPool(io_service_, thread_pool_size_));
+        // Create the thread pooi with immediate start.
+        threads_.reset(new HttpThreadPool(thread_io_service_, thread_pool_size_));
 
         // Instruct the HTTP listener to actually open socket, install
         // callback and start listening.
@@ -100,7 +100,7 @@ CmdHttpListener::resume() {
 void
 CmdHttpListener::stop() {
     // Nothing to do.
-    if (!io_service_) {
+    if (!thread_io_service_) {
         return;
     }
 
@@ -115,7 +115,7 @@ CmdHttpListener::stop() {
     http_listener_.reset();
 
     // Ditch the IOService.
-    io_service_.reset();
+    thread_io_service_.reset();
 
     LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_HTTP_LISTENER_STOPPED)
               .arg(address_)
@@ -132,10 +132,31 @@ CmdHttpListener::getRunState() const {
     return (threads_->getRunState());
 }
 
-bool
-CmdHttpListener::isListening() const {
-    return (threads_ && (threads_->getRunState() == HttpThreadPool::RunState::PAUSED 
-                         || threads_->getRunState() == HttpThreadPool::RunState::RUN));
+bool 
+CmdHttpListener::isRunning() {
+    if (threads_) {
+        return (threads_->getRunState() == HttpThreadPool::RunState::RUN);
+    }
+
+    return (false);
+}
+
+bool 
+CmdHttpListener::isStopped() {
+    if (threads_) {
+        return (threads_->getRunState() == HttpThreadPool::RunState::STOPPED);
+    }
+
+    return (true);
+}
+
+bool 
+CmdHttpListener::isPaused() {
+    if (threads_) {
+        return (threads_->getRunState() == HttpThreadPool::RunState::PAUSED);
+    }
+
+    return (false);
 }
 
 } // namespace isc::config
index 83115ab0c23fc955c9d54a390f3ba98136386c7e..5ba3f8703a0cfd85008019530a3e070fe2fc207c 100644 (file)
@@ -55,10 +55,23 @@ public:
     /// @return Run state of the pool.
     http::HttpThreadPool::RunState getRunState() const;
 
-    /// @brief Checks if we are listening to the HTTP requests.
+    /// @brief Indicates if the thread pool processing is running.
     ///
-    /// @return true if we are listening.
-    bool isListening() const;
+    /// @return True if the thread pool exists and is in the RUN state,
+    /// false otherwise.
+    bool isRunning();
+
+    /// @brief Indicates if the thread pool is stopped.
+    ///
+    /// @return True if the thread pool does not exist or it 
+    /// is in the STOPPED state, False otherwise.
+    bool isStopped();
+
+    /// @brief Indicates if the thread pool processing is running.
+    ///
+    /// @return True if the thread pool exists and is in the PAUSED state,
+    /// false otherwise.
+    bool isPaused();
 
     /// @brief Fetches the IP address on which to listen.
     ///
@@ -92,8 +105,8 @@ public:
         return (threads_->getThreadCount());
     }
 
-    asiolink::IOServicePtr getIOService() const {
-        return(io_service_);
+    asiolink::IOServicePtr getThreadIOService() const {
+        return(thread_io_service_);
     }
 
 private:
@@ -104,7 +117,7 @@ private:
     uint16_t port_;
 
     /// @brief IOService instance that drives our IO.
-    isc::asiolink::IOServicePtr io_service_;
+    isc::asiolink::IOServicePtr thread_io_service_;
 
     /// @brief The HttpListener instance
     http::HttpListenerPtr http_listener_;
index bb8e50dd992851b63e6a46e095322cf21b1976b0..6d57e1eeff7b9eef35cac192d35ab8b498f4f7cd 100644 (file)
@@ -381,9 +381,9 @@ public:
                                                                SERVER_PORT, num_threads)));
         ASSERT_TRUE(listener_);
 
-        // Start it and verify it is listening.
+        // Start it and verify it is running.
         ASSERT_NO_THROW_LOG(listener_->start());
-        ASSERT_TRUE(listener_->isListening());
+        ASSERT_TRUE(listener_->isRunning());
         EXPECT_EQ(listener_->getThreadCount(), num_threads);
 
         // Maps the number of clients served by a given thread-id.
@@ -401,7 +401,7 @@ public:
 
         // Stop the listener and then verify it has stopped.
         ASSERT_NO_THROW_LOG(listener_->stop());
-        ASSERT_FALSE(listener_->isListening());
+        ASSERT_TRUE(listener_->isStopped());
         EXPECT_EQ(listener_->getThreadCount(), 0);
 
         // Iterate over the clients, checking their outcomes.
@@ -574,8 +574,8 @@ TEST_F(CmdHttpListenerTest, basics) {
 
     // It should not have an IOService, should not be listening
     // should have no threads.
-    ASSERT_FALSE(listener_->getIOService());
-    EXPECT_FALSE(listener_->isListening());
+    ASSERT_FALSE(listener_->getThreadIOService());
+    EXPECT_TRUE(listener_->isStopped());
     EXPECT_EQ(listener_->getThreadCount(), 0);
     ASSERT_THROW_MSG(listener_->getRunState(), InvalidOperation,
                      "CmdHttpListener::getRunState - no thread pool!");
@@ -587,7 +587,7 @@ TEST_F(CmdHttpListenerTest, basics) {
                      " when multi-threading is disabled");
 
     // It should still not be listening and have no threads.
-    EXPECT_FALSE(listener_->isListening());
+    EXPECT_TRUE(listener_->isStopped());
     EXPECT_EQ(listener_->getThreadCount(), 0);
 
     // Enable multi-threading.
@@ -595,33 +595,32 @@ TEST_F(CmdHttpListenerTest, basics) {
 
     // Make sure we can start it and it's listening with 1 thread.
     ASSERT_NO_THROW_LOG(listener_->start());
-    ASSERT_TRUE(listener_->isListening());
+    ASSERT_TRUE(listener_->isRunning());
     EXPECT_EQ(listener_->getThreadCount(), 1);
-    ASSERT_TRUE(listener_->getIOService());
-    EXPECT_FALSE(listener_->getIOService()->stopped());
+    ASSERT_TRUE(listener_->getThreadIOService());
+    EXPECT_FALSE(listener_->getThreadIOService()->stopped());
     EXPECT_EQ(listener_->getRunState(),  HttpThreadPool::RunState::RUN);
 
     // Trying to start it again should fail.
     ASSERT_THROW_MSG(listener_->start(), InvalidOperation,
-                     "CmdHttpListener is already listening!");
+                     "CmdHttpListener already started!");
 
     // Stop it and verify we're no longer listening.
     ASSERT_NO_THROW_LOG(listener_->stop());
-    ASSERT_FALSE(listener_->isListening());
+    ASSERT_TRUE(listener_->isStopped());
     EXPECT_EQ(listener_->getThreadCount(), 0);
     EXPECT_EQ(listener_->getRunState(),  HttpThreadPool::RunState::STOPPED);
-    ASSERT_FALSE(listener_->getIOService());
+    ASSERT_FALSE(listener_->getThreadIOService());
 
     // Make sure we can call stop again without problems.
     ASSERT_NO_THROW_LOG(listener_->stop());
 
     // We should be able to restart it.
     ASSERT_NO_THROW_LOG(listener_->start());
-    ASSERT_TRUE(listener_->isListening());
+    ASSERT_TRUE(listener_->isRunning());
     EXPECT_EQ(listener_->getThreadCount(), 1);
-    ASSERT_TRUE(listener_->getIOService());
-    EXPECT_FALSE(listener_->getIOService()->stopped());
-    EXPECT_EQ(listener_->getRunState(),  HttpThreadPool::RunState::RUN);
+    ASSERT_TRUE(listener_->getThreadIOService());
+    EXPECT_FALSE(listener_->getThreadIOService()->stopped());
 
     // Destroying it should also stop it.
     // If the test timeouts we know it didn't!
@@ -633,32 +632,30 @@ TEST_F(CmdHttpListenerTest, basics) {
     EXPECT_EQ(listener_->getAddress(), address);
     EXPECT_EQ(listener_->getPort(), port);
     EXPECT_EQ(listener_->getThreadPoolSize(), 4);
-    ASSERT_TRUE(listener_->isListening());
-    ASSERT_TRUE(listener_->getIOService());
-    EXPECT_FALSE(listener_->getIOService()->stopped());
+    ASSERT_TRUE(listener_->isRunning());
+    ASSERT_TRUE(listener_->getThreadIOService());
+    EXPECT_FALSE(listener_->getThreadIOService()->stopped());
 
     // Verify we can pause it.  We should still be listening, threads intact,
     // IOservice stopped, state set to PAUSED.
     ASSERT_NO_THROW_LOG(listener_->pause());
-    ASSERT_TRUE(listener_->isListening());
+    ASSERT_TRUE(listener_->isPaused());
     EXPECT_EQ(listener_->getThreadCount(), 4);
-    ASSERT_TRUE(listener_->getIOService());
-    EXPECT_TRUE(listener_->getIOService()->stopped());
-    EXPECT_EQ(listener_->getRunState(),  HttpThreadPool::RunState::PAUSED);
+    ASSERT_TRUE(listener_->getThreadIOService());
+    EXPECT_TRUE(listener_->getThreadIOService()->stopped());
 
     // Verify we can resume it.
     ASSERT_NO_THROW_LOG(listener_->resume());
-    ASSERT_TRUE(listener_->isListening());
+    ASSERT_TRUE(listener_->isRunning());
     EXPECT_EQ(listener_->getThreadCount(), 4);
-    ASSERT_TRUE(listener_->getIOService());
-    EXPECT_FALSE(listener_->getIOService()->stopped());
-    EXPECT_EQ(listener_->getRunState(),  HttpThreadPool::RunState::RUN);
+    ASSERT_TRUE(listener_->getThreadIOService());
+    EXPECT_FALSE(listener_->getThreadIOService()->stopped());
 
     // Stop it and verify we're no longer listening.
     ASSERT_NO_THROW_LOG(listener_->stop());
-    ASSERT_FALSE(listener_->isListening());
+    ASSERT_TRUE(listener_->isStopped());
     EXPECT_EQ(listener_->getThreadCount(), 0);
-    ASSERT_FALSE(listener_->getIOService());
+    ASSERT_FALSE(listener_->getThreadIOService());
     EXPECT_EQ(listener_->getRunState(),  HttpThreadPool::RunState::STOPPED);
 }
 
@@ -673,7 +670,7 @@ TEST_F(CmdHttpListenerTest, basicListenAndRespond) {
 
     // Start the listener and verify it's listening with 1 thread.
     ASSERT_NO_THROW_LOG(listener_->start());
-    ASSERT_TRUE(listener_->isListening());
+    ASSERT_TRUE(listener_->isRunning());
     EXPECT_EQ(listener_->getThreadCount(), 1);
 
     // Now let's send a "foo" command.  This should create a client, connect
@@ -711,12 +708,12 @@ TEST_F(CmdHttpListenerTest, basicListenAndRespond) {
     EXPECT_EQ(hr->getBody(), "[ { \"arguments\": [ \"bar\" ], \"result\": 0 } ]");
 
     // Make sure the listener is still listening.
-    ASSERT_TRUE(listener_->isListening());
+    ASSERT_TRUE(listener_->isRunning());
     EXPECT_EQ(listener_->getThreadCount(), 1);
 
     // Stop the listener then verify it has stopped.
     ASSERT_NO_THROW_LOG(listener_->stop());
-    ASSERT_FALSE(listener_->isListening());
+    ASSERT_TRUE(listener_->isStopped());
     EXPECT_EQ(listener_->getThreadCount(), 0);
 }
 
index 7719047c9a754896546ce6fdfae7fb0b6597f280..0002a6830e5b0849bb5d97daa5ebbc64c2e7fc2a 100644 (file)
@@ -1790,6 +1790,9 @@ public:
         }
     }
 
+    /// @brief Pauses the thread pool operation.
+    ///
+    /// Suspends thread pool event processing.
     void pause() {
         if (!threads_) {
             isc_throw(InvalidOperation, "HttpClient::pause - no thread pool");
@@ -1799,7 +1802,9 @@ public:
         threads_->pause();
     }
 
-    /// @brief Pauses the thread pool's worker threads.
+    /// @brief Resumes the thread pool operation.
+    ///
+    /// Resumes thread pool event processing.
     void resume() {
         if (!threads_) {
             isc_throw(InvalidOperation, "HttpClient::resume - no thread pool");
@@ -1809,6 +1814,9 @@ public:
         threads_->resume();
     }
 
+    /// @brief Fetches the thread pool's operational state.
+    ///
+    /// @return Operational state of the thread pool.
     HttpThreadPool::RunState getRunState() const {
         if (!threads_) {
             isc_throw(InvalidOperation, "HttpClient::getRunState - no thread pool");
@@ -1817,6 +1825,42 @@ public:
         return (threads_->getRunState());
     }
 
+    /// @brief Indicates if the thread pool processing is running.
+    ///
+    /// @return True if the thread pool exists and is in the RUN state,
+    /// false otherwise.
+    bool isRunning() {
+        if (threads_) {
+            return (threads_->getRunState() == HttpThreadPool::RunState::RUN);
+        }
+
+        return (false);
+    }
+
+    /// @brief Indicates if the thread pool is stopped.
+    ///
+    /// @return True if the thread pool exists and is in the STOPPED state,
+    /// false otherwise
+    bool isStopped() {
+        if (threads_) {
+            return (threads_->getRunState() == HttpThreadPool::RunState::STOPPED);
+        }
+
+        return (false);
+    }
+
+    /// @brief Indicates if the thread pool processing is running.
+    ///
+    /// @return True if the thread pool exists and is in the PAUSED state,
+    /// false otherwise.
+    bool isPaused() {
+        if (threads_) {
+            return (threads_->getRunState() == HttpThreadPool::RunState::PAUSED);
+        }
+
+        return (false);
+    }
+
     /// @brief Fetches the internal IOService used in multi-threaded mode.
     ///
     /// @return A pointer to the IOService, or an empty pointer when
@@ -1956,5 +2000,20 @@ HttpClient::getRunState() const {
     return (impl_->getRunState());
 }
 
+bool
+HttpClient::isRunning() {
+    return (impl_->isRunning());
+}
+
+bool
+HttpClient::isStopped() {
+    return (impl_->isStopped());
+}
+
+bool
+HttpClient::isPaused() {
+    return (impl_->isPaused());
+}
+
 } // end of namespace isc::http
 } // end of namespace isc
index 326633177f898a2296669fad1fbdb74c12046c15..92682ec2a6500e64de61fcb8bd6acdf61af3d400 100644 (file)
@@ -293,10 +293,42 @@ public:
     /// @return the number of running threads.
     uint16_t getThreadCount() const;
 
+    /// @brief Pauses the thread pool operation.
+    ///
+    /// Suspends thread pool event processing.
+    /// @throw InvalidOperation if the thread pool does not exist.
     void pause();
+
+    /// @brief Resumes the thread pool operation.
+    ///
+    /// Resumes thread pool event processing.
+    /// @throw InvalidOperation if the thread pool does not exist.
     void resume();
+
+    /// @brief Fetches the thread pool's operational state.
+    ///
+    /// @return Operational state of the thread pool.
+    /// @throw InvalidOperation if the thread pool does not exist.
     HttpThreadPool::RunState getRunState() const;
 
+    /// @brief Indicates if the thread pool processing is running.
+    ///
+    /// @return True if the thread pool exists and is in the RUN state,
+    /// false otherwise.
+    bool isRunning();
+
+    /// @brief Indicates if the thread pool is stopped.
+    ///
+    /// @return True if the thread pool exists and is in the STOPPED state,
+    /// false otherwise.
+    bool isStopped();
+
+    /// @brief Indicates if the thread pool processing is running.
+    ///
+    /// @return True if the thread pool exists and is in the PAUSED state,
+    /// false otherwise.
+    bool isPaused();
+
 private:
 
     /// @brief Pointer to the HTTP client implementation.
index 630197770e0b45d24a6052cd6a813d9146ae8288..ece1612cc97e967e6ffb9591167ba1d77b24032d 100644 (file)
@@ -665,6 +665,11 @@ public:
         ASSERT_NO_THROW_LOG(client_.reset(new HttpClient(io_service_, num_threads)));
         ASSERT_TRUE(client_);
 
+        // Check convenience functions.
+        ASSERT_TRUE(client_->isRunning());
+        ASSERT_FALSE(client_->isPaused());
+        ASSERT_FALSE(client_->isStopped());
+
         if (num_threads_ == 0) {
             // If we single-threaded client should not have it's own IOService.
             ASSERT_FALSE(client_->getThreadIOService());
@@ -704,6 +709,11 @@ public:
             // Pause the client.
             ASSERT_NO_THROW(client_->pause());
             ASSERT_EQ(HttpThreadPool::RunState::PAUSED, client_->getRunState());
+
+            // Check convenience functions.
+            ASSERT_FALSE(client_->isRunning());
+            ASSERT_TRUE(client_->isPaused());
+            ASSERT_FALSE(client_->isStopped());
         }
 
         // We should have completed at least the expected number of requests
@@ -824,6 +834,11 @@ TEST_F(MtHttpClientTest, basics) {
     ASSERT_EQ(client->getThreadCount(), 3);
     ASSERT_EQ(client->getRunState(), HttpThreadPool::RunState::RUN);
 
+    // Check convenience functions.
+    ASSERT_TRUE(client->isRunning());
+    ASSERT_FALSE(client->isPaused());
+    ASSERT_FALSE(client->isStopped());
+
     // Verify stop doesn't throw.
     ASSERT_NO_THROW_LOG(client->stop());
 
@@ -833,6 +848,11 @@ TEST_F(MtHttpClientTest, basics) {
     ASSERT_EQ(client->getThreadPoolSize(), 3);
     ASSERT_EQ(client->getThreadCount(), 0);
 
+    // Check convenience functions.
+    ASSERT_FALSE(client->isRunning());
+    ASSERT_FALSE(client->isPaused());
+    ASSERT_TRUE(client->isStopped());
+
     // Verify a second call to stop() doesn't throw.
     ASSERT_NO_THROW_LOG(client->stop());
 
@@ -862,6 +882,11 @@ TEST_F(MtHttpClientTest, deferredStart) {
     ASSERT_EQ(client->getThreadCount(), 0);
     ASSERT_EQ(client->getRunState(), HttpThreadPool::RunState::STOPPED);
 
+    // Check convenience functions.
+    ASSERT_FALSE(client->isRunning());
+    ASSERT_FALSE(client->isPaused());
+    ASSERT_TRUE(client->isStopped());
+
     // We should be able to start it.
     ASSERT_NO_THROW(client->start());
 
@@ -871,6 +896,11 @@ TEST_F(MtHttpClientTest, deferredStart) {
     ASSERT_FALSE(client->getThreadIOService()->stopped());
     ASSERT_EQ(client->getRunState(), HttpThreadPool::RunState::RUN);
 
+    // Check convenience functions.
+    ASSERT_TRUE(client->isRunning());
+    ASSERT_FALSE(client->isPaused());
+    ASSERT_FALSE(client->isStopped());
+
     // Cannot start it twice.
     ASSERT_THROW_MSG(client->start(), InvalidOperation,
                      "HttpThreadPool::start already started!");
@@ -975,7 +1005,7 @@ TEST_F(MtHttpClientTest, fourByFourByTwo) {
 }
 
 // Verifies that we can cleanly work, pause, and resume repeatedly.
-TEST_F(MtHttpClientTest, workPauseResumee) {
+TEST_F(MtHttpClientTest, workPauseResume) {
     size_t num_threads = 12;
     size_t num_batches = 12;
     size_t num_listeners = 12;