Remove release_back_to_server
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
@@ -15,33 +17,31 @@
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Represents a single client connection with efficient memory management.
|
||||
*
|
||||
* Connection ownership model:
|
||||
* - Created by I/O thread, processed immediately, then transferred to epoll via
|
||||
* raw pointer
|
||||
* - I/O threads claim ownership by wrapping raw pointer in unique_ptr
|
||||
* - I/O thread optionally passes ownership to a thread pipeline
|
||||
* - Owner eventually transfers back to epoll by releasing unique_ptr to raw
|
||||
* pointer
|
||||
* - RAII cleanup happens if I/O thread doesn't transfer back
|
||||
*
|
||||
* Arena allocator thread safety:
|
||||
* Each Connection contains its own Arena instance that is accessed
|
||||
* exclusively by the thread that currently owns the connection. This ensures
|
||||
* thread safety without requiring locks:
|
||||
* - Arena is used by the owning thread for I/O buffers, request parsing, and
|
||||
* response generation
|
||||
* - Arena memory is automatically freed when the connection is destroyed
|
||||
* - reset() should only be called by the current owner thread
|
||||
*
|
||||
* Only the handler interface methods are public - all networking details are
|
||||
* private.
|
||||
*/
|
||||
// Forward declaration
|
||||
struct Server;
|
||||
|
||||
/**
|
||||
* Represents a single client connection with thread-safe concurrent access.
|
||||
*
|
||||
* Connection ownership model:
|
||||
* - Server owns all connections
|
||||
* - Handlers receive Connection& references, and can keep a WeakRef to
|
||||
* Connection for async responses.
|
||||
* - Multiple pipeline threads can safely access connection concurrently
|
||||
* - I/O thread has exclusive access to socket operations
|
||||
*
|
||||
* Threading model:
|
||||
* - Single mutex protects all connection state
|
||||
* - Pipeline threads call Connection methods (append_message, etc.)
|
||||
* - I/O thread processes socket events and message queue
|
||||
* - Pipeline threads manage epoll interests via Connection methods
|
||||
* - Connection tracks closed state to prevent EBADF errors
|
||||
*
|
||||
* Arena allocator usage:
|
||||
* - Request-scoped arenas created by handlers for each request
|
||||
* - No connection-owned arena for parsing/response generation
|
||||
* - Message queue stores spans + owning arenas until I/O completion
|
||||
*/
|
||||
struct Connection {
|
||||
// No public constructor or factory method - only Server can create
|
||||
// connections
|
||||
@@ -64,90 +64,63 @@ struct Connection {
|
||||
// Handler interface - public methods that handlers can use
|
||||
|
||||
/**
|
||||
* @brief Queue a message to be sent to the client.
|
||||
* @brief Queue an atomic message to be sent to the client.
|
||||
*
|
||||
* Adds data to the connection's outgoing message queue. The data will be sent
|
||||
* asynchronously by the server's I/O threads using efficient vectored
|
||||
* I/O.
|
||||
* Adds a complete message with all associated data to the connection's
|
||||
* outgoing message queue. The message will be sent asynchronously by a
|
||||
* server I/O thread using efficient vectored I/O.
|
||||
*
|
||||
* @param s The data to send (string view parameter for efficiency)
|
||||
* @param copy_to_arena If true (default), copies data to the connection's
|
||||
* arena for safe storage. If false, the caller must ensure the data remains
|
||||
* valid until all queued messages are sent.
|
||||
* @param data_parts Span of string_views pointing to arena-allocated data
|
||||
* @param arena Arena that owns all the memory referenced by data_parts
|
||||
* @param close_after_send Whether to close connection after sending this
|
||||
* message
|
||||
*
|
||||
* @warning Thread Safety: Only call from the thread that currently owns this
|
||||
* connection. The arena allocator is not thread-safe.
|
||||
* @note Thread Safety: This method is thread-safe and can be called
|
||||
* concurrently from multiple pipeline threads.
|
||||
*
|
||||
* @note Performance: Use copy_to_arena=false for static strings or data with
|
||||
* guaranteed lifetime, copy_to_arena=true for temporary/dynamic data.
|
||||
* @note The memory referenced by the data_parts span, must outlive @p arena.
|
||||
* The arena will be moved and kept alive until the message is fully sent.
|
||||
*
|
||||
* Example usage:
|
||||
* ```cpp
|
||||
* conn->append_message("HTTP/1.1 200 OK\r\n\r\n", false); // Static string
|
||||
* conn->append_message(dynamic_response, true); // Dynamic data
|
||||
* conn->append_message(arena_allocated_data, false); // Arena data
|
||||
* Arena arena;
|
||||
* auto* parts = arena.allocate<std::string_view>(2);
|
||||
* parts[0] = build_header(arena);
|
||||
* parts[1] = build_body(arena);
|
||||
* conn.append_message({parts, 2}, std::move(arena));
|
||||
* ```
|
||||
*/
|
||||
void append_message(std::string_view s, bool copy_to_arena = true);
|
||||
void append_message(std::span<std::string_view> data_parts, Arena arena,
|
||||
bool close_after_send = false);
|
||||
|
||||
/**
|
||||
* @brief Mark the connection to be closed after sending all queued messages.
|
||||
* @brief Get a WeakRef to this connection for async operations.
|
||||
*
|
||||
* Sets a flag that instructs the server to close this connection gracefully
|
||||
* after all currently queued messages have been successfully sent to the
|
||||
* client. This enables proper connection cleanup for protocols like HTTP/1.0
|
||||
* or when implementing connection limits.
|
||||
* Returns a WeakRef that can be safely used to access this connection
|
||||
* from other threads, such as pipeline processing threads. The WeakRef
|
||||
* allows safe access even if the connection might be destroyed by the
|
||||
* time the async operation executes.
|
||||
*
|
||||
* @note The connection will remain active until:
|
||||
* 1. All queued messages are sent to the client
|
||||
* 2. The server processes the close flag during the next I/O cycle
|
||||
* 3. The connection is properly closed and cleaned up
|
||||
* @return WeakRef to this connection
|
||||
*
|
||||
* @warning Thread Safety: Only call from the thread that currently owns this
|
||||
* connection.
|
||||
* @note Thread Safety: This method is thread-safe.
|
||||
*
|
||||
* Typical usage:
|
||||
* @note The WeakRef should be used with lock() to safely access the
|
||||
* connection. If lock() returns null, the connection has been destroyed.
|
||||
*
|
||||
* Example usage:
|
||||
* ```cpp
|
||||
* conn->append_message("HTTP/1.1 200 OK\r\n\r\nBye!");
|
||||
* conn->close_after_send(); // Close after sending response
|
||||
* auto weak_conn = conn.get_weak_ref();
|
||||
* async_processor.submit([weak_conn, request_data]() {
|
||||
* if (auto conn = weak_conn.lock()) {
|
||||
* Arena arena;
|
||||
* auto response = process_request(request_data, arena);
|
||||
* conn->append_message({&response, 1}, std::move(arena));
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
void close_after_send() { closeConnection_ = true; }
|
||||
|
||||
/**
|
||||
* @brief Get access to the connection's arena allocator.
|
||||
*
|
||||
* Returns a reference to this connection's private Arena instance,
|
||||
* which should be used for all temporary allocations during request
|
||||
* processing. The arena provides extremely fast allocation (~1ns) and
|
||||
* automatic cleanup when the connection is destroyed or reset.
|
||||
*
|
||||
* @return Reference to the connection's arena allocator
|
||||
*
|
||||
* @warning Thread Safety: Only access from the thread that currently owns
|
||||
* this connection. The arena allocator is not thread-safe and concurrent
|
||||
* access will result in undefined behavior.
|
||||
*
|
||||
* @note Memory Lifecycle: Arena memory is automatically freed when:
|
||||
* - The connection is destroyed
|
||||
* - reset() is called (keeps first block, frees others)
|
||||
* - The connection is moved (arena ownership transfers)
|
||||
*
|
||||
* Best practices:
|
||||
* ```cpp
|
||||
* Arena& arena = conn->get_arena();
|
||||
*
|
||||
* // Allocate temporary parsing buffers
|
||||
* char* buffer = arena.allocate<char>(1024);
|
||||
*
|
||||
* // Construct temporary objects
|
||||
* auto* request = arena.construct<HttpRequest>(arena);
|
||||
*
|
||||
* // Use arena-backed STL containers
|
||||
* std::vector<Token, ArenaStlAllocator<Token>> tokens{&arena};
|
||||
* ```
|
||||
*/
|
||||
Arena &get_arena() { return arena_; }
|
||||
WeakRef<Connection> get_weak_ref() const { return self_ref_.copy(); }
|
||||
|
||||
/**
|
||||
* @brief Get the unique identifier for this connection.
|
||||
@@ -210,11 +183,14 @@ struct Connection {
|
||||
* ```
|
||||
*/
|
||||
int64_t outgoing_bytes_queued() const {
|
||||
std::lock_guard lock(mutex_);
|
||||
#ifndef NDEBUG
|
||||
// Debug build: validate counter accuracy
|
||||
int64_t computed_total = 0;
|
||||
for (auto s : messages_) {
|
||||
computed_total += s.size();
|
||||
for (const auto &message : message_queue_) {
|
||||
for (const auto &part : message.data_parts) {
|
||||
computed_total += part.size();
|
||||
}
|
||||
}
|
||||
assert(
|
||||
outgoing_bytes_queued_ == computed_total &&
|
||||
@@ -268,50 +244,14 @@ struct Connection {
|
||||
*/
|
||||
void *user_data = nullptr;
|
||||
|
||||
/**
|
||||
* Reset the connection's arena allocator and message queue for reuse.
|
||||
*
|
||||
* This method efficiently reclaims arena memory by keeping the first block
|
||||
* and freeing all others, then reinitializes the message queue.
|
||||
*
|
||||
* @warning Thread Safety: This method should ONLY be called by the thread
|
||||
* that currently owns this connection. Calling reset() while the connection
|
||||
* is being transferred between threads or accessed by another thread will
|
||||
* result in undefined behavior.
|
||||
*
|
||||
* @note The assert(messages_.empty()) ensures all outgoing data has been
|
||||
* sent before resetting. This prevents data loss and indicates the connection
|
||||
* is in a clean state for reuse.
|
||||
*
|
||||
* Typical usage pattern:
|
||||
* - HTTP handlers call this after completing a request/response cycle
|
||||
*/
|
||||
void reset() {
|
||||
assert(messages_.empty());
|
||||
outgoing_bytes_queued_ = 0;
|
||||
arena_.reset();
|
||||
messages_ =
|
||||
std::deque<std::string_view, ArenaStlAllocator<std::string_view>>{
|
||||
ArenaStlAllocator<std::string_view>{&arena_}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @note Ownership Transfer: To release a connection back to the server for
|
||||
* continued processing, use the static method:
|
||||
* ```cpp
|
||||
* Server::release_back_to_server(std::move(connection_ptr));
|
||||
* ```
|
||||
*
|
||||
* This is the correct way to return connection ownership when:
|
||||
* - A handler has taken ownership via unique_ptr.release()
|
||||
* - Background processing of the connection is complete
|
||||
* - The connection should resume normal server-managed I/O processing
|
||||
*
|
||||
* The method is thread-safe and handles the case where the server may have
|
||||
* been destroyed while the connection was being processed elsewhere.
|
||||
*/
|
||||
|
||||
private:
|
||||
struct Message {
|
||||
Arena arena; // Owns all the memory (movable)
|
||||
std::span<std::string_view> data_parts; // Points to arena-allocated memory
|
||||
// (mutable for partial writes)
|
||||
bool close_after_send = false; // Close connection after sending
|
||||
};
|
||||
|
||||
// Server is a friend and can access all networking internals
|
||||
friend struct Server;
|
||||
|
||||
@@ -340,26 +280,31 @@ private:
|
||||
int readBytes(char *buf, size_t buffer_size);
|
||||
bool writeBytes();
|
||||
|
||||
// Direct access methods for Server
|
||||
// Direct access methods for Server (must hold mutex)
|
||||
int getFd() const { return fd_; }
|
||||
bool has_messages() const { return !messages_.empty(); }
|
||||
bool should_close() const { return closeConnection_; }
|
||||
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); }
|
||||
|
||||
// Immutable connection properties
|
||||
const 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
|
||||
Arena arena_;
|
||||
ConnectionHandler *handler_;
|
||||
WeakRef<Server> server_; // Weak reference to server for safe cleanup
|
||||
WeakRef<Server> server_; // Weak reference to server for safe cleanup
|
||||
WeakRef<Connection> self_ref_; // WeakRef to self for get_weak_ref()
|
||||
|
||||
std::deque<std::string_view, ArenaStlAllocator<std::string_view>> messages_{
|
||||
ArenaStlAllocator<std::string_view>{&arena_}};
|
||||
|
||||
// Counter tracking total bytes queued for transmission
|
||||
int64_t outgoing_bytes_queued_{0};
|
||||
|
||||
// Whether or not to close the connection after completing writing the
|
||||
// response
|
||||
bool closeConnection_{false};
|
||||
// Thread-safe state (protected by mutex_)
|
||||
mutable std::mutex mutex_; // Protects all mutable state
|
||||
std::deque<Message>
|
||||
message_queue_; // Queue of messages to send. Protectec by
|
||||
// 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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user