43 _endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(
"0.0.0.0"), port);
49 std::weak_ptr<Router> weak = shared_from_this();
52 auto self = weak.lock();
54 self->ConnectSession(std::move(session));
58 auto self = weak.lock();
60 self->DisconnectSession(std::move(session));
64 [weak](std::shared_ptr<carla::multigpu::Primary> session,
carla::Buffer buffer) {
65 auto self = weak.lock();
67 self->HandleResponse(std::move(session), std::move(buffer));
72 _listener->Listen(on_open, on_close, on_response);
91 std::scoped_lock<std::mutex> lock(
_mutex);
93 _sessions.emplace_back(std::move(session));
109 std::scoped_lock<std::mutex> lock(
_mutex);
121 auto pending =
_promises.find(session.get());
123 RejectPromise(pending->second,
"secondary server disconnected before responding");
136 std::shared_ptr<std::promise<SessionInfo>> promise,
137 std::string_view reason) {
138 log_error(
"multigpu router: rejecting pending request: ", reason);
143 std::scoped_lock<std::mutex> lock(
_mutex);
148 const std::string_view payload(
149 reinterpret_cast<const char *
>(buffer.
data()), buffer.
size());
151 log_info(
"Secondary episode ready, re-arming full resync");
152 auto loading =
_loading.find(session.get());
153 if ((loading !=
_loading.end()) && (loading->second == *ready_load_id)) {
155 }
else if (loading !=
_loading.end()) {
156 log_info(
"multigpu router: ignoring episode ready of load ", *ready_load_id,
157 ", waiting for load ", loading->second);
165 auto prom =
_promises.find(session.get());
167 log_info(
"Got data from secondary (with promise): ", buffer.
size());
168 prom->second->set_value({std::move(session), std::move(buffer)});
171 log_warning(
"multigpu router: ignoring unsolicited data with no pending request: ", buffer.
size(),
" bytes");
176 std::scoped_lock<std::mutex> lock(
_mutex);
185 log_info(
"Disconnecting all secondary servers");
192 header.
size = buffer.size();
193 Buffer buf_header(
reinterpret_cast<uint8_t *
>(&header),
sizeof(header));
200 std::scoped_lock<std::mutex> lock(
_mutex);
209 const std::shared_ptr<Primary> &session,
210 std::string_view map,
214 Buffer buffer(
reinterpret_cast<const unsigned char *
>(payload.data()), payload.size());
218 header.
size =
static_cast<uint32_t
>(buffer.
size());
219 Buffer buf_header(
reinterpret_cast<uint8_t *
>(&header),
sizeof(header));
230 std::scoped_lock<std::mutex> lock(
_mutex);
244 std::scoped_lock<std::mutex> lock(
_mutex);
249 std::scoped_lock<std::mutex> lock(
_mutex);
257 header.
size = buffer.size();
258 Buffer buf_header(
reinterpret_cast<uint8_t *
>(&header),
sizeof(header));
265 auto response = std::make_shared<std::promise<SessionInfo>>();
268 std::scoped_lock<std::mutex> lock(
_mutex);
275 RejectPromise(response,
"no secondary session available to route the command");
276 }
else if (
_promises.contains(s.get())) {
280 RejectPromise(response,
"a request is already pending on this secondary session");
292 return response->get_future();
296 std::scoped_lock<std::mutex> lock(
_mutex);
297 auto s = server.lock();
302 std::scoped_lock<std::mutex> lock(
_mutex);
310 header.
size = buffer.size();
311 Buffer buf_header(
reinterpret_cast<uint8_t *
>(&header),
sizeof(header));
318 auto response = std::make_shared<std::promise<SessionInfo>>();
321 std::scoped_lock<std::mutex> lock(
_mutex);
322 auto s = server.lock();
327 const bool still_connected =
329 if (!still_connected) {
330 RejectPromise(response,
"secondary session is no longer connected");
331 }
else if (
_promises.contains(s.get())) {
333 RejectPromise(response,
"a request is already pending on this secondary session");
341 return response->get_future();
#define DEBUG_ASSERT(predicate)
static std::shared_ptr< BufferView > CreateFrom(Buffer &&buffer)
const value_type * data() const noexcept
Direct access to the allocated memory or nullptr if no memory is allocated.
size_type size() const noexcept
void Stop()
Stop the ThreadPool and join all its threads.
void AsyncRun(size_t worker_threads)
Launch threads to run tasks asynchronously.
auto & io_context()
Return the underlying io_context.
std::function< void(std::shared_ptr< Primary >)> callback_function_type
std::function< void(std::shared_ptr< Primary >, carla::Buffer)> callback_function_type_response
void set_router(std::shared_ptr< Router > router)
static auto MakeMessage(Buffers... buffers)
boost::asio::ip::tcp::endpoint GetLocalEndpoint() const
PrimaryCommands _commander
void Write(MultiGPUCommand id, Buffer &&buffer)
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...
std::vector< std::shared_ptr< Primary > > _sessions
static void RejectPromise(std::shared_ptr< std::promise< SessionInfo > > promise, std::string_view reason)
Fails a pending request instead of leaving its future unresolved.
std::future< SessionInfo > WriteToNext(MultiGPUCommand id, Buffer &&buffer)
std::optional< std::string > _last_map
std::function< void(void)> _callback
void RejectAllPending(std::string_view reason)
Rejects and clears every currently-pending promise.
std::unordered_map< Primary *, std::shared_ptr< std::promise< SessionInfo > > > _promises
std::shared_ptr< Listener > _listener
LoadMapExtras _last_map_extras
std::vector< std::weak_ptr< Primary > > _new_sessions
void ConnectSession(std::shared_ptr< Primary > session)
void StopWaitingForSecondaryLoads()
std::future< SessionInfo > WriteToOne(std::weak_ptr< Primary > server, MultiGPUCommand id, Buffer &&buffer)
boost::asio::ip::tcp::endpoint _endpoint
request_observer_type _request_observer
bool IsConnected(const std::weak_ptr< Primary > &server)
Whether server is still one of the connected secondary sessions.
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.
bool IsAnySecondaryLoading()
Whether a connected secondary sent LOAD_MAP has not yet reported the episode of its latest LOAD_MAP r...
void DisconnectSession(std::shared_ptr< Primary > session)
void AsyncRun(size_t worker_threads)
void SetNewConnectionCallback(std::function< void(void)>)
std::vector< std::weak_ptr< Primary > > TakeNewSessions()
Sessions connected since the previous call, oldest first.
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,...
load_map_id_type _last_load_id
std::atomic_size_t _session_count
std::unordered_map< const Primary *, load_map_id_type > _loading
uint32_t load_map_id_type
Identifies one LOAD_MAP broadcast.
std::string MakeLoadMapPayload(std::string_view map, load_map_id_type load_id, const LoadMapExtras &extras={})
LOAD_MAP payload: the NUL-terminated map path, the load id, then the extras as [u8 kind][u32 size][by...
std::optional< load_map_id_type > ParseEpisodeReadyMessage(std::string_view payload)
Load id of an episode-ready message (0 for a bare marker), or nullopt if payload is not one.
This file contains definitions of common data structures used in traffic manager.
static void log_error(Args &&... args)
static void log_info(Args &&... args)
static void log_warning(Args &&... args)
Outcome of a routed request.