build: Split up features into separate libraries
This makes it much more obvious when a feature is missing for a user.
Change-Id: Ibb17d7ab1f185a1976a32f48933c01a252450dd1
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include-uring/meson.build b/include-uring/meson.build
new file mode 100644
index 0000000..91b426f
--- /dev/null
+++ b/include-uring/meson.build
@@ -0,0 +1,5 @@
+stdplus_headers += include_directories('.')
+
+install_headers(
+ 'stdplus/io_uring.hpp',
+ subdir: 'stdplus')
diff --git a/include-uring/stdplus/io_uring.hpp b/include-uring/stdplus/io_uring.hpp
new file mode 100644
index 0000000..6572e04
--- /dev/null
+++ b/include-uring/stdplus/io_uring.hpp
@@ -0,0 +1,140 @@
+#pragma once
+
+#include <liburing.h>
+
+#include <stdplus/fd/managed.hpp>
+#include <stdplus/handle/managed.hpp>
+
+#include <chrono>
+#include <optional>
+#include <vector>
+
+namespace stdplus
+{
+
+/** @brief Converts a chrono duration into a kernel duration */
+__kernel_timespec chronoToKTS(std::chrono::nanoseconds t) noexcept;
+
+class IoUring
+{
+ public:
+ struct CQEHandler
+ {
+ CQEHandler() = default;
+ CQEHandler(CQEHandler&&) = delete;
+ CQEHandler& operator=(CQEHandler&&) = delete;
+ CQEHandler(const CQEHandler&) = delete;
+ CQEHandler& operator=(const CQEHandler&) = delete;
+ virtual ~CQEHandler() = default;
+
+ virtual void handleCQE(io_uring_cqe&) noexcept = 0;
+ };
+
+ class FileHandle
+ {
+ public:
+ inline operator int() const
+ {
+ return *slot;
+ }
+
+ private:
+ explicit FileHandle(unsigned slot, IoUring& ring);
+
+ static void drop(unsigned&& slot, IoUring*& ring);
+
+ Managed<unsigned, IoUring*>::Handle<drop> slot;
+
+ friend class IoUring;
+ };
+
+ explicit IoUring(size_t queue_size = 10, int flags = 0);
+ explicit IoUring(size_t queue_size, io_uring_params& params);
+
+ IoUring(IoUring&&) = delete;
+ IoUring& operator=(IoUring&&) = delete;
+ IoUring(const IoUring&) = delete;
+ IoUring& operator=(const IoUring&) = delete;
+ ~IoUring();
+
+ /** @brief Reserves an additional number of file descriptor slots
+ *
+ * @param[in] num - The number of slots to register
+ * @throws std::system_error if the allocation fails
+ */
+ void reserveFiles(size_t num);
+
+ /** @brief Registers a file descriptor with a slot on the ring
+ *
+ * @param[in] fd - The file descriptor to register
+ * @throws std::system_error if the allocation fails
+ * @return A handle to the registered file on the ring
+ */
+ [[nodiscard]] FileHandle registerFile(int fd);
+
+ /** @brief Get current list of files descriptors registered on the ring.
+ * Note this view potentially expires when registrations change. */
+ inline std::span<const int> getFiles() const noexcept
+ {
+ return files;
+ }
+
+ /** @brief Gets an unused SQE from the ring
+ *
+ * @throws std::system_error if the allocation fails
+ * @return An SQE on the ring
+ */
+ io_uring_sqe& getSQE();
+
+ /** @brief Associates the SQE with a user provided callback handler
+ *
+ * @param[in] sqe - The SQE that we want to register
+ * @param[in] h - The handler which will be run when the CQE comes back
+ */
+ void setHandler(io_uring_sqe& sqe, CQEHandler* h) noexcept;
+
+ /** @brief Cancels the outstanding request associated with a handler
+ *
+ * @param[in] h - The handler associated with the request
+ */
+ void cancelHandler(CQEHandler& h);
+
+ /** @brief Submits all outstanding SQEs to the kernel
+ *
+ * @throws std::system_error if the submission fails
+ */
+ void submit();
+
+ /** @brief Non-blocking process all outstanding CQEs */
+ void process() noexcept;
+
+ /** @brief Waits for new CQEs to become available */
+ void wait();
+ void wait(std::chrono::nanoseconds timeout);
+
+ /** @brief Returns the EventFD associated with the ring
+ * A new descriptor is created if it does not yet exist
+ *
+ * @throws std::system_error if constructing the event fd fails
+ * @return A reference to the descriptor
+ */
+ stdplus::ManagedFd& getEventFd();
+
+ /** @brief Non-blocking process all outstanding eventFd events
+ * Should be used instead of process() to clear eventFd events.
+ */
+ void processEvents();
+
+ private:
+ io_uring ring;
+ std::optional<stdplus::ManagedFd> event_fd;
+ std::vector<CQEHandler*> handlers;
+ std::vector<int> files;
+ size_t filesAllocated = 0;
+
+ void dropHandler(CQEHandler* h, io_uring_cqe& cqe) noexcept;
+ void setFile(unsigned slot, int fd) noexcept;
+ void updateFile(unsigned slot, int fd);
+};
+
+} // namespace stdplus