50class BundledProvidedBufferQueue {
52 BundledProvidedBufferQueue(
Runtime *runtime, uint32_t capacity,
54 : runtime_(runtime), capacity_(std::bit_ceil(capacity)),
55 mask_(io_uring_buf_ring_mask(capacity_)), buf_lens_(capacity_, 0),
57 auto &bgid_pool = runtime_->bgid_pool_internal();
59 bgid_ = bgid_pool.allocate();
60 auto d = detail::defer([&]() { bgid_pool.recycle(bgid_); });
63 br_ = io_uring_setup_buf_ring(runtime_->ring_internal().ring(),
64 capacity_, bgid_, br_flags_, &err);
65 if (br_ ==
nullptr) [[unlikely]] {
66 throw detail::make_system_error(
"io_uring_setup_buf_ring", -err);
72 ~BundledProvidedBufferQueue() {
73 assert(br_ !=
nullptr);
74 [[maybe_unused]]
int r = io_uring_free_buf_ring(
75 runtime_->ring_internal().ring(), br_, capacity_, bgid_);
78 runtime_->bgid_pool_internal().recycle(bgid_);
83 CONDY_DELETE_COPY_MOVE(BundledProvidedBufferQueue);
89 size_t size()
const noexcept {
return size_; }
94 size_t capacity()
const noexcept {
return capacity_; }
106 template <BufferLike Buffer> uint16_t push(
const Buffer &
buffer) {
107 if (size_ >= capacity_) [[unlikely]] {
108 throw std::logic_error(
"Capacity exceeded");
111 uint16_t bid = br_->tail & mask_;
112 io_uring_buf_ring_add(br_,
buffer.data(),
buffer.size(), bid, mask_, 0);
113 buf_lens_[bid] =
buffer.size();
114 io_uring_buf_ring_advance(br_, 1);
121 uint16_t bgid()
const noexcept {
return bgid_; }
123 BufferInfo handle_finish(io_uring_cqe *cqe)
noexcept {
124 assert(cqe !=
nullptr);
125 int32_t res = cqe->res;
126 uint32_t flags = cqe->flags;
128 if (!(flags & IORING_CQE_F_BUFFER)) {
135 .bid =
static_cast<uint16_t
>(flags >> IORING_CQE_BUFFER_SHIFT),
139 bool is_incr =
false;
140#if !IO_URING_CHECK_VERSION(2, 8)
141 is_incr = br_flags_ & IOU_PBUF_RING_INC;
144 uint16_t idx = result.
bid;
147 uint16_t curr_bid = idx & mask_;
150 buf_len = std::min<uint32_t>(bytes, buf_lens_[curr_bid]);
151 buf_lens_[curr_bid] -= buf_len;
153 buf_len = std::exchange(buf_lens_[curr_bid], 0);
156 if (buf_lens_[curr_bid] == 0) {
169 io_uring_buf_ring *br_ =
nullptr;
174 std::vector<uint32_t> buf_lens_;
175 unsigned int br_flags_;
212 unsigned int flags = 0)
213 : BundledProvidedBufferQueue(&runtime, capacity, flags) {}
215 BufferInfo handle_finish(io_uring_cqe *cqe)
noexcept {
216 assert(cqe !=
nullptr);
217 auto result = BundledProvidedBufferQueue::handle_finish(cqe);
218 assert(result.num_buffers <= 1);
224class BundledProvidedBufferPool;
236 :
public detail::ManagedBuffer<detail::BundledProvidedBufferPool> {
238 using Base = detail::ManagedBuffer<detail::BundledProvidedBufferPool>;
244class BundledProvidedBufferPool {
246 BundledProvidedBufferPool(
Runtime *runtime,
void *buffer_data,
247 uint32_t num_buffers,
size_t buffer_size,
249 : runtime_(runtime), buffers_base_(static_cast<char *>(buffer_data)),
250 num_buffers_(std::bit_ceil(num_buffers)),
251 mask_(io_uring_buf_ring_mask(num_buffers_)),
252 buffer_size_(buffer_size), curr_buf_len_(buffer_size),
254 auto &bgid_pool = runtime_->bgid_pool_internal();
256 bgid_ = bgid_pool.allocate();
257 auto d = detail::defer([&]() { bgid_pool.recycle(bgid_); });
260 br_ = io_uring_setup_buf_ring(runtime_->ring_internal().ring(),
261 num_buffers_, bgid_, br_flags_, &err);
262 if (br_ ==
nullptr) [[unlikely]] {
263 throw detail::make_system_error(
"io_uring_setup_buf_ring", -err);
266 for (
size_t bid = 0; bid < num_buffers_; bid++) {
267 char *ptr = buffers_base_ + bid * buffer_size_;
268 io_uring_buf_ring_add(br_, ptr, buffer_size_, bid, mask_,
269 static_cast<int>(bid));
271 io_uring_buf_ring_advance(br_,
static_cast<int>(num_buffers_));
276 ~BundledProvidedBufferPool() {
277 assert(br_ !=
nullptr);
278 [[maybe_unused]]
int r = io_uring_free_buf_ring(
279 runtime_->ring_internal().ring(), br_, num_buffers_, bgid_);
282 runtime_->bgid_pool_internal().recycle(bgid_);
287 CONDY_DELETE_COPY_MOVE(BundledProvidedBufferPool);
293 size_t capacity() const noexcept {
return num_buffers_; }
298 size_t buffer_size() const noexcept {
return buffer_size_; }
301 uint16_t bgid() const noexcept {
return bgid_; }
303 std::vector<ProvidedBuffer> handle_finish(io_uring_cqe *cqe)
noexcept {
304 assert(cqe !=
nullptr);
305 int32_t res = cqe->res;
306 uint32_t flags = cqe->flags;
307 std::vector<ProvidedBuffer> buffers;
309 if (!(flags & IORING_CQE_F_BUFFER)) {
315 bool is_incr =
false;
316#if !IO_URING_CHECK_VERSION(2, 8)
317 is_incr = br_flags_ & IOU_PBUF_RING_INC;
322 auto *buf_ptr = curr_io_uring_buf_();
323 uint16_t bid = buf_ptr->bid;
325 char *data = get_buffer_(bid) + (buffer_size_ - curr_buf_len_);
328 buf_len = std::min<uint32_t>(bytes, curr_buf_len_);
329 curr_buf_len_ -= buf_len;
331 buf_len = std::exchange(curr_buf_len_, 0);
334 if (curr_buf_len_ == 0) {
335 buffers.emplace_back(data, buf_len,
this);
336 advance_io_uring_buf_();
337 curr_buf_len_ = buffer_size_;
339 buffers.emplace_back(data, buf_len,
nullptr);
346 void add_buffer_back(
void *ptr, [[maybe_unused]]
size_t size)
noexcept {
347 assert(size <= buffer_size_);
348 assert(ptr >= buffers_base_);
349 size_t offset =
static_cast<char *
>(ptr) - buffers_base_;
350 size_t bid = offset / buffer_size_;
351 assert(bid < num_buffers_);
352 char *buffer_ptr = buffers_base_ + bid * buffer_size_;
353 io_uring_buf_ring_add(br_, buffer_ptr, buffer_size_, bid, mask_, 0);
354 io_uring_buf_ring_advance(br_, 1);
358 char *get_buffer_(uint16_t bid)
const noexcept {
359 return buffers_base_ +
static_cast<size_t>(bid) * buffer_size_;
362 io_uring_buf *curr_io_uring_buf_() noexcept {
363 return &br_->bufs[br_head_ & mask_];
366 void advance_io_uring_buf_() noexcept { br_head_++; }
370 io_uring_buf_ring *br_ =
nullptr;
371 char *buffers_base_ =
nullptr;
372 uint32_t num_buffers_;
374 uint32_t buffer_size_;
375 uint32_t curr_buf_len_;
377 uint16_t br_head_ = 0;
378 unsigned int br_flags_;
405 unsigned int flags = 0)
407 buffer_size, flags) {}
419 size_t buffer_size,
unsigned int flags = 0)
420 : BundledProvidedBufferPool(
422 alloc_buffer_data_(std::bit_ceil(num_buffers) * buffer_size),
423 num_buffers, buffer_size, flags),
424 external_memory_(false) {}
434 size_t buffer_size,
unsigned int flags = 0)
436 num_buffers, buffer_size, flags) {}
448 uint32_t num_buffers,
size_t buffer_size,
449 unsigned int flags = 0)
450 : BundledProvidedBufferPool(&runtime, buffer_data, num_buffers,
452 external_memory_(true) {}
455 if (!external_memory_) {
456 munmap(buffers_base_, capacity() * buffer_size());
461 ProvidedBuffer handle_finish(io_uring_cqe *cqe)
noexcept {
462 assert(cqe !=
nullptr);
463 auto buffers = BundledProvidedBufferPool::handle_finish(cqe);
464 if (buffers.empty()) {
465 return ProvidedBuffer();
467 assert(buffers.size() == 1);
468 return std::move(buffers[0]);
472 static void *alloc_buffer_data_(
size_t buf_size) {
473 void *buf_data = mmap(
nullptr, buf_size, PROT_READ | PROT_WRITE,
474 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
475 if (buf_data == MAP_FAILED) [[unlikely]] {
476 throw detail::make_system_error(
"mmap");
481 bool external_memory_ =
false;
494 return static_cast<detail::BundledProvidedBufferPool &
>(
buffer);
504 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.