From 55d270a8c20054bffbaed93b83b84251c2a1d0a7 Mon Sep 17 00:00:00 2001 From: Dominik Thalhammer Date: Tue, 7 Feb 2023 12:37:03 +0100 Subject: [PATCH 1/2] Improve backwards compatibility Allows compilation with g++11 on ubuntu 22.04. Uses try_run to detect the highest supported op. closes #2 --- CMakeLists.txt | 9 +- cmake/detect-max-op.c | 3 + examples/echo-client.cpp | 6 +- examples/echo-server.cpp | 6 +- include/asyncpp/uring/capability_set.h | 4 +- include/asyncpp/uring/index_set.h | 2 + include/asyncpp/uring/io_service.h | 146 ++++++++++++++++++------- test/io_service.cpp | 23 ++-- 8 files changed, 143 insertions(+), 56 deletions(-) create mode 100644 cmake/detect-max-op.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a3549b9..f549c22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,10 +9,10 @@ if(HUNTER_ENABLED) # Workaround hunter hideing system libs set(HUNTER_LIBPATH $ENV{PKG_CONFIG_LIBDIR}) unset(ENV{PKG_CONFIG_LIBDIR}) - pkg_search_module(URING REQUIRED NO_CMAKE_PATH liburing>=2.3 uring>=2.3) + pkg_search_module(URING REQUIRED NO_CMAKE_PATH liburing uring) set(ENV{PKG_CONFIG_LIBDIR} ${HUNTER_LIBPATH}) else() - pkg_search_module(URING REQUIRED NO_CMAKE_PATH liburing>=2.3 uring>=2.3) + pkg_search_module(URING REQUIRED NO_CMAKE_PATH liburing uring) endif() option(ASYNCPP_BUILD_TEST "Enable test builds" ON) @@ -26,12 +26,17 @@ else() include(cmake/Fetch_asyncpp.cmake) endif() +try_run(OP_LAST OP_LAST_COMPILE_RESULT SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/detect-max-op.c) + add_library(asyncpp_uring INTERFACE) target_link_libraries(asyncpp_uring INTERFACE asyncpp Threads::Threads ${URING_LIBRARIES}) target_include_directories(asyncpp_uring INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) target_compile_features(asyncpp_uring INTERFACE cxx_std_20) +if(OP_LAST_COMPILE_RESULT) + target_compile_definitions(asyncpp_uring INTERFACE ASYNCPP_URING_OP_LAST=${OP_LAST}) +endif() if(ASYNCPP_BUILD_TEST) enable_testing() diff --git a/cmake/detect-max-op.c b/cmake/detect-max-op.c new file mode 100644 index 0000000..a77fd84 --- /dev/null +++ b/cmake/detect-max-op.c @@ -0,0 +1,3 @@ +#include + +int main() { return IORING_OP_LAST - 1; } \ No newline at end of file diff --git a/examples/echo-client.cpp b/examples/echo-client.cpp index 0642406..cd61988 100644 --- a/examples/echo-client.cpp +++ b/examples/echo-client.cpp @@ -19,10 +19,14 @@ int main() { io_service io{p}; buffer_group buffers{io, 32 * 1024, 32}; io.launch([](io_service& io, buffer_group& buffers) -> task<> { - // Try create a socket and fallback to sync if unsupported + // Try create a socket and fallback to sync if unsupported +#if ASYNCPP_URING_OP_LAST >= 45 int sock = io.has_capability(IORING_OP_SOCKET) // ? co_await io.socket(AF_INET, SOCK_STREAM, 0, 0) // : socket(AF_INET, SOCK_STREAM, 0); +#else + int sock = socket(AF_INET, SOCK_STREAM, 0); +#endif if (sock < 0) { std::cout << "failed to create socket: " << strerror(-sock) << std::endl; co_return; diff --git a/examples/echo-server.cpp b/examples/echo-server.cpp index 97c601f..ed5cd20 100644 --- a/examples/echo-server.cpp +++ b/examples/echo-server.cpp @@ -52,10 +52,14 @@ int main() { }(io, exit)); buffer_group buffers{io, 32 * 1024, 32}; io.launch([](io_service& io, buffer_group& buffers, std::stop_source& exit) -> task<> { - // Try create a socket and fallback to sync if unsupported + // Try create a socket and fallback to sync if unsupported +#if ASYNCPP_URING_OP_LAST >= 45 int sock = io.has_capability(IORING_OP_SOCKET) // ? co_await io.socket(AF_INET, SOCK_STREAM, 0, 0) // : socket(AF_INET, SOCK_STREAM, 0); +#else + int sock = socket(AF_INET, SOCK_STREAM, 0); +#endif if (sock < 0) { std::cout << "failed to create socket: " << strerror(-sock) << std::endl; co_return; diff --git a/include/asyncpp/uring/capability_set.h b/include/asyncpp/uring/capability_set.h index 98fe44b..7d60bcd 100644 --- a/include/asyncpp/uring/capability_set.h +++ b/include/asyncpp/uring/capability_set.h @@ -12,7 +12,7 @@ namespace asyncpp::uring { public: constexpr static struct { - } no_parse_tag; + } no_parse_tag{}; capability_set(struct io_uring* ring) : m_supported{} { this->parse(ring); } capability_set() : m_supported{} { this->parse(nullptr); } constexpr capability_set(decltype(no_parse_tag)) noexcept : m_supported{} {} @@ -115,6 +115,8 @@ namespace asyncpp::uring { "GETXATTR", "SOCKET", "URING_CMD", + "SEND_ZC", + "SENDMSG_ZC", }; if (index >= (sizeof(names) / sizeof(names[0]))) return ""; diff --git a/include/asyncpp/uring/index_set.h b/include/asyncpp/uring/index_set.h index f435c10..eaff041 100644 --- a/include/asyncpp/uring/index_set.h +++ b/include/asyncpp/uring/index_set.h @@ -65,6 +65,8 @@ namespace asyncpp::uring { ~bit_storage() noexcept { clear(); } bit_storage(const bit_storage& other) : m_allocator{other.m_allocator} { + memset(&m_data, 0, sizeof(m_data)); + m_data.inplace.size = 0x80; resize_bits(other.bit_size()); memcpy(data(), other.data(), std::min(byte_size(), other.byte_size())); } diff --git a/include/asyncpp/uring/io_service.h b/include/asyncpp/uring/io_service.h index e1ce3f9..870e7ed 100644 --- a/include/asyncpp/uring/io_service.h +++ b/include/asyncpp/uring/io_service.h @@ -19,22 +19,28 @@ #include +#ifndef ASYNCPP_URING_OP_LAST +#define ASYNCPP_URING_OP_LAST 48 +#endif + namespace asyncpp::uring { struct io_service; - class buffer_group; - class buffer_handle; enum class ioseq_flag { none = 0, - fixed_file = IOSQE_FIXED_FILE, - io_drain = IOSQE_IO_DRAIN, - io_link = IOSQE_IO_LINK, - io_hardlink = IOSQE_IO_HARDLINK, - async = IOSQE_ASYNC, - buffer_select = IOSQE_BUFFER_SELECT, - cqe_skip_success = IOSQE_CQE_SKIP_SUCCESS, + fixed_file = 1, + io_drain = 2, + io_link = 4, + io_hardlink = 8, + async = 16, + buffer_select = 32, + cqe_skip_success = 64, }; +#if ASYNCPP_URING_OP_LAST >= 32 + class buffer_group; + class buffer_handle; + class buffer_group { io_service& m_service; uint16_t m_group_index; @@ -49,9 +55,9 @@ namespace asyncpp::uring { buffer_group& operator=(const buffer_group&) = delete; ~buffer_group() noexcept; - uint16_t group_index() const noexcept { return m_group_index; } - size_t block_size() const noexcept { return m_block_size; } - size_t block_count() const noexcept { return m_block_count; } + constexpr uint16_t group_index() const noexcept { return m_group_index; } + constexpr size_t block_size() const noexcept { return m_block_size; } + constexpr size_t block_count() const noexcept { return m_block_count; } void* ref_buffer(uint16_t buf) noexcept; void unref_buffer(uint16_t buf) noexcept; @@ -74,13 +80,15 @@ namespace asyncpp::uring { constexpr size_t byte_size() const noexcept { return m_group->block_size(); } constexpr void* get() const noexcept { return m_pointer; } template - requires std::is_trivial_v constexpr std::span typed() const noexcept { + requires std::is_trivial_v + constexpr std::span typed() const noexcept { return std::span{static_cast(get()), byte_size() / sizeof(T)}; } void* release() noexcept; void reset() noexcept; }; +#endif template struct sqe_awaitable; @@ -141,6 +149,7 @@ namespace asyncpp::uring { constexpr int await_resume() const noexcept; }; +#if ASYNCPP_URING_OP_LAST >= 32 template<> struct sqe_awaitable { private: @@ -204,6 +213,7 @@ namespace asyncpp::uring { } std::pair await_resume() const noexcept; }; +#endif class uring { public: @@ -212,44 +222,45 @@ namespace asyncpp::uring { io_uring_params m_params; public: - params() : m_sqe_size{128}, m_params{} {} - params& sqe_size(unsigned int size) noexcept { + constexpr params() noexcept : m_sqe_size{128}, m_params{} {} + constexpr params& sqe_size(unsigned int size) noexcept { m_sqe_size = size; return *this; } - params& cqe_size(unsigned int size) noexcept { + constexpr params& cqe_size(unsigned int size) noexcept { m_params.cq_entries = size; return enable_flags(IORING_SETUP_CQSIZE); } - params& enable_flags(uint32_t flags) noexcept { + constexpr params& enable_flags(uint32_t flags) noexcept { m_params.flags |= flags; return *this; } - params& disable_flags(uint32_t flags) noexcept { + constexpr params& disable_flags(uint32_t flags) noexcept { m_params.flags &= ~flags; return *this; } - params& attach_wq(uint32_t wq_fd) noexcept { + constexpr params& attach_wq(uint32_t wq_fd) noexcept { m_params.wq_fd = wq_fd; return enable_flags(IORING_SETUP_ATTACH_WQ); } - params& sq_affinity(uint32_t cpu) noexcept { + constexpr params& sq_affinity(uint32_t cpu) noexcept { m_params.sq_thread_cpu = cpu; return enable_flags(IORING_SETUP_SQ_AFF); } - params& sq_poll(std::chrono::milliseconds timeout) noexcept { + constexpr params& sq_poll(std::chrono::milliseconds timeout) noexcept { m_params.sq_thread_idle = timeout.count(); return enable_flags(IORING_SETUP_SQPOLL); } - io_uring_params& raw() noexcept { return m_params; } - const io_uring_params& raw() const noexcept { return m_params; } - unsigned int sqe_size() const noexcept { return m_sqe_size; } + constexpr io_uring_params& raw() noexcept { return m_params; } + constexpr const io_uring_params& raw() const noexcept { return m_params; } + constexpr unsigned int sqe_size() const noexcept { return m_sqe_size; } }; uring(const params& p = params{}) : m_ring{}, m_caps{capability_set::no_parse_tag}, m_params{p} { auto res = io_uring_queue_init_params(m_params.sqe_size(), &m_ring, &m_params.raw()); + //auto res = io_uring_queue_init(m_params.sqe_size(), &m_ring, 0); if (res < 0) throw std::system_error(-res, std::system_category()); try { @@ -295,15 +306,18 @@ namespace asyncpp::uring { [[nodiscard]] io_uring_sqe* get_sqe() noexcept { auto sqe = io_uring_get_sqe(&m_ring); if (sqe == nullptr) [[unlikely]] { - // Submit queue is full, submit all existing ones and retry - io_uring_submit(&m_ring); - sqe = io_uring_get_sqe(&m_ring); - } - if (sqe == nullptr) [[unlikely]] { - // If we reach this something is really really wrong.... - std::cerr << "========== Failed to get a sqe after submit ==========" << std::endl; + // Submit queue is full, submit all existing ones and retry + if (int res = io_uring_submit(&m_ring); res < 0) { + std::cerr << "========== Failed to submit sqe batch ==========\n" << strerror(-res) << std::endl; std::terminate(); } + sqe = io_uring_get_sqe(&m_ring); + } + if (sqe == nullptr) [[unlikely]] { + // If we reach this something is really really wrong.... + std::cerr << "========== Failed to get a sqe after submit ==========" << std::endl; + std::terminate(); + } // Zero out the sqe to avoid dangling user_data memset(sqe, 0, sizeof(*sqe)); return sqe; @@ -380,7 +394,12 @@ namespace asyncpp::uring { io_service(const params& params = io_service::params{}) // : uring{params}, // m_dispatched_wake{eventfd(0, 0)}, // - skip_success_flags{has_feature(IORING_FEAT_CQE_SKIP) ? ioseq_flag::cqe_skip_success : ioseq_flag::none} { +#ifdef IORING_FEAT_CQE_SKIP + skip_success_flags{has_feature(IORING_FEAT_CQE_SKIP) ? ioseq_flag::cqe_skip_success : ioseq_flag::none} +#else + skip_success_flags{ioseq_flag::none} +#endif + { set_null_cqe_handler([](const io_uring_cqe* cqe) { if (cqe->res < 0) std::cerr << "Error on null sqe: " << cqe->res << " " << strerror(-cqe->res) << std::endl; }); @@ -389,7 +408,7 @@ namespace asyncpp::uring { const auto sqe = get_sqe(); memset(sqe, 0, sizeof(*sqe)); io_uring_prep_read(sqe, m_dispatched_wake, &m_dispatched_wake_value, sizeof(m_dispatched_wake_value), 0); - io_uring_sqe_set_data64(sqe, 1); + io_uring_sqe_set_data(sqe, reinterpret_cast(1)); io_uring_submit(raw_handle()); } } @@ -432,7 +451,7 @@ namespace asyncpp::uring { m_async_scope.launch(std::move(awaitable), std::move(allocator)); } - const struct io_uring_cqe* current_cqe() const noexcept { return m_current_cqe; } + constexpr const struct io_uring_cqe* current_cqe() const noexcept { return m_current_cqe; } uint16_t allocate_buffer_group_index() { return m_buffer_idx.allocate_index(); } void return_buffer_group_index(uint16_t idx) { m_buffer_idx.return_index(idx); } @@ -468,6 +487,7 @@ namespace asyncpp::uring { return sqe_awaitable{*this, sqe, std::move(st)}; } +#if ASYNCPP_URING_OP_LAST >= 32 /** * \brief Prepare a uring operation using the function passed using PrepareFN and returns an sqe_awaitable for it. * Associates a buffer_group for automatic buffer selection. @@ -500,6 +520,7 @@ namespace asyncpp::uring { PrepareFN(sqe, std::forward(args)...); return sqe_awaitable{*this, sqe, group, std::move(st)}; } +#endif auto nop() noexcept { return do_call<&io_uring_prep_nop>(); } auto nop(std::stop_token st) noexcept { return do_call<&io_uring_prep_nop>(std::move(st)); } @@ -532,8 +553,10 @@ namespace asyncpp::uring { auto poll_add(int fd, short mask) noexcept { return do_call<&io_uring_prep_poll_add>(fd, mask); } auto poll_add(int fd, short mask, std::stop_token st) noexcept { return do_call<&io_uring_prep_poll_add>(std::move(st), fd, mask); } // TODO: How usefull is this ? - auto poll_remove(uint64_t udata) noexcept { return do_call<&io_uring_prep_poll_remove>(udata); } - auto poll_remove(uint64_t udata, std::stop_token st) noexcept { return do_call<&io_uring_prep_poll_remove>(std::move(st), udata); } + auto poll_remove(uint64_t udata) noexcept { return do_call<&io_uring_prep_poll_remove>(reinterpret_cast(udata)); } + auto poll_remove(uint64_t udata, std::stop_token st) noexcept { + return do_call<&io_uring_prep_poll_remove>(std::move(st), reinterpret_cast(udata)); + } auto sync_file_range(int fd, unsigned int len, uint64_t offset, int flags) noexcept { return do_call<&io_uring_prep_sync_file_range>(fd, len, offset, flags); } @@ -596,12 +619,14 @@ namespace asyncpp::uring { auto read(int fd, void* buf, unsigned nbytes, off_t offset, std::stop_token st) noexcept { return do_call<&io_uring_prep_read>(std::move(st), fd, buf, nbytes, offset); } +#if ASYNCPP_URING_OP_LAST >= 32 auto read(int fd, buffer_group& buffers, off_t offset) noexcept { return do_call<&io_uring_prep_read>(buffers, fd, nullptr, buffers.block_size(), offset); } auto read(int fd, buffer_group& buffers, off_t offset, std::stop_token st) noexcept { return do_call<&io_uring_prep_read>(buffers, std::move(st), fd, nullptr, buffers.block_size(), offset); } +#endif auto write(int fd, const void* buf, unsigned nbytes, off_t offset) noexcept { return do_call<&io_uring_prep_write>(fd, buf, nbytes, offset); } auto write(int fd, const void* buf, unsigned nbytes, off_t offset, std::stop_token st) noexcept { return do_call<&io_uring_prep_write>(std::move(st), fd, buf, nbytes, offset); @@ -614,10 +639,13 @@ namespace asyncpp::uring { auto madvise(void* addr, off_t length, int advice, std::stop_token st) noexcept { return do_call<&io_uring_prep_madvise>(std::move(st), addr, length, advice); } +#if ASYNCPP_URING_OP_LAST >= 26 auto send(int sockfd, const void* buf, size_t len, int flags) noexcept { return do_call<&io_uring_prep_send>(sockfd, buf, len, flags); } auto send(int sockfd, const void* buf, size_t len, int flags, std::stop_token st) noexcept { return do_call<&io_uring_prep_send>(std::move(st), sockfd, buf, len, flags); } +#endif +#if ASYNCPP_URING_OP_LAST >= 27 auto recv(int sockfd, void* buf, size_t len, int flags) noexcept { return do_call<&io_uring_prep_recv>(sockfd, buf, len, flags); } auto recv(int sockfd, void* buf, size_t len, int flags, std::stop_token st) noexcept { return do_call<&io_uring_prep_recv>(std::move(st), sockfd, buf, len, flags); @@ -628,90 +656,127 @@ namespace asyncpp::uring { auto recv(int sockfd, buffer_group& buffers, int flags, std::stop_token st) noexcept { return do_call<&io_uring_prep_recv>(buffers, std::move(st), sockfd, nullptr, buffers.block_size(), flags); } +#endif +#if ASYNCPP_URING_OP_LAST >= 28 auto openat(int dfd, const char* path, struct open_how* how) noexcept { return do_call<&io_uring_prep_openat2>(dfd, path, how); } auto openat(int dfd, const char* path, struct open_how* how, std::stop_token st) noexcept { return do_call<&io_uring_prep_openat2>(std::move(st), dfd, path, how); } +#endif +#if ASYNCPP_URING_OP_LAST >= 29 auto epoll_ctl(int epfd, int fd, int op, struct epoll_event* ev) noexcept { return do_call<&io_uring_prep_epoll_ctl>(epfd, fd, op, ev); } auto epoll_ctl(int epfd, int fd, int op, struct epoll_event* ev, std::stop_token st) noexcept { return do_call<&io_uring_prep_epoll_ctl>(std::move(st), epfd, fd, op, ev); } +#endif +#if ASYNCPP_URING_OP_LAST >= 30 auto splice(int fd_in, int64_t off_in, int fd_out, int64_t off_out, unsigned int nbytes, unsigned int splice_flags) noexcept { return do_call<&io_uring_prep_splice>(fd_in, off_in, fd_out, off_out, nbytes, splice_flags); } auto splice(int fd_in, int64_t off_in, int fd_out, int64_t off_out, unsigned int nbytes, unsigned int splice_flags, std::stop_token st) noexcept { return do_call<&io_uring_prep_splice>(std::move(st), fd_in, off_in, fd_out, off_out, nbytes, splice_flags); } +#endif +#if ASYNCPP_URING_OP_LAST >= 31 auto provide_buffers(void* addr, int len, int nr, int bgid, int bid) noexcept { return do_call<&io_uring_prep_provide_buffers>(addr, len, nr, bgid, bid); } auto provide_buffers(void* addr, int len, int nr, int bgid, int bid, std::stop_token st) noexcept { return do_call<&io_uring_prep_provide_buffers>(std::move(st), addr, len, nr, bgid, bid); } +#endif +#if ASYNCPP_URING_OP_LAST >= 32 auto remove_buffers(int nr, int bgid) noexcept { return do_call<&io_uring_prep_remove_buffers>(nr, bgid); } auto remove_buffers(int nr, int bgid, std::stop_token st) noexcept { return do_call<&io_uring_prep_remove_buffers>(std::move(st), nr, bgid); } +#endif +#if ASYNCPP_URING_OP_LAST >= 33 auto tee(int fd_in, int fd_out, unsigned int nbytes, unsigned int splice_flags) noexcept { return do_call<&io_uring_prep_tee>(fd_in, fd_out, nbytes, splice_flags); } auto tee(int fd_in, int fd_out, unsigned int nbytes, unsigned int splice_flags, std::stop_token st) noexcept { return do_call<&io_uring_prep_tee>(std::move(st), fd_in, fd_out, nbytes, splice_flags); } +#endif +#if ASYNCPP_URING_OP_LAST >= 34 auto shutdown(int fd, int how) noexcept { return do_call<&io_uring_prep_shutdown>(fd, how); } auto shutdown(int fd, int how, std::stop_token st) noexcept { return do_call<&io_uring_prep_shutdown>(std::move(st), fd, how); } +#endif +#if ASYNCPP_URING_OP_LAST >= 35 auto renameat(int olddfd, const char* oldpath, int newdfd, const char* newpath, int flags) noexcept { return do_call<&io_uring_prep_renameat>(olddfd, oldpath, newdfd, newpath, flags); } auto renameat(int olddfd, const char* oldpath, int newdfd, const char* newpath, int flags, std::stop_token st) noexcept { return do_call<&io_uring_prep_renameat>(std::move(st), olddfd, oldpath, newdfd, newpath, flags); } +#endif +#if ASYNCPP_URING_OP_LAST >= 36 auto unlinkat(int dfd, const char* path, int flags) noexcept { return do_call<&io_uring_prep_unlinkat>(dfd, path, flags); } auto unlinkat(int dfd, const char* path, int flags, std::stop_token st) noexcept { return do_call<&io_uring_prep_unlinkat>(std::move(st), dfd, path, flags); } +#endif +#if ASYNCPP_URING_OP_LAST >= 37 auto mkdirat(int dfd, const char* path, mode_t mode) noexcept { return do_call<&io_uring_prep_mkdirat>(dfd, path, mode); } auto mkdirat(int dfd, const char* path, mode_t mode, std::stop_token st) noexcept { return do_call<&io_uring_prep_mkdirat>(std::move(st), dfd, path, mode); } +#endif +#if ASYNCPP_URING_OP_LAST >= 38 auto symlinkat(const char* target, int newdirfd, const char* linkpath) noexcept { return do_call<&io_uring_prep_symlinkat>(target, newdirfd, linkpath); } auto symlinkat(const char* target, int newdirfd, const char* linkpath, std::stop_token st) noexcept { return do_call<&io_uring_prep_symlinkat>(std::move(st), target, newdirfd, linkpath); } +#endif +#if ASYNCPP_URING_OP_LAST >= 39 auto linkat(int olddfd, const char* oldpath, int newdfd, const char* newpath, int flags) noexcept { return do_call<&io_uring_prep_linkat>(olddfd, oldpath, newdfd, newpath, flags); } auto linkat(int olddfd, const char* oldpath, int newdfd, const char* newpath, int flags, std::stop_token st) noexcept { return do_call<&io_uring_prep_linkat>(std::move(st), olddfd, oldpath, newdfd, newpath, flags); } +#endif +#if ASYNCPP_URING_OP_LAST >= 40 auto msg_ring(int fd, unsigned int len, uint64_t data, unsigned int flags) noexcept { return do_call<&io_uring_prep_msg_ring>(fd, len, data, flags); } auto msg_ring(int fd, unsigned int len, uint64_t data, unsigned int flags, std::stop_token st) noexcept { return do_call<&io_uring_prep_msg_ring>(std::move(st), fd, len, data, flags); } +#endif +#if ASYNCPP_URING_OP_LAST >= 41 auto fsetxattr(int fd, const char* name, const char* value, int flags, size_t len) noexcept { return do_call<&io_uring_prep_fsetxattr>(fd, name, value, flags, len); } auto fsetxattr(int fd, const char* name, const char* value, int flags, size_t len, std::stop_token st) noexcept { return do_call<&io_uring_prep_fsetxattr>(std::move(st), fd, name, value, flags, len); } +#endif +#if ASYNCPP_URING_OP_LAST >= 42 auto setxattr(const char* name, const char* value, const char* path, int flags, size_t len) noexcept { return do_call<&io_uring_prep_setxattr>(name, value, path, flags, len); } auto setxattr(const char* name, const char* value, const char* path, int flags, size_t len, std::stop_token st) noexcept { return do_call<&io_uring_prep_setxattr>(std::move(st), name, value, path, flags, len); } +#endif +#if ASYNCPP_URING_OP_LAST >= 43 auto fgetxattr(int fd, const char* name, char* value, size_t len) noexcept { return do_call<&io_uring_prep_fgetxattr>(fd, name, value, len); } auto fgetxattr(int fd, const char* name, char* value, size_t len, std::stop_token st) noexcept { return do_call<&io_uring_prep_fgetxattr>(std::move(st), fd, name, value, len); } +#endif +#if ASYNCPP_URING_OP_LAST >= 44 auto getxattr(const char* name, char* value, const char* path, size_t len) noexcept { return do_call<&io_uring_prep_getxattr>(name, value, path, len); } auto getxattr(const char* name, char* value, const char* path, size_t len, std::stop_token st) noexcept { return do_call<&io_uring_prep_getxattr>(std::move(st), name, value, path, len); } +#endif +#if ASYNCPP_URING_OP_LAST >= 45 auto socket(int domain, int type, int protocol, unsigned int flags) noexcept { return do_call<&io_uring_prep_socket>(domain, type, protocol, flags); } auto socket(int domain, int type, int protocol, unsigned int flags, std::stop_token st) noexcept { return do_call<&io_uring_prep_socket>(std::move(st), domain, type, protocol, flags); } +#endif // TODO: io_uring_prep_uring_cmd private: @@ -750,8 +815,8 @@ namespace asyncpp::uring { inline bool io_service::handle_completions() noexcept { unsigned head; io_uring_for_each_cqe(raw_handle(), head, m_current_cqe) { - const auto data = io_uring_cqe_get_data64(m_current_cqe); - switch (data) { + const auto data = io_uring_cqe_get_data(m_current_cqe); + switch (reinterpret_cast(data)) { case 0: { if (m_handle_null_cqe) m_handle_null_cqe(m_current_cqe); break; @@ -766,7 +831,7 @@ namespace asyncpp::uring { const auto sqe = get_sqe(); memset(sqe, 0, sizeof(*sqe)); io_uring_prep_read(sqe, m_dispatched_wake, &m_dispatched_wake_value, sizeof(m_dispatched_wake_value), 0); - io_uring_sqe_set_data64(sqe, 1); + io_uring_sqe_set_data(sqe, reinterpret_cast(1)); io_uring_submit(raw_handle()); break; } @@ -786,6 +851,7 @@ namespace asyncpp::uring { inline constexpr int sqe_awaitable::await_resume() const noexcept { return m_service.current_cqe()->res; } +#if ASYNCPP_URING_OP_LAST >= 32 inline void sqe_awaitable::cancel_executor::operator()() noexcept { auto sqe = that->m_service.get_sqe(); io_uring_prep_cancel(sqe, that->m_handle.address(), 0); @@ -893,5 +959,5 @@ namespace asyncpp::uring { m_buffer_index = 0; m_pointer = nullptr; } - +#endif } // namespace asyncpp::uring diff --git a/test/io_service.cpp b/test/io_service.cpp index 5540437..2005366 100644 --- a/test/io_service.cpp +++ b/test/io_service.cpp @@ -2,7 +2,9 @@ #include #include #include +#include #include +#include #include #include @@ -61,32 +63,31 @@ TEST(ASYNCPP_URING, IoServicePerformance) { } TEST(ASYNCPP_URING, PlainPerformance) { - io_service io; + io_uring ring; + ASSERT_EQ(io_uring_queue_init(8, &ring, 0), 0); { - auto ring = io.raw_handle(); for (size_t i = 0; i < num_prewarm; i++) { - auto* sqe = io_uring_get_sqe(ring); + auto* sqe = io_uring_get_sqe(&ring); io_uring_prep_nop(sqe); - io_uring_submit_and_wait(ring, 1); + io_uring_submit_and_wait(&ring, 1); io_uring_cqe* cqe; - io_uring_peek_cqe(ring, &cqe); + io_uring_peek_cqe(&ring, &cqe); (void)cqe->res; - io_uring_cqe_seen(ring, cqe); + io_uring_cqe_seen(&ring, cqe); } } { - auto ring = io.raw_handle(); stopwatch sw{"plain", num_samples}; for (size_t i = 0; i < num_samples; i++) { - auto* sqe = io_uring_get_sqe(ring); + auto* sqe = io_uring_get_sqe(&ring); io_uring_prep_nop(sqe); - io_uring_submit_and_wait(ring, 1); + io_uring_submit_and_wait(&ring, 1); io_uring_cqe* cqe; - io_uring_peek_cqe(ring, &cqe); + io_uring_peek_cqe(&ring, &cqe); (void)cqe->res; - io_uring_cqe_seen(ring, cqe); + io_uring_cqe_seen(&ring, cqe); } } } From c5843ff1a36e11def23ca9fe7a4cd44d881aa1aa Mon Sep 17 00:00:00 2001 From: Dominik Thalhammer Date: Wed, 15 Feb 2023 13:56:33 +0100 Subject: [PATCH 2/2] :art: Formatting --- CMakeLists.txt | 6 ++++-- include/asyncpp/uring/io_service.h | 31 ++++++++++++++++-------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f549c22..d360fd7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,8 @@ else() include(cmake/Fetch_asyncpp.cmake) endif() -try_run(OP_LAST OP_LAST_COMPILE_RESULT SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/detect-max-op.c) +try_run(OP_LAST OP_LAST_COMPILE_RESULT SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/detect-max-op.c) add_library(asyncpp_uring INTERFACE) target_link_libraries(asyncpp_uring INTERFACE asyncpp Threads::Threads @@ -35,7 +36,8 @@ target_include_directories(asyncpp_uring INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) target_compile_features(asyncpp_uring INTERFACE cxx_std_20) if(OP_LAST_COMPILE_RESULT) - target_compile_definitions(asyncpp_uring INTERFACE ASYNCPP_URING_OP_LAST=${OP_LAST}) + target_compile_definitions(asyncpp_uring + INTERFACE ASYNCPP_URING_OP_LAST=${OP_LAST}) endif() if(ASYNCPP_BUILD_TEST) diff --git a/include/asyncpp/uring/io_service.h b/include/asyncpp/uring/io_service.h index 870e7ed..c1ce232 100644 --- a/include/asyncpp/uring/io_service.h +++ b/include/asyncpp/uring/io_service.h @@ -80,8 +80,7 @@ namespace asyncpp::uring { constexpr size_t byte_size() const noexcept { return m_group->block_size(); } constexpr void* get() const noexcept { return m_pointer; } template - requires std::is_trivial_v - constexpr std::span typed() const noexcept { + requires std::is_trivial_v constexpr std::span typed() const noexcept { return std::span{static_cast(get()), byte_size() / sizeof(T)}; } @@ -306,18 +305,18 @@ namespace asyncpp::uring { [[nodiscard]] io_uring_sqe* get_sqe() noexcept { auto sqe = io_uring_get_sqe(&m_ring); if (sqe == nullptr) [[unlikely]] { - // Submit queue is full, submit all existing ones and retry - if (int res = io_uring_submit(&m_ring); res < 0) { - std::cerr << "========== Failed to submit sqe batch ==========\n" << strerror(-res) << std::endl; - std::terminate(); + // Submit queue is full, submit all existing ones and retry + if (int res = io_uring_submit(&m_ring); res < 0) { + std::cerr << "========== Failed to submit sqe batch ==========\n" << strerror(-res) << std::endl; + std::terminate(); + } + sqe = io_uring_get_sqe(&m_ring); } - sqe = io_uring_get_sqe(&m_ring); - } if (sqe == nullptr) [[unlikely]] { - // If we reach this something is really really wrong.... - std::cerr << "========== Failed to get a sqe after submit ==========" << std::endl; - std::terminate(); - } + // If we reach this something is really really wrong.... + std::cerr << "========== Failed to get a sqe after submit ==========" << std::endl; + std::terminate(); + } // Zero out the sqe to avoid dangling user_data memset(sqe, 0, sizeof(*sqe)); return sqe; @@ -395,9 +394,13 @@ namespace asyncpp::uring { : uring{params}, // m_dispatched_wake{eventfd(0, 0)}, // #ifdef IORING_FEAT_CQE_SKIP - skip_success_flags{has_feature(IORING_FEAT_CQE_SKIP) ? ioseq_flag::cqe_skip_success : ioseq_flag::none} + skip_success_flags { + has_feature(IORING_FEAT_CQE_SKIP) ? ioseq_flag::cqe_skip_success : ioseq_flag::none + } #else - skip_success_flags{ioseq_flag::none} + skip_success_flags { + ioseq_flag::none + } #endif { set_null_cqe_handler([](const io_uring_cqe* cqe) {