upload co_context
This commit is contained in:
parent
3e95395cb2
commit
1688905f7f
15
engine/3rdparty/co_context/include/co_context/detail/io_context_meta.h
vendored
Normal file
15
engine/3rdparty/co_context/include/co_context/detail/io_context_meta.h
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
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
|
||||||
29
engine/3rdparty/co_context/include/co_context/detail/lazy_io_awaiter.h
vendored
Normal file
29
engine/3rdparty/co_context/include/co_context/detail/lazy_io_awaiter.h
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
#include <span>
|
||||||
|
#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<const char> buf, uint64_t offset) noexcept {}
|
||||||
|
};
|
||||||
|
}
|
||||||
205
engine/3rdparty/co_context/include/co_context/detail/spsc_cursor.h
vendored
Normal file
205
engine/3rdparty/co_context/include/co_context/detail/spsc_cursor.h
vendored
Normal file
@ -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<T>::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
|
||||||
13
engine/3rdparty/co_context/include/co_context/detail/task_info.h
vendored
Normal file
13
engine/3rdparty/co_context/include/co_context/detail/task_info.h
vendored
Normal file
@ -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<uint64_t>(reinterpret_cast<uintptr_t>(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
221
engine/3rdparty/co_context/include/co_context/detail/task_promise.h
vendored
Normal file
221
engine/3rdparty/co_context/include/co_context/detail/task_promise.h
vendored
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <concepts>
|
||||||
|
#include <coroutine>
|
||||||
|
#include <exception>
|
||||||
|
#include <memory>
|
||||||
|
namespace co_context {
|
||||||
|
template<typename T>
|
||||||
|
class task;
|
||||||
|
namespace detail {
|
||||||
|
template<typename T>
|
||||||
|
class task_promise_base;
|
||||||
|
/**
|
||||||
|
* @brief When current task<> finishes, resume its parent.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct task_final_awaiter {
|
||||||
|
static constexpr bool await_ready() noexcept { return false; }
|
||||||
|
|
||||||
|
template<std::derived_from<task_promise_base<T>> Promise>
|
||||||
|
std::coroutine_handle<>
|
||||||
|
await_suspend(std::coroutine_handle<Promise> 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<void> {
|
||||||
|
static constexpr bool await_ready() noexcept { return false; }
|
||||||
|
|
||||||
|
template<std::derived_from<task_promise_base<void>> Promise>
|
||||||
|
std::coroutine_handle<>
|
||||||
|
await_suspend(std::coroutine_handle<Promise> 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<typename T>
|
||||||
|
class task_promise_base {
|
||||||
|
friend struct task_final_awaiter<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
task_promise_base() noexcept = default;
|
||||||
|
|
||||||
|
inline constexpr std::suspend_always initial_suspend() noexcept {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr task_final_awaiter<T> 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<typename T>
|
||||||
|
class task_promise final : public task_promise_base<T> {
|
||||||
|
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<T> get_return_object() noexcept;
|
||||||
|
|
||||||
|
void unhandled_exception() noexcept {
|
||||||
|
exception_ptr = std::current_exception();
|
||||||
|
state = value_state::exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Value>
|
||||||
|
requires std::convertible_to<Value&&, T>
|
||||||
|
void return_value(Value&& result
|
||||||
|
) noexcept(std::is_nothrow_constructible_v<T, Value&&>) {
|
||||||
|
std::construct_at(
|
||||||
|
std::addressof(value), std::forward<Value>(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<void> final : public task_promise_base<void> {
|
||||||
|
friend struct task_final_awaiter<void>;
|
||||||
|
friend class task<void>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
task_promise() noexcept : is_detached_flag(0) {};
|
||||||
|
|
||||||
|
~task_promise() noexcept {
|
||||||
|
if (is_detached_flag != is_detached) {
|
||||||
|
exception_ptr.~exception_ptr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task<void> 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<typename T>
|
||||||
|
class task_promise<T&> final : public task_promise_base<T&> {
|
||||||
|
public:
|
||||||
|
task_promise() noexcept = default;
|
||||||
|
|
||||||
|
task<T&> 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
16
engine/3rdparty/co_context/include/co_context/detail/thread_meta.h
vendored
Normal file
16
engine/3rdparty/co_context/include/co_context/detail/thread_meta.h
vendored
Normal file
@ -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<ctx_id_t>(-1);
|
||||||
|
};
|
||||||
|
extern thread_local thread_meta this_thread; // NOLINT(*global-variables)
|
||||||
|
} // namespace co_context::detail
|
||||||
|
}
|
||||||
18
engine/3rdparty/co_context/include/co_context/detail/type.h
vendored
Normal file
18
engine/3rdparty/co_context/include/co_context/detail/type.h
vendored
Normal file
@ -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
|
||||||
50
engine/3rdparty/co_context/include/co_context/detail/worker_meta.h
vendored
Normal file
50
engine/3rdparty/co_context/include/co_context/detail/worker_meta.h
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include <array>
|
||||||
|
#include <coroutine>
|
||||||
|
#include <mutex>
|
||||||
|
#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<std::coroutine_handle<>, swap_capacity> reap_swap;
|
||||||
|
spsc_cursor<cur_t, swap_capacity, unsafe> 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -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
|
#pragma once
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include "task.h"
|
||||||
|
#include "detail/worker_meta.h"
|
||||||
namespace co_context {
|
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<void>&& entrance) noexcept;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void do_submission_part() noexcept;
|
||||||
|
void do_completion_part() noexcept;
|
||||||
|
void do_worker_part();
|
||||||
|
};
|
||||||
} // namespace co_context
|
} // namespace co_context
|
||||||
12
engine/3rdparty/co_context/include/co_context/lazy_io.h
vendored
Normal file
12
engine/3rdparty/co_context/include/co_context/lazy_io.h
vendored
Normal file
@ -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<const char> buf, uint64_t offset) noexcept {
|
||||||
|
return detail::lazy_write{fd, buf , offset};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
engine/3rdparty/co_context/include/co_context/task.h
vendored
Normal file
45
engine/3rdparty/co_context/include/co_context/task.h
vendored
Normal file
@ -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<typename T = void>
|
||||||
|
class [[CO_CONTEXT_AWAIT_HINT]] task {
|
||||||
|
public:
|
||||||
|
using promise_type = detail::task_promise<T>;
|
||||||
|
private:
|
||||||
|
std::coroutine_handle<promise_type> handle;
|
||||||
|
public:
|
||||||
|
explicit task(std::coroutine_handle<promise_type> current) noexcept
|
||||||
|
: handle(current) {}
|
||||||
|
std::coroutine_handle<promise_type> get_handle() noexcept { return handle; }
|
||||||
|
void detach() noexcept {
|
||||||
|
if constexpr (std::is_void_v<T>) {
|
||||||
|
handle.promise().is_detached_flag = promise_type::is_detached;
|
||||||
|
}
|
||||||
|
handle = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template<typename T>
|
||||||
|
inline task<T> task_promise<T>::get_return_object() noexcept {
|
||||||
|
return task<T>{
|
||||||
|
std::coroutine_handle<task_promise>::from_promise(*this)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline task<void> task_promise<void>::get_return_object() noexcept {
|
||||||
|
return task<void>{
|
||||||
|
std::coroutine_handle<task_promise>::from_promise(*this)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline task<T&> task_promise<T&>::get_return_object() noexcept {
|
||||||
|
return task<T&>{
|
||||||
|
std::coroutine_handle<task_promise>::from_promise(*this)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace co_context
|
||||||
54
engine/3rdparty/co_context/src/co_context/detail/worker_meta.cpp
vendored
Normal file
54
engine/3rdparty/co_context/src/co_context/detail/worker_meta.cpp
vendored
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,8 +1,70 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <thread>
|
#include "co_context/io_context.h"
|
||||||
|
#include "co_context/detail/io_context_meta.h"
|
||||||
namespace co_context {
|
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<ctx_id_t>(-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<void>&& 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
|
} // namespace co_context
|
||||||
|
|||||||
20
engine/3rdparty/co_context/src/main.cpp
vendored
Normal file
20
engine/3rdparty/co_context/src/main.cpp
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include<iostream>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
93
engine/3rdparty/co_context/test/co_future_promise.cpp
vendored
Normal file
93
engine/3rdparty/co_context/test/co_future_promise.cpp
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include<iostream>
|
||||||
|
#include<coroutine>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
struct future_type_int {
|
||||||
|
struct promise_type;
|
||||||
|
using co_handle_type = std::coroutine_handle<promise_type>;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
6
engine/3rdparty/co_context/xmake.lua
vendored
6
engine/3rdparty/co_context/xmake.lua
vendored
@ -1,5 +1,5 @@
|
|||||||
target("co_context")
|
target("co_context")
|
||||||
set_kind("static")
|
set_kind("binary")
|
||||||
add_includedirs("include")
|
add_includedirs("include", {public = true})
|
||||||
add_files("src/**.cpp")
|
add_files("src/**.cpp","src/main.cpp")
|
||||||
add_headerfiles("include/**.h")
|
add_headerfiles("include/**.h")
|
||||||
|
|||||||
@ -3,7 +3,7 @@ includes("test/**xmake.lua")
|
|||||||
|
|
||||||
target("zengine")
|
target("zengine")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
add_deps("co_context")
|
--add_deps("co_context")
|
||||||
add_includedirs("src")
|
add_includedirs("src")
|
||||||
add_files("src/*.cpp", "src/**.cpp")
|
add_files("src/*.cpp", "src/**.cpp")
|
||||||
add_headerfiles("src/**.h", "src/**.inl")
|
add_headerfiles("src/**.h", "src/**.inl")
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
add_rules("mode.debug", "mode.release")
|
add_rules("mode.debug", "mode.release")
|
||||||
set_arch("x64")
|
set_arch("x64")
|
||||||
set_languages("cxx17")
|
set_languages("cxx20")
|
||||||
set_project("zengine")
|
set_project("zengine")
|
||||||
set_toolchains("clang")
|
set_toolchains("clang")
|
||||||
includes("engine")
|
includes("engine")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user