8#if defined(__linux__) && __has_include(<linux/io_uring.h>)
9 #include <linux/io_uring.h>
11 #include <sys/syscall.h>
17#if defined(__linux__) && __has_include(<linux/io_uring.h>) && defined(__NR_io_uring_setup)
23 io_ring(io_ring
const&) =
delete;
24 io_ring& operator=(io_ring
const&) =
delete;
26 if (sqes_ !=
nullptr) ::munmap(sqes_, sqes_bytes_);
27 if (cq_ !=
nullptr) ::munmap(cq_, cq_bytes_);
28 if (sq_ !=
nullptr) ::munmap(sq_, sq_bytes_);
34 [[nodiscard]]
static std::unique_ptr<io_ring>
make(
unsigned depth) {
35 io_uring_params params{};
36 int const fd =
static_cast<int>(::syscall(__NR_io_uring_setup, depth, ¶ms));
37 if (fd < 0)
return nullptr;
38 std::unique_ptr<io_ring> ring(
new io_ring(fd));
39 if (!ring->map(params))
return nullptr;
45 void write(
int fd,
void const* data, uint32_t bytes, uint64_t offset, uint64_t user_data) {
46 auto const tail = std::atomic_ref(*sq_tail_).load(std::memory_order_relaxed);
47 auto const index = tail & sq_mask_;
48 auto& sqe = sqes_[index];
50 sqe.opcode = IORING_OP_WRITE;
52 sqe.addr =
reinterpret_cast<uint64_t
>(data);
55 sqe.user_data = user_data;
56 sq_array_[index] = index;
57 std::atomic_ref(*sq_tail_).store(tail + 1, std::memory_order_release);
64 template <
typename OnComplete>
65 [[nodiscard]]
int submit_and_wait(
unsigned wait_for, OnComplete&& on_complete) {
68 seen += reap(on_complete);
69 if (queued_ == 0 && seen >= wait_for)
return 0;
70 auto const waiting = seen >= wait_for ? 0U : 1U;
71 auto const entered = ::syscall(
72 __NR_io_uring_enter, fd_, queued_, waiting, IORING_ENTER_GETEVENTS,
nullptr, 0
76 if (errno == EINTR || errno == EAGAIN || errno == EBUSY)
continue;
79 queued_ -=
static_cast<unsigned>(entered);
84 explicit io_ring(
int fd) : fd_(fd) {}
86 [[nodiscard]]
bool map(io_uring_params
const& params) {
87 auto const region = [&](
size_t bytes, off_t offset) ->
void* {
88 void*
const mapped = ::mmap(
89 nullptr, bytes, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd_, offset
91 return mapped == MAP_FAILED ? nullptr : mapped;
93 sq_bytes_ = params.sq_off.array + params.sq_entries *
sizeof(uint32_t);
94 cq_bytes_ = params.cq_off.cqes + params.cq_entries *
sizeof(io_uring_cqe);
95 sqes_bytes_ = params.sq_entries *
sizeof(io_uring_sqe);
96 sq_ =
static_cast<char*
>(region(sq_bytes_, IORING_OFF_SQ_RING));
97 cq_ =
static_cast<char*
>(region(cq_bytes_, IORING_OFF_CQ_RING));
98 sqes_ =
static_cast<io_uring_sqe*
>(region(sqes_bytes_, IORING_OFF_SQES));
99 if (sq_ ==
nullptr || cq_ ==
nullptr || sqes_ ==
nullptr)
return false;
100 sq_tail_ =
reinterpret_cast<uint32_t*
>(sq_ + params.sq_off.tail);
101 sq_mask_ = *
reinterpret_cast<uint32_t*
>(sq_ + params.sq_off.ring_mask);
102 sq_array_ =
reinterpret_cast<uint32_t*
>(sq_ + params.sq_off.array);
103 cq_head_ =
reinterpret_cast<uint32_t*
>(cq_ + params.cq_off.head);
104 cq_tail_ =
reinterpret_cast<uint32_t*
>(cq_ + params.cq_off.tail);
105 cq_mask_ = *
reinterpret_cast<uint32_t*
>(cq_ + params.cq_off.ring_mask);
106 cqes_ =
reinterpret_cast<io_uring_cqe*
>(cq_ + params.cq_off.cqes);
110 template <
typename OnComplete>
111 unsigned reap(OnComplete& on_complete) {
112 auto head = std::atomic_ref(*cq_head_).load(std::memory_order_relaxed);
113 auto const tail = std::atomic_ref(*cq_tail_).load(std::memory_order_acquire);
114 unsigned const count = tail - head;
115 for (; head != tail; ++head) {
116 auto const& cqe = cqes_[head & cq_mask_];
117 on_complete(cqe.user_data, cqe.res);
119 std::atomic_ref(*cq_head_).store(head, std::memory_order_release);
126 io_uring_sqe* sqes_ =
nullptr;
127 size_t sq_bytes_ = 0, cq_bytes_ = 0, sqes_bytes_ = 0;
128 uint32_t* sq_tail_ =
nullptr;
129 uint32_t* sq_array_ =
nullptr;
130 uint32_t* cq_head_ =
nullptr;
131 uint32_t* cq_tail_ =
nullptr;
132 io_uring_cqe* cqes_ =
nullptr;
133 uint32_t sq_mask_ = 0, cq_mask_ = 0;
134 unsigned queued_ = 0;
140 [[nodiscard]]
static std::unique_ptr<io_ring>
make(
unsigned) {
143 void write(
int,
void const*, uint32_t, uint64_t, uint64_t) {}
144 template <
typename OnComplete>
Stand-in where the build has no io_uring headers: never available.
void write(int, void const *, uint32_t, uint64_t, uint64_t)
int submit_and_wait(unsigned, OnComplete &&)
static std::unique_ptr< io_ring > make(unsigned)