43 : ring_(create_ring_(options)),
44 event_interval_(options.event_interval_),
45 disable_register_ring_fd_(options.disable_register_ring_fd_),
46 fd_table_(*ring_.ring()), buffer_table_(*ring_.ring()),
47 settings_(*ring_.ring()) {}
49 CONDY_DELETE_COPY_MOVE(
Runtime);
52 void schedule_internal(detail::WorkInvoker *work)
noexcept {
53 auto *curr_runtime = detail::Context::current().runtime();
54 if (curr_runtime ==
this) {
55 local_queue_.push_back(work);
59 if (ring_enabled_.load(std::memory_order_relaxed)) {
62 detail::tsan_release(work);
65 detail::encode_work(work, detail::WorkType::Schedule));
69 std::unique_lock<std::mutex> lock(mutex_);
70 if (ring_enabled_.load(std::memory_order_relaxed)) {
72 detail::tsan_release(work);
75 detail::encode_work(work, detail::WorkType::Schedule));
77 global_queue_.push_back(work);
82 void cancel_internal(uintptr_t data)
noexcept {
83 auto *curr_runtime = detail::Context::current().runtime();
84 if (curr_runtime ==
this) {
85 io_uring_sqe *sqe = ring_.get_sqe();
86 prep_cancel_(sqe, data);
90 if (!ring_enabled_.load(std::memory_order_relaxed)) {
95 schedule_msg_ring_(curr_runtime, detail::encode_work_ptr(
96 data, detail::WorkType::Cancel));
97 if (curr_runtime !=
nullptr) {
99 curr_runtime->ring_.submit();
103 void pend_work_internal() noexcept {
104 assert(detail::Context::current().runtime() ==
this);
108 void resume_work_internal() noexcept {
109 assert(detail::Context::current().runtime() ==
this);
113 auto &bgid_pool_internal() noexcept {
return bgid_pool_; }
115 auto &ring_internal() noexcept {
return ring_; }
126 exit_allowed_.store(
true, std::memory_order_release);
139 detail::Context::current().init(
this);
140 auto d = detail::defer([] { detail::Context::current().reset(); });
142 if (run_thread_id_ == std::thread::id()) {
143 int r = io_uring_enable_rings(ring_.ring());
145 throw detail::make_system_error(
"io_uring_enable_rings", -r);
148 run_thread_id_ = std::this_thread::get_id();
151 std::lock_guard<std::mutex> lock(mutex_);
152 flush_global_queue_();
153 ring_enabled_.store(
true, std::memory_order_relaxed);
155 }
else if (run_thread_id_ == std::this_thread::get_id()) {
158 throw std::runtime_error(
159 "Runtime::run() can only be called from the same thread");
162 if (!disable_register_ring_fd_) {
163 io_uring_register_ring_fd(ring_.ring());
165 auto d2 = detail::defer([&] {
166 if (!disable_register_ring_fd_) {
167 io_uring_unregister_ring_fd(ring_.ring());
171 auto d3 = detail::defer([&] {
173 exit_allowed_.store(
false, std::memory_order_release);
178 if (tick_count_ >= event_interval_) {
183 if (
auto *work = local_queue_.pop_front()) {
188 if (pending_works_ == 0 &&
189 exit_allowed_.load(std::memory_order_acquire)) {
216 io_uring_params params;
217 std::memset(¶ms, 0,
sizeof(params));
219 params.flags |= IORING_SETUP_CLAMP;
220 params.flags |= IORING_SETUP_SINGLE_ISSUER;
221 params.flags |= IORING_SETUP_SUBMIT_ALL;
222 params.flags |= IORING_SETUP_R_DISABLED;
224 size_t ring_entries = options.sq_size_;
225 if (options.cq_size_ != 0) {
226 params.flags |= IORING_SETUP_CQSIZE;
227 params.cq_entries = options.cq_size_;
230 if (options.enable_iopoll_) {
231 params.flags |= IORING_SETUP_IOPOLL;
232#if CONDY_URING_VERSION_GE(2, 9)
233 if (options.enable_hybrid_iopoll_) {
234 params.flags |= IORING_SETUP_HYBRID_IOPOLL;
239 if (options.enable_sqpoll_) {
240 params.flags |= IORING_SETUP_SQPOLL;
241 params.sq_thread_idle = options.sqpoll_idle_time_ms_;
242 if (options.sqpoll_thread_cpu_.has_value()) {
243 params.flags |= IORING_SETUP_SQ_AFF;
244 params.sq_thread_cpu = *options.sqpoll_thread_cpu_;
248 if (options.attach_wq_target_ !=
nullptr) {
249 params.flags |= IORING_SETUP_ATTACH_WQ;
250 params.wq_fd = options.attach_wq_target_->ring_.ring()->ring_fd;
253 if (options.enable_defer_taskrun_) {
254 params.flags |= IORING_SETUP_DEFER_TASKRUN;
255 params.flags |= IORING_SETUP_TASKRUN_FLAG;
258 if (options.enable_coop_taskrun_) {
259 params.flags |= IORING_SETUP_COOP_TASKRUN;
260 params.flags |= IORING_SETUP_TASKRUN_FLAG;
263 if (options.enable_sqe128_) {
264 params.flags |= IORING_SETUP_SQE128;
267 if (options.enable_cqe32_) {
268 params.flags |= IORING_SETUP_CQE32;
271#if CONDY_URING_VERSION_GE(2, 13)
272 if (options.enable_sqe_mixed_) {
273 params.flags |= IORING_SETUP_SQE_MIXED;
277#if CONDY_URING_VERSION_GE(2, 13)
278 if (options.enable_cqe_mixed_) {
279 params.flags |= IORING_SETUP_CQE_MIXED;
285#if CONDY_URING_VERSION_GE(2, 5)
286 if (options.enable_no_mmap_) {
287 params.flags |= IORING_SETUP_NO_MMAP;
288 buf = options.no_mmap_buf_;
289 buf_size = options.no_mmap_buf_size_;
293#if CONDY_URING_VERSION_GE(2, 14)
294 if (options.enable_sq_rewind_) {
295 params.flags |= IORING_SETUP_SQ_REWIND;
299 size_t submit_batch = options.submit_batch_;
300 if (submit_batch == 0) {
301 if (options.enable_sqpoll_) {
302 submit_batch = std::min<size_t>(32, ring_entries);
304 submit_batch = std::numeric_limits<size_t>::max();
307 assert(submit_batch > 0);
309 return detail::Ring(ring_entries, ¶ms, buf, buf_size, submit_batch);
312 void schedule_msg_ring_(
Runtime *curr_runtime, uintptr_t data)
noexcept {
313 int ring_fd = this->ring_.ring()->ring_fd;
314 if (curr_runtime !=
nullptr) {
315 io_uring_sqe *sqe = curr_runtime->ring_.get_sqe();
316 prep_msg_ring_(ring_fd, sqe, data);
317 curr_runtime->pend_work_internal();
319 io_uring_sqe sqe = {};
320 prep_msg_ring_(ring_fd, &sqe, data);
321 int r = detail::sync_msg_ring(&sqe);
324 std::format(
"sync_msg_ring: {}", std::strerror(-r)));
330 void wakeup_() noexcept {
331 auto *curr_runtime = detail::Context::current().runtime();
332 if (curr_runtime ==
this) {
336 if (!ring_enabled_.load(std::memory_order_relaxed)) {
342 detail::encode_work(
nullptr, detail::WorkType::Ignore));
343 if (curr_runtime !=
nullptr) {
346 curr_runtime->ring_.submit();
350 void flush_global_queue_() noexcept {
351 local_queue_.push_back(std::move(global_queue_));
354 static void prep_msg_ring_(
int ring_fd, io_uring_sqe *sqe,
355 uintptr_t data)
noexcept {
356 io_uring_prep_msg_ring(sqe, ring_fd, 0, data, 0);
357 io_uring_sqe_set_data64(
358 sqe, detail::encode_work(
nullptr, detail::WorkType::Schedule));
361 static void prep_cancel_(io_uring_sqe *sqe, uintptr_t data)
noexcept {
362 io_uring_prep_cancel64(sqe, data, 0);
363 io_uring_sqe_set_data64(
364 sqe, detail::encode_work(
nullptr, detail::WorkType::Ignore));
365 io_uring_sqe_set_flags(sqe, IOSQE_CQE_SKIP_SUCCESS);
368 void flush_ring_() noexcept {
369 auto r = ring_.reap_completions(
370 [
this](io_uring_cqe *cqe) { process_cqe_(cqe); });
372 detail::panic_on(std::format(
"io_uring_peek_cqe: {}",
373 std::strerror(
static_cast<int>(-r))));
377 void flush_ring_wait_() noexcept {
378 auto r = ring_.reap_completions_wait(
379 [
this](io_uring_cqe *cqe) { process_cqe_(cqe); });
381 detail::panic_on(std::format(
"io_uring_submit_and_wait: {}",
382 std::strerror(
static_cast<int>(-r))));
386 void process_cqe_(io_uring_cqe *cqe)
noexcept {
387 auto [data, type] = detail::decode_work(io_uring_cqe_get_data64(cqe));
389 if (type == detail::WorkType::Ignore) {
391 assert(cqe->res != -EINVAL);
392 }
else if (type == detail::WorkType::Schedule) {
393 if (data ==
nullptr) {
395 detail::panic_on(std::format(
"io_uring_prep_msg_ring: {}",
396 std::strerror(-cqe->res)));
398 resume_work_internal();
400 auto *work =
static_cast<detail::WorkInvoker *
>(data);
401 detail::tsan_acquire(work);
404 }
else if (type == detail::WorkType::Cancel) {
405 io_uring_sqe *sqe = ring_.get_sqe();
406 prep_cancel_(sqe,
reinterpret_cast<uintptr_t
>(data));
407 }
else if (type == detail::WorkType::Common) {
408 auto *handle =
static_cast<detail::OpFinishHandleBase *
>(data);
409 auto op_finish = handle->handle(cqe);
411 resume_work_internal();
414 detail::unreachable();
419 using WorkListQueue =
420 detail::IntrusiveSingleList<detail::WorkInvoker,
421 &detail::WorkInvoker::work_queue_entry_>;
425 WorkListQueue global_queue_;
426 std::atomic_bool exit_allowed_ =
false;
427 std::atomic_bool ring_enabled_ =
false;
430 WorkListQueue local_queue_;
432 size_t pending_works_ = 0;
433 size_t tick_count_ = 0;
434 std::thread::id run_thread_id_;
437 size_t event_interval_ = 61;
438 bool disable_register_ring_fd_ =
false;
441 BufferTable buffer_table_;
442 RingSettings settings_;
443 detail::IdPool<uint16_t> bgid_pool_;