From f99d56ce9fe2a26a8376b7c72af13a95c4d2a221 Mon Sep 17 00:00:00 2001 From: Kononov Nikolai Date: Sat, 27 May 2023 19:22:59 +0200 Subject: [PATCH 1/6] Open source internal implementation --- include/ione/async/accept.hpp | 56 ++++++++++++++++ include/ione/async/close.hpp | 29 +++++++++ include/ione/async/connect.hpp | 32 +++++++++ include/ione/async/fadvise.hpp | 31 +++++++++ include/ione/async/fallocate.hpp | 29 +++++++++ include/ione/async/fsync.hpp | 29 +++++++++ include/ione/async/madvise.hpp | 29 +++++++++ include/ione/async/nop.hpp | 30 +++++++++ include/ione/async/openat.hpp | 38 +++++++++++ include/ione/async/openat2.hpp | 41 ++++++++++++ include/ione/async/read.hpp | 58 +++++++++++++++++ include/ione/async/recv.hpp | 57 ++++++++++++++++ include/ione/async/send.hpp | 51 +++++++++++++++ include/ione/async/splice.hpp | 43 +++++++++++++ include/ione/async/statx.hpp | 44 +++++++++++++ include/ione/async/timeout.hpp | 47 ++++++++++++++ include/ione/async/write.hpp | 53 +++++++++++++++ include/ione/context.hpp | 107 +++++++++++++++++++++++++++++++ include/ione/sum/sum.hpp | 7 -- src/CMakeLists.txt | 94 ++++++++++++++------------- src/async/CMakeLists.txt | 21 ++++++ src/context.cpp | 65 +++++++++++++++++++ src/sum/CMakeLists.txt | 10 --- src/sum/sum.cpp | 9 --- 24 files changed, 938 insertions(+), 72 deletions(-) create mode 100644 include/ione/async/accept.hpp create mode 100644 include/ione/async/close.hpp create mode 100644 include/ione/async/connect.hpp create mode 100644 include/ione/async/fadvise.hpp create mode 100644 include/ione/async/fallocate.hpp create mode 100644 include/ione/async/fsync.hpp create mode 100644 include/ione/async/madvise.hpp create mode 100644 include/ione/async/nop.hpp create mode 100644 include/ione/async/openat.hpp create mode 100644 include/ione/async/openat2.hpp create mode 100644 include/ione/async/read.hpp create mode 100644 include/ione/async/recv.hpp create mode 100644 include/ione/async/send.hpp create mode 100644 include/ione/async/splice.hpp create mode 100644 include/ione/async/statx.hpp create mode 100644 include/ione/async/timeout.hpp create mode 100644 include/ione/async/write.hpp create mode 100644 include/ione/context.hpp delete mode 100644 include/ione/sum/sum.hpp create mode 100644 src/async/CMakeLists.txt create mode 100644 src/context.cpp delete mode 100644 src/sum/CMakeLists.txt delete mode 100644 src/sum/sum.cpp diff --git a/include/ione/async/accept.hpp b/include/ione/async/accept.hpp new file mode 100644 index 0000000..46d4c1d --- /dev/null +++ b/include/ione/async/accept.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] AcceptAwaiter : public CompletionAwaiter { + public: + struct Result { + sockaddr addr; + socklen_t addrlen; + int error_code; + }; + + AcceptAwaiter(Context& ctx, int fd, sockaddr addr, socklen_t addrlen, int flags) : _addr{addr}, _addrlen{addrlen} { + SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_accept(sqe, fd, &_addr, &_addrlen, flags); + }); + } + + Result await_resume() { + return {_addr, _addrlen, res}; + } + + private: + sockaddr _addr; + socklen_t _addrlen; +}; + +class [[nodiscard]] DefaultAcceptAwaiter : public CompletionAwaiter { + public: + using Result = int; + + DefaultAcceptAwaiter(Context& ctx, int fd, int flags) { + SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_accept(sqe, fd, nullptr, nullptr, flags); + }); + } + + Result await_resume() { + return res; + } +}; + +} // namespace detail + +YACLIB_INLINE auto Accept(Context& ctx, int fd, sockaddr addr, socklen_t addrlen, int flags) { + return detail::AcceptAwaiter{ctx, fd, addr, addrlen, flags}; +} + +YACLIB_INLINE auto Accept(Context& ctx, int fd, int flags) { + return detail::DefaultAcceptAwaiter{ctx, fd, flags}; +} + +} // namespace ione diff --git a/include/ione/async/close.hpp b/include/ione/async/close.hpp new file mode 100644 index 0000000..57f1798 --- /dev/null +++ b/include/ione/async/close.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] CloseAwaiter : public CompletionAwaiter { + public: + using Result = int; + + CloseAwaiter(Context& ctx, int fd) { + SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_close(sqe, fd); + }); + } + + Result await_resume() { + return this->res; + } +}; + +} // namespace detail + +YACLIB_INLINE auto Close(Context& ctx, int fd) { + return detail::CloseAwaiter{ctx, fd}; +} + +} // namespace ione diff --git a/include/ione/async/connect.hpp b/include/ione/async/connect.hpp new file mode 100644 index 0000000..50260ae --- /dev/null +++ b/include/ione/async/connect.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] ConnectAwaiter : public CompletionAwaiter { + public: + using Result = int; + + ConnectAwaiter(Context& ctx, int fd, sockaddr addr, socklen_t addrlen) : _addr{addr} { + SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_connect(sqe, fd, &_addr, addrlen); + }); + } + + Result await_resume() { + return res; + } + + private: + sockaddr _addr; +}; + +} // namespace detail + +YACLIB_INLINE auto Connect(Context& ctx, int fd, sockaddr addr, socklen_t addrlen) { + return detail::ConnectAwaiter{ctx, fd, addr, addrlen}; +} + +} // namespace ione diff --git a/include/ione/async/fadvise.hpp b/include/ione/async/fadvise.hpp new file mode 100644 index 0000000..2504740 --- /dev/null +++ b/include/ione/async/fadvise.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] FadviseAwaiter : public CompletionAwaiter { + public: + using Result = int; + + FadviseAwaiter(Context& ctx, int fd, uint64_t offset, uint64_t length, int advise) { + SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_fadvise(sqe, fd, offset, length, advise); + }); + } + + Result await_resume() { + return this->res; + } +}; + +} // namespace detail + +YACLIB_INLINE auto Fadvise(Context& ctx, int fd, uint64_t offset, uint64_t length, int advise) { + return detail::FadviseAwaiter{ctx, fd, offset, length, advise}; +} + +} // namespace ione diff --git a/include/ione/async/fallocate.hpp b/include/ione/async/fallocate.hpp new file mode 100644 index 0000000..e1ffa31 --- /dev/null +++ b/include/ione/async/fallocate.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] Fallocate : public CompletionAwaiter { + public: + using Result = int; + + Fallocate(Context& ctx, int fd, int mode, uint64_t offset, uint64_t length) { + SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_fallocate(sqe, fd, mode, offset, length); + }); + } + + Result await_resume() { + return this->res; + } +}; + +} // namespace detail + +YACLIB_INLINE auto Fallocate(Context& ctx, int fd, int mode, uint64_t offset, uint64_t length) { + return detail::Fallocate{ctx, fd, mode, offset, length}; +} + +} // namespace ione diff --git a/include/ione/async/fsync.hpp b/include/ione/async/fsync.hpp new file mode 100644 index 0000000..fbb0343 --- /dev/null +++ b/include/ione/async/fsync.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] FsyncAwaiter : public CompletionAwaiter { + public: + using Result = int; + + FsyncAwaiter(Context& ctx, int fd, unsigned flags) { + SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_fsync(sqe, fd, flags); + }); + } + + Result await_resume() { + return this->res; + } +}; + +} // namespace detail + +YACLIB_INLINE auto Fsync(Context& ctx, int fd, unsigned flags) { + return detail::FsyncAwaiter{ctx, fd, flags}; +} + +} // namespace ione diff --git a/include/ione/async/madvise.hpp b/include/ione/async/madvise.hpp new file mode 100644 index 0000000..0c5a4ac --- /dev/null +++ b/include/ione/async/madvise.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] MadviseAwaiter : public CompletionAwaiter { + public: + using Result = int; + + MadviseAwaiter(Context& ctx, void* addr, uint64_t length, int advise) { + SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_madvise(sqe, addr, length, advise); + }); + } + + Result await_resume() { + return this->res; + } +}; + +} // namespace detail + +YACLIB_INLINE auto Madvise(Context& ctx, void* addr, uint64_t length, int advise) { + return detail::MadviseAwaiter{ctx, addr, length, advise}; +} + +} // namespace ione diff --git a/include/ione/async/nop.hpp b/include/ione/async/nop.hpp new file mode 100644 index 0000000..c6efc22 --- /dev/null +++ b/include/ione/async/nop.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] NopAwaiter : public CompletionAwaiter { + public: + using Result = int; + + NopAwaiter(Context& ctx) { + SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_nop(sqe); + }); + } + + Result await_resume() { + return this->res; + } +}; + +} // namespace detail + +YACLIB_INLINE auto Nop(Context& ctx) { + // TODO: maybe return void? + return detail::NopAwaiter{ctx}; +} + +} // namespace ione diff --git a/include/ione/async/openat.hpp b/include/ione/async/openat.hpp new file mode 100644 index 0000000..4f4a23b --- /dev/null +++ b/include/ione/async/openat.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +namespace ione { +namespace detail { + +template +class [[nodiscard]] OpenAtAwaiter : public ScopeAwaiter { + public: + using Result = int; + + OpenAtAwaiter(Context& ctx, int dirfd, const char* path, int flags, mode_t mode, Func func) + : ScopeAwaiter{std::move(func)} { + this->SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_openat(sqe, dirfd, path, flags, mode); + }); + } + + Result await_resume() { + this->_func(); + return {this->res}; + } +}; + +} // namespace detail + +template +YACLIB_INLINE auto OpenAt(Context& ctx, int dirfd, const char* path, int flags, mode_t mode, Func&& func) { + return detail::OpenAtAwaiter{ctx, dirfd, path, flags, mode, std::forward(func)}; +} + +YACLIB_INLINE auto OpenAt(Context& ctx, int dirfd, const char* path, int flags, mode_t mode) { + return OpenAt(ctx, dirfd, path, flags, mode, [] { + }); +} + +} // namespace ione diff --git a/include/ione/async/openat2.hpp b/include/ione/async/openat2.hpp new file mode 100644 index 0000000..4670e9f --- /dev/null +++ b/include/ione/async/openat2.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include + +namespace ione { +namespace detail { + +template +class [[nodiscard]] OpenAt2Awaiter : public ScopeAwaiter { + public: + using Result = int; + + OpenAt2Awaiter(Context& ctx, int dirfd, const char* path, open_how how, Func func) + : ScopeAwaiter{std::move(func)}, _how{how} { + this->SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_openat2(sqe, dirfd, path, &_how); + }); + } + + Result await_resume() { + this->_func(); + return {this->res}; + } + + private: + open_how _how; +}; + +} // namespace detail + +template +YACLIB_INLINE auto OpenAt2(Context& ctx, int dirfd, const char* path, open_how how, Func&& func) { + return detail::OpenAt2Awaiter{ctx, dirfd, path, how, std::forward(func)}; +} + +YACLIB_INLINE auto OpenAt2(Context& ctx, int dirfd, const char* path, open_how how) { + return OpenAt2(ctx, dirfd, path, how, [] { + }); +} + +} // namespace ione diff --git a/include/ione/async/read.hpp b/include/ione/async/read.hpp new file mode 100644 index 0000000..d22c105 --- /dev/null +++ b/include/ione/async/read.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] ReadAwaiter : public CompletionAwaiter { + public: + using Result = int; + + ReadAwaiter(Context& ctx, int fd, void* data, size_t size, uint64_t offset) { + this->SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_read(sqe, fd, data, size, offset); + }); + } + + Result await_resume() { + return this->res; + } +}; + +class [[nodiscard]] ReadToBufferAwaiter : public CompletionAwaiter { + public: + struct Result { + std::vector buffer; + int error_code; + }; + + ReadToBufferAwaiter(Context& ctx, int fd, size_t size, uint64_t offset) : _buffer(size) { + this->SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_read(sqe, fd, _buffer.data(), _buffer.size(), offset); + }); + } + + Result await_resume() { + return {std::move(_buffer), res}; + } + + private: + std::vector _buffer; +}; + +} // namespace detail + +YACLIB_INLINE auto Read(Context& ctx, int fd, void* data, size_t size, uint64_t offset) { + return detail::ReadAwaiter{ctx, fd, data, size, offset}; +} + +YACLIB_INLINE auto Read(Context& ctx, int fd, size_t size, uint64_t offset) { + return detail::ReadToBufferAwaiter{ctx, fd, size, offset}; +} + +// TODO: ReadV + +} // namespace ione diff --git a/include/ione/async/recv.hpp b/include/ione/async/recv.hpp new file mode 100644 index 0000000..9637294 --- /dev/null +++ b/include/ione/async/recv.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] RecvAwaiter : public CompletionAwaiter { + public: + using Result = int; + + RecvAwaiter(Context& ctx, int fd, void* data, size_t size, int flags) { + this->SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_recv(sqe, fd, data, size, flags); + }); + } + + Result await_resume() { + return this->res; + } +}; + +class [[nodiscard]] RecvToBufferAwaiter : public CompletionAwaiter { + public: + struct Result { + std::vector buffer; + int error_code; + }; + + RecvToBufferAwaiter(Context& ctx, int fd, size_t size, int flags) : _buffer(size) { + this->SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_recv(sqe, fd, _buffer.data(), _buffer.size(), flags); + }); + } + + Result await_resume() { + return {std::move(_buffer), res}; + } + + private: + std::vector _buffer; +}; + +} // namespace detail + +template +YACLIB_INLINE auto Recv(Context& ctx, int fd, void* data, size_t size, int flags) { + return detail::RecvAwaiter{ctx, fd, data, size, flags}; +} + +YACLIB_INLINE auto Recv(Context& ctx, int fd, size_t size, int flags) { + return detail::RecvToBufferAwaiter{ctx, fd, size, flags}; +} + +} // namespace ione diff --git a/include/ione/async/send.hpp b/include/ione/async/send.hpp new file mode 100644 index 0000000..f5be566 --- /dev/null +++ b/include/ione/async/send.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include + +#include + +namespace ione { +namespace detail { + +template +class [[nodiscard]] SendAwaiter : public ScopeAwaiter { + public: + using Result = int; + + SendAwaiter(Context& ctx, int fd, const void* data, size_t size, int flags, Func func) + : ScopeAwaiter{std::move(func)} { + this->SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_send(sqe, fd, data, size, flags); + }); + } + + Result await_resume() { + this->_func(); + return this->res; + } +}; + +} // namespace detail + +template +YACLIB_INLINE auto Send(Context& ctx, int fd, const void* data, size_t size, int flags, Func&& func) { + return detail::SendAwaiter{ctx, fd, data, size, flags, std::forward(func)}; +} + +YACLIB_INLINE auto Send(Context& ctx, int fd, const void* data, size_t size, int flags) { + return Send(ctx, fd, data, size, flags, [] { + }); +} + +template +auto Send(Context& ctx, int fd, std::vector buffer, int flags) { + static_assert(std::is_standard_layout_v); + + auto data = buffer.data(); + auto size = buffer.size() * sizeof(T); + return Send(ctx, fd, data, size, flags, [buf = std::move(buffer)]() mutable { + buf = {}; + }); +} + +} // namespace ione diff --git a/include/ione/async/splice.hpp b/include/ione/async/splice.hpp new file mode 100644 index 0000000..74f0d51 --- /dev/null +++ b/include/ione/async/splice.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] SpliceAwaiter : public CompletionAwaiter { + public: + using Result = int; + + SpliceAwaiter(Context& ctx, int fd_in, int64_t off_in, int fd_out, int64_t off_out, size_t len, unsigned flags) { + SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_splice(sqe, fd_in, off_in, fd_out, off_out, len, flags); + }); + } + + Result await_resume() { + return res; + } +}; + +} // namespace detail + +YACLIB_INLINE auto Splice(Context& ctx, int fd_in, uint64_t off_in, int fd_out, uint64_t off_out, size_t len, + unsigned flags) { + return detail::SpliceAwaiter{ctx, fd_in, static_cast(off_in), fd_out, static_cast(off_out), + len, flags}; +} + +YACLIB_INLINE auto Splice(Context& ctx, int fd_in, uint64_t off_in, int fd_out, size_t len, unsigned flags) { + return detail::SpliceAwaiter{ctx, fd_in, static_cast(off_in), fd_out, -1, len, flags}; +} + +YACLIB_INLINE auto Splice(Context& ctx, int fd_in, int fd_out, uint64_t off_out, size_t len, unsigned flags) { + return detail::SpliceAwaiter{ctx, fd_in, -1, fd_out, static_cast(off_out), len, flags}; +} + +YACLIB_INLINE auto Splice(Context& ctx, int fd_in, int fd_out, size_t len, unsigned flags) { + return detail::SpliceAwaiter{ctx, fd_in, -1, fd_out, -1, len, flags}; +} + +} // namespace ione diff --git a/include/ione/async/statx.hpp b/include/ione/async/statx.hpp new file mode 100644 index 0000000..77e7838 --- /dev/null +++ b/include/ione/async/statx.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include + +namespace ione { +namespace detail { + +template +class [[nodiscard]] StatxAwaiter : public ScopeAwaiter { + public: + struct Result { + struct statx statx {}; + int error_code; + }; + + StatxAwaiter(Context& ctx, int dirfd, const char* path, int flags, unsigned mask, Func func) + : ScopeAwaiter{std::move(func)} { + this->SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_statx(sqe, dirfd, path, flags, mask, &_statx); + }); + } + + Result await_resume() { + this->_func(); + return {_statx, this->res}; + } + + private: + struct statx _statx {}; +}; + +} // namespace detail + +template +YACLIB_INLINE auto Statx(Context& ctx, int dirfd, const char* path, int flags, unsigned mask, Func&& func) { + return detail::StatxAwaiter{ctx, dirfd, path, flags, mask, std::forward(func)}; +} + +YACLIB_INLINE auto Statx(Context& ctx, int dirfd, const char* path, int flags, unsigned mask) { + return Statx(ctx, dirfd, path, flags, mask, [] { + }); +} + +} // namespace ione diff --git a/include/ione/async/timeout.hpp b/include/ione/async/timeout.hpp new file mode 100644 index 0000000..e70e27e --- /dev/null +++ b/include/ione/async/timeout.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include +#include +#include + +namespace ione { +namespace detail { + +class [[nodiscard]] TimeoutAwaiter : public CompletionAwaiter { + public: + using Result = int; + + TimeoutAwaiter(Context& ctx, __kernel_timespec time, unsigned flags) : _time{time} { + this->SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_timeout(sqe, &_time, 1, flags); + }); + } + + Result await_resume() { + return this->res; + } + + private: + __kernel_timespec _time; +}; + +template +__kernel_timespec DurationToTimespec(const std::chrono::duration& duration) { + auto sec_duration = std::chrono::duration_cast(duration); + auto sec = sec_duration.count(); + static_assert(std::is_integral_v); + auto nsec_duration = duration - sec_duration; + auto nsec = nsec_duration.count(); + return {sec, nsec}; +} + +} // namespace detail + +template +YACLIB_INLINE auto SleepFor(Context& ctx, const std::chrono::duration& sleep_duration) { + return detail::TimeoutAwaiter{ctx, detail::DurationToTimespec(sleep_duration), 0}; +} + +} // namespace ione diff --git a/include/ione/async/write.hpp b/include/ione/async/write.hpp new file mode 100644 index 0000000..dc4b161 --- /dev/null +++ b/include/ione/async/write.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include + +#include + +namespace ione { +namespace detail { + +template +class [[nodiscard]] WriteAwaiter : public ScopeAwaiter { + public: + using Result = int; + + WriteAwaiter(Context& ctx, int fd, const void* data, size_t size, uint64_t offset, Func func) + : ScopeAwaiter{std::move(func)} { + this->SetUp(ctx, [&](io_uring_sqe* sqe) { + io_uring_prep_write(sqe, fd, data, size, offset); + }); + } + + Result await_resume() { + this->_func(); + return this->res; + } +}; + +} // namespace detail + +template +YACLIB_INLINE auto Write(Context& ctx, int fd, const void* data, size_t size, uint64_t offset, Func&& func) { + return detail::WriteAwaiter{ctx, fd, data, size, offset, std::forward(func)}; +} + +YACLIB_INLINE auto Write(Context& ctx, int fd, const void* data, size_t size, uint64_t offset) { + return Write(ctx, fd, data, size, offset, [] { + }); +} + +template +auto Write(Context& ctx, int fd, std::vector buffer, uint64_t offset) { + static_assert(std::is_standard_layout_v); + + auto data = buffer.data(); + auto size = buffer.size() * sizeof(T); + return Write(ctx, fd, data, size, offset, [buf = std::move(buffer)]() mutable { + buf = {}; + }); +} + +// TODO: WriteV + +} // namespace ione diff --git a/include/ione/context.hpp b/include/ione/context.hpp new file mode 100644 index 0000000..5ba8256 --- /dev/null +++ b/include/ione/context.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +namespace ione { + +class Context { + public: + Context() = default; + + void Init(unsigned entries, unsigned flags); + + void Init(unsigned entries, unsigned flags, unsigned sq_thread_cpu, unsigned sq_thread_idle); + + io_uring& GetRing() { + assert(_initialized); + return _ring; + } + + void Destroy(); + + void Run(); + + int Submit(); + + unsigned Poll(); + + ~Context(); + + private: + bool _initialized = false; + io_uring _ring; +}; + +namespace detail { + +struct CompletionAwaiter { + union { + io_uring_sqe* sqe; + yaclib_std::coroutine_handle<> handle; + }; + int res = 0; + + void SetFlags(unsigned flags) { + assert(sqe != nullptr); + sqe->flags = flags; + } + + void AddFlags(unsigned flags) { + assert(sqe != nullptr); + sqe->flags |= flags; + } + + void Discard() && { + AddFlags(IOSQE_CQE_SKIP_SUCCESS); + handle = {}; + } + + constexpr bool await_ready() const noexcept { + return false; + } + + void await_suspend(yaclib_std::coroutine_handle<> handle) { + this->handle = handle; + } + + protected: + CompletionAwaiter() : sqe{} { + } + + ~CompletionAwaiter() { + handle.~coroutine_handle<>(); + } + + template + void SetUp(Context& ctx, Func&& func) { + auto* ring = &ctx.GetRing(); + sqe = io_uring_get_sqe(ring); + if (sqe == nullptr) { + ctx.Submit(); + sqe = io_uring_get_sqe(ring); + assert(sqe != nullptr); + } + func(sqe); + io_uring_sqe_set_data(sqe, this); + } +}; + +template +struct ScopeAwaiter : CompletionAwaiter { + protected: + ScopeAwaiter(Func func) : _func{std::move(func)} { + } + + // TODO(kononovk): make func union T and call ~T in await_resume + YACLIB_NO_UNIQUE_ADDRESS Func _func; +}; + +} // namespace detail + +} // namespace ione diff --git a/include/ione/sum/sum.hpp b/include/ione/sum/sum.hpp deleted file mode 100644 index 8933d9e..0000000 --- a/include/ione/sum/sum.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace ione { - -int Sum(int, int); - -} // namespace ione diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a955d1c..1530400 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,21 +1,23 @@ macro(add_files) - set(IONE_INCLUDES ${IONE_INCLUDES} PARENT_SCOPE) - set(IONE_HEADERS ${IONE_HEADERS} PARENT_SCOPE) - set(IONE_SOURCES ${IONE_SOURCES} PARENT_SCOPE) + set(IONE_INCLUDES ${IONE_INCLUDES} PARENT_SCOPE) + set(IONE_HEADERS ${IONE_HEADERS} PARENT_SCOPE) + set(IONE_SOURCES ${IONE_SOURCES} PARENT_SCOPE) endmacro() set(IONE_INCLUDE_DIR ${IONE_SOURCE_DIR}/include/ione) set(IONE_STD_INCLUDE_DIR ${IONE_SOURCE_DIR}/include/ione_std) set(IONE_INCLUDES - ${IONE_BINARY_DIR}/include/ione/config.hpp - ) + ${IONE_BINARY_DIR}/include/ione/config.hpp + ${IONE_INCLUDE_DIR}/context.hpp + ) set(IONE_HEADERS - ) + ) set(IONE_SOURCES - ) + ${CMAKE_CURRENT_SOURCE_DIR}/context.cpp + ) -add_subdirectory(sum) +add_subdirectory(async) configure_file(config.hpp.in ${IONE_BINARY_DIR}/include/ione/config.hpp) @@ -23,50 +25,50 @@ add_library(ione STATIC) # yaclib if (NOT TARGET yaclib) - include(FetchContent) - FetchContent_Declare(yaclib - GIT_REPOSITORY https://github.com/YACLib/YACLib.git - GIT_TAG main - ) - set(YACLIB_FLAGS "CORO") - FetchContent_MakeAvailable(yaclib) -endif() + include(FetchContent) + FetchContent_Declare(yaclib + GIT_REPOSITORY https://github.com/YACLib/YACLib.git + GIT_TAG main + ) + set(YACLIB_FLAGS "CORO") + FetchContent_MakeAvailable(yaclib) +endif () target_link_libraries(ione PUBLIC yaclib) # liburing based on this # https://github.com/questdb/questdb/blob/master/core/CMakeLists.txt#L186 if (UNIX AND NOT APPLE AND CMAKE_SYSTEM_NAME MATCHES "Linux") - include(ExternalProject) - ExternalProject_Add( - liburing_git - GIT_REPOSITORY http://github.com/axboe/liburing.git - GIT_TAG liburing-2.2 - BUILD_IN_SOURCE 1 - BUILD_BYPRODUCTS "/src/liburing.a" - BUILD_COMMAND make "CC=${CMAKE_C_COMPILER}" "CXX=${CMAKE_CXX_COMPILER}" "AR=${CMAKE_AR}" "RANLIB=${CMAKE_RANLIB}" - CONFIGURE_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - LOG_BUILD ON - ) - ExternalProject_Get_Property(liburing_git SOURCE_DIR) - add_library(liburing INTERFACE) - add_dependencies(liburing liburing_git) - target_include_directories(liburing INTERFACE ${SOURCE_DIR}/src/include) - target_link_libraries(liburing INTERFACE ${SOURCE_DIR}/src/liburing.a) - target_link_libraries(ione PUBLIC liburing) + include(ExternalProject) + ExternalProject_Add( + liburing_git + GIT_REPOSITORY http://github.com/axboe/liburing.git + GIT_TAG liburing-2.2 + BUILD_IN_SOURCE 1 + BUILD_BYPRODUCTS "/src/liburing.a" + BUILD_COMMAND make "CC=${CMAKE_C_COMPILER}" "CXX=${CMAKE_CXX_COMPILER}" "AR=${CMAKE_AR}" "RANLIB=${CMAKE_RANLIB}" + CONFIGURE_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + LOG_BUILD ON + ) + ExternalProject_Get_Property(liburing_git SOURCE_DIR) + add_library(liburing INTERFACE) + add_dependencies(liburing liburing_git) + target_include_directories(liburing INTERFACE ${SOURCE_DIR}/src/include) + target_link_libraries(liburing INTERFACE ${SOURCE_DIR}/src/liburing.a) + target_link_libraries(ione PUBLIC liburing) endif () target_sources(ione - PUBLIC ${IONE_INCLUDES} - PRIVATE ${IONE_HEADERS} - PRIVATE ${IONE_SOURCES} - ) + PUBLIC ${IONE_INCLUDES} + PRIVATE ${IONE_HEADERS} + PRIVATE ${IONE_SOURCES} + ) if (WARN IN_LIST IONE_FLAGS) - include(ione_warn_lib) - message("IOneRing/library warnings: ${IONE_WARN}") + include(ione_warn_lib) + message("IOneRing/library warnings: ${IONE_WARN}") endif () target_compile_options(ione PRIVATE ${IONE_COMPILE_OPTIONS} ${IONE_WARN}) target_compile_definitions(ione PRIVATE ${IONE_DEFINITIONS}) @@ -75,11 +77,11 @@ find_package(Threads REQUIRED) target_link_libraries(ione PUBLIC Threads::Threads) target_include_directories(ione - PUBLIC ${IONE_SOURCE_DIR}/include - PUBLIC ${IONE_BINARY_DIR}/include # for config.hpp - PRIVATE ${IONE_SOURCE_DIR}/src - ) + PUBLIC ${IONE_SOURCE_DIR}/include + PUBLIC ${IONE_BINARY_DIR}/include # for config.hpp + PRIVATE ${IONE_SOURCE_DIR}/src + ) if (IONE_INSTALL) - install(TARGETS ione) + install(TARGETS ione) endif () diff --git a/src/async/CMakeLists.txt b/src/async/CMakeLists.txt new file mode 100644 index 0000000..658e48e --- /dev/null +++ b/src/async/CMakeLists.txt @@ -0,0 +1,21 @@ +list(APPEND IONE_INCLUDES + ${IONE_INCLUDE_DIR}/async/accept.hpp + ${IONE_INCLUDE_DIR}/async/close.hpp + ${IONE_INCLUDE_DIR}/async/connect.hpp + ${IONE_INCLUDE_DIR}/async/fadvise.hpp + ${IONE_INCLUDE_DIR}/async/fallocate.hpp + ${IONE_INCLUDE_DIR}/async/fsync.hpp + ${IONE_INCLUDE_DIR}/async/madvise.hpp + ${IONE_INCLUDE_DIR}/async/nop.hpp + ${IONE_INCLUDE_DIR}/async/openat.hpp + ${IONE_INCLUDE_DIR}/async/openat2.hpp + ${IONE_INCLUDE_DIR}/async/read.hpp + ${IONE_INCLUDE_DIR}/async/recv.hpp + ${IONE_INCLUDE_DIR}/async/send.hpp + ${IONE_INCLUDE_DIR}/async/splice.hpp + ${IONE_INCLUDE_DIR}/async/statx.hpp + ${IONE_INCLUDE_DIR}/async/write.hpp + ${IONE_INCLUDE_DIR}/async/timeout.hpp + ) + +add_files() diff --git a/src/context.cpp b/src/context.cpp new file mode 100644 index 0000000..9494f03 --- /dev/null +++ b/src/context.cpp @@ -0,0 +1,65 @@ +#include + +#include + +#include + +namespace ione { + +namespace fs = std::filesystem; + +void Context::Destroy() { + assert(_initialized); + io_uring_queue_exit(&_ring); + _initialized = false; +} + +Context::~Context() { + if (_initialized) { + Destroy(); + } +} + +void Context::Init(unsigned entries, unsigned flags, unsigned sq_thread_cpu, unsigned sq_thread_idle) { + assert(!_initialized); + assert(entries != 0); + io_uring_params params{.flags = flags, .sq_thread_cpu = sq_thread_cpu, .sq_thread_idle = sq_thread_idle}; + io_uring_queue_init_params(entries, &_ring, ¶ms); + _initialized = true; +} + +void Context::Init(unsigned entries, unsigned flags) { + assert(!_initialized); + assert(entries != 0); + auto ret = io_uring_queue_init(entries, &_ring, flags); + if (ret < 0) { + throw std::runtime_error("queue_init error"); + } + _initialized = true; +} + +void Context::Run() { + io_uring_submit_and_wait(&_ring, 0); + Poll(); +} + +int Context::Submit() { + return io_uring_submit(&_ring); +} + +unsigned Context::Poll() { + io_uring_cqe* cqe; + unsigned head; + unsigned counter = 0; + // TODO(kononovk): think about io_uring_wait_cqe syscall + io_uring_for_each_cqe(&_ring, head, cqe) { + auto* awaiter = static_cast(io_uring_cqe_get_data(cqe)); + awaiter->res = cqe->res; + awaiter->handle.resume(); + ++counter; + } + io_uring_cq_advance(&_ring, counter); + return counter; +} + +} // namespace ione diff --git a/src/sum/CMakeLists.txt b/src/sum/CMakeLists.txt deleted file mode 100644 index 6cdadca..0000000 --- a/src/sum/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -list(APPEND IONE_INCLUDES - ${IONE_INCLUDE_DIR}/sum/sum.hpp - ) -list(APPEND IONE_HEADERS - ) -list(APPEND IONE_HEADERS - ${CMAKE_CURRENT_SOURCE_DIR}/sum.cpp - ) - -add_files() diff --git a/src/sum/sum.cpp b/src/sum/sum.cpp deleted file mode 100644 index 53293d1..0000000 --- a/src/sum/sum.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -namespace ione { - -int Sum(int a, int b) { - return a + b; -} - -} // namespace ione From c29d45b169dc6d65bf60b647b364f8f6da3c6e49 Mon Sep 17 00:00:00 2001 From: Kononov Nikolai Date: Sat, 27 May 2023 19:25:33 +0200 Subject: [PATCH 2/6] Change CI: change OS version and compilers in CI --- .github/ISSUE_TEMPLATE/bug_report.md | 17 ++- .github/workflows/example.yml | 144 ++++++++++++++++++ .../workflows/{linux.yml => linux_test.yml} | 86 ++--------- .../{sanitizer.yml => sanitizer_test.yml} | 6 +- 4 files changed, 167 insertions(+), 86 deletions(-) create mode 100644 .github/workflows/example.yml rename .github/workflows/{linux.yml => linux_test.yml} (76%) rename .github/workflows/{sanitizer.yml => sanitizer_test.yml} (98%) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..3205926 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -12,6 +12,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +25,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/workflows/example.yml b/.github/workflows/example.yml new file mode 100644 index 0000000..4aa14ee --- /dev/null +++ b/.github/workflows/example.yml @@ -0,0 +1,144 @@ +name: Example / Linux + +on: + workflow_dispatch: + push: + branches: [ main ] + paths: [ '**.cpp', '**.hpp*', '**.cmake', '**/CMakeLists.txt' ] + pull_request: + branches: [ main ] + paths: [ '**.cpp', '**.hpp*', '**.cmake', '**/CMakeLists.txt' ] + schedule: + - cron: '0 12 * * 3' + +jobs: + main: + runs-on: 'ubuntu-${{ matrix.os }}' + strategy: + fail-fast: false + matrix: + os: [ 22.04 ] + compiler: [ clang-15, gcc-11 ] + isPR: + - ${{ github.event_name == 'pull_request' }} + env: + BUILD_TYPE: 'Debug RelWithDebInfo' + + steps: + - uses: actions/checkout@v3 + + - name: Update dependencies + run: | + sudo apt-get update + sudo apt-get install ninja-build googletest libunwind-dev + + - name: Install dependencies clang + if: 1 && !startsWith(matrix.compiler, 'gcc') + run: | + sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + ubuntu="jammy" + gcc_version=11 + llvm_version=15 + compiler=${{ matrix.compiler }} + clang_version=${compiler:6} + if [[ $clang_version -ge $llvm_version ]]; then + sudo add-apt-repository "deb http://apt.llvm.org/$ubuntu/ llvm-toolchain-$ubuntu-${clang_version} main" + fi + sudo apt-get update + sudo apt-get install clang-${clang_version} libc++-${clang_version}-dev libc++abi-${clang_version}-dev \ + gcc-${gcc_version} g++-${gcc_version} libstdc++-${gcc_version}-dev + + sudo update-alternatives \ + --install /usr/bin/gcc gcc /usr/bin/gcc-${gcc_version} 200 \ + --slave /usr/bin/g++ g++ /usr/bin/g++-${gcc_version} \ + --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-${gcc_version} \ + --slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-${gcc_version} \ + --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-${gcc_version} \ + --slave /usr/bin/gcov gcov /usr/bin/gcov-${gcc_version} \ + --slave /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-${gcc_version} \ + --slave /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-${gcc_version} + sudo update-alternatives --auto gcc + + sudo update-alternatives \ + --install /usr/bin/cpp cpp /usr/bin/cpp-${gcc_version} 200 + sudo update-alternatives --auto cpp + + - name: Install dependencies gcc + if: startsWith(matrix.compiler, 'gcc') + run: | + compiler=${{ matrix.compiler }} + version=${compiler:4} + sudo apt-get install gcc-$version g++-$version libstdc++-$version-dev + sudo update-alternatives \ + --install /usr/bin/gcc gcc /usr/bin/gcc-$version 200 \ + --slave /usr/bin/g++ g++ /usr/bin/g++-$version \ + --slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-$version \ + --slave /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-$version \ + --slave /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-$version \ + --slave /usr/bin/gcov gcov /usr/bin/gcov-$version \ + --slave /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-$version \ + --slave /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-$version + sudo update-alternatives --auto gcc + + sudo update-alternatives \ + --install /usr/bin/cpp cpp /usr/bin/cpp-$version 200 + sudo update-alternatives --auto cpp + + - name: Configure CMake + run: | + compiler=${{ matrix.compiler }} + if [[ "$compiler" == gcc* ]]; then + version=${compiler:4} + c_compiler="gcc" + compiler="g++" + else + version=${compiler:6} + c_compiler="clang-$version" + compiler="clang++-$version" + fi + + + stdlib_names=(libcxx libstdcxx) + link_options=( + "-stdlib=libc++;-lc++abi" + "-stdlib=libstdc++" + ) + compile_options=( + "-stdlib=libc++" + "-stdlib=libstdc++" + ) + + for (( j=0; j<${#stdlib_names[*]}; j+=1 )); do + for build_type in ${BUILD_TYPE[*]}; do + link_option=""; compile_option="" + if [[ "$compiler" == "g++" ]]; then + if [[ "${stdlib_names[$j]}" != "libstdcxx" ]]; then + continue # TODO(kononovk) I dunno how to get GNU GCC to work with other stdlibs + fi + else + link_option=${link_options[$j]}; compile_option=${compile_options[$j]} + if [[ "${stdlib_names[$j]}" == "libstdcxx" ]]; then + continue; + fi + fi + dir="build_${compiler}_${stdlib_names[$j]}_${build_type}" + echo $dir + + cmake -S . -B $dir \ + -DCMAKE_BUILD_TYPE="$build_type" \ + -DIONE_CXX_STANDARD="20" \ + -DIONE_EXAMPLE=ON \ + -DIONE_FLAGS="$flags" \ + -DCMAKE_CXX_COMPILER="$compiler" \ + -DCMAKE_C_COMPILER="$c_compiler" \ + -G"Ninja" \ + -DIONE_LINK_OPTIONS="$link_option" \ + -DIONE_COMPILE_OPTIONS="$compile_option" + done + done + + - name: Build + run: | + for dir in build*/; do + ninja -C $dir + done diff --git a/.github/workflows/linux.yml b/.github/workflows/linux_test.yml similarity index 76% rename from .github/workflows/linux.yml rename to .github/workflows/linux_test.yml index 0de17ed..cf6adc0 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux_test.yml @@ -1,4 +1,4 @@ -name: Linux +name: Test / Linux on: workflow_dispatch: @@ -9,75 +9,18 @@ on: branches: [ main ] paths: [ '**.cpp', '**.hpp*', '**.cmake', '**/CMakeLists.txt' ] schedule: - - cron: '0 12 * * 1-5' + - cron: '0 12 * * 3' jobs: - # TODO(kononovk) Add clang-5.0, clang-6.0, clang-7 - # Fucking cppreference liars, clang doesn't support simple variant usage, before clang-8! - # We can only support this when we remove the variant from the Result - # TODO(kononovk) Add clang-15 when it release - # TODO(kononovk) Add other compilers, like Intel C++? - # TODO(kononovk) libstdc++-7-dev, libc++ old version - main: runs-on: 'ubuntu-${{ matrix.os }}' strategy: fail-fast: false matrix: - os: [ 20.04, 22.04 ] - compiler: [ clang-9, clang-10, clang-11, clang-12, clang-13, clang-14, - gcc-10, gcc-11, gcc-12 ] + os: [ 22.04 ] + compiler: [ clang-15, gcc-11 ] isPR: - ${{ github.event_name == 'pull_request' }} - exclude: - - isPR: true - os: 20.04 - compiler: gcc-12 - - - isPR: false - os: 20.04 - compiler: gcc-12 - - - isPR: true - os: 22.04 - compiler: clang-9 - - isPR: true - os: 22.04 - compiler: clang-10 - - isPR: true - os: 22.04 - compiler: clang-11 - - isPR: true - os: 22.04 - compiler: clang-12 - - isPR: true - os: 22.04 - compiler: clang-13 - - isPR: true - os: 22.04 - compiler: clang-14 - - - isPR: true - os: 22.04 - compiler: gcc-10 - - isPR: true - os: 22.04 - compiler: gcc-11 - - isPR: true - os: 22.04 - compiler: gcc-12 - - - isPR: false - os: 22.04 - compiler: clang-8 - - isPR: false - os: 22.04 - compiler: clang-9 - - isPR: false - os: 22.04 - compiler: clang-10 - - env: BUILD_TYPE: 'Debug RelWithDebInfo' @@ -93,19 +36,9 @@ jobs: if: 1 && !startsWith(matrix.compiler, 'gcc') run: | sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - if [[ ${{ matrix.os }} == "18.04" ]]; then - ubuntu="bionic" - gcc_version=7 - llvm_version=9 - elif [[ ${{ matrix.os }} == "20.04" ]]; then - ubuntu="focal" - gcc_version=7 - llvm_version=9 - else - ubuntu="jammy" - gcc_version=9 - llvm_version=13 - fi + ubuntu="jammy" + gcc_version=11 + llvm_version=15 compiler=${{ matrix.compiler }} clang_version=${compiler:6} if [[ $clang_version -ge $llvm_version ]]; then @@ -164,7 +97,7 @@ jobs: compiler="clang++-$version" fi - + stdlib_names=(libcxx libstdcxx) link_options=( "-stdlib=libc++;-lc++abi" @@ -194,7 +127,7 @@ jobs: cmake -S . -B $dir \ -DCMAKE_BUILD_TYPE="$build_type" \ -DIONE_CXX_STANDARD="20" \ - -DIONE_TEST=SINGLE \ + -DIONE_TEST=ON \ -DIONE_FLAGS="$flags" \ -DCMAKE_CXX_COMPILER="$compiler" \ -DCMAKE_C_COMPILER="$c_compiler" \ @@ -210,6 +143,7 @@ jobs: ninja -C $dir done + - name: Test run: | for dir in build*/; do diff --git a/.github/workflows/sanitizer.yml b/.github/workflows/sanitizer_test.yml similarity index 98% rename from .github/workflows/sanitizer.yml rename to .github/workflows/sanitizer_test.yml index a47da83..18cdde5 100644 --- a/.github/workflows/sanitizer.yml +++ b/.github/workflows/sanitizer_test.yml @@ -10,7 +10,7 @@ on: paths: [ '**.cpp', '**.hpp*', '**.cmake', '**/CMakeLists.txt' ] types: [ assigned ] schedule: - - cron: '0 12 * * 1-5' + - cron: '0 12 * * 3' jobs: # TODO(kononovk) Add MEMSAN? @@ -59,7 +59,7 @@ jobs: run: | flags=${{ matrix.flags }} build_type=${{ matrix.build_type }} - compiler_names=(clang gnu apple_clang) + compiler_names=(clang gnu) stdlib_names=(libcxx libstdcxx default) linker="" cxx_compilers=(clang++-14 g++) @@ -75,7 +75,7 @@ jobs: slowdown=1 linker="-fuse-ld=lld-14" - + for (( i=0; i<${#cxx_compilers[*]}; i+=1 )); do for (( j=0; j<${#link_options[*]}; j+=1 )); do link_option=${link_options[$j]}; compile_option=${compile_options[$j]} From dcde8d5fcd84a9437b0e21a077adc118392a24f8 Mon Sep 17 00:00:00 2001 From: Kononov Nikolai Date: Sat, 27 May 2023 19:26:48 +0200 Subject: [PATCH 3/6] Add examples of usage --- example/CMakeLists.txt | 11 ++ example/cat.cpp | 108 +++++++++++++++++ example/cp.cpp | 106 +++++++++++++++++ example/liburing_cat.cpp | 164 ++++++++++++++++++++++++++ example/liburing_cp.cpp | 244 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 633 insertions(+) create mode 100644 example/CMakeLists.txt create mode 100644 example/cat.cpp create mode 100644 example/cp.cpp create mode 100644 example/liburing_cat.cpp create mode 100644 example/liburing_cp.cpp diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..73be7cc --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable(cat cat.cpp) +target_link_libraries(cat PRIVATE ione) + +add_executable(liburing_cat liburing_cat.cpp) +target_link_libraries(liburing_cat PRIVATE liburing) + +add_executable(cp cp.cpp) +target_link_libraries(cp PRIVATE ione) + +add_executable(liburing_cp liburing_cp.cpp) +target_link_libraries(liburing_cp PRIVATE liburing) diff --git a/example/cat.cpp b/example/cat.cpp new file mode 100644 index 0000000..1c597fb --- /dev/null +++ b/example/cat.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +namespace { + +uint64_t GetFileSize(int fd) { + struct stat st; + + if (fstat(fd, &st) < 0) { + perror("fstat"); + return -1; + } + + if (S_ISBLK(st.st_mode)) { + unsigned long long bytes; + if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) { + perror("ioctl"); + return -1; + } + return bytes; + } else if (S_ISREG(st.st_mode)) + return st.st_size; + + return -1; +} + +void OutputToConsole(const char* buf, size_t len) { + while (len--) { + fputc(*buf++, stdout); + } +} + +} // namespace + +yaclib::Task<> ReadFile(ione::Context& ctx, const char* file_path) { + int file_fd = open(file_path, O_RDONLY); + if (file_fd < 0) { + throw std::runtime_error{"File open error"}; + } + + auto file_size = GetFileSize(file_fd); + if (file_size == -1) { + throw std::runtime_error{"Incorrect file size"}; + } + constexpr size_t kSize = 100; + char buffer[kSize]; + auto& current = co_await yaclib::CurrentExecutor(); + size_t offset = 0; + while (file_size != 0) { + auto res = co_await Read(ctx, file_fd, buffer, std::min(file_size, kSize), offset); + if (res < 0) { + throw std::runtime_error{"Error during reading"}; + } + // fprintf(stderr, "file_path %s, res: %d\n", file_path, res); + OutputToConsole(buffer, res); + co_await On(current); + file_size -= res; + offset += res; + } + co_return{}; +} + +int main(int file_num, char* argv[]) { + if (file_num < 2) { + std::fprintf(stderr, "Usage: %s [file name] <[file name] ...>\n", argv[0]); + return 1; + } + + ione::Context ctx; + ctx.Init(10, 0); + + yaclib::FairThreadPool tp1{1}; + auto coro = [&]() -> yaclib::Task<> { + for (int i = 1; i < file_num; ++i) { + co_await ReadFile(ctx, argv[i]); + } + co_return{}; + }; + auto f = coro().ToFuture(tp1); + + std::atomic_bool run{true}; + std::thread poller{[&] { + while (run.load()) { + ctx.Run(); + } + }}; + + Wait(f); + + run.store(false); + poller.join(); + + tp1.HardStop(); + tp1.Wait(); + + return 0; +} diff --git a/example/cp.cpp b/example/cp.cpp new file mode 100644 index 0000000..0511cb9 --- /dev/null +++ b/example/cp.cpp @@ -0,0 +1,106 @@ +#include + +#include +#include + +#include +#include + +#include +#include + +namespace { + +uint64_t GetFileSize(int fd) { + struct stat st; + + if (fstat(fd, &st) < 0) { + perror("fstat"); + return -1; + } + + if (S_ISBLK(st.st_mode)) { + unsigned long long bytes; + if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) { + perror("ioctl"); + return -1; + } + return bytes; + } else if (S_ISREG(st.st_mode)) + return st.st_size; + + return -1; +} + +} // namespace + +yaclib::Task<> CopyFile(ione::Context& ctx, int src_fd, int dst_fd, uint64_t len) { + constexpr size_t kSize = 1024; + char buffer[kSize]; + size_t offset = 0; + size_t write_offset = 0; + while (len != 0) { + auto res = co_await Read(ctx, src_fd, buffer, std::min(len, kSize), offset); + if (res < 0) { + throw std::runtime_error{"Error during reading"}; + } + len -= res; + offset += res; + + while (res != 0) { + auto written_len = co_await Write(ctx, dst_fd, buffer, res, write_offset); + res -= written_len; + write_offset += written_len; + } + } + co_return{}; +} + +int main(int argc, char* argv[]) { + if (argc < 3) { + std::fprintf(stderr, "Usage: %s [file name] <[file name] ...>\n", argv[0]); + return 1; + } + auto infd = open(argv[1], O_RDONLY); + if (infd < 0) { + perror("open infile"); + return 1; + } + auto outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (outfd < 0) { + perror("open outfile"); + return 1; + } + ione::Context ctx; + static constexpr int kEntriesNum = 2; + static constexpr int kFlags = 0; + ctx.Init(kEntriesNum, kFlags); + + auto insize = GetFileSize(infd); + if (insize == -1) { + throw std::runtime_error{"Incorrect file size"}; + } + + auto cp_future = CopyFile(ctx, infd, outfd, insize).ToFuture(); + + std::atomic_bool run{true}; + std::thread poller{[&] { + while (run.load()) { + ctx.Run(); + } + }}; + + try { + std::ignore = std::move(cp_future).Get().Ok(); + } catch (const std::exception& err) { + perror(err.what()); + return 1; + } + + run.store(false); + poller.join(); + + close(infd); + close(outfd); + return 0; +} diff --git a/example/liburing_cat.cpp b/example/liburing_cat.cpp new file mode 100644 index 0000000..776d326 --- /dev/null +++ b/example/liburing_cat.cpp @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include +#include + +#define QUEUE_DEPTH 1 +#define BLOCK_SZ 1024 + +struct file_info { + off_t file_sz; + struct iovec iovecs[]; /* Referred by readv/writev */ +}; + +/* + * Returns the size of the file whose open file descriptor is passed in. + * Properly handles regular file and block devices as well. Pretty. + * */ + +off_t get_file_size(int fd) { + struct stat st; + + if (fstat(fd, &st) < 0) { + perror("fstat"); + return -1; + } + + if (S_ISBLK(st.st_mode)) { + unsigned long long bytes; + if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) { + perror("ioctl"); + return -1; + } + return bytes; + } else if (S_ISREG(st.st_mode)) + return st.st_size; + + return -1; +} + +/* + * Output a string of characters of len length to stdout. + * We use buffered output here to be efficient, + * since we need to output character-by-character. + * */ +void output_to_console(char* buf, int len) { + while (len--) { + fputc(*buf++, stdout); + } +} + +/* + * Wait for a completion to be available, fetch the data from + * the readv operation and print it to the console. + * */ + +int get_completion_and_print(struct io_uring* ring) { + struct io_uring_cqe* cqe; + int ret = io_uring_wait_cqe(ring, &cqe); + if (ret < 0) { + perror("io_uring_wait_cqe"); + return 1; + } + if (cqe->res < 0) { + fprintf(stderr, "Async readv failed.\n"); + return 1; + } + struct file_info* fi = (struct file_info*)io_uring_cqe_get_data(cqe); + int blocks = (int)fi->file_sz / BLOCK_SZ; + if (fi->file_sz % BLOCK_SZ) + blocks++; + for (int i = 0; i < blocks; i++) + output_to_console((char*)fi->iovecs[i].iov_base, fi->iovecs[i].iov_len); + + io_uring_cqe_seen(ring, cqe); + return 0; +} + +/* + * Submit the readv request via liburing + * */ +int submit_read_request(char* file_path, struct io_uring* ring) { + int file_fd = open(file_path, O_RDONLY); + if (file_fd < 0) { + perror("open"); + return 1; + } + off_t file_sz = get_file_size(file_fd); + off_t bytes_remaining = file_sz; + off_t offset = 0; + int current_block = 0; + int blocks = (int)file_sz / BLOCK_SZ; + if (file_sz % BLOCK_SZ) + blocks++; + struct file_info* fi = (struct file_info*)malloc(sizeof(*fi) + (sizeof(struct iovec) * blocks)); + char* buff = (char*)malloc(file_sz); + if (!buff) { + fprintf(stderr, "Unable to allocate memory.\n"); + return 1; + } + + /* + * For each block of the file we need to read, we allocate an iovec struct + * which is indexed into the iovecs array. This array is passed in as part + * of the submission. If you don't understand this, then you need to look + * up how the readv() and writev() system calls work. + * */ + while (bytes_remaining) { + off_t bytes_to_read = bytes_remaining; + if (bytes_to_read > BLOCK_SZ) + bytes_to_read = BLOCK_SZ; + + offset += bytes_to_read; + fi->iovecs[current_block].iov_len = bytes_to_read; + void* buf; + if (posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ)) { + perror("posix_memalign"); + return 1; + } + fi->iovecs[current_block].iov_base = buf; + + current_block++; + bytes_remaining -= bytes_to_read; + } + fi->file_sz = file_sz; + + /* Get an SQE */ + struct io_uring_sqe* sqe = io_uring_get_sqe(ring); + /* Setup a readv operation */ + io_uring_prep_readv(sqe, file_fd, fi->iovecs, blocks, 0); + /* Set user data */ + io_uring_sqe_set_data(sqe, fi); + /* Finally, submit the request */ + io_uring_submit(ring); + + return 0; +} + +int main(int argc, char* argv[]) { + struct io_uring ring; + + if (argc < 2) { + fprintf(stderr, "Usage: %s [file name] <[file name] ...>\n", argv[0]); + return 1; + } + + /* Initialize io_uring */ + io_uring_queue_init(QUEUE_DEPTH, &ring, 0); + + for (int i = 1; i < argc; i++) { + int ret = submit_read_request(argv[i], &ring); + if (ret) { + fprintf(stderr, "Error reading file: %s\n", argv[i]); + return 1; + } + get_completion_and_print(&ring); + } + + /* Call the clean-up function. */ + io_uring_queue_exit(&ring); + return 0; +} diff --git a/example/liburing_cp.cpp b/example/liburing_cp.cpp new file mode 100644 index 0000000..11b62b2 --- /dev/null +++ b/example/liburing_cp.cpp @@ -0,0 +1,244 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QD 2 +#define BS (16 * 1024) + +static int infd, outfd; + +struct io_data { + int read; + off_t first_offset, offset; + size_t first_len; + struct iovec iov; +}; + +static int setup_context(unsigned entries, struct io_uring* ring) { + int ret; + + ret = io_uring_queue_init(entries, ring, 0); + if (ret < 0) { + fprintf(stderr, "queue_init: %s\n", strerror(-ret)); + return -1; + } + + return 0; +} + +static int get_file_size(int fd, off_t* size) { + struct stat st; + + if (fstat(fd, &st) < 0) + return -1; + if (S_ISREG(st.st_mode)) { + *size = st.st_size; + return 0; + } else if (S_ISBLK(st.st_mode)) { + unsigned long long bytes; + + if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) + return -1; + + *size = bytes; + return 0; + } + return -1; +} + +static void queue_prepped(struct io_uring* ring, struct io_data* data) { + struct io_uring_sqe* sqe; + + sqe = io_uring_get_sqe(ring); + assert(sqe); + + if (data->read) + io_uring_prep_readv(sqe, infd, &data->iov, 1, data->offset); + else + io_uring_prep_writev(sqe, outfd, &data->iov, 1, data->offset); + + io_uring_sqe_set_data(sqe, data); +} + +static int queue_read(struct io_uring* ring, off_t size, off_t offset) { + io_uring_sqe* sqe; + io_data* data; + + data = static_cast(malloc(size + sizeof(*data))); + if (!data) + return 1; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + free(data); + return 1; + } + + data->read = 1; + data->offset = data->first_offset = offset; + + data->iov.iov_base = data + 1; + data->iov.iov_len = size; + data->first_len = size; + + io_uring_prep_readv(sqe, infd, &data->iov, 1, offset); + io_uring_sqe_set_data(sqe, data); + return 0; +} + +static void queue_write(struct io_uring* ring, struct io_data* data) { + data->read = 0; + data->offset = data->first_offset; + + data->iov.iov_base = data + 1; + data->iov.iov_len = data->first_len; + + queue_prepped(ring, data); + io_uring_submit(ring); +} + +int copy_file(struct io_uring* ring, off_t insize) { + unsigned long reads, writes; + struct io_uring_cqe* cqe; + off_t write_left, offset; + int ret; + + write_left = insize; + writes = reads = offset = 0; + + while (insize || write_left) { + int had_reads, got_comp; + + /* Queue up as many reads as we can */ + had_reads = reads; + while (insize) { + off_t this_size = insize; + + if (reads + writes >= QD) + break; + if (this_size > BS) + this_size = BS; + else if (!this_size) + break; + + if (queue_read(ring, this_size, offset)) + break; + + insize -= this_size; + offset += this_size; + reads++; + } + + if (had_reads != reads) { + ret = io_uring_submit(ring); + if (ret < 0) { + fprintf(stderr, "io_uring_submit: %s\n", strerror(-ret)); + break; + } + } + + /* Queue is full at this point. Let's find at least one completion */ + got_comp = 0; + while (write_left) { + struct io_data* data; + + if (!got_comp) { + ret = io_uring_wait_cqe(ring, &cqe); + got_comp = 1; + } else { + ret = io_uring_peek_cqe(ring, &cqe); + if (ret == -EAGAIN) { + cqe = NULL; + ret = 0; + } + } + if (ret < 0) { + fprintf(stderr, "io_uring_peek_cqe: %s\n", strerror(-ret)); + return 1; + } + if (!cqe) + break; + + data = static_cast(io_uring_cqe_get_data(cqe)); + if (cqe->res < 0) { + if (cqe->res == -EAGAIN) { + queue_prepped(ring, data); + io_uring_cqe_seen(ring, cqe); + continue; + } + fprintf(stderr, "cqe failed: %s\n", strerror(-cqe->res)); + return 1; + } else if (cqe->res != data->iov.iov_len) { + /* short read/write; adjust and requeue */ + char* data_iov_base = static_cast(data->iov.iov_base); + data_iov_base += cqe->res; + data->iov.iov_base = data_iov_base; + data->iov.iov_len -= cqe->res; + queue_prepped(ring, data); + io_uring_cqe_seen(ring, cqe); + continue; + } + + /* + * All done. If write, nothing else to do. If read, + * queue up corresponding write. + * */ + + if (data->read) { + queue_write(ring, data); + write_left -= data->first_len; + reads--; + writes++; + } else { + free(data); + writes--; + } + io_uring_cqe_seen(ring, cqe); + } + } + + return 0; +} + +int main(int argc, char* argv[]) { + struct io_uring ring; + off_t insize; + int ret; + + if (argc < 3) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + infd = open(argv[1], O_RDONLY); + if (infd < 0) { + perror("open infile"); + return 1; + } + + outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (outfd < 0) { + perror("open outfile"); + return 1; + } + + if (setup_context(QD, &ring)) + return 1; + + if (get_file_size(infd, &insize)) + return 1; + + ret = copy_file(&ring, insize); + + close(infd); + close(outfd); + io_uring_queue_exit(&ring); + return ret; +} From 79d4857acb729b6b4995b41f1ac943722e7d6f5b Mon Sep 17 00:00:00 2001 From: Kononov Nikolai Date: Sat, 27 May 2023 19:27:10 +0200 Subject: [PATCH 4/6] Add unit tests --- test/CMakeLists.txt | 87 ++++++++++++++++++++------------------- test/unit/compilation.cpp | 26 ++++++++++++ test/unit/context.cpp | 16 +++++++ test/unit/sum/sum.cpp | 11 ----- test/unit/timers.cpp | 51 +++++++++++++++++++++++ test/util/time.hpp | 35 ++++++++++++++++ 6 files changed, 172 insertions(+), 54 deletions(-) create mode 100644 test/unit/compilation.cpp create mode 100644 test/unit/context.cpp delete mode 100644 test/unit/sum/sum.cpp create mode 100644 test/unit/timers.cpp create mode 100644 test/util/time.hpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 85808e1..3e91550 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,73 +1,74 @@ cmake_minimum_required(VERSION 3.13) if (NOT IONE_LINK_OPTIONS AND NOT IONE_COMPILE_OPTIONS) - find_package(GTest QUIET) + find_package(GTest QUIET) endif () if (NOT GTEST_FOUND) - include(FetchContent) - FetchContent_Declare(googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG main - ) - set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) - set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) # May be enabled later - # For Windows: Prevent overriding the parent project's compiler/linker settings - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - FetchContent_MakeAvailable(googletest) + include(FetchContent) + FetchContent_Declare(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG main + ) + set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) + set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) # May be enabled later + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) endif () set(TEST_UTIL_INCLUDES - ) + util/time.hpp + ) set(TEST_UTIL_SOURCES - ) + ) -set(IONE_EXAMPLE_SOURCES - ) set(IONE_UNIT_SOURCES - unit/sum/sum - ) + unit/compilation + unit/context + unit/timers + ) -set(IONE_TEST_SOURCES ${IONE_UNIT_SOURCES} ${IONE_EXAMPLE_SOURCES}) +set(IONE_TEST_SOURCES ${IONE_UNIT_SOURCES}) set(OLD_IONE_DEFINITIONS IONE_DEFINITIONS) if (NOT IONE_DEFINITIONS MATCHES "IONE_CI_SLOWDOWN") - list(APPEND IONE_DEFINITIONS "IONE_CI_SLOWDOWN=1") + list(APPEND IONE_DEFINITIONS "IONE_CI_SLOWDOWN=1") endif () function(ione_add_test TEST_NAME) - target_compile_options(${TEST_NAME} PRIVATE ${IONE_WARN}) - target_compile_definitions(${TEST_NAME} PRIVATE ${IONE_DEFINITIONS}) - target_sources(${TEST_NAME} - PRIVATE ${TEST_UTIL_INCLUDES} - ) - target_link_libraries(${TEST_NAME} - PRIVATE GTest::gtest - PRIVATE ione - ) - target_include_directories(${TEST_NAME} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE ${IONE_SOURCE_DIR}/src - ) - add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) + target_compile_options(${TEST_NAME} PRIVATE ${IONE_WARN}) + target_compile_definitions(${TEST_NAME} PRIVATE ${IONE_DEFINITIONS}) + target_sources(${TEST_NAME} + PRIVATE ${TEST_UTIL_INCLUDES} + ) + target_link_libraries(${TEST_NAME} + PRIVATE GTest::gtest + PRIVATE ione + ) + target_include_directories(${TEST_NAME} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${IONE_SOURCE_DIR}/src + ) + add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) endfunction() if (WARN IN_LIST IONE_FLAGS) - include(ione_warn_test) - message("IOneRing/test warnings: ${IONE_WARN}") + include(ione_warn_test) + message("IOneRing/test warnings: ${IONE_WARN}") endif () if (IONE_TEST STREQUAL "SINGLE") - list(TRANSFORM IONE_TEST_SOURCES APPEND .cpp) - add_executable(ione_test test.cpp ${IONE_TEST_SOURCES}) - ione_add_test(ione_test) + list(TRANSFORM IONE_TEST_SOURCES APPEND .cpp) + add_executable(ione_test test.cpp ${IONE_TEST_SOURCES}) + ione_add_test(ione_test) else () - foreach (TEST_SOURCE ${IONE_TEST_SOURCES}) - string(REPLACE "/" "_" TEST_NAME ${TEST_SOURCE}) - add_executable(${TEST_NAME} test.cpp ${TEST_SOURCE}.cpp) - ione_add_test(${TEST_NAME}) - endforeach () + foreach (TEST_SOURCE ${IONE_TEST_SOURCES}) + string(REPLACE "/" "_" TEST_NAME ${TEST_SOURCE}) + add_executable(${TEST_NAME} test.cpp ${TEST_SOURCE}.cpp) + ione_add_test(${TEST_NAME}) + endforeach () endif () unset(IONE_TEST_SOURCES) diff --git a/test/unit/compilation.cpp b/test/unit/compilation.cpp new file mode 100644 index 0000000..f9d131e --- /dev/null +++ b/test/unit/compilation.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { + +TEST(Compilation, JustWorks) { +} + +} // namespace diff --git a/test/unit/context.cpp b/test/unit/context.cpp new file mode 100644 index 0000000..0ba467d --- /dev/null +++ b/test/unit/context.cpp @@ -0,0 +1,16 @@ +#include + +#include + +namespace { + +TEST(Context, EmptySubmit) { + static constexpr int kEntries = 1; + static constexpr int kFlags = 0; + + ione::Context ctx; + ctx.Init(kEntries, kFlags); + EXPECT_EQ(ctx.Submit(), 0); +} + +} // namespace diff --git a/test/unit/sum/sum.cpp b/test/unit/sum/sum.cpp deleted file mode 100644 index 16f4683..0000000 --- a/test/unit/sum/sum.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -#include - -namespace { - -TEST(Simple, JustWorks) { - EXPECT_EQ(ione::Sum(2, 3), 5); -} - -} // namespace diff --git a/test/unit/timers.cpp b/test/unit/timers.cpp new file mode 100644 index 0000000..3e80304 --- /dev/null +++ b/test/unit/timers.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +#include + +#include + +#include + +namespace { + +using namespace std::chrono_literals; + +template +yaclib::Task<> Sleep(ione::Context& ctx, const std::chrono::duration& sleep_duration) { + co_await ione::SleepFor(ctx, sleep_duration); + std::cout << std::chrono::duration_cast(sleep_duration).count() << " seconds passed" + << std::endl; + co_return{}; +} + +TEST(Timers, SleepNonBlocking) { + ione::Context ctx; + ctx.Init(3, 0); + yaclib::FairThreadPool tp(1); + test::util::StopWatch watch; + auto f1 = Sleep(ctx, 1s).ToFuture(tp); + auto f2 = Sleep(ctx, 2s).ToFuture(tp); + auto f3 = Sleep(ctx, 3s).ToFuture(tp); + + std::atomic_bool run{true}; + std::thread poller{[&] { + while (run.load()) { + ctx.Run(); + } + }}; + + yaclib::Wait(f1, f2, f3); + EXPECT_EQ(std::chrono::duration_cast(watch.Elapsed()).count(), 3); + EXPECT_NO_THROW(std::ignore = std::move(f1).Get().Ok()); + EXPECT_NO_THROW(std::ignore = std::move(f2).Get().Ok()); + EXPECT_NO_THROW(std::ignore = std::move(f3).Get().Ok()); + run.store(false); + tp.HardStop(); + tp.Wait(); + poller.join(); +} + +} // namespace diff --git a/test/util/time.hpp b/test/util/time.hpp new file mode 100644 index 0000000..c409041 --- /dev/null +++ b/test/util/time.hpp @@ -0,0 +1,35 @@ +#pragma once +#include + +namespace test::util { + +using Duration = std::chrono::nanoseconds; + +template +class StopWatch { + using TimePoint = typename Clock::time_point; + + public: + StopWatch() : start_(Now()) { + static_assert(Clock::is_steady, "Steady clock required"); + } + + Duration Elapsed() const { + return Now() - start_; + } + + Duration Restart() { + auto elapsed = Elapsed(); + start_ = Now(); + return elapsed; + } + + static TimePoint Now() { + return Clock::now(); + } + + private: + TimePoint start_; +}; + +} // namespace test::util From 6c07383e8eb68d2f5b1bb71112026564b69eecca Mon Sep 17 00:00:00 2001 From: Kononov Nikolai Date: Sat, 27 May 2023 19:27:47 +0200 Subject: [PATCH 5/6] Change README, add badges with links --- README.md | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c208041..8fd0956 100644 --- a/README.md +++ b/README.md @@ -1 +1,32 @@ -# IOneRing \ No newline at end of file +# IOneRing + +[One io_uring to rule all IO](https://github.com/YACLib/IOneRing) + +[![GitHub license]( +https://img.shields.io/badge/license-MIT-blue.svg)]( +https://raw.githubusercontent.com/YACLib/IOneRing/main/LICENSE) + +[![Linux]( +https://github.com/YACLib/IOneRing/actions/workflows/linux_test.yml/badge.svg?branch=main)]( +https://github.com/YACLib/IOneRing/actions/workflows/linux_test.yml) + +[![Sanitizers]( +https://github.com/YACLib/IOneRing/actions/workflows/sanitizer_test.yml/badge.svg?branch=main)]( +https://github.com/YACLib/IOneRing/actions/workflows/sanitizer_test.yml) + +[![Examples]( +https://github.com/YACLib/IOneRing/actions/workflows/example.yml/badge.svg?branch=main)]( +https://github.com/YACLib/IOneRing/actions/workflows/example.yml) + + + +[![Discord]( +https://discordapp.com/api/guilds/898966884471423026/widget.png)]( +https://discord.gg/xy2fDKj8VZ) From 0b6fd09bd9e67384bd5e5aa1864c1a7cb1eb7d7e Mon Sep 17 00:00:00 2001 From: Kononov Nikolai Date: Sat, 27 May 2023 19:28:19 +0200 Subject: [PATCH 6/6] Fix cmake format --- CMakeLists.txt | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c698067..c1badbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.10) project(ione - LANGUAGES CXX - ) + LANGUAGES CXX + ) set(IONE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(IONE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) if (CMAKE_CXX_COMPILER_ID STREQUAL GNU) - add_compile_options(-fcoroutines) + add_compile_options(-fcoroutines) endif () list(APPEND YACLIB_COMPILE_OPTIONS "${YACLIB_CORO_FLAGS}") @@ -16,7 +16,7 @@ list(APPEND YACLIB_COMPILE_OPTIONS "${YACLIB_CORO_FLAGS}") # Include guards if (IONE_SOURCE_DIR STREQUAL IONE_BINARY_DIR) - message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.") + message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.") endif () set(CMAKE_CXX_STANDARD 20) @@ -31,10 +31,10 @@ list(APPEND CMAKE_MODULE_PATH "${IONE_SOURCE_DIR}/cmake") include(ione_flags) if (IONE_INSTALL) - include(GNUInstallDirs) - install(DIRECTORY ${IONE_SOURCE_DIR}/include/ione TYPE INCLUDE) - install(DIRECTORY ${IONE_BINARY_DIR}/include/ione TYPE INCLUDE) -endif() + include(GNUInstallDirs) + install(DIRECTORY ${IONE_SOURCE_DIR}/include/ione TYPE INCLUDE) + install(DIRECTORY ${IONE_BINARY_DIR}/include/ione TYPE INCLUDE) +endif () add_link_options(${IONE_LINK_OPTIONS}) add_compile_options(${IONE_COMPILE_OPTIONS}) @@ -42,11 +42,15 @@ add_compile_options(${IONE_COMPILE_OPTIONS}) add_subdirectory(src) # Create static library if (IONE_TEST) - enable_testing() - add_subdirectory(test) - message("CMAKE_CXX_COMPILER_ID : ${CMAKE_CXX_COMPILER_ID}") - message("CMAKE_CXX_SIMULATE_ID : ${CMAKE_CXX_SIMULATE_ID}") - message("IONE_LINK_OPTIONS : ${IONE_LINK_OPTIONS}") - message("IONE_COMPILE_OPTIONS: ${IONE_COMPILE_OPTIONS}") - message("IONE_DEFINITIONS : ${IONE_DEFINITIONS}") + enable_testing() + add_subdirectory(test) + message("CMAKE_CXX_COMPILER_ID : ${CMAKE_CXX_COMPILER_ID}") + message("CMAKE_CXX_SIMULATE_ID : ${CMAKE_CXX_SIMULATE_ID}") + message("IONE_LINK_OPTIONS : ${IONE_LINK_OPTIONS}") + message("IONE_COMPILE_OPTIONS: ${IONE_COMPILE_OPTIONS}") + message("IONE_DEFINITIONS : ${IONE_DEFINITIONS}") +endif () + +if (IONE_EXAMPLE) + add_subdirectory(example) endif ()