99 lines
2.5 KiB
C++
99 lines
2.5 KiB
C++
|
|
/// @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
|