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)) {
94 detail::CancelRequest request(data);
95 detail::tsan_release(&request);
98 detail::encode_work(&request, detail::WorkType::Cancel));
99 if (curr_runtime !=
nullptr) {
101 curr_runtime->ring_.submit();
109 void pend_work_internal() noexcept {
110 assert(detail::Context::current().runtime() ==
this);
114 void resume_work_internal() noexcept {
115 assert(detail::Context::current().runtime() ==
this);
119 auto &bgid_pool_internal() noexcept {
return bgid_pool_; }
121 auto &ring_internal() noexcept {
return ring_; }
132 exit_allowed_.store(
true, std::memory_order_release);
145 detail::Context::current().init(
this);
146 auto d = detail::defer([]() { detail::Context::current().reset(); });
148 if (run_thread_id_ == std::thread::id()) {
149 int r = io_uring_enable_rings(ring_.ring());
151 throw detail::make_system_error(
"io_uring_enable_rings", -r);
154 run_thread_id_ = std::this_thread::get_id();
157 std::lock_guard<std::mutex> lock(mutex_);
158 flush_global_queue_();
159 ring_enabled_.store(
true, std::memory_order_relaxed);
162 if (!disable_register_ring_fd_) {
163 io_uring_register_ring_fd(ring_.ring());
165 }
else if (run_thread_id_ == std::this_thread::get_id()) {
168 throw std::runtime_error(
169 "Runtime::run() can only be called from the same thread");
175 if (tick_count_ >= event_interval_) {
180 if (
auto *work = local_queue_.pop_front()) {
185 if (pending_works_ == 0 &&
186 exit_allowed_.load(std::memory_order_acquire)) {
193 exit_allowed_.store(
false, std::memory_order_release);
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 !IO_URING_CHECK_VERSION(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 !IO_URING_CHECK_VERSION(2, 13)
272 if (options.enable_sqe_mixed_) {
273 params.flags |= IORING_SETUP_SQE_MIXED;
277#if !IO_URING_CHECK_VERSION(2, 13)
278 if (options.enable_cqe_mixed_) {
279 params.flags |= IORING_SETUP_CQE_MIXED;
285#if !IO_URING_CHECK_VERSION(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 !IO_URING_CHECK_VERSION(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));
345 void flush_global_queue_() noexcept {
346 local_queue_.push_back(std::move(global_queue_));
349 static void prep_msg_ring_(
int ring_fd, io_uring_sqe *sqe,
350 uintptr_t data)
noexcept {
351 io_uring_prep_msg_ring(sqe, ring_fd, 0, data, 0);
352 io_uring_sqe_set_data64(
353 sqe, detail::encode_work(
nullptr, detail::WorkType::Schedule));
356 static void prep_cancel_(io_uring_sqe *sqe, uintptr_t data)
noexcept {
357 io_uring_prep_cancel64(sqe, data, 0);
358 io_uring_sqe_set_data64(
359 sqe, detail::encode_work(
nullptr, detail::WorkType::Ignore));
360 io_uring_sqe_set_flags(sqe, IOSQE_CQE_SKIP_SUCCESS);
363 void flush_ring_() noexcept {
364 auto r = ring_.reap_completions(
365 [
this](io_uring_cqe *cqe) { process_cqe_(cqe); });
367 detail::panic_on(std::format(
"io_uring_peek_cqe: {}",
368 std::strerror(
static_cast<int>(-r))));
372 void flush_ring_wait_() noexcept {
373 auto r = ring_.reap_completions_wait(
374 [
this](io_uring_cqe *cqe) { process_cqe_(cqe); });
376 detail::panic_on(std::format(
"io_uring_submit_and_wait: {}",
377 std::strerror(
static_cast<int>(-r))));
381 void process_cqe_(io_uring_cqe *cqe)
noexcept {
382 auto [data, type] = detail::decode_work(io_uring_cqe_get_data64(cqe));
384 if (type == detail::WorkType::Ignore) {
386 assert(cqe->res != -EINVAL);
387 }
else if (type == detail::WorkType::Schedule) {
388 if (data ==
nullptr) {
390 detail::panic_on(std::format(
"io_uring_prep_msg_ring: {}",
391 std::strerror(-cqe->res)));
393 resume_work_internal();
395 auto *work =
static_cast<detail::WorkInvoker *
>(data);
396 detail::tsan_acquire(work);
399 }
else if (type == detail::WorkType::Cancel) {
400 detail::CancelRequest *request =
401 static_cast<detail::CancelRequest *
>(data);
402 detail::tsan_acquire(request);
403 io_uring_sqe *sqe = ring_.get_sqe();
404 prep_cancel_(sqe, request->data());
406 }
else if (type == detail::WorkType::Common) {
407 auto *handle =
static_cast<detail::OpFinishHandleBase *
>(data);
408 auto op_finish = handle->handle(cqe);
410 resume_work_internal();
413 detail::unreachable();
418 using WorkListQueue =
419 detail::IntrusiveSingleList<detail::WorkInvoker,
420 &detail::WorkInvoker::work_queue_entry_>;
424 WorkListQueue global_queue_;
425 std::atomic_bool exit_allowed_ =
false;
426 std::atomic_bool ring_enabled_ =
false;
429 WorkListQueue local_queue_;
431 size_t pending_works_ = 0;
432 size_t tick_count_ = 0;
433 std::thread::id run_thread_id_;
436 size_t event_interval_ = 61;
437 bool disable_register_ring_fd_ =
false;
440 BufferTable buffer_table_;
441 RingSettings settings_;
442 detail::IdPool<uint16_t> bgid_pool_;