Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## Latest Changes
* Added NumPy 2 compatibility to the PythonAPI: replaced removed aliases (`np.bool`, `np.matrix`) in example scripts and upgraded Boost to 1.90.0, which ships the upstream NumPy 2 C ABI fix (boostorg/python#432) so the C extension builds against both NumPy 1.x (>=1.18.4) and NumPy 2.x
* Fixed North/South latitude inversion in geo-coordinate conversion for Transverse Mercator and UTM projections
* Decoupled ROS2 DDS middleware from a hard FastDDS dependency to an agnostic strategy-pattern abstraction supporting both FastDDS and CycloneDDS, selectable at runtime via `--dds-middleware=` CLI flag
* Fixed all 282 compiler warnings in LibCarla across 27 files (unused variables, missing braces, narrowing conversions, implicit casts, initializer order, etc.) establishing a zero-warning baseline for server and client release builds
Expand Down
1 change: 1 addition & 0 deletions LibCarla/source/carla/FileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "carla/StringUtil.h"

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/directory.hpp>

namespace carla {

Expand Down
5 changes: 3 additions & 2 deletions LibCarla/source/carla/ThreadPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace carla {
class ThreadPool : private NonCopyable {
public:

ThreadPool() : _work_to_do(_io_context) {}
ThreadPool() : _work_to_do(boost::asio::make_work_guard(_io_context)) {}

/// Stops the ThreadPool and joins all its threads.
~ThreadPool() {
Expand Down Expand Up @@ -80,6 +80,7 @@ namespace carla {

/// Stop the ThreadPool and join all its threads.
void Stop() {
_work_to_do.reset();
_io_context.stop();
_workers.JoinAll();
}
Expand All @@ -88,7 +89,7 @@ namespace carla {

boost::asio::io_context _io_context;

boost::asio::io_context::work _work_to_do;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> _work_to_do;

ThreadGroup _workers;
};
Expand Down
2 changes: 1 addition & 1 deletion LibCarla/source/carla/multigpu/listener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace multigpu {
_acceptor.cancel();
_acceptor.close();
_io_context.stop();
_io_context.reset();
_io_context.restart();
}

void Listener::OpenSession(
Expand Down
9 changes: 5 additions & 4 deletions LibCarla/source/carla/multigpu/primary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <boost/asio/post.hpp>

#include <atomic>
#include <chrono>
#include <thread>

namespace carla {
Expand All @@ -34,7 +35,7 @@ namespace multigpu {
_session_id(SESSION_COUNTER++),
_socket(io_context),
_timeout(timeout),
_deadline(io_context),
_deadline(io_context, std::chrono::steady_clock::time_point::max()),
_strand(io_context),
_buffer_pool(std::make_shared<BufferPool>()) {}

Expand Down Expand Up @@ -85,7 +86,7 @@ namespace multigpu {
}
};

self->_deadline.expires_from_now(self->_timeout);
self->_deadline.expires_after(self->_timeout.to_chrono());
boost::asio::async_write(
self->_socket,
message->GetBufferSequence(),
Expand All @@ -103,7 +104,7 @@ namespace multigpu {
}

// sent first size buffer
self->_deadline.expires_from_now(self->_timeout);
self->_deadline.expires_after(self->_timeout.to_chrono());
int this_size = static_cast<int>(text.size());
boost::asio::async_write(
self->_socket,
Expand Down Expand Up @@ -181,7 +182,7 @@ namespace multigpu {
}

void Primary::StartTimer() {
if (_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now()) {
if (_deadline.expiry() <= std::chrono::steady_clock::now()) {
log_debug("session ", _session_id, " time out");
Close();
} else {
Expand Down
4 changes: 2 additions & 2 deletions LibCarla/source/carla/multigpu/primary.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include "carla/streaming/detail/Message.h"
#include "carla/multigpu/listener.h"

#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
Expand Down Expand Up @@ -92,7 +92,7 @@ namespace multigpu {

time_duration _timeout;

boost::asio::deadline_timer _deadline;
boost::asio::steady_timer _deadline;

boost::asio::io_context::strand _strand;

Expand Down
2 changes: 1 addition & 1 deletion LibCarla/source/carla/multigpu/router.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ void Router::Stop() {
Router::Router(uint16_t port) :
_next(0) {

_endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string("0.0.0.0"), port);
_endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address("0.0.0.0"), port);
_listener = std::make_shared<carla::multigpu::Listener>(_pool.io_context(), _endpoint);
}

Expand Down
6 changes: 4 additions & 2 deletions LibCarla/source/carla/multigpu/secondary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "carla/Time.h"

#include <boost/asio/connect.hpp>

#include <chrono>
#include <boost/asio/read.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/post.hpp>
Expand Down Expand Up @@ -48,7 +50,7 @@ namespace multigpu {
_connection_timer(_pool.io_context()),
_buffer_pool(std::make_shared<BufferPool>()) {

boost::asio::ip::address ip_address = boost::asio::ip::address::from_string(ip);
boost::asio::ip::address ip_address = boost::asio::ip::make_address(ip);
_endpoint = boost::asio::ip::tcp::endpoint(ip_address, port);
_commander.set_callback(callback);
}
Expand Down Expand Up @@ -117,7 +119,7 @@ namespace multigpu {

void Secondary::Reconnect() {
std::weak_ptr<Secondary> weak = shared_from_this();
_connection_timer.expires_from_now(time_duration::seconds(1u));
_connection_timer.expires_after(std::chrono::seconds(1));
_connection_timer.async_wait([weak](boost::system::error_code ec) {
auto self = weak.lock();
if (!self) return;
Expand Down
4 changes: 2 additions & 2 deletions LibCarla/source/carla/multigpu/secondary.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include "carla/streaming/detail/Types.h"
#include "carla/ThreadPool.h"

#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
Expand Down Expand Up @@ -76,7 +76,7 @@ namespace multigpu {
boost::asio::ip::tcp::socket _socket;
boost::asio::ip::tcp::endpoint _endpoint;
boost::asio::io_context::strand _strand;
boost::asio::deadline_timer _connection_timer;
boost::asio::steady_timer _connection_timer;
std::shared_ptr<BufferPool> _buffer_pool;
std::atomic_bool _done {false};
SecondaryCommands _commander;
Expand Down
2 changes: 1 addition & 1 deletion LibCarla/source/carla/rpc/Server.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ namespace rpc {
TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__);
#include <compiler/disable-ue4-macros.h>
#endif // LIBCARLA_INCLUDED_FROM_UE4
_sync_io_context.reset();
_sync_io_context.restart();
_sync_io_context.run_for(duration.to_chrono());
}

Expand Down
14 changes: 7 additions & 7 deletions LibCarla/source/carla/streaming/EndPoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ namespace detail {
static inline auto make_address(const std::string &address) {
boost::asio::io_context io_context;
boost::asio::ip::tcp::resolver resolver(io_context);
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), address, "", boost::asio::ip::tcp::resolver::query::canonical_name);
boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query);
boost::asio::ip::tcp::resolver::iterator end;
while (iter != end)
{
boost::asio::ip::tcp::endpoint endpoint = *iter++;
return endpoint.address();
auto results = resolver.resolve(
boost::asio::ip::tcp::v4(),
address,
std::string(),
boost::asio::ip::resolver_base::canonical_name);
for (const auto &result : results) {
return result.endpoint().address();
}
return boost::asio::ip::make_address(address);
}
Expand Down
4 changes: 3 additions & 1 deletion LibCarla/source/carla/streaming/detail/tcp/Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "carla/Time.h"

#include <boost/asio/connect.hpp>

#include <chrono>
#include <boost/asio/read.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/bind_executor.hpp>
Expand Down Expand Up @@ -150,7 +152,7 @@ namespace tcp {

void Client::Reconnect() {
auto self = shared_from_this();
_connection_timer.expires_from_now(time_duration::seconds(1u));
_connection_timer.expires_after(std::chrono::seconds(1));
_connection_timer.async_wait([this, self](boost::system::error_code ec) {
if (!ec) {
Connect();
Expand Down
4 changes: 2 additions & 2 deletions LibCarla/source/carla/streaming/detail/tcp/Client.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include "carla/streaming/detail/Token.h"
#include "carla/streaming/detail/Types.h"

#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
Expand Down Expand Up @@ -72,7 +72,7 @@ namespace tcp {

boost::asio::io_context::strand _strand;

boost::asio::deadline_timer _connection_timer;
boost::asio::steady_timer _connection_timer;

std::shared_ptr<BufferPool> _buffer_pool;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace tcp {
_session_id(SESSION_COUNTER++),
_socket(io_context),
_timeout(timeout),
_deadline(io_context),
_deadline(io_context, std::chrono::steady_clock::time_point::max()),
_strand(io_context) {}

void ServerSession::Open(
Expand Down Expand Up @@ -68,7 +68,7 @@ namespace tcp {
};

// Read the stream id.
_deadline.expires_from_now(_timeout);
_deadline.expires_after(_timeout.to_chrono());
boost::asio::async_read(
_socket,
boost::asio::buffer(&_stream_id, sizeof(_stream_id)),
Expand Down Expand Up @@ -110,7 +110,7 @@ namespace tcp {

log_debug("session", _session_id, ": sending message of", message->size(), "bytes");

_deadline.expires_from_now(_timeout);
_deadline.expires_after(_timeout.to_chrono());
boost::asio::async_write(_socket, message->GetBufferSequence(),
boost::asio::bind_executor(_strand, handle_sent));
}
Expand All @@ -120,7 +120,7 @@ namespace tcp {
}

void ServerSession::StartTimer() {
if (_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now()) {
if (_deadline.expiry() <= std::chrono::steady_clock::now()) {
log_debug("session", _session_id, "timed out");
Close();
} else {
Expand Down
4 changes: 2 additions & 2 deletions LibCarla/source/carla/streaming/detail/tcp/ServerSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wshadow"
#endif
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
Expand Down Expand Up @@ -74,7 +74,7 @@ namespace tcp {

time_duration _timeout;

boost::asio::deadline_timer _deadline;
boost::asio::steady_timer _deadline;

boost::asio::io_context::strand _strand;

Expand Down
109 changes: 109 additions & 0 deletions LibCarla/source/test/common/test_boost_deadline_timer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.

// Regression guard for boost::asio steady_timer after the Boost 1.84->1.90
// upgrade. CARLA uses steady_timer in streaming/detail/tcp/{Client,ServerSession}
// and multigpu/{primary,secondary} for connection and session deadlines.

#include "test.h"

#include <boost/asio/error.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/steady_timer.hpp>

#include <atomic>
#include <chrono>
#include <thread>

// Verifies that steady_timer fires its handler within a reasonable wall-clock
// period.
TEST(BoostAsioTimer, TimerFiresWithinReasonableTime) {
boost::asio::io_context io;
boost::asio::steady_timer timer{io, std::chrono::milliseconds(10)};

std::atomic<bool> fired{false};
timer.async_wait([&](const boost::system::error_code &ec) {
EXPECT_FALSE(ec) << "steady_timer error: " << ec.message();
fired = true;
});

std::thread t{[&] { io.run(); }};
t.join();

EXPECT_TRUE(fired) << "steady_timer handler never fired";
}

// Verifies the expires_at() <= steady_clock::now() pattern used in the
// LibCarla deadline-checking callbacks (e.g. ServerSession::StartTimer).
TEST(BoostAsioTimer, ExpiresAtNowComparison) {
boost::asio::io_context io;
boost::asio::steady_timer timer{io, std::chrono::milliseconds(5)};

// Before expiry: expires_at() should be in the future.
auto expires_at = timer.expiry();
auto now_before = std::chrono::steady_clock::now();
EXPECT_GT(expires_at, now_before);

// Run until the timer fires to advance real time past the expiry point.
std::atomic<bool> done{false};
timer.async_wait([&](const boost::system::error_code &) { done = true; });
std::thread t{[&] { io.run(); }};
t.join();

// After expiry: now() should be >= expires_at().
auto now_after = std::chrono::steady_clock::now();
EXPECT_GE(now_after, expires_at);
EXPECT_TRUE(done);
}

// Verifies that std::chrono::milliseconds and io_context still compose
// correctly.
TEST(BoostAsioTimer, ChronoDurationComposition) {
const std::chrono::milliseconds d{250};
EXPECT_EQ(d.count(), 250);
EXPECT_EQ(std::chrono::duration_cast<std::chrono::seconds>(d).count(), 0);
}

// Pins the std::chrono::steady_clock::time_point::max() initialization used in
// ServerSession::ServerSession and Primary::Primary to mirror the old
// deadline_timer default of pos_infin. StartTimer() guards on
// expiry() <= now(); a regression here would fire Close() before the socket is
// set up.
TEST(BoostAsioTimer, InitializedWithMaxIsInDistantFuture) {
boost::asio::io_context io;
boost::asio::steady_timer timer{io, std::chrono::steady_clock::time_point::max()};
EXPECT_GT(timer.expiry(), std::chrono::steady_clock::now());
}

// Documents the trap that prompted the explicit max() init: a default
// constructed steady_timer has expiry() in the past (time_point::min()),
// unlike deadline_timer which defaulted to pos_infin. If Boost ever changes
// this default, this test breaks loudly and the explicit max() inits can be
// dropped.
TEST(BoostAsioTimer, DefaultConstructedExpiryIsInPast) {
boost::asio::io_context io;
boost::asio::steady_timer timer{io};
EXPECT_LE(timer.expiry(), std::chrono::steady_clock::now());
}

// Mirrors the cancel-before-close pattern in ServerSession::CloseNow and
// Primary::CloseNow: cancelling a pending wait must invoke the handler with
// operation_aborted, not silently drop it.
TEST(BoostAsioTimer, CancelTriggersAbortedError) {
boost::asio::io_context io;
boost::asio::steady_timer timer{io, std::chrono::seconds(60)};
boost::system::error_code captured;
std::atomic<bool> fired{false};
timer.async_wait([&](const boost::system::error_code &ec) {
captured = ec;
fired = true;
});
timer.cancel();
io.run();
EXPECT_TRUE(fired);
EXPECT_EQ(captured, boost::asio::error::operation_aborted);
}
Loading
Loading