CARLA
Loading...
Searching...
No Matches
router.h
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#pragma once
8
9// #include "carla/Logging.h"
11#include "carla/ThreadPool.h"
15
16#include <boost/asio/io_context.hpp>
17#include <boost/asio/ip/tcp.hpp>
18
19#include <atomic>
20#include <functional>
21#include <mutex>
22#include <optional>
23#include <string>
24#include <vector>
25#include <sstream>
26#include <string_view>
27#include <unordered_map>
28
29namespace carla {
30namespace multigpu {
31
32 // class Primary;
33 class Listener;
34
35 /// Outcome of a routed request. A null @a session means the request was
36 /// rejected (see Router::RejectPromise): no secondary answered it.
37 struct SessionInfo {
38 std::shared_ptr<Primary> session;
40 };
41
42 class Router : public std::enable_shared_from_this<Router> {
43 public:
44
45 Router(void);
46 explicit Router(uint16_t port);
47 ~Router();
48
49 void Write(MultiGPUCommand id, Buffer &&buffer);
50 /// Broadcasts LOAD_MAP tagged with a new load id; each connected secondary
51 /// counts as loading until it reports that load's episode ready.
52 load_map_id_type WriteLoadMap(std::string_view map, const LoadMapExtras &extras = {});
53 std::future<SessionInfo> WriteToNext(MultiGPUCommand id, Buffer &&buffer);
54 std::future<SessionInfo> WriteToOne(std::weak_ptr<Primary> server, MultiGPUCommand id, Buffer &&buffer);
55 void Stop();
56
57 void SetCallbacks();
58 void SetNewConnectionCallback(std::function<void(void)>);
59
60 void AsyncRun(size_t worker_threads);
61
62 boost::asio::ip::tcp::endpoint GetLocalEndpoint() const;
63
64 /// Whether @a server is still one of the connected secondary sessions.
65 [[nodiscard]]
66 bool IsConnected(const std::weak_ptr<Primary> &server);
67
68 /// Sessions connected since the previous call, oldest first.
69 [[nodiscard]]
70 std::vector<std::weak_ptr<Primary>> TakeNewSessions();
71
72 /// Whether a connected secondary sent LOAD_MAP has not yet reported the
73 /// episode of its latest LOAD_MAP ready (a disconnect also clears it).
74 [[nodiscard]]
76
78
79 /// Any thread: reads a count kept next to the session list, which is
80 /// itself only touched under the router's mutex.
81 bool HasClientsConnected() const {
82 return _session_count.load() > 0u;
83 }
84
88
90 std::function<void(const Primary *, MultiGPUCommand, const carla::BufferView &)>;
91
92#ifdef LIBCARLA_WITH_GTEST
93 // Test-only access to the session bookkeeping, bypassing the real
94 // Listener accept flow so the promise-fulfillment logic can be
95 // unit-tested without a live TCP connection.
96 void TestConnectSession(std::shared_ptr<Primary> session) {
97 ConnectSession(std::move(session));
98 }
99 void TestDisconnectSession(std::shared_ptr<Primary> session) {
100 DisconnectSession(std::move(session));
101 }
102 // Test-only: invokes the same response-handling path SetCallbacks()
103 // wires to the Listener, so the resync-on-episode-ready contract can be
104 // exercised without a live secondary.
105 void TestHandleResponse(std::shared_ptr<Primary> session, Buffer buffer) {
106 HandleResponse(std::move(session), std::move(buffer));
107 }
108 bool TestHasPendingRequest(const std::shared_ptr<Primary> &session) {
109 std::scoped_lock<std::mutex> lock(_mutex);
110 return _promises.contains(session.get());
111 }
112 // Test-only: sees every request WriteToOne/WriteToNext and every LOAD_MAP send, under _mutex.
113 void TestSetRequestObserver(request_observer_type observer) {
114 std::scoped_lock<std::mutex> lock(_mutex);
115 _request_observer = std::move(observer);
116 }
117#endif // LIBCARLA_WITH_GTEST
118
119 private:
120 void ConnectSession(std::shared_ptr<Primary> session);
121 void DisconnectSession(std::shared_ptr<Primary> session);
122 void ClearSessions();
123
124 /// Sends LOAD_MAP to one session. Caller must already hold _mutex.
125 void SendLoadMap(
126 const std::shared_ptr<Primary> &session,
127 std::string_view map,
128 load_map_id_type load_id,
129 const LoadMapExtras &extras);
130
131 /// Re-arms the new-connection resync if @a buffer is the secondary's
132 /// episode-ready marker (checked first, so it can never be misdelivered
133 /// to a pending promise on the same session); otherwise resolves the
134 /// pending promise for @a session with @a buffer, or logs and drops the
135 /// data if no request is currently pending on it.
136 void HandleResponse(std::shared_ptr<Primary> session, Buffer buffer);
137
138 /// Fails a pending request instead of leaving its future unresolved.
139 /// A session can vanish (dead weak_ptr, disconnect, empty router) between
140 /// the moment a promise is created and the moment a response would have
141 /// arrived; without this, the caller's std::future::get() blocks forever.
142 ///
143 /// The failure is delivered as a value (a SessionInfo with a null
144 /// session), never through std::promise::set_exception(): inside the
145 /// CarlaUnreal process std::rethrow_exception() binds to the libc++abi
146 /// copy exported by libcarla-ros2-native.so, while exceptions are
147 /// allocated and caught by libstdc++'s ABI. std::current_exception() then
148 /// sees a foreign exception and returns null, so the rpc sync-call
149 /// packaged_task becomes ready with neither a value nor an exception and
150 /// the rpc thread reads an unconstructed result (SIGSEGV).
151 static void RejectPromise(
152 std::shared_ptr<std::promise<SessionInfo>> promise,
153 std::string_view reason);
154
155 /// Rejects and clears every currently-pending promise. Caller must
156 /// already hold _mutex.
157 void RejectAllPending(std::string_view reason);
158
159 // mutex and thread pool must be at the beginning to be destroyed last
160 std::mutex _mutex;
162 boost::asio::ip::tcp::endpoint _endpoint;
163 std::vector<std::shared_ptr<Primary>> _sessions;
164 std::atomic_size_t _session_count{0u};
165 std::vector<std::weak_ptr<Primary>> _new_sessions;
166 std::shared_ptr<Listener> _listener;
167 uint32_t _next;
168 std::unordered_map<Primary *, std::shared_ptr<std::promise<SessionInfo>>> _promises;
169 std::unordered_map<const Primary *, load_map_id_type> _loading;
171 std::optional<std::string> _last_map;
174 std::function<void(void)> _callback;
176 };
177
178} // namespace multigpu
179} // namespace carla
Creating a constant view from an existing buffer.
Definition BufferView.h:32
A piece of raw data.
A thread pool based on Boost.Asio's io context.
Definition ThreadPool.h:25
A TCP server session.
Definition primary.h:35
boost::asio::ip::tcp::endpoint GetLocalEndpoint() const
Definition router.cpp:85
PrimaryCommands _commander
Definition router.h:173
void Write(MultiGPUCommand id, Buffer &&buffer)
Definition router.cpp:188
load_map_id_type WriteLoadMap(std::string_view map, const LoadMapExtras &extras={})
Broadcasts LOAD_MAP tagged with a new load id; each connected secondary counts as loading until it re...
Definition router.cpp:229
std::vector< std::shared_ptr< Primary > > _sessions
Definition router.h:163
static void RejectPromise(std::shared_ptr< std::promise< SessionInfo > > promise, std::string_view reason)
Fails a pending request instead of leaving its future unresolved.
Definition router.cpp:135
std::future< SessionInfo > WriteToNext(MultiGPUCommand id, Buffer &&buffer)
Definition router.cpp:253
std::optional< std::string > _last_map
Definition router.h:171
std::function< void(void)> _callback
Definition router.h:174
void RejectAllPending(std::string_view reason)
Rejects and clears every currently-pending promise.
Definition router.cpp:128
std::unordered_map< Primary *, std::shared_ptr< std::promise< SessionInfo > > > _promises
Definition router.h:168
std::shared_ptr< Listener > _listener
Definition router.h:166
LoadMapExtras _last_map_extras
Definition router.h:172
std::vector< std::weak_ptr< Primary > > _new_sessions
Definition router.h:165
void ConnectSession(std::shared_ptr< Primary > session)
Definition router.cpp:89
void StopWaitingForSecondaryLoads()
Definition router.cpp:248
std::future< SessionInfo > WriteToOne(std::weak_ptr< Primary > server, MultiGPUCommand id, Buffer &&buffer)
Definition router.cpp:306
boost::asio::ip::tcp::endpoint _endpoint
Definition router.h:162
request_observer_type _request_observer
Definition router.h:175
bool IsConnected(const std::weak_ptr< Primary > &server)
Whether server is still one of the connected secondary sessions.
Definition router.cpp:295
void SendLoadMap(const std::shared_ptr< Primary > &session, std::string_view map, load_map_id_type load_id, const LoadMapExtras &extras)
Sends LOAD_MAP to one session. Caller must already hold _mutex.
Definition router.cpp:208
bool IsAnySecondaryLoading()
Whether a connected secondary sent LOAD_MAP has not yet reported the episode of its latest LOAD_MAP r...
Definition router.cpp:243
void DisconnectSession(std::shared_ptr< Primary > session)
Definition router.cpp:107
void AsyncRun(size_t worker_threads)
Definition router.cpp:81
void SetNewConnectionCallback(std::function< void(void)>)
Definition router.cpp:76
std::function< void(const Primary *, MultiGPUCommand, const carla::BufferView &)> request_observer_type
Definition router.h:90
std::vector< std::weak_ptr< Primary > > TakeNewSessions()
Sessions connected since the previous call, oldest first.
Definition router.cpp:301
void HandleResponse(std::shared_ptr< Primary > session, Buffer buffer)
Re-arms the new-connection resync if buffer is the secondary's episode-ready marker (checked first,...
Definition router.cpp:142
load_map_id_type _last_load_id
Definition router.h:170
bool HasClientsConnected() const
Any thread: reads a count kept next to the session list, which is itself only touched under the route...
Definition router.h:81
std::atomic_size_t _session_count
Definition router.h:164
std::unordered_map< const Primary *, load_map_id_type > _loading
Definition router.h:169
PrimaryCommands & GetCommander()
Definition router.h:85
uint32_t load_map_id_type
Identifies one LOAD_MAP broadcast.
Definition commands.h:46
This file contains definitions of common data structures used in traffic manager.
What a LOAD_MAP carries besides the map: for a world the primary generated from OpenDRIVE (generate_o...
Definition commands.h:51
Outcome of a routed request.
Definition router.h:37
std::shared_ptr< Primary > session
Definition router.h:38