Files
codam-webserver-team/include/net/SlotPool.hpp
T
2026-09-07 00:45:35 +02:00

105 lines
4.4 KiB
C++

/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* SlotPool.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: acossari <acossari@student.codam.nl> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2026/08/28 21:51:32 by acossari #+# #+# */
/* Updated: 2026/08/29 20:33:13 by acossari ### ########.fr */
/* */
/* ************************************************************************** */
#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <vector>
#include "event/EventToken.hpp"
#include "net/Connection.hpp"
#include "net/FileDescriptor.hpp"
namespace webserv {
// SlotPool uses fixed storage instead of growing a container for every new
// connection. This representation provides three guarantees:
//
// 1. Every slot keeps a permanent index and stable storage. EventToken pairs
// that index with an endpoint generation so stale events can be rejected.
//
// 2. The slot array is allocated once. Connections are constructed and
// destroyed in place, so their slots are never relocated.
//
// 3. The free list limits the pool to CAPACITY connections. acquire() returns
// no index when it is exhausted, and drain() makes retired slots reusable.
class SlotPool final {
public:
// Slot indices use uint32_t to match their field in the 64-bit EventToken.
// 256 is a calculated tradeoff, not an optimal value. It doubles the
// target of 128 concurrent clients exercised by the provided tester. It
// assumes a conservative limit of 1024 fds per process, common on Linux.
//
// fd budget = 4 base fds (standard streams and epoll)
// + L listener fds
// + 2 * 256 connection fds (socket + possible temporary file)
// + 2 * 24 CGI pipe fds (the tester reaches 20, leaving 4 spare)
// + 8 spare fds (temporary setup and general working margin)
// = 572 + L
//
// This leaves space for up to 452 listeners, giving substantial headroom.
// A capacity of 512 would require 1084 + L fds, already exceeding 1024.
static constexpr std::uint32_t CAPACITY = 256;
// Each slot tracks the client socket and the CGI stdin and stdout pipes.
static constexpr std::size_t ENDPOINTS_PER_SLOT = 3;
struct Endpoint {
std::uint32_t generation = 0;
std::uint32_t registeredEvents = 0;
bool registered = false;
};
SlotPool();
bool full() const noexcept { return freeHead_ == CAPACITY; }
// Constructs a Connection for the socket in a free slot and returns its
// index. Returns std::nullopt when the pool is full.
std::optional<std::uint32_t> acquire(FileDescriptor socket);
// Returns true only if the token still identifies a registered endpoint
// in an active slot with the same generation.
bool isCurrent(const EventToken& token) const noexcept;
// Accesses the connection or endpoint metadata already stored in a slot.
Connection& connection(std::uint32_t index);
Endpoint& endpoint(std::uint32_t index, EndpointKind kind);
// Quarantines an active slot until the current event batch is complete.
void retire(std::uint32_t index) noexcept;
// Recycles all quarantined slots and returns how many became available.
std::size_t drain();
private:
enum class SlotState { Free, InUse, Retired };
struct Slot {
std::array<Endpoint, ENDPOINTS_PER_SLOT> endpoints;
std::optional<Connection> connection;
SlotState state = SlotState::Free;
std::uint32_t nextFree = 0;
};
std::array<Slot, CAPACITY> slots_;
// Holds retired slot indices until they can return to the free list.
// With storage reserved for CAPACITY entries, it acts as if it were a fixed
// array while tracking how many entries are active without another counter.
std::vector<std::uint32_t> pendingFree_;
std::uint32_t freeHead_ = 0;
};
} // namespace webserv