Separate Connection and Request lifetimes

This commit is contained in:
2025-09-14 15:04:37 -04:00
parent cf0c1b7cc2
commit 16c7ee0408
14 changed files with 519 additions and 381 deletions

View File

@@ -20,6 +20,40 @@
// Forward declaration
struct Server;
/**
* Base interface for sending messages to a connection.
* This restricted interface is safe for use by pipeline threads,
* containing only the append_message method needed for responses.
* Pipeline threads should use WeakRef<MessageSender> to safely
* send responses without accessing other connection functionality
* that should only be used by the I/O thread.
*/
struct MessageSender {
/**
* @brief Append message data to connection's outgoing message queue.
*
* Thread-safe method that can be called from any thread, including
* pipeline processing threads. The arena is moved into the connection
* to maintain data lifetime until the message is sent.
*
* @param data_parts Span of string_view parts to send (arena-allocated)
* @param arena Arena containing the memory for data_parts string_views
* @param close_after_send Whether to close connection after sending
*
* Example usage:
* ```cpp
* auto response_parts = std::span{arena.allocate<std::string_view>(2), 2};
* response_parts[0] = "HTTP/1.1 200 OK\r\n\r\n";
* response_parts[1] = "Hello World";
* conn.append_message(response_parts, std::move(arena));
* ```
*/
virtual void append_message(std::span<std::string_view> data_parts,
Arena arena, bool close_after_send = false) = 0;
virtual ~MessageSender() = default;
};
/**
* Represents a single client connection with thread-safe concurrent access.
*
@@ -42,7 +76,7 @@ struct Server;
* - No connection-owned arena for parsing/response generation
* - Message queue stores spans + owning arenas until I/O completion
*/
struct Connection {
struct Connection : MessageSender {
// No public constructor or factory method - only Server can create
// connections
@@ -91,7 +125,7 @@ struct Connection {
* ```
*/
void append_message(std::span<std::string_view> data_parts, Arena arena,
bool close_after_send = false);
bool close_after_send = false) override;
/**
* @brief Get a WeakRef to this connection for async operations.
@@ -120,7 +154,10 @@ struct Connection {
* });
* ```
*/
WeakRef<Connection> get_weak_ref() const { return self_ref_.copy(); }
WeakRef<MessageSender> get_weak_ref() const {
assert(self_ref_.lock());
return self_ref_.copy();
}
/**
* @brief Get the unique identifier for this connection.
@@ -278,23 +315,26 @@ private:
// Networking interface - only accessible by Server
int readBytes(char *buf, size_t buffer_size);
bool writeBytes();
enum WriteBytesResult {
Error = 1 << 0,
Progress = 1 << 1,
Drained = 1 << 2,
Close = 1 << 3,
};
uint32_t write_bytes();
// Direct access methods for Server (must hold mutex)
int getFd() const { return fd_; }
bool has_messages() const { return !message_queue_.empty(); }
bool should_close() const { return close_after_send_; }
size_t getEpollIndex() const { return epoll_index_; }
// Server can set self-reference after creation
void setSelfRef(WeakRef<Connection> self) { self_ref_ = std::move(self); }
void close();
// Immutable connection properties
const int fd_;
int fd_;
const int64_t id_;
const size_t epoll_index_; // Index of the epoll instance this connection uses
struct sockaddr_storage addr_; // sockaddr_storage handles IPv4/IPv6
ConnectionHandler *handler_;
ConnectionHandler *const handler_;
WeakRef<Server> server_; // Weak reference to server for safe cleanup
WeakRef<Connection> self_ref_; // WeakRef to self for get_weak_ref()
@@ -305,6 +345,13 @@ private:
// mutex_, but if non-empty mutex_ can be
// dropped while server accesses existing elements.
int64_t outgoing_bytes_queued_{0}; // Counter of queued bytes
bool close_after_send_{false}; // Close after sending all messages
bool is_closed_{false}; // Connection closed state
#if __has_feature(thread_sanitizer)
void tsan_acquire() { tsan_sync.load(std::memory_order_acquire); }
void tsan_release() { tsan_sync.store(0, std::memory_order_release); }
std::atomic<int> tsan_sync;
#else
void tsan_acquire() {}
void tsan_release() {}
#endif
};