diff --git a/src/bin/d2/d2_process.cc b/src/bin/d2/d2_process.cc index 7db49a3474..1236cff626 100644 --- a/src/bin/d2/d2_process.cc +++ b/src/bin/d2/d2_process.cc @@ -150,18 +150,27 @@ D2Process::run() { size_t D2Process::runIO() { - // Handle events registered by hooks using external IOService objects. - IOServiceMgr::instance().pollIOServices(); - // We want to block until at least one handler is called. - // Poll runs all that are ready. If none are ready it returns immediately - // with a count of zero. - size_t cnt = getIOService()->poll(); + // We want to process all ready handlers on both the managed IOServices + // and the main IOservice. If no handlers were executed on any of the + // IOServices will wait on the main IOService until at least one handler + // executes or we time out. + size_t cnt = IOServiceMgr::instance().pollIOServices(); + cnt += getIOService()->poll(); if (!cnt) { - // Poll ran no handlers either none are ready or the service has been - // stopped. Either way, call runOne to wait for a IO event. If the - // service is stopped it will return immediately with a cnt of zero. - cnt = getIOService()->runOne(); + // Polling ran no handlers so either none are ready or the service has been + // stopped. Either way, call runOneFor() to wait for a IO event on the + // main service. If the service is stopped it will return immediately + // with a cnt of zero and timed_out set to false. + bool timed_out; + /// @todo TKM wait time should probably be configurable. + /// Currently microseconds, should be milliseconds? + cnt = getIOService()->runOneFor(100 * 1000, timed_out); + if (timed_out) { + // Return 1 so caller knows the service has not stopped. + return (1); + } } + return (cnt); } diff --git a/src/lib/asiolink/io_service.cc b/src/lib/asiolink/io_service.cc index a3f7739ea1..cb63b57b32 100644 --- a/src/lib/asiolink/io_service.cc +++ b/src/lib/asiolink/io_service.cc @@ -14,6 +14,10 @@ #include #include +#include + +using namespace std::chrono; + namespace isc { namespace asiolink { @@ -56,6 +60,26 @@ public: return (static_cast(io_service_.run_one())); }; + /// @brief Run the underlying event loop for a single event or until + /// a wait time expires. + /// + /// This method returns control to the caller as soon as the + /// first handler has completed or the wait time elapses. If the + /// number of handlers executed is zero and timed_out is set to + /// false this indicates that the IOService was stopped. + /// + /// @param wait_time_usecs wait time in microseconds + /// @param[out] time_out set to true if th wait time expired + /// without any handlers executing. + /// timed_out parameter will be set true if the wait time elapsed + /// + /// @return The number of handlers that were executed. + size_t runOneFor(size_t wait_time_usecs, bool& timed_out) { + size_t cnt = io_service_.run_one_for(microseconds(wait_time_usecs)); + timed_out = (!cnt && !io_service_.stopped()); + return (cnt); + }; + /// @brief Run the underlying event loop for a ready events. /// /// This method executes handlers for all ready events and returns. @@ -139,6 +163,11 @@ IOService::runOne() { return (io_impl_->runOne()); } +size_t +IOService::runOneFor(size_t wait_time_usecs, bool& timed_out) { + return (io_impl_->runOneFor(wait_time_usecs, timed_out)); +}; + size_t IOService::poll() { return (io_impl_->poll()); diff --git a/src/lib/asiolink/io_service.h b/src/lib/asiolink/io_service.h index 5318b7a0bc..5d60dbb2ea 100644 --- a/src/lib/asiolink/io_service.h +++ b/src/lib/asiolink/io_service.h @@ -61,6 +61,22 @@ public: /// @return The number of handlers that were executed. size_t runOne(); + /// @brief Run the underlying event loop for a single event or until + /// a wait time expires. + /// + /// This method returns control to the caller as soon as the + /// first handler has completed or the wait time elapses. If the + /// number of handlers executed is zero and timed_out is set to + /// false this indicates that the IOService was stopped. + /// + /// @param wait_time_usecs wait time in microseconds + /// @param[out] time_out set to true if th wait time expired + /// without any handlers executing. + /// timed_out parameter will be set true if the wait time elapsed + /// + /// @return The number of handlers that were executed. + size_t runOneFor(size_t wait_time_usecs, bool& timed_out); + /// @brief Run the underlying event loop for a ready events. /// /// This method executes handlers for all ready events and returns. diff --git a/src/lib/asiolink/io_service_mgr.cc b/src/lib/asiolink/io_service_mgr.cc index 480a132e9a..0bd8ff66aa 100644 --- a/src/lib/asiolink/io_service_mgr.cc +++ b/src/lib/asiolink/io_service_mgr.cc @@ -35,11 +35,14 @@ IOServiceMgr::unregisterIOService(IOServicePtr io_service) { } } -void +size_t IOServiceMgr::pollIOServices() { + size_t cnt = 0; for (auto& io_service : io_services_) { - io_service->poll(); + cnt += io_service->poll(); } + + return (cnt); } } // namespace asiolink diff --git a/src/lib/asiolink/io_service_mgr.h b/src/lib/asiolink/io_service_mgr.h index 3c47d73e48..ef820ca2c6 100644 --- a/src/lib/asiolink/io_service_mgr.h +++ b/src/lib/asiolink/io_service_mgr.h @@ -62,7 +62,9 @@ public: } /// @brief Poll IOService objects. - void pollIOServices(); + /// + /// @return The number of handlers that were executed. + size_t pollIOServices(); private: diff --git a/src/lib/asiolink/tests/io_service_unittest.cc b/src/lib/asiolink/tests/io_service_unittest.cc index 4defa1de58..dc1ab5fd97 100644 --- a/src/lib/asiolink/tests/io_service_unittest.cc +++ b/src/lib/asiolink/tests/io_service_unittest.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -45,4 +46,35 @@ TEST(IOService, post) { EXPECT_EQ(3, called[2]); } +// Check the runOneFor() operates correctly. +TEST(IOService, runOneFor) { + IOServicePtr io_service(new IOService()); + + // Setup up a timer to expire in 200 ms. + IntervalTimer timer(io_service); + size_t wait_ms = 200; + bool timer_fired = false; + timer.setup([&timer_fired] { timer_fired = true; }, + wait_ms, IntervalTimer::ONE_SHOT); + + size_t time_outs = 0; + while (timer_fired == false && time_outs < 5) { + // Call runOneFor() with 1/4 of the timer duration. + bool timed_out = false; + auto cnt = io_service->runOneFor(50 * 1000, timed_out); + if (cnt || timer_fired) { + ASSERT_FALSE(timed_out); + } else { + ASSERT_TRUE(timed_out); + ++time_outs; + } + } + + // Should have had at least two time outs. + EXPECT_GE(time_outs, 2); + + // Timer should have fired. + EXPECT_EQ(timer_fired, 1); +} + }