51class BundledProvidedBufferQueue {
53 BundledProvidedBufferQueue(
Runtime *runtime, uint32_t capacity,
55 : runtime_(runtime), capacity_(std::bit_ceil(capacity)),
56 mask_(io_uring_buf_ring_mask(capacity_)), buf_lens_(capacity_, 0),
59 auto d = detail::defer([&]() {
65 auto &bgid_pool = runtime_->bgid_pool_internal();
67 bgid_ = bgid_pool.allocate();
70 br_ = io_uring_setup_buf_ring(runtime_->ring_internal().ring(),
71 capacity_, bgid_, br_flags_, &err);
72 if (br_ ==
nullptr) [[unlikely]] {
73 throw detail::make_system_error(
"io_uring_setup_buf_ring", -err);
79 ~BundledProvidedBufferQueue() { cleanup_(); }
82 CONDY_DELETE_COPY_MOVE(BundledProvidedBufferQueue);
88 size_t size()
const noexcept {
return size_; }
93 size_t capacity()
const noexcept {
return capacity_; }
105 template <BufferLike Buffer> uint16_t push(
const Buffer &
buffer) {
106 if (size_ >= capacity_) [[unlikely]] {
107 throw std::logic_error(
"Capacity exceeded");
110 uint16_t bid = br_->tail & mask_;
111 io_uring_buf_ring_add(br_,
buffer.data(),
buffer.size(), bid, mask_, 0);
112 buf_lens_[bid] =
buffer.size();
113 io_uring_buf_ring_advance(br_, 1);
120 uint16_t bgid()
const noexcept {
return bgid_; }
122 BufferInfo handle_finish(io_uring_cqe *cqe)
noexcept {
123 assert(cqe !=
nullptr);
124 int32_t res = cqe->res;
125 uint32_t flags = cqe->flags;
127 if (!(flags & IORING_CQE_F_BUFFER)) {
134 .bid =
static_cast<uint16_t
>(flags >> IORING_CQE_BUFFER_SHIFT),
138 bool is_incr =
false;
139#if CONDY_URING_VERSION_GE(2, 8)
140 is_incr = br_flags_ & IOU_PBUF_RING_INC;
143 uint16_t idx = result.
bid;
146 uint16_t curr_bid = idx & mask_;
149 buf_len = std::min<uint32_t>(bytes, buf_lens_[curr_bid]);
150 buf_lens_[curr_bid] -= buf_len;
152 buf_len = std::exchange(buf_lens_[curr_bid], 0);
155 if (buf_lens_[curr_bid] == 0) {
167 void cleanup_()
noexcept {
168 if (br_ !=
nullptr) {
169 [[maybe_unused]]
int r = io_uring_free_buf_ring(
170 runtime_->ring_internal().ring(), br_, capacity_, bgid_);
174 if (bgid_ != std::numeric_limits<uint16_t>::max()) {
175 runtime_->bgid_pool_internal().recycle(bgid_);
181 io_uring_buf_ring *br_ =
nullptr;
185 uint16_t bgid_ = std::numeric_limits<uint16_t>::max();
186 std::vector<uint32_t> buf_lens_;
187 unsigned int br_flags_;
224 unsigned int flags = 0)
225 : BundledProvidedBufferQueue(&runtime, capacity, flags) {}
227 BufferInfo handle_finish(io_uring_cqe *cqe)
noexcept {
228 assert(cqe !=
nullptr);
229 auto result = BundledProvidedBufferQueue::handle_finish(cqe);
230 assert(result.num_buffers <= 1);
236class BundledProvidedBufferPool;
248 :
public detail::ManagedBuffer<detail::BundledProvidedBufferPool> {
250 using Base = detail::ManagedBuffer<detail::BundledProvidedBufferPool>;
256class BundledProvidedBufferPool {
258 BundledProvidedBufferPool(
Runtime *runtime,
void *buffer_data,
259 uint32_t num_buffers,
size_t buffer_size,
260 unsigned int flags,
bool owns_buffers)
261 : runtime_(runtime), buffers_base_(static_cast<char *>(buffer_data)),
262 num_buffers_(std::bit_ceil(num_buffers)),
263 mask_(io_uring_buf_ring_mask(num_buffers_)),
264 buffer_size_(buffer_size), curr_buf_len_(buffer_size),
265 br_flags_(flags), owns_buffers_(owns_buffers) {
267 auto d = detail::defer([&]() {
273 auto &bgid_pool = runtime_->bgid_pool_internal();
275 bgid_ = bgid_pool.allocate();
278 br_ = io_uring_setup_buf_ring(runtime_->ring_internal().ring(),
279 num_buffers_, bgid_, br_flags_, &err);
280 if (br_ ==
nullptr) [[unlikely]] {
281 throw detail::make_system_error(
"io_uring_setup_buf_ring", -err);
284 for (
size_t bid = 0; bid < num_buffers_; bid++) {
285 char *ptr = buffers_base_ + bid * buffer_size_;
286 io_uring_buf_ring_add(br_, ptr, buffer_size_, bid, mask_,
287 static_cast<int>(bid));
289 io_uring_buf_ring_advance(br_,
static_cast<int>(num_buffers_));
294 ~BundledProvidedBufferPool() { cleanup_(); }
297 CONDY_DELETE_COPY_MOVE(BundledProvidedBufferPool);
303 size_t capacity() const noexcept {
return num_buffers_; }
308 size_t buffer_size() const noexcept {
return buffer_size_; }
311 uint16_t bgid() const noexcept {
return bgid_; }
313 std::vector<ProvidedBuffer> handle_finish(io_uring_cqe *cqe)
noexcept {
314 assert(cqe !=
nullptr);
315 int32_t res = cqe->res;
316 uint32_t flags = cqe->flags;
317 std::vector<ProvidedBuffer> buffers;
319 if (!(flags & IORING_CQE_F_BUFFER)) {
325 bool is_incr =
false;
326#if CONDY_URING_VERSION_GE(2, 8)
327 is_incr = br_flags_ & IOU_PBUF_RING_INC;
332 auto *buf_ptr = curr_io_uring_buf_();
333 uint16_t bid = buf_ptr->bid;
335 char *data = get_buffer_(bid) + (buffer_size_ - curr_buf_len_);
338 buf_len = std::min<uint32_t>(bytes, curr_buf_len_);
339 curr_buf_len_ -= buf_len;
341 buf_len = std::exchange(curr_buf_len_, 0);
344 if (curr_buf_len_ == 0) {
345 buffers.emplace_back(data, buf_len,
this);
346 advance_io_uring_buf_();
347 curr_buf_len_ = buffer_size_;
349 buffers.emplace_back(data, buf_len,
nullptr);
356 void add_buffer_back(
void *ptr, [[maybe_unused]]
size_t size)
noexcept {
357 assert(size <= buffer_size_);
358 assert(ptr >= buffers_base_);
359 size_t offset =
static_cast<char *
>(ptr) - buffers_base_;
360 size_t bid = offset / buffer_size_;
361 assert(bid < num_buffers_);
362 char *buffer_ptr = buffers_base_ + bid * buffer_size_;
363 io_uring_buf_ring_add(br_, buffer_ptr, buffer_size_, bid, mask_, 0);
364 io_uring_buf_ring_advance(br_, 1);
368 char *get_buffer_(uint16_t bid)
const noexcept {
369 return buffers_base_ +
static_cast<size_t>(bid) * buffer_size_;
372 io_uring_buf *curr_io_uring_buf_() noexcept {
373 return &br_->bufs[br_head_ & mask_];
376 void advance_io_uring_buf_() noexcept { br_head_++; }
378 void cleanup_() noexcept {
379 [[maybe_unused]]
int r;
380 if (br_ !=
nullptr) {
381 r = io_uring_free_buf_ring(runtime_->ring_internal().ring(), br_,
382 num_buffers_, bgid_);
386 if (bgid_ != std::numeric_limits<uint16_t>::max()) {
387 runtime_->bgid_pool_internal().recycle(bgid_);
390 r = munmap(buffers_base_,
391 static_cast<size_t>(num_buffers_) * buffer_size_);
393 owns_buffers_ =
false;
394 buffers_base_ =
nullptr;
400 io_uring_buf_ring *br_ =
nullptr;
401 char *buffers_base_ =
nullptr;
402 uint32_t num_buffers_;
404 uint32_t buffer_size_;
405 uint32_t curr_buf_len_;
406 uint16_t bgid_ = std::numeric_limits<uint16_t>::max();
407 uint16_t br_head_ = 0;
408 unsigned int br_flags_;
436 unsigned int flags = 0)
438 buffer_size, flags) {}
450 size_t buffer_size,
unsigned int flags = 0)
451 : BundledProvidedBufferPool(
453 alloc_buffer_data_(std::bit_ceil(num_buffers) * buffer_size),
454 num_buffers, buffer_size, flags, true) {}
464 size_t buffer_size,
unsigned int flags = 0)
466 num_buffers, buffer_size, flags) {}
478 uint32_t num_buffers,
size_t buffer_size,
479 unsigned int flags = 0)
480 : BundledProvidedBufferPool(&runtime, buffer_data, num_buffers,
481 buffer_size, flags, false) {}
485 assert(cqe !=
nullptr);
486 auto buffers = BundledProvidedBufferPool::handle_finish(cqe);
487 if (buffers.empty()) {
490 assert(buffers.size() == 1);
491 return std::move(buffers[0]);
495 static void *alloc_buffer_data_(
size_t buf_size) {
496 void *buf_data = mmap(
nullptr, buf_size, PROT_READ | PROT_WRITE,
497 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
498 if (buf_data == MAP_FAILED) [[unlikely]] {
499 throw detail::make_system_error(
"mmap");
515 return static_cast<detail::BundledProvidedBufferPool &
>(
buffer);
525 return static_cast<detail::BundledProvidedBufferQueue &
>(
buffer);
ProvidedBufferPool(uint32_t num_buffers, size_t buffer_size, unsigned int flags=0)
Construct a new ProvidedBufferPool object in current Runtime.
ProvidedBufferPool(Runtime &runtime, uint32_t num_buffers, size_t buffer_size, unsigned int flags=0)
Construct a new Provided Buffer Pool object with specified Runtime.
ProvidedBufferPool(void *buffer_data, uint32_t num_buffers, size_t buffer_size, unsigned int flags=0)
Construct with externally provided buffer memory.
ProvidedBufferPool(Runtime &runtime, void *buffer_data, uint32_t num_buffers, size_t buffer_size, unsigned int flags=0)
Construct with externally provided buffer memory and specified Runtime.
ProvidedBufferQueue(Runtime &runtime, uint32_t capacity, unsigned int flags=0)
Construct a new Provided Buffer Queue object with specified Runtime.
ProvidedBufferQueue(uint32_t capacity, unsigned int flags=0)
Construct a new ProvidedBufferQueue object in current Runtime.
The event loop runtime for executing asynchronous.
Basic buffer types and conversion utilities.
The main namespace for the Condy library.
auto & bundled(ProvidedBufferPool &buffer)
Get the bundled variant of a provided buffer pool. This will enable buffer bundling feature of io_uri...
MutableBuffer buffer(void *data, size_t size) noexcept
Create a buffer object from various data sources.
Wrapper classes for liburing interfaces.
Runtime type for running the io_uring event loop.
Information about buffers consumed from a provided buffer queue.
uint16_t bid
Buffer ID of the first buffer consumed.
uint16_t num_buffers
Number of buffers consumed.
Internal utility classes and functions used by Condy.