#pragma once // Project includes #include "file_descriptor.hpp" #include "task.hpp" // Standard includes #include #include #include #include #include #include #include // Third-party includes #include #include #include #include class UringContext { public: explicit UringContext(unsigned entries = 256); UringContext() = delete; UringContext(const UringContext &) = delete; UringContext &operator=(const UringContext &) = delete; UringContext(UringContext &&other) noexcept; UringContext &operator=(UringContext &&other) noexcept; ~UringContext() noexcept; /** * @brief Asynchronously accepts a new connection on the given server file descriptor. * * @param server_fd The server file descriptor to accept connections on * @return kev::task The client file descriptor of the accepted connection */ kev::task async_accept(RawFileDescriptor server_fd); /** * @brief Asynchronously reads data from the given file descriptor using io_uring. * * @param fd The file descriptor to read from * @param buffer The buffer to read data into * @return kev::task The number of bytes read */ kev::task async_read(RawFileDescriptor fd, std::span buffer); /** * @brief Asynchronously writes all data to the given file descriptor using io_uring. * * @param fd The file descriptor to write to * @param data The data to write * @return kev::task */ kev::task async_write_all(RawFileDescriptor fd, std::span data); /** * @brief Runs the event loop until the given sender completes. * * @tparam Sender * @param sender The sender whose completion will stop the event loop. */ template void run(Sender sender) { struct receiver { std::atomic *done; stdexec::inline_scheduler scheduler{}; using is_receiver = void; static_assert(std::is_same_v); auto get_env() const noexcept { return exec::make_env(exec::with(stdexec::get_scheduler, scheduler)); } void set_value() noexcept { done->store(true, std::memory_order_release); } void set_error(std::exception_ptr) noexcept { done->store(true, std::memory_order_release); } void set_stopped() noexcept { done->store(true, std::memory_order_release); } }; std::atomic done{false}; auto op = stdexec::connect(std::move(sender), receiver{&done}); stdexec::start(op); while (!done.load(std::memory_order_acquire)) { run_once(); } } struct io_uring *ring() noexcept; private: /** * @brief Runs a single iteration of the event loop, processing one completed io_uring event. */ void run_once(); /** * @brief Asynchronously writes data to the given file descriptor using io_uring. * * @param fd The file descriptor to write to * @param data The data to write * @return kev::task Number of bytes written */ kev::task async_write(RawFileDescriptor fd, std::span data); private: struct io_uring m_ring{}; };