this repo has no description
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};