Use writev

This commit is contained in:
2025-08-19 12:04:13 -04:00
parent 5dc800a088
commit 2b458747f8

View File

@@ -10,11 +10,13 @@
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h> #include <inttypes.h>
#include <iostream> #include <iostream>
#include <limits.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h> #include <sys/un.h>
#include <thread> #include <thread>
#include <unistd.h> #include <unistd.h>
@@ -147,18 +149,13 @@ struct Connection {
} }
} }
struct Message { std::deque<std::string_view, ArenaStlAllocator<std::string_view>> messages{
// Owned by Connection::arena ArenaStlAllocator<std::string_view>{&arena}};
std::string_view s;
int written = 0;
};
std::deque<Message, ArenaStlAllocator<Message>> messages{
ArenaStlAllocator<Message>{&arena}};
// Copies s into arena, and appends to messages // Copies s into arena, and appends to messages
void appendMessage(std::string_view s) { void appendMessage(std::string_view s) {
char *arena_str = arena.allocate<char>(s.size()); char *arena_str = arena.allocate<char>(s.size());
std::memcpy(arena_str, s.data(), s.size()); std::memcpy(arena_str, s.data(), s.size());
messages.emplace_back(std::string_view(arena_str, s.size()), 0); messages.emplace_back(arena_str, s.size());
} }
// Whether or not to close the connection after completing writing the // Whether or not to close the connection after completing writing the
// response // response
@@ -192,11 +189,28 @@ struct Connection {
bool writeBytes() { bool writeBytes() {
while (!messages.empty()) { while (!messages.empty()) {
auto &front = messages.front(); // Build iovec array up to IOV_MAX limit
int w; struct iovec iov[IOV_MAX];
int iov_count = 0;
for (auto it = messages.begin();
it != messages.end() && iov_count < IOV_MAX; ++it) {
const auto &msg = *it;
if (msg.size() > 0) {
iov[iov_count] = {
const_cast<void *>(static_cast<const void *>(msg.data())),
msg.size()};
iov_count++;
}
}
if (iov_count == 0) {
break;
}
ssize_t w;
for (;;) { for (;;) {
w = write(fd, front.s.data() + front.written, w = writev(fd, iov, iov_count);
front.s.size() - front.written);
if (w == -1) { if (w == -1) {
if (errno == EINTR) { if (errno == EINTR) {
continue; // Standard practice: retry on signal interruption continue; // Standard practice: retry on signal interruption
@@ -204,15 +218,29 @@ struct Connection {
if (errno == EAGAIN) { if (errno == EAGAIN) {
return false; return false;
} }
perror("write"); perror("writev");
return true; return true;
} }
break; break;
} }
assert(w != 0);
front.written += w; assert(w > 0);
if (front.written == int(front.s.size())) {
messages.pop_front(); // Handle partial writes by updating string_view data/size
size_t bytes_written = static_cast<size_t>(w);
while (bytes_written > 0 && !messages.empty()) {
auto &front = messages.front();
if (bytes_written >= front.size()) {
// This message is completely written
bytes_written -= front.size();
messages.pop_front();
} else {
// Partial write of this message - update string_view
front = std::string_view(front.data() + bytes_written,
front.size() - bytes_written);
bytes_written = 0;
}
} }
} }
assert(messages.empty()); assert(messages.empty());