-
-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
27 changed files
with
423 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,42 @@ | ||
#include "ll/core/LeviLamina.h" | ||
|
||
#include "ll/api/chrono/GameChrono.h" | ||
#include "ll/api/thread/ServerThreadExecuter.h" | ||
#include "ll/api/thread/ThreadPoolExecuter.h" | ||
#include "ll/api/coro/CoroTask.h" | ||
#include "ll/api/thread/ServerThreadExecutor.h" | ||
#include "ll/api/thread/ThreadPoolExecutor.h" | ||
|
||
size_t i = 0; | ||
using namespace ll; | ||
using namespace coro; | ||
|
||
static bool run = [] { | ||
using namespace ll; | ||
using namespace literals; | ||
|
||
CoroTask<Expected<int>> coroutine() { | ||
for (size_t i = 0;; i++) { | ||
getLogger().info( | ||
"coroutine: {}, thread: {}", | ||
chrono::GameTickClock::now().time_since_epoch(), | ||
std::this_thread::get_id() | ||
); | ||
co_await 2_tick; | ||
|
||
// co_await yield; | ||
|
||
auto& exec = thread::ThreadPoolExecuter::getDefault(); | ||
if (i > 10) { | ||
break; | ||
// throw std::runtime_error("test coroutine"); | ||
} | ||
} | ||
co_return 1234567; | ||
} | ||
|
||
static std::function<void()> fn = [&]() { | ||
getLogger().info("now: {}", std::chrono::steady_clock::now().time_since_epoch()); | ||
|
||
exec.addTaskAfter(fn, chrono::ticks{20}); | ||
getLogger().info("erase: {}", exec.removeFromSch(exec.addTaskAfter(fn, chrono::ticks{1}))); | ||
}; | ||
// exec.addTask(fn); | ||
static bool run = [] { | ||
using namespace ll; | ||
|
||
thread::ThreadPoolExecutor::getDefault().addTask([&] { | ||
auto val = coroutine().syncLaunch(thread::ServerThreadExecutor::getDefault()); | ||
getLogger().info("coroutine done, {}", val.value()); | ||
}); | ||
|
||
return true; | ||
}(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
#pragma once | ||
|
||
#include <coroutine> | ||
|
||
#include "ll/api/Expected.h" | ||
#include "ll/api/base/Concepts.h" | ||
#include "ll/api/coro/ForwardAwaiter.h" | ||
#include "ll/api/coro/SleepWaiter.h" | ||
#include "ll/api/coro/YieldAwaiter.h" | ||
|
||
namespace ll::coro { | ||
|
||
template <class T> | ||
class CoroTask; | ||
|
||
struct CoroPromiseBase { | ||
std::coroutine_handle<> handle; | ||
void* local{}; | ||
ExecutorRef exec{}; | ||
|
||
constexpr CoroPromiseBase() noexcept = default; | ||
|
||
struct FinalAwaiter { | ||
constexpr bool await_ready() const noexcept { return false; } | ||
template <std::derived_from<CoroPromiseBase> P> | ||
constexpr auto await_suspend(std::coroutine_handle<P> h) noexcept { | ||
return h.promise().handle; | ||
} | ||
constexpr void await_resume() noexcept {} | ||
}; | ||
constexpr std::suspend_always initial_suspend() noexcept { return {}; } | ||
constexpr FinalAwaiter final_suspend() noexcept { return {}; } | ||
|
||
template <concepts::Awaitable T> | ||
constexpr decltype(auto) await_transform(T&& awaitable) { | ||
if constexpr (requires { awaitable.setExecutor(exec); }) { | ||
awaitable.setExecutor(exec); | ||
} | ||
return std::forward<T>(awaitable); | ||
} | ||
|
||
constexpr ForwardAwaiter<thread::TaskExecutor const&> await_transform(CurrentExecutor) { return {exec.get()}; } | ||
|
||
constexpr YieldAwaiter await_transform(Yield) { return {exec}; } | ||
|
||
template <class R, class P> | ||
constexpr SleepWaiter await_transform(std::chrono::duration<R, P> dur) { | ||
return {dur, exec}; | ||
} | ||
template <class C, class D> | ||
constexpr SleepWaiter await_transform(std::chrono::time_point<C, D> time) { | ||
return {time, exec}; | ||
} | ||
}; | ||
template <class T> | ||
struct CoroPromise : public CoroPromiseBase { | ||
using ExpectedT = std::conditional_t<concepts::IsLeviExpected<T>, T, Expected<T>>; | ||
|
||
ExpectedT result{}; | ||
|
||
constexpr CoroPromise() noexcept = default; | ||
|
||
template <class V> | ||
void return_value(V&& value) noexcept(std::is_nothrow_constructible_v<T, V>) | ||
requires(std::is_constructible_v<T, V>) | ||
{ | ||
if constexpr (std::is_same_v<T, ExpectedT>) { | ||
result = T{std::forward<V>(value)}; | ||
} else { | ||
result.emplace(std::forward<V>(value)); | ||
} | ||
} | ||
void unhandled_exception() noexcept { result = makeExceptionError(); } | ||
|
||
constexpr CoroTask<T> get_return_object() noexcept; | ||
}; | ||
template <> | ||
struct CoroPromise<void> : public CoroPromiseBase { | ||
using ExpectedT = Expected<>; | ||
|
||
ExpectedT result{std::in_place}; | ||
|
||
constexpr CoroPromise() noexcept = default; | ||
|
||
void return_void() noexcept {} | ||
|
||
void unhandled_exception() noexcept { result = makeExceptionError(); } | ||
|
||
constexpr CoroTask<void> get_return_object() noexcept; | ||
}; | ||
} // namespace ll::coro |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
#include "ll/api/coro/CoroTask.h" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,108 @@ | ||
#pragma once | ||
|
||
#include <semaphore> | ||
|
||
#include "ll/api/coro/CoroPromise.h" | ||
#include "ll/api/coro/CoroTaskWaiter.h" | ||
|
||
namespace ll::coro { | ||
constexpr inline struct Yield { | ||
} yield; | ||
constexpr inline struct CurrentExecutor { | ||
} currentExecutor; | ||
|
||
template <class T = void> | ||
class CoroTask { | ||
public: | ||
using promise_type = CoroPromise<T>; | ||
using Handle = std::coroutine_handle<promise_type>; | ||
|
||
using ExpectedT = typename CoroPromise<T>::ExpectedT; | ||
|
||
friend promise_type; | ||
|
||
private: | ||
Handle handle; | ||
|
||
constexpr explicit CoroTask(Handle h) noexcept : handle(h) {} | ||
|
||
using WaiterBase = CoroTaskWaiter<T>; | ||
|
||
public: | ||
struct ExpectedAwaiter : public WaiterBase { | ||
constexpr ExpectedAwaiter(Handle h) : WaiterBase(h) {} | ||
constexpr ExpectedT await_resume() noexcept { return WaiterBase::getResult(); }; | ||
}; | ||
|
||
struct ValueAwaiter : public WaiterBase { | ||
constexpr ValueAwaiter(Handle h) : WaiterBase(h) {} | ||
constexpr T await_resume() { | ||
if constexpr (std::is_same_v<T, ExpectedT>) { | ||
return WaiterBase::getResult(); | ||
} else { | ||
return WaiterBase::getResult().value(); | ||
} | ||
} | ||
}; | ||
|
||
CoroTask(CoroTask const&) = delete; | ||
CoroTask& operator=(CoroTask const&) = delete; | ||
|
||
CoroTask(CoroTask&& other) noexcept : handle(std::exchange(other.handle, nullptr)) {} | ||
|
||
constexpr CoroTask& operator=(CoroTask&& other) noexcept { | ||
std::swap(handle, other.handle); | ||
return *this; | ||
} | ||
|
||
constexpr ~CoroTask() { | ||
if (handle) { | ||
std::exchange(handle, nullptr).destroy(); | ||
} | ||
}; | ||
|
||
constexpr void setExecutor(ExecutorRef ex) { handle.promise().exec = ex; } | ||
|
||
constexpr ExecutorRef getExecutor() { return handle.promise().exec; } | ||
|
||
bool done() const { return !handle || handle.done(); } | ||
|
||
auto operator co_await() { return ValueAwaiter(std::exchange(handle, nullptr)); } | ||
|
||
auto tryGet() { return ExpectedAwaiter(std::exchange(handle, nullptr)); } | ||
|
||
template <class F = decltype([](auto&&) {})> | ||
void launch(Executor const& executor, F&& callback = {}) | ||
requires(std::is_invocable_v<F, ExpectedT>) | ||
{ | ||
setExecutor(executor); | ||
struct Launcher { | ||
struct promise_type : public CoroPromiseBase { | ||
constexpr std::suspend_never initial_suspend() noexcept { return {}; } | ||
constexpr std::suspend_never final_suspend() noexcept { return {}; } | ||
constexpr void return_void() noexcept {} | ||
constexpr void unhandled_exception() {} | ||
constexpr Launcher get_return_object() noexcept { return {}; } | ||
}; | ||
}; | ||
[](CoroTask lazy, std::decay_t<F> cb) -> Launcher { | ||
cb(co_await lazy.tryGet()); | ||
}(std::move(*this), std::forward<F>(callback)); | ||
} | ||
|
||
ExpectedT syncLaunch(Executor const& executor) { | ||
std::binary_semaphore cond{0}; | ||
ExpectedT value; | ||
launch(executor, [&](ExpectedT&& result) { | ||
value = std::move(result); | ||
cond.release(); | ||
}); | ||
cond.acquire(); | ||
return value; | ||
} | ||
}; | ||
|
||
template <class T> | ||
constexpr CoroTask<T> CoroPromise<T>::get_return_object() noexcept { | ||
return CoroTask<T>(CoroTask<T>::Handle::from_promise(*this)); | ||
} | ||
constexpr CoroTask<void> CoroPromise<void>::get_return_object() noexcept { | ||
return CoroTask<void>(CoroTask<void>::Handle::from_promise(*this)); | ||
} | ||
} // namespace ll::coro |
Oops, something went wrong.