76 lines
3.1 KiB
C++
76 lines
3.1 KiB
C++
#include <boost/test/unit_test.hpp>
|
|
#include <boost/asio/co_spawn.hpp>
|
|
#include <boost/asio/executor_work_guard.hpp>
|
|
#include <boost/asio/io_context.hpp>
|
|
#include <boost/asio/use_future.hpp>
|
|
#include <ligo/detail/lifecycle.hpp>
|
|
#include <future>
|
|
#include <thread>
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(session_drain_handles_notifications_before_and_during_registration) {
|
|
for (int iteration = 0; iteration < 64; ++iteration) {
|
|
auto io = boost::asio::io_context{};
|
|
auto registry = ligo::detail::session_registry<int>{};
|
|
auto session = std::make_shared<int>(1);
|
|
registry.add(session);
|
|
auto result = boost::asio::co_spawn(io, registry.wait_empty(std::chrono::seconds{2}), boost::asio::use_future);
|
|
auto work = boost::asio::make_work_guard(io);
|
|
auto first = std::thread{[&] { io.run(); }};
|
|
auto second = std::thread{[&] { io.run(); }};
|
|
registry.remove(session);
|
|
auto ready = result.wait_for(std::chrono::milliseconds{500}) == std::future_status::ready;
|
|
work.reset();
|
|
io.stop();
|
|
first.join();
|
|
second.join();
|
|
BOOST_REQUIRE(ready);
|
|
BOOST_CHECK(result.get());
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(session_drain_keeps_absolute_deadline_under_repeated_notifications) {
|
|
auto io = boost::asio::io_context{};
|
|
auto registry = ligo::detail::session_registry<int>{};
|
|
auto session = std::make_shared<int>(1);
|
|
registry.add(session);
|
|
auto result = boost::asio::co_spawn(io, registry.wait_empty(std::chrono::milliseconds{20}), boost::asio::use_future);
|
|
auto work = boost::asio::make_work_guard(io);
|
|
auto first = std::thread{[&] { io.run(); }};
|
|
auto second = std::thread{[&] { io.run(); }};
|
|
for (int i = 0; i < 200 && result.wait_for(std::chrono::milliseconds{1}) != std::future_status::ready; ++i) {
|
|
registry.notify_waiters();
|
|
}
|
|
auto ready = result.wait_for(std::chrono::milliseconds{0}) == std::future_status::ready;
|
|
work.reset();
|
|
io.stop();
|
|
first.join();
|
|
second.join();
|
|
BOOST_REQUIRE(ready);
|
|
auto value = result.get();
|
|
BOOST_REQUIRE(!value);
|
|
BOOST_CHECK(value.error().code() == ligo::detail::error_timeout);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(session_drain_observes_expiry_and_removal_before_wait_is_armed) {
|
|
auto io = boost::asio::io_context{};
|
|
auto registry = ligo::detail::session_registry<int>{};
|
|
auto session = std::make_shared<int>(1);
|
|
registry.add(session);
|
|
auto timer = std::make_shared<boost::asio::steady_timer>(boost::asio::make_strand(io));
|
|
registry.waiters.push_back(timer);
|
|
// A notification is queued before registration. It must neither extend an
|
|
// elapsed deadline nor prevent observing a session that already finished.
|
|
registry.notify_waiters();
|
|
auto expired = boost::asio::co_spawn(timer->get_executor(), registry.wait_registered(timer, std::chrono::steady_clock::now()), boost::asio::use_future);
|
|
io.run();
|
|
auto result = expired.get();
|
|
BOOST_REQUIRE(!result);
|
|
BOOST_CHECK(result.error().code() == ligo::detail::error_timeout);
|
|
io.restart();
|
|
registry.remove(session);
|
|
auto drained = boost::asio::co_spawn(timer->get_executor(), registry.wait_registered(timer, std::chrono::steady_clock::now() + std::chrono::hours{1}), boost::asio::use_future);
|
|
io.run();
|
|
BOOST_CHECK(drained.get());
|
|
}
|