CARLA
Loading...
Searching...
No Matches
primary.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
8
9#include "carla/Debug.h"
10#include "carla/Logging.h"
15
16#include <boost/asio/read.hpp>
17#include <boost/asio/write.hpp>
18#include <boost/asio/bind_executor.hpp>
19#include <boost/asio/post.hpp>
20
21#include <atomic>
22#include <chrono>
23#include <thread>
24
25namespace carla {
26namespace multigpu {
27
28 static std::atomic_size_t SESSION_COUNTER{0u};
29
31 boost::asio::io_context &io_context,
32 const time_duration timeout,
33 Listener &server)
35 std::string("tcp multigpu server session ") + std::to_string(SESSION_COUNTER)),
36 _server(server),
37 _session_id(SESSION_COUNTER++),
38 _socket(io_context),
39 _timeout(timeout),
40 _deadline(io_context, std::chrono::steady_clock::time_point::max()),
41 _strand(io_context),
42 _buffer_pool(std::make_shared<BufferPool>()) {}
43
45 if (_socket.is_open()) {
46 boost::system::error_code ec;
47 _socket.shutdown(boost::asio::socket_base::shutdown_both, ec);
48 _socket.close();
49 }
50 }
51
56 DEBUG_ASSERT(on_opened && on_closed);
57
58 // This forces not using Nagle's algorithm.
59 // Improves the sync mode velocity on Linux by a factor of ~3.
60 const boost::asio::ip::tcp::no_delay option(true);
61 _socket.set_option(option);
63
64 // callbacks
65 _on_closed = std::move(on_closed);
66 _on_response = std::move(on_response);
67 on_opened(shared_from_this());
68
69 ReadData();
70 }
71
72 void Primary::Write(std::shared_ptr<const carla::streaming::detail::tcp::Message> message) {
73 DEBUG_ASSERT(message != nullptr);
74 DEBUG_ASSERT(!message->empty());
75 std::weak_ptr<Primary> weak = shared_from_this();
76 boost::asio::post(_strand, [weak, message]() {
77 auto self = weak.lock();
78 if (!self) return;
79 if (!self->_socket.is_open()) {
80 return;
81 }
82 self->_write_queue.push_back(message);
83 if (!self->_is_writing) {
84 self->WriteNext();
85 }
86 });
87 }
88
90 if (_write_queue.empty() || !_socket.is_open()) {
91 _write_queue.clear();
92 _is_writing = false;
93 return;
94 }
95 _is_writing = true;
96 const message_type message = _write_queue.front();
97 std::weak_ptr<Primary> weak = shared_from_this();
98 auto handle_sent = [weak, message](const boost::system::error_code &ec, size_t) {
99 auto self = weak.lock();
100 if (!self) return;
101 if (ec) {
102 self->_write_queue.clear();
103 self->_is_writing = false;
104 // A write aborted by our own close needs no second close. Otherwise
105 // close the socket too, so that later writes are dropped instead of
106 // failing (and closing the session) again.
107 if (self->_socket.is_open()) {
108 log_error("session ", self->_session_id, ": error sending data: ", ec.message());
109 self->CloseNow();
110 }
111 return;
112 }
113 self->_write_queue.pop_front();
114 self->WriteNext();
115 };
116
117 _deadline.expires_after(_timeout.to_chrono());
118 boost::asio::async_write(
119 _socket,
120 message->GetBufferSequence(),
121 boost::asio::bind_executor(_strand, handle_sent));
122 }
123
124 void Primary::Write(std::string text) {
125 // One message, size prefix included, through the same ordered queue.
126 Buffer buffer(reinterpret_cast<const unsigned char *>(text.data()), text.size());
127 Write(MakeMessage(BufferView::CreateFrom(std::move(buffer))));
128 }
129
131 std::weak_ptr<Primary> weak = shared_from_this();
132 boost::asio::post(_strand, [weak]() {
133 auto self = weak.lock();
134 if (!self) return;
135
136 auto message = std::make_shared<IncomingMessage>(self->_buffer_pool->Pop());
137
138 auto handle_read_data = [weak, message](boost::system::error_code ec, size_t DEBUG_ONLY(bytes)) {
139 auto self = weak.lock();
140 if (!self) return;
141 if (!ec) {
142 DEBUG_ASSERT_EQ(bytes, message->size());
143 DEBUG_ASSERT_NE(bytes, 0u);
144 // Move the buffer to the callback function and start reading the next
145 // piece of data.
146 self->_on_response(self, message->pop());
147 std::cout << "Getting data on listener\n";
148 self->ReadData();
149 } else {
150 // As usual, if anything fails start over from the very top.
151 log_error("primary server: failed to read data: ", ec.message());
152 }
153 };
154
155 auto handle_read_header = [weak, message, handle_read_data](
156 boost::system::error_code ec,
157 size_t) {
158 auto self = weak.lock();
159 if (!self) return;
160 if (!ec && (message->size() > kMaxMultiGpuMessageBytes)) {
161 log_error("primary server: secondary announced a", message->size(),
162 "byte message, larger than any valid one; closing the session");
163 self->Close();
164 } else if (!ec && (message->size() > 0u)) {
165 // Now that we know the size of the coming buffer, we can allocate our
166 // buffer and start putting data into it.
167 boost::asio::async_read(
168 self->_socket,
169 message->buffer(),
170 boost::asio::bind_executor(self->_strand, handle_read_data));
171 } else {
172 if (ec) {
173 log_error("Primary server: failed to read header: ", ec.message());
174 }
175 // Connect();
176 self->Close();
177 }
178 };
179
180 // Read the size of the buffer that is coming.
181 boost::asio::async_read(
182 self->_socket,
183 message->size_as_buffer(),
184 boost::asio::bind_executor(self->_strand, handle_read_header));
185 });
186 }
187
189 std::weak_ptr<Primary> weak = shared_from_this();
190 boost::asio::post(_strand, [weak]() {
191 auto self = weak.lock();
192 if (!self) return;
193 self->CloseNow();
194 });
195 }
196
198 if (_deadline.expiry() <= std::chrono::steady_clock::now()) {
199 log_debug("session ", _session_id, " time out");
200 Close();
201 } else {
202 std::weak_ptr<Primary> weak = shared_from_this();
203 _deadline.async_wait([weak](boost::system::error_code ec) {
204 auto self = weak.lock();
205 if (!self) return;
206 if (!ec) {
207 self->StartTimer();
208 } else {
209 log_error("session ", self->_session_id, " timed out error: ", ec.message());
210 }
211 });
212 }
213 }
214
215 void Primary::CloseNow(boost::system::error_code ec) {
216 _deadline.cancel();
217 if (!ec)
218 {
219 if (_socket.is_open()) {
220 boost::system::error_code ec2;
221 _socket.shutdown(boost::asio::socket_base::shutdown_both, ec2);
222 _socket.close();
223 }
224 }
225 _on_closed(shared_from_this());
226 log_debug("session", _session_id, "closed");
227 }
228
229} // namespace multigpu
230} // namespace carla
#define DEBUG_ASSERT(predicate)
Definition Debug.h:66
#define DEBUG_ASSERT_EQ(lhs, rhs)
Definition Debug.h:76
#define DEBUG_ASSERT_NE(lhs, rhs)
Definition Debug.h:77
#define DEBUG_ONLY(code)
Definition Debug.h:55
#define LIBCARLA_INITIALIZE_LIFETIME_PROFILER(display_name)
A pool of Buffer.
Definition BufferPool.h:29
static std::shared_ptr< BufferView > CreateFrom(Buffer &&buffer)
Definition BufferView.h:56
A piece of raw data.
std::function< void(std::shared_ptr< Primary >)> callback_function_type
Definition listener.h:32
std::function< void(std::shared_ptr< Primary >, carla::Buffer)> callback_function_type_response
Definition listener.h:33
const size_t _session_id
Definition primary.h:95
std::deque< message_type > _write_queue
Messages waiting to be written, oldest first, and whether one is being written.
Definition primary.h:114
void WriteNext()
Starts writing the front of the write queue. Strand only.
Definition primary.cpp:89
std::shared_ptr< const carla::streaming::detail::tcp::Message > message_type
Definition primary.h:82
socket_type _socket
Definition primary.h:97
time_duration _timeout
Definition primary.h:99
void Open(Listener::callback_function_type on_opened, Listener::callback_function_type on_closed, Listener::callback_function_type_response on_response)
Starts the session and calls on_opened after successfully reading the stream id, and on_closed once t...
Definition primary.cpp:52
Listener::callback_function_type _on_closed
Definition primary.h:105
Primary(boost::asio::io_context &io_context, time_duration timeout, Listener &server)
Definition primary.cpp:30
boost::asio::io_context::strand _strand
Definition primary.h:103
static auto MakeMessage(Buffers... buffers)
Definition primary.h:55
Listener::callback_function_type_response _on_response
Definition primary.h:107
boost::asio::steady_timer _deadline
Definition primary.h:101
void Write(std::shared_ptr< const carla::streaming::detail::tcp::Message > message)
Writes some data to the socket.
Definition primary.cpp:72
void CloseNow(boost::system::error_code ec=boost::system::error_code())
Definition primary.cpp:215
void Close()
Post a job to close the session.
Definition primary.cpp:188
void ReadData()
read data
Definition primary.cpp:130
Positive time duration up to milliseconds resolution.
Definition Time.h:20
constexpr auto to_chrono() const
Definition Time.h:54
void EnableKeepAlive(boost::asio::ip::tcp::socket &socket)
Lets either end notice a peer that vanished without closing the connection (a killed host,...
Definition keepAlive.h:21
constexpr std::size_t kMaxMultiGpuMessageBytes
Largest message either end accepts: a frame (capped at 1 GiB by the primary) plus room for its header...
Definition commands.h:37
static std::atomic_size_t SESSION_COUNTER
Definition primary.cpp:28
This file contains definitions of common data structures used in traffic manager.
static void log_error(Args &&... args)
Definition Logging.h:110
static void log_debug(Args &&... args)
Definition Logging.h:68