MCPcopy Create free account
hub / github.com/ashvardanian/less_slow.cpp / rpc_asio_server

Class rpc_asio_server

less_slow.cpp:7661–7720  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

7659#include <asio.hpp>
7660
7661class rpc_asio_server {
7662
7663 asio::io_context context_;
7664 asio::ip::udp::socket socket_;
7665 std::thread context_thread_;
7666
7667 /// @brief Buffers, one per concurrent request
7668 std::vector<rpc_buffer_t> buffers_;
7669 /// @brief Where did the packets come from
7670 std::vector<asio::ip::udp::endpoint> clients_;
7671 /// @brief Which buffers are available?
7672 std::vector<std::size_t> buffers_available_;
7673 /// @brief Flag to stop the server without corrupting the state
7674 std::atomic_bool should_stop_;
7675 // Use a work guard so the io_context doesn’t run out of work and exit.
7676 asio::executor_work_guard<asio::io_context::executor_type> work_guard_;
7677
7678 std::size_t failed_receptions_ = 0;
7679 std::size_t failed_responses_ = 0;
7680
7681 public:
7682 rpc_asio_server(std::string const &address, std::uint16_t port, std::size_t max_concurrency)
7683 : context_(), socket_(context_), buffers_(max_concurrency), clients_(max_concurrency),
7684 work_guard_(asio::make_work_guard(context_)) {
7685 // Use your helper function to create and bind the native socket.
7686 auto server = rpc_server_socket(port, address);
7687 // Now assign the native socket to the ASIO socket.
7688 socket_.assign(asio::ip::udp::v4(), server.socket_descriptor);
7689 }
7690
7691 void stop() { should_stop_.store(true, std::memory_order_seq_cst); }
7692 void close() {
7693 socket_.cancel();
7694 context_.stop();
7695 if (context_thread_.joinable()) context_thread_.join();
7696 }
7697
7698 void operator()() {
7699 // For per-operation cancellations we could use the `asio::cancellation_signal`.
7700 // Let's issue a receive operation for each buffer, which will call a chain of
7701 // operations to process the packet and send a response, and repeat again.
7702 for (std::size_t job = 0; job < buffers_.size(); ++job) reuse_buffer(job);
7703 // Start listening for incoming packets.
7704 context_thread_ = std::thread([this] { context_.run(); });
7705 }
7706
7707 private:
7708 void reuse_buffer(std::size_t job) {
7709 auto finalize = [this, job](std::error_code error, std::size_t) {
7710 if (error) failed_responses_++;
7711 if (should_stop_.load(std::memory_order_seq_cst)) return;
7712 reuse_buffer(job);
7713 };
7714 auto respond = [this, finalize, job](std::error_code error, std::size_t bytes) {
7715 if (error) { reuse_buffer(job); }
7716 else { socket_.async_send_to(asio::buffer(buffers_[job], bytes), clients_[job], finalize); }
7717 };
7718 socket_.async_receive_from(asio::buffer(buffers_[job]), clients_[job], respond);

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected