io_uring.cpp (2463B)
1 #include <liburing.h> 2 #include <iostream> 3 #include <coroutine> 4 #include <span> 5 6 struct awaitable; 7 8 struct uring 9 { 10 uring(unsigned entries, unsigned flags) 11 { 12 if (io_uring_queue_init(entries, &ring, flags)) 13 throw std::system_error(errno, std::system_category()); 14 } 15 16 ~uring(void) 17 { 18 io_uring_queue_exit(&ring); 19 } 20 21 uring(const uring&) = delete; 22 uring& operator =(const uring&) = delete; 23 24 io_uring_sqe* get_sqe(void) 25 { 26 return io_uring_get_sqe(&ring); 27 } 28 29 int submit(void) 30 { 31 return io_uring_submit(&ring); 32 } 33 34 bool process_completion(void); 35 awaitable read(int, std::span<char>, unsigned); 36 awaitable write(int, std::span<char const>, unsigned); 37 unsigned n = 0; 38 private: 39 io_uring ring; 40 }; 41 42 struct task 43 { 44 struct promise_type 45 { 46 int res; 47 48 void get_return_object(void) const noexcept 49 {} 50 auto initial_suspend(void) const noexcept 51 { 52 return std::suspend_never{}; 53 } 54 auto final_suspend(void) const noexcept 55 { 56 return std::suspend_always{}; 57 } 58 void unhandled_exception(void) const noexcept 59 { 60 std::terminate(); 61 } 62 void return_void(void) const noexcept 63 {} 64 }; 65 }; 66 67 struct awaitable 68 { 69 uring& u; 70 io_uring_sqe& sqe; 71 task::promise_type* p; 72 73 bool await_ready(void) const noexcept 74 { 75 return false; 76 } 77 void await_suspend(std::coroutine_handle<task::promise_type> h) 78 { 79 p = &h.promise(); 80 io_uring_sqe_set_data(&sqe, h.address()); 81 u.submit(); 82 u.n++; 83 } 84 int await_resume() 85 { 86 return p->res; 87 } 88 }; 89 90 bool uring::process_completion(void) 91 { 92 if (!n) 93 return false; 94 --n; 95 96 io_uring_cqe* cqe; 97 if (io_uring_wait_cqe(&ring, &cqe)) 98 throw std::system_error(errno, std::system_category()); 99 100 auto addr = io_uring_cqe_get_data(cqe); 101 auto h = std::coroutine_handle<task::promise_type>::from_address(addr); 102 h.promise().res = cqe->res; 103 h.resume(); 104 105 return true; 106 } 107 108 awaitable uring::read(int fd, std::span<char> buf, unsigned flags) 109 { 110 auto sqe = get_sqe(); 111 io_uring_prep_read(sqe, fd, buf.data(), buf.size(), flags); 112 return {*this, *sqe, {}}; 113 } 114 awaitable uring::write(int fd, std::span<char const> buf, unsigned flags) 115 { 116 auto sqe = get_sqe(); 117 io_uring_prep_write(sqe, fd, buf.data(), buf.size(), flags); 118 return {*this, *sqe, {}}; 119 } 120 121 task read_routine(uring& u) 122 { 123 char b[128]; 124 std::span<char> buf(b); 125 int n = co_await u.read(0, buf, 0); 126 if (n > 0) 127 co_await u.write(1, {b, (std::size_t)n}, 0); 128 co_return; 129 } 130 131 int main() 132 { 133 uring r(128, 0); 134 135 read_routine(r); 136 137 while (r.process_completion()) 138 {} 139 140 return 0; 141 }