2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-22 01:49:48 +00:00

[#4049] Preliminary commit

/src/bin/d2/d2_process.cc
    D2Process::runIO() - use new IOService::runOneFor()

/src/lib/asiolink/io_service.*
    IOServcie::runOneFor() - new func

/src/lib/asiolink/io_service_mgr.*
    IOServiceMgr::pollIOServices() - return count of
    handlers executed

/src/lib/asiolink/tests/io_service_unittest.cc
    TEST(IOService, runOneFor) - new test
This commit is contained in:
Thomas Markwalder 2025-08-07 09:39:08 -04:00
parent 5a3fbb5dd5
commit 688d0fd67e
6 changed files with 104 additions and 13 deletions

View File

@ -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);
}

View File

@ -14,6 +14,10 @@
#include <boost/shared_ptr.hpp>
#include <sys/socket.h>
#include <chrono>
using namespace std::chrono;
namespace isc {
namespace asiolink {
@ -56,6 +60,26 @@ public:
return (static_cast<size_t>(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());

View File

@ -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.

View File

@ -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

View File

@ -62,7 +62,9 @@ public:
}
/// @brief Poll IOService objects.
void pollIOServices();
///
/// @return The number of handlers that were executed.
size_t pollIOServices();
private:

View File

@ -7,6 +7,7 @@
#include <config.h>
#include <asiolink/io_service.h>
#include <asiolink/interval_timer.h>
#include <gtest/gtest.h>
#include <functional>
@ -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);
}
}