this repo has no description
at main 119 lines 3.6 kB view raw
1#pragma once 2// Project includes 3#include "file_descriptor.hpp" 4#include "task.hpp" 5// Standard includes 6#include <cassert> 7#include <cstddef> 8#include <cstring> 9#include <exception> 10#include <span> 11#include <type_traits> 12#include <unistd.h> 13// Third-party includes 14#include <exec/env.hpp> 15#include <exec/task.hpp> 16#include <liburing.h> 17#include <stdexec/execution.hpp> 18 19class UringContext 20{ 21 public: 22 explicit UringContext(unsigned entries = 256); 23 UringContext() = delete; 24 UringContext(const UringContext &) = delete; 25 UringContext &operator=(const UringContext &) = delete; 26 UringContext(UringContext &&other) noexcept; 27 UringContext &operator=(UringContext &&other) noexcept; 28 ~UringContext() noexcept; 29 30 /** 31 * @brief Asynchronously accepts a new connection on the given server file descriptor. 32 * 33 * @param server_fd The server file descriptor to accept connections on 34 * @return kev::task<int> The client file descriptor of the accepted connection 35 */ 36 kev::task<int> async_accept(RawFileDescriptor server_fd); 37 38 /** 39 * @brief Asynchronously reads data from the given file descriptor using io_uring. 40 * 41 * @param fd The file descriptor to read from 42 * @param buffer The buffer to read data into 43 * @return kev::task<size_t> The number of bytes read 44 */ 45 kev::task<size_t> async_read(RawFileDescriptor fd, std::span<std::byte> buffer); 46 47 /** 48 * @brief Asynchronously writes all data to the given file descriptor using io_uring. 49 * 50 * @param fd The file descriptor to write to 51 * @param data The data to write 52 * @return kev::task<void> 53 */ 54 kev::task<void> async_write_all(RawFileDescriptor fd, std::span<const std::byte> data); 55 56 /** 57 * @brief Runs the event loop until the given sender completes. 58 * 59 * @tparam Sender 60 * @param sender The sender whose completion will stop the event loop. 61 */ 62 template <stdexec::sender Sender> void run(Sender sender) 63 { 64 struct receiver 65 { 66 std::atomic<bool> *done; 67 stdexec::inline_scheduler scheduler{}; 68 69 using is_receiver = void; 70 static_assert(std::is_same_v<is_receiver, void>); 71 72 auto get_env() const noexcept 73 { 74 return exec::make_env(exec::with(stdexec::get_scheduler, scheduler)); 75 } 76 77 void set_value() noexcept 78 { 79 done->store(true, std::memory_order_release); 80 } 81 82 void set_error(std::exception_ptr) noexcept 83 { 84 done->store(true, std::memory_order_release); 85 } 86 87 void set_stopped() noexcept 88 { 89 done->store(true, std::memory_order_release); 90 } 91 }; 92 std::atomic<bool> done{false}; 93 auto op = stdexec::connect(std::move(sender), receiver{&done}); 94 stdexec::start(op); 95 while (!done.load(std::memory_order_acquire)) 96 { 97 run_once(); 98 } 99 } 100 struct io_uring *ring() noexcept; 101 102 private: 103 /** 104 * @brief Runs a single iteration of the event loop, processing one completed io_uring event. 105 */ 106 void run_once(); 107 108 /** 109 * @brief Asynchronously writes data to the given file descriptor using io_uring. 110 * 111 * @param fd The file descriptor to write to 112 * @param data The data to write 113 * @return kev::task<size_t> Number of bytes written 114 */ 115 kev::task<size_t> async_write(RawFileDescriptor fd, std::span<const std::byte> data); 116 117 private: 118 struct io_uring m_ring{}; 119};