blob: 260b59502e0f567fd2ddd64876bd2a1635900237 [file] [log] [blame]
#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
*/
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<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