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

Class rpc_libc_client

less_slow.cpp:6801–6861  ·  view source on GitHub ↗

* @brief A minimal RPC @b client using LibC functionality to setup the UDP socket, * and synchronous blocking POSIX calls to send and receive packets - * @b one at a time! */

Source from the content-addressed store, hash-verified

6799 * @b one at a time!
6800 */
6801class rpc_libc_client {
6802 int socket_descriptor_;
6803 sockaddr_in server_address_;
6804 std::size_t concurrency_;
6805
6806 public:
6807 rpc_libc_client(std::string const &server_address_str, std::uint16_t port, std::size_t concurrency)
6808 : concurrency_(concurrency) {
6809
6810 auto [socket_descriptor, server_address] = rpc_client_socket(server_address_str, port);
6811 socket_descriptor_ = socket_descriptor;
6812 server_address_ = server_address;
6813
6814 // Let's make sure we don't block forever on `recvfrom`
6815 struct timeval duration;
6816 duration.tv_sec = 0;
6817 duration.tv_usec = to_microseconds(rpc_batch_timeout_k).count();
6818 if (setsockopt(socket_descriptor_, SOL_SOCKET, SO_RCVTIMEO, &duration, sizeof(duration)) < 0)
6819 raise_system_error("Failed to set sockets batch timeout");
6820 }
6821
6822 ~rpc_libc_client() noexcept { close(socket_descriptor_); }
6823
6824 rpc_batch_result operator()() noexcept {
6825 rpc_batch_result result;
6826
6827 sockaddr_in reply_addr;
6828 socklen_t reply_len = sizeof(reply_addr);
6829 rpc_buffer_t send_buffer, receive_buffer;
6830 send_buffer.fill('X');
6831
6832 for (std::size_t i = 0; i < concurrency_; ++i) {
6833 auto send_time = std::chrono::steady_clock::now();
6834 ssize_t sent_length = sendto(socket_descriptor_, send_buffer.data(), send_buffer.size(), 0,
6835 reinterpret_cast<sockaddr *>(&server_address_), sizeof(server_address_));
6836 result.sent_packets++;
6837 if (sent_length <= 0) continue;
6838
6839 // In general, `select` is used to monitor multiple file descriptors or sockets at once
6840 // to see if they are ready for I/O, but in this case we use it to constrain the time
6841 // we are willing to wait for a single response.
6842 struct timeval expiry;
6843 expiry.tv_sec = 0;
6844 expiry.tv_usec = to_microseconds(rpc_packet_timeout_k).count();
6845 fd_set available_descriptors;
6846 FD_ZERO(&available_descriptors);
6847 FD_SET(socket_descriptor_, &available_descriptors);
6848 if (select(socket_descriptor_ + 1, &available_descriptors, nullptr, nullptr, &expiry) <= 0) continue;
6849
6850 ssize_t received_length = recvfrom(socket_descriptor_, receive_buffer.data(), receive_buffer.size(), 0,
6851 reinterpret_cast<sockaddr *>(&reply_addr), &reply_len);
6852 if (received_length <= 0) continue;
6853 auto response_time = std::chrono::steady_clock::now();
6854 auto diff = response_time - send_time;
6855 result.batch_latency += diff;
6856 result.max_packet_latency = std::max(result.max_packet_latency, diff);
6857 result.received_packets++;
6858 }

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected