Add C++ wrapper for liburing
Test: th
Bug: 250637970
Change-Id: I6303617ad02c6be2f2a6d3b28cd81f10406302bb
diff --git a/liburing_cpp/src/IoUring.cpp b/liburing_cpp/src/IoUring.cpp
new file mode 100644
index 0000000..a5fe76f
--- /dev/null
+++ b/liburing_cpp/src/IoUring.cpp
@@ -0,0 +1,187 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <asm-generic/errno-base.h>
+#include <liburing_cpp/IoUring.h>
+#include <string.h>
+
+#include <algorithm>
+#include <iostream>
+#include <memory>
+
+#include "liburing.h"
+#include "liburing_cpp/IoUringCQE.h"
+
+namespace io_uring_cpp {
+
+template <typename T>
+bool IsZeroInitialized(const T& val) {
+ auto begin = reinterpret_cast<const char*>(&val);
+ auto end = begin + sizeof(val);
+ return std::all_of(begin, end, [](const auto& a) { return a == 0; });
+}
+
+class IoUring final : public IoUringInterface {
+ public:
+ ~IoUring() override {
+ if (!IsZeroInitialized(ring)) {
+ if (buffer_registered_) {
+ UnregisterBuffers();
+ }
+ if (files_registered_) {
+ UnregisterFiles();
+ }
+ io_uring_queue_exit(&ring);
+ }
+ }
+ IoUring(const IoUring&) = delete;
+ IoUring(IoUring&& rhs) {
+ ring = rhs.ring;
+ memset(&rhs.ring, 0, sizeof(rhs.ring));
+ }
+ IoUring& operator=(IoUring&& rhs) {
+ std::swap(ring, rhs.ring);
+ return *this;
+ }
+ Errno RegisterBuffers(const struct iovec* iovecs,
+ size_t iovec_size) override {
+ const auto ret =
+ Errno(io_uring_register_buffers(&ring, iovecs, iovec_size));
+ buffer_registered_ = ret.IsOk();
+ return ret;
+ }
+
+ Errno UnregisterBuffers() override {
+ const auto ret = Errno(io_uring_unregister_buffers(&ring));
+ buffer_registered_ = !ret.IsOk();
+ return ret;
+ }
+
+ Errno RegisterFiles(const int* files, size_t files_size) override {
+ const auto ret = Errno(io_uring_register_files(&ring, files, files_size));
+ files_registered_ = ret.IsOk();
+ return ret;
+ }
+
+ Errno UnregisterFiles() {
+ const auto ret = Errno(io_uring_unregister_files(&ring));
+ files_registered_ = !ret.IsOk();
+ return ret;
+ }
+
+ IoUringSQE PrepRead(int fd, void* buf, unsigned nbytes,
+ uint64_t offset) override {
+ auto sqe = io_uring_get_sqe(&ring);
+ if (sqe == nullptr) {
+ return IoUringSQE{nullptr};
+ }
+ io_uring_prep_read(sqe, fd, buf, nbytes, offset);
+ return IoUringSQE{static_cast<void*>(sqe)};
+ }
+ IoUringSQE PrepWrite(int fd, const void* buf, unsigned nbytes,
+ uint64_t offset) override {
+ auto sqe = io_uring_get_sqe(&ring);
+ if (sqe == nullptr) {
+ return IoUringSQE{nullptr};
+ }
+ io_uring_prep_write(sqe, fd, buf, nbytes, offset);
+ return IoUringSQE{static_cast<void*>(sqe)};
+ }
+ IoUringSubmitResult Submit() override {
+ return IoUringSubmitResult{io_uring_submit(&ring)};
+ }
+
+ IoUringSubmitResult SubmitAndWait(size_t completions) override {
+ return IoUringSubmitResult{io_uring_submit_and_wait(&ring, completions)};
+ }
+
+ Result<Errno, std::vector<IoUringCQE>> PopCQE(
+ const unsigned int count) override {
+ std::vector<io_uring_cqe*> cqe_ptrs;
+ cqe_ptrs.resize(count);
+ const auto ret = io_uring_wait_cqe_nr(&ring, cqe_ptrs.data(), count);
+ if (ret != 0) {
+ return {Errno(ret)};
+ }
+ const auto filled = io_uring_peek_batch_cqe(&ring, cqe_ptrs.data(), count);
+ if (filled != count) {
+ return {Errno(EAGAIN)};
+ }
+ std::vector<IoUringCQE> cqes;
+ cqes.reserve(count);
+ for (const auto& cqe : cqe_ptrs) {
+ if (cqe == nullptr) {
+ return {Errno(EAGAIN)};
+ }
+ cqes.push_back(IoUringCQE(cqe->res, cqe->flags, cqe->user_data));
+ io_uring_cqe_seen(&ring, cqe);
+ }
+ return {cqes};
+ }
+
+ Result<Errno, IoUringCQE> PopCQE() override {
+ struct io_uring_cqe* ptr{};
+ const auto ret = io_uring_wait_cqe(&ring, &ptr);
+ if (ret != 0) {
+ return {Errno(ret)};
+ }
+ const auto cqe = IoUringCQE(ptr->res, ptr->flags, ptr->user_data);
+ io_uring_cqe_seen(&ring, ptr);
+ return {cqe};
+ }
+
+ Result<Errno, IoUringCQE> PeekCQE() override {
+ struct io_uring_cqe* ptr{};
+ const auto ret = io_uring_peek_cqe(&ring, &ptr);
+ if (ret != 0) {
+ return {Errno(ret)};
+ }
+ return {IoUringCQE(ptr->res, ptr->flags, ptr->user_data)};
+ }
+
+ IoUring(struct io_uring r) : ring(r) {}
+
+ private:
+ struct io_uring ring {};
+ bool buffer_registered_ = false;
+ bool files_registered_ = false;
+ std::atomic<size_t> request_id_{};
+};
+
+const char* Errno::ErrMsg() {
+ if (error_code == 0) {
+ return nullptr;
+ }
+ return strerror(error_code);
+}
+
+std::ostream& operator<<(std::ostream& out, Errno err) {
+ out << err.ErrCode() << ", " << err.ErrMsg();
+ return out;
+}
+
+std::unique_ptr<IoUringInterface> IoUringInterface::CreateLinuxIoUring(
+ int queue_depth, int flags) {
+ struct io_uring ring {};
+ const auto err = io_uring_queue_init(queue_depth, &ring, flags);
+ if (err) {
+ errno = -err;
+ return {};
+ }
+ return std::unique_ptr<IoUringInterface>(new IoUring(ring));
+}
+
+} // namespace io_uring_cpp