libdpf/include/dpf/net/buffer_pool.hpp

99 lines
2.5 KiB
C++
Raw Permalink Normal View History

/// @file dpf/net/buffer_pool.hpp
/// @brief Recycled byte buffers for framed writes.
/// @details `acquire_buffer` hands out a `shared_ptr` whose deleter returns the
/// vector to a process-wide free list (capped). Callers that already
/// own a buffer pass it to `async_write_owned` so the socket path can
/// scatter-gather without a second payload copy.
#ifndef LIBDPF_INCLUDE_DPF_NET_BUFFER_POOL_HPP__
#define LIBDPF_INCLUDE_DPF_NET_BUFFER_POOL_HPP__
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <mutex>
#include <utility>
#include <vector>
#include "dpf/net/policy.hpp"
namespace dpf
{
namespace net
{
/// @brief Default outstanding-byte window (see `wire_policy::window_bytes`).
inline std::size_t default_wire_window() noexcept
{
return wire_policy{}.window_bytes;
}
/// @brief Default receive cap (see `wire_policy::max_frame`).
inline constexpr std::size_t k_max_frame = std::size_t{16} << 20;
namespace detail
{
struct pool_state
{
std::mutex mu;
std::vector<std::unique_ptr<std::vector<std::uint8_t>>> free;
};
inline pool_state & buffers()
{
static pool_state s;
return s;
}
inline void recycle(std::vector<std::uint8_t> * p)
{
std::unique_ptr<std::vector<std::uint8_t>> owned(p);
owned->clear();
if (owned->capacity() > (std::size_t{1} << 20))
return;
auto & st = buffers();
std::lock_guard<std::mutex> lock(st.mu);
if (st.free.size() < 128)
st.free.push_back(std::move(owned));
}
} // namespace detail
/// @brief Buffer of at least `n` bytes, returned to the pool on last release.
inline std::shared_ptr<std::vector<std::uint8_t>> acquire_buffer(std::size_t n)
{
std::unique_ptr<std::vector<std::uint8_t>> raw;
{
auto & st = detail::buffers();
std::lock_guard<std::mutex> lock(st.mu);
if (!st.free.empty())
{
raw = std::move(st.free.back());
st.free.pop_back();
}
}
if (!raw)
raw.reset(new std::vector<std::uint8_t>());
if (raw->capacity() < n)
raw->reserve(n);
raw->resize(n);
std::vector<std::uint8_t> * p = raw.release();
return std::shared_ptr<std::vector<std::uint8_t>>(p, &detail::recycle);
}
/// @brief Copy `n` bytes into a pooled buffer.
inline std::shared_ptr<std::vector<std::uint8_t>> copy_buffer(const void * src,
std::size_t n)
{
auto buf = acquire_buffer(n);
if (n != 0 && src != nullptr)
std::memcpy(buf->data(), src, n);
return buf;
}
} // namespace net
} // namespace dpf
#endif