From 1688905f7f3b3824d6b08f589a651051387c3eea Mon Sep 17 00:00:00 2001 From: ouczb Date: Mon, 29 Jan 2024 12:16:21 +0800 Subject: [PATCH] upload co_context --- .../co_context/detail/io_context_meta.h | 15 ++ .../co_context/detail/lazy_io_awaiter.h | 29 +++ .../include/co_context/detail/spsc_cursor.h | 205 ++++++++++++++++ .../include/co_context/detail/task_info.h | 13 ++ .../include/co_context/detail/task_promise.h | 221 ++++++++++++++++++ .../include/co_context/detail/thread_meta.h | 16 ++ .../include/co_context/detail/type.h | 18 ++ .../include/co_context/detail/worker_meta.h | 50 ++++ .../include/co_context/io_context.h | 45 ++-- .../co_context/include/co_context/lazy_io.h | 12 + .../co_context/include/co_context/task.h | 45 ++++ .../src/co_context/detail/worker_meta.cpp | 54 +++++ .../co_context/src/co_context/io_context.cpp | 70 +++++- engine/3rdparty/co_context/src/main.cpp | 20 ++ .../co_context/test/co_future_promise.cpp | 93 ++++++++ engine/3rdparty/co_context/xmake.lua | 6 +- engine/src/main.cpp | 2 +- engine/xmake.lua | 2 +- xmake.lua | 2 +- 19 files changed, 888 insertions(+), 30 deletions(-) create mode 100644 engine/3rdparty/co_context/include/co_context/detail/io_context_meta.h create mode 100644 engine/3rdparty/co_context/include/co_context/detail/lazy_io_awaiter.h create mode 100644 engine/3rdparty/co_context/include/co_context/detail/spsc_cursor.h create mode 100644 engine/3rdparty/co_context/include/co_context/detail/task_info.h create mode 100644 engine/3rdparty/co_context/include/co_context/detail/task_promise.h create mode 100644 engine/3rdparty/co_context/include/co_context/detail/thread_meta.h create mode 100644 engine/3rdparty/co_context/include/co_context/detail/type.h create mode 100644 engine/3rdparty/co_context/include/co_context/detail/worker_meta.h create mode 100644 engine/3rdparty/co_context/include/co_context/lazy_io.h create mode 100644 engine/3rdparty/co_context/include/co_context/task.h create mode 100644 engine/3rdparty/co_context/src/co_context/detail/worker_meta.cpp create mode 100644 engine/3rdparty/co_context/src/main.cpp create mode 100644 engine/3rdparty/co_context/test/co_future_promise.cpp diff --git a/engine/3rdparty/co_context/include/co_context/detail/io_context_meta.h b/engine/3rdparty/co_context/include/co_context/detail/io_context_meta.h new file mode 100644 index 0000000..bf7b780 --- /dev/null +++ b/engine/3rdparty/co_context/include/co_context/detail/io_context_meta.h @@ -0,0 +1,15 @@ +#include +#include + +namespace co_context::detail { + + struct io_context_meta_type { + std::mutex mtx; + std::condition_variable cv; + ctx_id_t create_count; // Do not initialize this + ctx_id_t ready_count; // Do not initialize this + }; + + inline io_context_meta_type io_context_meta; + +} // namespace co_context::detail \ No newline at end of file diff --git a/engine/3rdparty/co_context/include/co_context/detail/lazy_io_awaiter.h b/engine/3rdparty/co_context/include/co_context/detail/lazy_io_awaiter.h new file mode 100644 index 0000000..9c41366 --- /dev/null +++ b/engine/3rdparty/co_context/include/co_context/detail/lazy_io_awaiter.h @@ -0,0 +1,29 @@ +#include +#include +#include "task_info.h" +namespace co_context::detail { + class lazy_awaiter { + protected: + task_info io_info; + public: + lazy_awaiter() noexcept { + this_thread.worker->forward_lazy(); + } + [[nodiscard]] + int32_t result() const noexcept { + return io_info.result; + } + static constexpr bool await_ready() noexcept { return false; } + int32_t await_resume() const noexcept { + return result(); + } + void await_suspend(std::coroutine_handle<> current) noexcept { + io_info.handle = current; + } + }; + + struct lazy_write : lazy_awaiter { + inline + lazy_write(int fd, std::span buf, uint64_t offset) noexcept {} + }; +} \ No newline at end of file diff --git a/engine/3rdparty/co_context/include/co_context/detail/spsc_cursor.h b/engine/3rdparty/co_context/include/co_context/detail/spsc_cursor.h new file mode 100644 index 0000000..46389e7 --- /dev/null +++ b/engine/3rdparty/co_context/include/co_context/detail/spsc_cursor.h @@ -0,0 +1,205 @@ +#pragma once +#include "type.h" +namespace co_context { + + template< + std::unsigned_integral T, + T capacity, + safety is_thread_safe = safety::safe, + bool is_blocking = is_thread_safe> + struct spsc_cursor { + static_assert(std::has_single_bit(capacity)); + static_assert(!is_thread_safe || std::atomic::is_always_lock_free); + static_assert( + is_thread_safe || !is_blocking, + "a thread-unsafe instance " + "can not be blocking" + ); + + inline static constexpr T mask = capacity - 1; + + T m_head = 0; + T m_tail = 0; + + [[nodiscard]] + inline T head() const noexcept { + return m_head & mask; + } + + [[nodiscard]] + inline T tail() const noexcept { + return m_tail & mask; + } + + [[nodiscard]] + inline T raw_head() const noexcept { + return m_head; + } + + [[nodiscard]] + inline T raw_tail() const noexcept { + return m_tail; + } + + [[nodiscard]] + inline bool is_empty() const noexcept { + return m_head == m_tail; + } + + [[nodiscard]] + inline T size() const noexcept { + return m_tail - m_head; + } + + [[nodiscard]] + inline T available_number() const noexcept { + return capacity - (m_tail - m_head); + } + + [[nodiscard]] + inline bool is_available() const noexcept { + return bool(capacity - (m_tail - m_head)); + } + + [[nodiscard]] + inline T load_head() const noexcept { + if constexpr (is_thread_safe) { + return as_c_atomic(m_head).load(std::memory_order_acquire) & mask; + } + else { + return head(); + } + } + + [[nodiscard]] + inline T load_tail() const noexcept { + if constexpr (is_thread_safe) { + return as_c_atomic(m_tail).load(std::memory_order_acquire) & mask; + } + else { + return tail(); + } + } + + [[nodiscard]] + inline T load_raw_head() const noexcept { + if constexpr (is_thread_safe) { + return as_c_atomic(m_head).load(std::memory_order_acquire); + } + else { + return raw_head(); + } + } + + [[nodiscard]] + inline T load_raw_tail() const noexcept { + if constexpr (is_thread_safe) { + return as_c_atomic(m_tail).load(std::memory_order_acquire); + } + else { + return raw_tail(); + } + } + + [[nodiscard]] + inline T load_raw_tail_relaxed() const noexcept { + if constexpr (is_thread_safe) { + return as_c_atomic(m_tail).load(std::memory_order_relaxed); + } + else { + return raw_tail(); + } + } + + // inline void set_raw_tail(T tail) const noexcept { m_tail = tail; } + + inline void store_raw_tail(T tail) noexcept { + if constexpr (is_thread_safe) { + as_atomic(m_tail).store(tail, std::memory_order_release); + } + else { + m_tail = tail; + } + } + + [[nodiscard]] + inline bool is_empty_load_head() const noexcept { + return m_tail == load_raw_head(); + } + + [[nodiscard]] + inline bool is_empty_load_tail() const noexcept { + return m_head == load_raw_tail(); + } + + [[nodiscard]] + inline bool is_empty_load_tail_relaxed() const noexcept { + return m_head == load_raw_tail_relaxed(); + } + + [[nodiscard]] + inline bool is_available_load_head() const noexcept { + return bool(capacity - (m_tail - load_raw_head())); + } + + inline void wait_for_available() const noexcept { + const T head_full = m_tail - capacity; + if constexpr (!is_thread_safe) { + assert(head_full != load_raw_head()); + return; + } + if constexpr (is_blocking) { + as_c_atomic(m_head).wait(head_full, std::memory_order_acquire); + } + else { + while (head_full == load_raw_head()) {} + } + } + + inline void wait_for_not_empty() const noexcept { + const T tail_empty = m_head; + if constexpr (!is_thread_safe) { + assert(tail_empty != load_raw_tail()); + return; + } + if constexpr (is_blocking) { + as_c_atomic(m_tail).wait(tail_empty, std::memory_order_acquire); + } + else { + while (tail_empty == load_raw_tail()) {} + } + } + + inline void push(T num = 1) noexcept { + if constexpr (is_thread_safe) { + as_atomic(m_tail).store(m_tail + num, std::memory_order_release); + } + else { + m_tail += num; + } + } + + inline void pop(T num = 1) noexcept { + if constexpr (is_thread_safe) { + as_atomic(m_head).store(m_head + num, std::memory_order_release); + } + else { + m_head += num; + } + } + + inline void push_notify(T num = 1) noexcept { + push(num); + if constexpr (is_thread_safe && is_blocking) { + as_atomic(m_tail).notify_one(); + } + } + + inline void pop_notify(T num = 1) noexcept { + pop(num); + if constexpr (is_thread_safe && is_blocking) { + as_atomic(m_head).notify_one(); + } + } + }; +} // namespace co_context diff --git a/engine/3rdparty/co_context/include/co_context/detail/task_info.h b/engine/3rdparty/co_context/include/co_context/detail/task_info.h new file mode 100644 index 0000000..25b7103 --- /dev/null +++ b/engine/3rdparty/co_context/include/co_context/detail/task_info.h @@ -0,0 +1,13 @@ +namespace co_context::detail { + + struct [[nodiscard]] task_info { + std::coroutine_handle<> handle; + + int32_t result; + + [[nodiscard]] + uint64_t as_user_data() const noexcept { + return static_cast(reinterpret_cast(this)); + } + }; +} \ No newline at end of file diff --git a/engine/3rdparty/co_context/include/co_context/detail/task_promise.h b/engine/3rdparty/co_context/include/co_context/detail/task_promise.h new file mode 100644 index 0000000..6937a13 --- /dev/null +++ b/engine/3rdparty/co_context/include/co_context/detail/task_promise.h @@ -0,0 +1,221 @@ +#include +#include +#include +#include +#include +namespace co_context { + template + class task; + namespace detail { + template + class task_promise_base; + /** + * @brief When current task<> finishes, resume its parent. + */ + template + struct task_final_awaiter { + static constexpr bool await_ready() noexcept { return false; } + + template> Promise> + std::coroutine_handle<> + await_suspend(std::coroutine_handle current) noexcept { + return current.promise().parent_coro; + } + + // Won't be resumed anyway + constexpr void await_resume() const noexcept {} + }; + + /** + * @brief When current task<> finishes, resume its parent. + */ + template<> + struct task_final_awaiter { + static constexpr bool await_ready() noexcept { return false; } + + template> Promise> + std::coroutine_handle<> + await_suspend(std::coroutine_handle current) noexcept { + auto& promise = current.promise(); + + std::coroutine_handle<> continuation = promise.parent_coro; + + if (promise.is_detached_flag == Promise::is_detached) { + current.destroy(); + } + + return continuation; + } + + // Won't be resumed anyway + constexpr void await_resume() const noexcept {} + }; + + /** + * @brief Define the behavior of all tasks. + * + * final_suspend: yes, and return to parent + */ + template + class task_promise_base { + friend struct task_final_awaiter; + + public: + task_promise_base() noexcept = default; + + inline constexpr std::suspend_always initial_suspend() noexcept { + return {}; + } + + inline constexpr task_final_awaiter final_suspend() noexcept { + return {}; + } + + inline void set_parent(std::coroutine_handle<> continuation) noexcept { + parent_coro = continuation; + } + + task_promise_base(const task_promise_base&) = delete; + task_promise_base(task_promise_base&&) = delete; + task_promise_base& operator=(const task_promise_base&) = delete; + task_promise_base& operator=(task_promise_base&&) = delete; + + private: + std::coroutine_handle<> parent_coro{ std::noop_coroutine() }; + }; + + /** + * @brief task<> with a return value + * + * @tparam T the type of the final result + */ + template + class task_promise final : public task_promise_base { + public: + task_promise() noexcept : state(value_state::mono) {}; + + ~task_promise() { + switch (state) { + [[likely]] case value_state::value: + value.~T(); + break; + case value_state::exception: + exception_ptr.~exception_ptr(); + break; + default: break; + } + }; + + task get_return_object() noexcept; + + void unhandled_exception() noexcept { + exception_ptr = std::current_exception(); + state = value_state::exception; + } + + template + requires std::convertible_to + void return_value(Value&& result + ) noexcept(std::is_nothrow_constructible_v) { + std::construct_at( + std::addressof(value), std::forward(result) + ); + state = value_state::value; + } + + // get the lvalue ref + T& result()& { + if (state == value_state::exception) [[unlikely]] { + std::rethrow_exception(exception_ptr); + } + assert(state == value_state::value); + return value; + } + + // get the prvalue + T&& result()&& { + if (state == value_state::exception) [[unlikely]] { + std::rethrow_exception(exception_ptr); + } + assert(state == value_state::value); + return std::move(value); + } + + private: + union { + T value; + std::exception_ptr exception_ptr; + }; + enum class value_state : uint8_t { mono, value, exception } state; + }; + + template<> + class task_promise final : public task_promise_base { + friend struct task_final_awaiter; + friend class task; + + public: + task_promise() noexcept : is_detached_flag(0) {}; + + ~task_promise() noexcept { + if (is_detached_flag != is_detached) { + exception_ptr.~exception_ptr(); + } + } + + task get_return_object() noexcept; + + constexpr void return_void() noexcept {} + + void unhandled_exception() { + if (is_detached_flag == is_detached) { + std::rethrow_exception(std::current_exception()); + } + else { + exception_ptr = std::current_exception(); + } + } + + void result() const { + if (this->exception_ptr) [[unlikely]] { + std::rethrow_exception(this->exception_ptr); + } + } + + private: + inline static constexpr uintptr_t is_detached = -1ULL; + + union { + uintptr_t is_detached_flag; // set to `is_detached` if is detached. + std::exception_ptr exception_ptr; + }; + }; // namespace co_context + + template + class task_promise final : public task_promise_base { + public: + task_promise() noexcept = default; + + task get_return_object() noexcept; + + void unhandled_exception() noexcept { + this->exception_ptr = std::current_exception(); + } + + void return_value(T& result) noexcept { + value = std::addressof(result); + } + + T& result() { + if (exception_ptr) [[unlikely]] { + std::rethrow_exception(exception_ptr); + } + return *value; + } + + private: + T* value = nullptr; + std::exception_ptr exception_ptr; + }; + } +} \ No newline at end of file diff --git a/engine/3rdparty/co_context/include/co_context/detail/thread_meta.h b/engine/3rdparty/co_context/include/co_context/detail/thread_meta.h new file mode 100644 index 0000000..5a2fafd --- /dev/null +++ b/engine/3rdparty/co_context/include/co_context/detail/thread_meta.h @@ -0,0 +1,16 @@ +#pragma once +#include "type.h" +namespace co_context { + class io_context; + namespace detail { + struct worker_meta; + struct thread_meta { + // The running io_context on this thread. + io_context *ctx = nullptr; + worker_meta *worker = nullptr; // ctx + offset = worker + + ctx_id_t ctx_id = static_cast(-1); + }; + extern thread_local thread_meta this_thread; // NOLINT(*global-variables) + } // namespace co_context::detail +} \ No newline at end of file diff --git a/engine/3rdparty/co_context/include/co_context/detail/type.h b/engine/3rdparty/co_context/include/co_context/detail/type.h new file mode 100644 index 0000000..2fbda64 --- /dev/null +++ b/engine/3rdparty/co_context/include/co_context/detail/type.h @@ -0,0 +1,18 @@ +#pragma once + +#define CO_CONTEXT_AWAIT_HINT nodiscard("Did you forget to co_await?") + +#if defined(__GNUG__) +#define CO_CONTEXT_NOINLINE gnu::noinline +#elif defined(__clang__) +#define CO_CONTEXT_NOINLINE clang::noinline +#else +#define CO_CONTEXT_NOINLINE gnu::noinline, clang::noinline, msvc::noinline +#endif + +namespace co_context { + enum safety : bool { unsafe = false, safe = true }; + using cur_t = uint32_t; + using ctx_id_t = uint8_t; + inline constexpr cur_t swap_capacity = 16384; +} // namespace co_context diff --git a/engine/3rdparty/co_context/include/co_context/detail/worker_meta.h b/engine/3rdparty/co_context/include/co_context/detail/worker_meta.h new file mode 100644 index 0000000..67b9860 --- /dev/null +++ b/engine/3rdparty/co_context/include/co_context/detail/worker_meta.h @@ -0,0 +1,50 @@ +#include +#include +#include +#include "thread_meta.h" +#include "spsc_cursor.h" +namespace co_context::detail { + + struct worker_meta final { + // number of I/O tasks running inside io_uring + int32_t requests_to_reap = 0; + // if there is at least one entry to submit to io_uring + uint32_t requests_to_submit = 0; + + std::array, swap_capacity> reap_swap; + spsc_cursor reap_cur; + + void init(); + void deinit(); + + void forward_lazy() noexcept; + void forward_task(std::coroutine_handle<> handle) noexcept; + + [[nodiscard]] + cur_t number_to_schedule() const noexcept { + const auto& cur = this->reap_cur; + return cur.size(); + } + // if there is at least one task newly spawned or forwarded + [[nodiscard]] + bool has_task_ready() const noexcept { + return !reap_cur.is_empty(); + } + // Get a coroutine to run. Guarantee to be non-null. + [[nodiscard]] + std::coroutine_handle<> schedule() noexcept; + void work_once(); + + /** + * @brief poll the submission swap zone + */ + void poll_submission() noexcept; + + /** + * @brief poll the uring completion queue + * + * @return number of cqes handled by worker + */ + uint32_t poll_completion() noexcept; + }; +} \ No newline at end of file diff --git a/engine/3rdparty/co_context/include/co_context/io_context.h b/engine/3rdparty/co_context/include/co_context/io_context.h index 8131ac8..24b160e 100644 --- a/engine/3rdparty/co_context/include/co_context/io_context.h +++ b/engine/3rdparty/co_context/include/co_context/io_context.h @@ -1,24 +1,29 @@ -/* - * A coroutine framework aimed at high-concurrency io with reasonable latency, - * based on liburingcxx. - * - * Copyright 2022 Zifeng Deng - * - * 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. - */ #pragma once #include - +#include "task.h" +#include "detail/worker_meta.h" namespace co_context { + class [[nodiscard]] io_context final { + private: + friend struct detail::worker_meta; + using worker_meta = detail::worker_meta; + private: + std::thread host_thread; + worker_meta worker; + // should io_context stop + bool will_stop = false; + ctx_id_t id; + public: + explicit io_context() noexcept; + void init(); + void deinit(); + void start(); + void run(); + void co_spawn(task&& entrance) noexcept; -} // namespace co_context + public: + void do_submission_part() noexcept; + void do_completion_part() noexcept; + void do_worker_part(); + }; +} // namespace co_context \ No newline at end of file diff --git a/engine/3rdparty/co_context/include/co_context/lazy_io.h b/engine/3rdparty/co_context/include/co_context/lazy_io.h new file mode 100644 index 0000000..63c052f --- /dev/null +++ b/engine/3rdparty/co_context/include/co_context/lazy_io.h @@ -0,0 +1,12 @@ +#pragma once + +#include "detail/type.h" +#include "detail/lazy_io_awaiter.h" +namespace co_context { + inline namespace lazy { + inline detail::lazy_write + write(int fd, std::span buf, uint64_t offset) noexcept { + return detail::lazy_write{fd, buf , offset}; + } + } +} \ No newline at end of file diff --git a/engine/3rdparty/co_context/include/co_context/task.h b/engine/3rdparty/co_context/include/co_context/task.h new file mode 100644 index 0000000..8df7e55 --- /dev/null +++ b/engine/3rdparty/co_context/include/co_context/task.h @@ -0,0 +1,45 @@ +#pragma once +#include "detail/type.h" +#include "detail/task_promise.h" +namespace co_context { +#define CO_CONTEXT_AWAIT_HINT nodiscard("Did you forget to co_await?") + template + class [[CO_CONTEXT_AWAIT_HINT]] task { + public: + using promise_type = detail::task_promise; + private: + std::coroutine_handle handle; + public: + explicit task(std::coroutine_handle current) noexcept + : handle(current) {} + std::coroutine_handle get_handle() noexcept { return handle; } + void detach() noexcept { + if constexpr (std::is_void_v) { + handle.promise().is_detached_flag = promise_type::is_detached; + } + handle = nullptr; + } + }; + + namespace detail { + template + inline task task_promise::get_return_object() noexcept { + return task{ + std::coroutine_handle::from_promise(*this) + }; + } + + inline task task_promise::get_return_object() noexcept { + return task{ + std::coroutine_handle::from_promise(*this) + }; + } + + template + inline task task_promise::get_return_object() noexcept { + return task{ + std::coroutine_handle::from_promise(*this) + }; + } + } // namespace detail +} // namespace co_context \ No newline at end of file diff --git a/engine/3rdparty/co_context/src/co_context/detail/worker_meta.cpp b/engine/3rdparty/co_context/src/co_context/detail/worker_meta.cpp new file mode 100644 index 0000000..eaeeab8 --- /dev/null +++ b/engine/3rdparty/co_context/src/co_context/detail/worker_meta.cpp @@ -0,0 +1,54 @@ +#include "co_context/detail/worker_meta.h" +namespace co_context::detail { + thread_local thread_meta this_thread; // NOLINT(*global-variables) + + void worker_meta::init() + { + this_thread.worker = this; + } + + void worker_meta::deinit() + { + this_thread.worker = nullptr; + } + + void worker_meta::forward_lazy() noexcept + { + ++requests_to_reap; // NOTE may required no reap or required multi-reap. + ++requests_to_submit; + } + void worker_meta::forward_task(std::coroutine_handle<> handle) noexcept + { + auto& cur = reap_cur; + reap_swap[cur.tail()] = handle; + cur.push(); + } + + std::coroutine_handle<> worker_meta::schedule() noexcept + { + auto& cur = this->reap_cur; + std::coroutine_handle<> chosen_coro = reap_swap[cur.head()]; + cur.pop(); + return chosen_coro; + } + void worker_meta::work_once() + { + const auto coro = this->schedule(); + coro.resume(); + } + + void worker_meta::poll_submission() noexcept + { + // submit sqes + if (requests_to_submit) [[likely]] { + bool will_wait = !has_task_ready(); + requests_to_submit = 0; + } + } + + uint32_t worker_meta::poll_completion() noexcept + { + return 0; + } + +} \ No newline at end of file diff --git a/engine/3rdparty/co_context/src/co_context/io_context.cpp b/engine/3rdparty/co_context/src/co_context/io_context.cpp index 7a6af76..e7e6d31 100644 --- a/engine/3rdparty/co_context/src/co_context/io_context.cpp +++ b/engine/3rdparty/co_context/src/co_context/io_context.cpp @@ -1,8 +1,70 @@ #pragma once -#include - +#include "co_context/io_context.h" +#include "co_context/detail/io_context_meta.h" namespace co_context { - class [[nodiscard]] io_context final { + io_context::io_context() noexcept + { + auto& meta = detail::io_context_meta; + std::lock_guard lg{meta.mtx}; + id = meta.create_count++; + } + void io_context::init() + { + detail::this_thread.ctx = this; + detail::this_thread.ctx_id = id; + worker.init(); + } + void io_context::deinit() + { + detail::this_thread.ctx = nullptr; + detail::this_thread.ctx_id = static_cast(-1); - }; + worker.deinit(); + + auto& meta = detail::io_context_meta; + std::lock_guard lg{meta.mtx}; + --meta.ready_count; + --meta.create_count; + } + void io_context::start() + { + init(); + run(); + } + void io_context::co_spawn(task&& entrance) noexcept { + auto handle = entrance.get_handle(); + entrance.detach(); + worker.forward_task(handle); + } + void io_context::run() + { + while (!will_stop) [[likely]] { + do_worker_part(); + + do_submission_part(); + + do_completion_part(); + } + } + void io_context::do_submission_part() noexcept + { + worker.poll_submission(); + } + void io_context::do_completion_part() noexcept + { + const uint32_t handled_num = worker.poll_completion(); + + bool is_not_over = handled_num | worker.requests_to_reap; + + if (!is_not_over) [[unlikely]] { + will_stop = true; + } + } + void io_context::do_worker_part() + { + auto num = worker.number_to_schedule(); + for (; num > 0; --num) { + worker.work_once(); + } + } } // namespace co_context diff --git a/engine/3rdparty/co_context/src/main.cpp b/engine/3rdparty/co_context/src/main.cpp new file mode 100644 index 0000000..ed4bfda --- /dev/null +++ b/engine/3rdparty/co_context/src/main.cpp @@ -0,0 +1,20 @@ +#include +#include "co_context/io_context.h" +#include "co_context/lazy_io.h" +using namespace std; +using namespace co_context; +task<> the_answer(int t) { + const char* name = "hello"; + cout << "lazyout" << endl; + co_await lazy::write(1, { name, 5 }, 10); + cout << name << t << endl; + co_return; +} +int main() { + io_context context; + int a = 3; + context.co_spawn(the_answer(a)); + context.co_spawn(the_answer(5)); + context.start(); + return 0; +} \ No newline at end of file diff --git a/engine/3rdparty/co_context/test/co_future_promise.cpp b/engine/3rdparty/co_context/test/co_future_promise.cpp new file mode 100644 index 0000000..6589194 --- /dev/null +++ b/engine/3rdparty/co_context/test/co_future_promise.cpp @@ -0,0 +1,93 @@ +#include +#include +using namespace std; + +struct future_type_int { + struct promise_type; + using co_handle_type = std::coroutine_handle; + + struct promise_type { + int ret_val; + promise_type() { + std::cout << "promise_type constructor" << std::endl; + } + ~promise_type() { + std::cout << "promise_type destructor" << std::endl; + } + auto get_return_object() { + std::cout << "get_return_object" << std::endl; + return co_handle_type::from_promise(*this); + } + auto initial_suspend() { + std::cout << "initial_suspend" << std::endl; + return std::suspend_always(); + } + auto final_suspend() noexcept(true) { + std::cout << "final_suspend" << std::endl; + return std::suspend_never(); + } + void return_value(int val) { + std::cout << "return_value : " << val << std::endl; + ret_val = val; + } + void unhandled_exception() { + std::cout << "unhandled_exception" << std::endl; + std::terminate(); + } + auto yield_value(int val) { + std::cout << "yield_value : " << val << std::endl; + ret_val = val; + return std::suspend_always(); + } + }; + future_type_int(co_handle_type co_handle) { + std::cout << "future_type_int constructor" << std::endl; + co_handle_ = co_handle; + } + ~future_type_int() { + std::cout << "future_type_int destructor" << std::endl; + co_handle_.destroy(); + } + future_type_int(const future_type_int&) = delete; + future_type_int(future_type_int&&) = delete; + + bool resume() { + if (!co_handle_.done()) { + co_handle_.resume(); + } + return !co_handle_.done(); + } + co_handle_type co_handle_; +}; + +future_type_int three_step_coroutine() { + std::cout << "three_step_coroutine begin" << std::endl; + co_yield 222; + std::cout << "three_step_coroutine running" << std::endl; + co_yield 333; + std::cout << "three_step_coroutine end" << std::endl; + co_return 444; +} +void three_step_coroutine2(future_type_int& future_obj) { + std::cout << "three_step_coroutine begin" << std::endl; + future_obj.co_handle_.promise().yield_value(222); +} +int main() { + int a = 11; + future_type_int future_obj = three_step_coroutine(); + + std::cout << "=======calling first resume======" << std::endl; + future_obj.resume(); + std::cout << "ret_val = " << future_obj.co_handle_.promise().ret_val << std::endl; + + std::cout << "=======calling second resume=====" << std::endl; + future_obj.resume(); + std::cout << "ret_val = " << future_obj.co_handle_.promise().ret_val << std::endl; + + std::cout << "=======calling third resume======" << std::endl; + future_obj.resume(); + std::cout << "ret_val = " << future_obj.co_handle_.promise().ret_val << std::endl; + std::cout << "=======main end======" << std::endl; + + return 0; +} \ No newline at end of file diff --git a/engine/3rdparty/co_context/xmake.lua b/engine/3rdparty/co_context/xmake.lua index a3f6c0b..4db8258 100644 --- a/engine/3rdparty/co_context/xmake.lua +++ b/engine/3rdparty/co_context/xmake.lua @@ -1,5 +1,5 @@ target("co_context") - set_kind("static") - add_includedirs("include") - add_files("src/**.cpp") + set_kind("binary") + add_includedirs("include", {public = true}) + add_files("src/**.cpp","src/main.cpp") add_headerfiles("include/**.h") diff --git a/engine/src/main.cpp b/engine/src/main.cpp index 305ae9d..fafe423 100644 --- a/engine/src/main.cpp +++ b/engine/src/main.cpp @@ -4,7 +4,7 @@ #include "engine/vulkanapi/device/device.h" #include "engine/render/pass/forwardpass.h" using namespace std; - + int main(int argc, char** argv) { const char* name = "hello"; diff --git a/engine/xmake.lua b/engine/xmake.lua index 23b5838..9cec358 100644 --- a/engine/xmake.lua +++ b/engine/xmake.lua @@ -3,7 +3,7 @@ includes("test/**xmake.lua") target("zengine") set_kind("binary") - add_deps("co_context") + --add_deps("co_context") add_includedirs("src") add_files("src/*.cpp", "src/**.cpp") add_headerfiles("src/**.h", "src/**.inl") diff --git a/xmake.lua b/xmake.lua index 899c567..2ed6a28 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,6 +1,6 @@ add_rules("mode.debug", "mode.release") set_arch("x64") -set_languages("cxx17") +set_languages("cxx20") set_project("zengine") set_toolchains("clang") includes("engine")