CARLA
Loading...
Searching...
No Matches
test_boost_asio_timer.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
7// Regression guard for boost::asio steady_timer after the Boost 1.84 -> 1.90
8// upgrade. CARLA uses steady_timer in streaming/detail/tcp/{Client,ServerSession}
9// and multigpu/{primary,secondary} for connection and session deadlines.
10
11#include "test.h"
12
13#include <boost/asio/error.hpp>
14#include <boost/asio/io_context.hpp>
15#include <boost/asio/post.hpp>
16#include <boost/asio/steady_timer.hpp>
17
18#include <atomic>
19#include <chrono>
20#include <thread>
21
22// Verifies that steady_timer fires its handler within a reasonable wall-clock
23// period.
24TEST(BoostAsioTimer, TimerFiresWithinReasonableTime) {
25 boost::asio::io_context io;
26 boost::asio::steady_timer timer{io, std::chrono::milliseconds(10)};
27
28 std::atomic<bool> fired{false};
29 timer.async_wait([&](const boost::system::error_code &ec) {
30 EXPECT_FALSE(ec) << "steady_timer error: " << ec.message();
31 fired = true;
32 });
33
34 std::thread t{[&] { io.run(); }};
35 t.join();
36
37 EXPECT_TRUE(fired) << "steady_timer handler never fired";
38}
39
40// Verifies the expiry() <= steady_clock::now() pattern used in the LibCarla
41// deadline-checking callbacks (Primary::StartTimer, ServerSession::StartTimer).
42TEST(BoostAsioTimer, ExpiresAtNowComparison) {
43 boost::asio::io_context io;
44 boost::asio::steady_timer timer{io, std::chrono::milliseconds(5)};
45
46 // Before expiry: expiry() should be in the future.
47 auto expires_at = timer.expiry();
48 auto now_before = std::chrono::steady_clock::now();
49 EXPECT_GT(expires_at, now_before);
50
51 // Run until the timer fires to advance real time past the expiry point.
52 std::atomic<bool> done{false};
53 timer.async_wait([&](const boost::system::error_code &) { done = true; });
54 std::thread t{[&] { io.run(); }};
55 t.join();
56
57 // After expiry: now() should be >= expiry().
58 auto now_after = std::chrono::steady_clock::now();
59 EXPECT_GE(now_after, expires_at);
60 EXPECT_TRUE(done);
61}
62
63// Verifies that std::chrono::milliseconds and io_context still compose
64// correctly (needed by Time.h::time_duration::to_chrono used at every call
65// site of steady_timer::expires_after in LibCarla).
66TEST(BoostAsioTimer, ChronoDurationComposition) {
67 const std::chrono::milliseconds d{250};
68 EXPECT_EQ(d.count(), 250);
69 EXPECT_EQ(std::chrono::duration_cast<std::chrono::seconds>(d).count(), 0);
70}
71
72// Pins the std::chrono::steady_clock::time_point::max() initialization used
73// in ServerSession::ServerSession and Primary::Primary to mirror the old
74// deadline_timer default of pos_infin. StartTimer() guards on
75// expiry() <= now(); a regression here would fire Close() before the socket
76// is set up.
77TEST(BoostAsioTimer, InitializedWithMaxIsInDistantFuture) {
78 boost::asio::io_context io;
79 boost::asio::steady_timer timer{io, std::chrono::steady_clock::time_point::max()};
80 EXPECT_GT(timer.expiry(), std::chrono::steady_clock::now());
81}
82
83// Documents the trap that prompted the explicit max() init: a default
84// constructed steady_timer has expiry() in the past (time_point::min()),
85// unlike deadline_timer which defaulted to pos_infin. If Boost ever changes
86// this default, this test breaks loudly and the explicit max() inits can be
87// dropped.
88TEST(BoostAsioTimer, DefaultConstructedExpiryIsInPast) {
89 boost::asio::io_context io;
90 boost::asio::steady_timer timer{io};
91 EXPECT_LE(timer.expiry(), std::chrono::steady_clock::now());
92}
93
94// Mirrors the cancel-before-close pattern in ServerSession::CloseNow and
95// Primary::CloseNow: cancelling a pending wait must invoke the handler with
96// operation_aborted, not silently drop it.
97TEST(BoostAsioTimer, CancelTriggersAbortedError) {
98 boost::asio::io_context io;
99 boost::asio::steady_timer timer{io, std::chrono::seconds(60)};
100 boost::system::error_code captured;
101 std::atomic<bool> fired{false};
102 timer.async_wait([&](const boost::system::error_code &ec) {
103 captured = ec;
104 fired = true;
105 });
106 timer.cancel();
107 io.run();
108 EXPECT_TRUE(fired);
109 EXPECT_EQ(captured, boost::asio::error::operation_aborted);
110}
TEST(BoostAsioTimer, TimerFiresWithinReasonableTime)