Описание проблемы:
У меня C++ 20, использующее Boost.Asio 1.85, которое отлично работает в Windows, но аварийно завершает работу в Ubuntu Linux из-за ошибки сегментации. Сбой происходит при обработке HTTP-запроса, особенно внутри функции Flat_buffer::prepare() Boost.Beast. Я подозреваю, что это как-то связано с распределением памяти или доступом, возможно, связано с оптимизацией привязки NUMA/CPU или обработкой потоков в Linux.
Код: Выделить всё
#include
#include
#include
#include
#include
#include
#include
namespace asio = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
using tcp = asio::ip::tcp;
static constexpr uint16_t SERVERPORT = 7878;
class JsonEchoServer final : public std::enable_shared_from_this {
public:
explicit JsonEchoServer(asio::io_context& io_context, asio::ip::port_type port)
: io_context_(io_context)
, acceptor_(io_context, {tcp::v4(), port})
, thread_pool_(std::max(1u, std::thread::hardware_concurrency())) {
assert(acceptor_.is_open() && "Acceptor must be open.");
}
JsonEchoServer() = delete;
void run_server() {
co_spawn(io_context_, listener(), asio::detached);
std::size_t num_threads = std::max(1, std::thread::hardware_concurrency());
std::vector threads;
threads.reserve(num_threads);
for (std::size_t i = 0; i < num_threads; ++i) {
threads.emplace_back([this]() {
optimize_for_platform();
io_context_.run();
});
}
for (auto& th : threads) {
if (th.joinable())
th.join();
}
}
private:
asio::io_context& io_context_;
tcp::acceptor acceptor_;
class ThreadPool {
public:
explicit ThreadPool(std::size_t num_threads) : stop_(false) {
workers_.reserve(num_threads);
for (std::size_t i = 0; i < num_threads; ++i) {
workers_.emplace_back([this]() {
optimize_for_platform();
worker_thread();
});
}
}
~ThreadPool() {
stop_.store(true, std::memory_order_release);
condition_.notify_all();
for (auto& worker : workers_) {
if (worker.joinable())
worker.join();
}
}
void enqueue(std::function task) {
tasks_.enqueue(std::move(task));
condition_.notify_one();
}
private:
moodycamel::ConcurrentQueue tasks_;
std::vector workers_;
std::atomic stop_;
std::condition_variable condition_;
std::mutex mutex_;
void worker_thread() {
optimize_for_platform();
while (!stop_.load(std::memory_order_acquire)) {
std::function task;
if (tasks_.try_dequeue(task)) {
task();
} else {
std::unique_lock lock(mutex_);
condition_.wait_for(lock, std::chrono::milliseconds(1));
}
}
}
static void optimize_for_platform() {
#ifdef linux
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
int cpu = sched_getcpu();
assert(cpu >= 0 && "sched_getcpu() failed on Linux.");
CPU_SET(cpu, &cpuset);
int set_res = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
assert(set_res == 0 && "pthread_setaffinity_np() failed.");
int node = numa_node_of_cpu(cpu);
numa_run_on_node(node);
numa_set_preferred(node);
#endif
}
};
ThreadPool thread_pool_;
asio::awaitable handle_resultaten_endpoint( //
[[maybe_unused]] tcp::socket socket, //
[[maybe_unused]] http::request const& req) {
// ... implementation ...
co_return;
}
asio::awaitable handle_bestand_other_generator_endpoint( //
[[maybe_unused]] tcp::socket socket, //
[[maybe_unused]] http::request const& req) {
// ... implementation ...
co_return;
}
asio::awaitable handle_request(tcp::socket socket) {
socket.set_option(tcp::no_delay(true));
beast::flat_buffer buffer;
http::request_parser req_parser;
req_parser.body_limit(std::numeric_limits::max());
co_await http::async_read(socket, buffer, req_parser, asio::deferred);
http::request req(std::move(req_parser.get()));
static constexpr std::string_view resultaten_sv = "/resultaten";
static constexpr std::string_view bestand_other_sv = "/bestand_other";
std::string_view target_sv = req.target();
if (target_sv.starts_with(bestand_other_sv)) {
co_await handle_bestand_other_generator_endpoint(std::move(socket), std::move(req));
} else if (target_sv.starts_with(resultaten_sv)) {
co_await handle_resultaten_endpoint(std::move(socket), std::move(req));
} else {
http::response res;
res.version(req.version());
res.result(http::status::not_found);
res.set(http::field::content_type, "application/json");
res.body() = R"({"Status":"Fataal","Omschrijving":"Endpoint niet gevonden"})";
res.prepare_payload();
co_await http::async_write(socket, res, asio::deferred);
boost::beast::error_code shutdown_ec;
socket.shutdown(tcp::socket::shutdown_send, shutdown_ec);
}
co_return;
}
template auto offload_to_threadpool(Func&& func) -> asio::awaitable {
using result_type = decltype(func());
auto token = asio::use_awaitable;
co_return co_await asio::async_initiate(
[this, f = std::forward(func)](auto handler) mutable {
using handler_type = std::decay_t;
auto handler_ptr = std::make_shared(std::move(handler));
thread_pool_.enqueue([f = std::move(f), handler_ptr, this]() mutable {
try {
result_type result = f();
asio::post(io_context_, [handler_ptr, r = std::move(result)]() mutable {
(*handler_ptr)(boost::system::error_code(), std::move(r));
});
} catch (...) {
assert(false && "Caught unexpected exception in offload_to_threadpool.");
asio::post(io_context_, [handler_ptr]() {
(*handler_ptr)(asio::error::operation_aborted, result_type{});
});
}
});
},
token);
}
asio::awaitable listener() {
while (true) {
tcp::socket socket = co_await acceptor_.async_accept(asio::use_awaitable);
auto self = shared_from_this();
asio::co_spawn(
io_context_,
[this, self, s = std::move(socket)]() mutable { return handle_request(std::move(s)); },
asio::detached);
}
}
static void optimize_for_platform() {
#ifdef linux
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
int cpu = sched_getcpu();
assert(cpu >= 0 && "sched_getcpu() failed on Linux.");
CPU_SET(cpu, &cpuset);
int set_res = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
assert(set_res == 0 && "pthread_setaffinity_np() failed.");
int node = numa_node_of_cpu(cpu);
numa_run_on_node(node);
numa_set_preferred(node);
#endif
}
};
inline static void launch_server_process() {
asio::io_context io_context;
auto server = std::make_shared(io_context, SERVERPORT);
server->run_server();
}
int main() {
launch_server_process();
return 0;
}
Сервер работает нормально в Windows, но вызывает ошибку сегментации в Linux при поступлении запроса.
Сбой происходит в вызове Flat_buffer::prepare() Boost.Beast во время асинхронного чтения HTTP-запроса.
Это предполагает возможное повреждение памяти или неправильное использование Flat_buffer — возможно, из-за неправильное использование ниток или конфигурация для конкретной платформы в Linux.
Вопрос:
Что именно вызывает ошибку сегментации в Flat_buffer::prepare() в Linux, и как я могу решить эту проблему?
Я тщательно просмотрел код, исследуя его на предмет возможного повреждения памяти и проблем с безопасностью потоков, связанных с использованием Flat_buffer. Я проверил, что не было одновременных обращений без надлежащей синхронизации и что все объекты буфера управлялись правильно.
Я также исследовал оптимизацию для конкретной платформы, такую как привязка ЦП и вызовы NUMA в оптимизации_for_platform( ), учитывая, что эти специфичные для Linux вызовы могут вызвать неожиданное поведение или сбои, если, например, необходимые библиотеки NUMA не были связаны или оборудование не поддерживало определенные функции.
Чтобы получить больше информации в ошибку сегментации, я использовал инструменты отладки, такие как GDB, Valgrind и AddressSanitizer. Я попытался получить подробную информацию с помощью этих инструментов, но, несмотря на все эти усилия, трассировки стека и сообщения об ошибках не указали мне прямо на основную причину сбоя внутри Flat_buffer::prepare(). Это вывод bt при запуске gdb: https://drive.google.com/file/d/1MjXw_n ... sp=sharing
Код: Выделить всё
Thread 23 "ServerExecutable" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fff83f6d6c0 (LWP 9305)]
0x000055555587ea05 in boost::beast::basic_flat_buffer >::prepare (this=0x7fff70001298, n=512) at ISA_Software/ServerExecutable/../Kernel/boost/beast/core/impl/flat_buffer.hpp:322
#1 0x000055555587ca1f in boost::beast::detail::dynamic_buffer_prepare, boost::beast::basic_flat_buffer, true>::operator(), boost::beast::basic_flat_buffer, true, detail::composed_op >(boost::asio::basic_stream_socket&, boost::beast::basic_flat_buffer, detail::composed_work, detail::awaitable_handler, void (error_code, unsigned long)>&&) (stream=..., buffer=..., parser=..., handler=...) at ISA_Software/ServerExecutable/../Kernel/boost/beast/http/impl/read.hpp:463
...
#16 0x0000555555863759 in detail::awaitable_frame_base::resume (this=0x7fff70001660) at ISA_Software/ServerExecutable/../Kernel/boost/asio/impl/awaitable.hpp:503
...
#39 0x00005555557b352a in boost::asio::co_spawn(boost::asio::io_context&, JsonEchoServer::listener()::{lambda()#1}&&, boost::asio::detached_t const&, boost::asio::constraint
Подробнее здесь: [url]https://stackoverflow.com/questions/79363261/weird-sigmentation-fault-on-linux-c-boost-asio-server-build[/url]