Skip to content

Commit

Permalink
feat: add generator
Browse files Browse the repository at this point in the history
  • Loading branch information
OEOTYAN committed Oct 20, 2024
1 parent a58e2d8 commit 7f318d8
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 5 deletions.
17 changes: 15 additions & 2 deletions src-test/common/ExecTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "ll/api/chrono/GameChrono.h"
#include "ll/api/coro/CoroTask.h"
#include "ll/api/coro/Generator.h"
#include "ll/api/thread/InplaceExecutor.h"
#include "ll/api/thread/ServerThreadExecutor.h"
#include "ll/api/thread/ThreadPoolExecutor.h"
Expand All @@ -20,6 +21,12 @@ CoroTask<float> val2() {
co_return 20;
}

Generator<size_t> generator(size_t n) {
for (size_t i = 0; i < n; i++) {
co_yield i;
}
}

CoroTask<Expected<int>> coroutine() {
for (size_t i = 0;; i++) {
getLogger().info("coroutine: {}, thread: {}", std::chrono::system_clock::now(), std::this_thread::get_id());
Expand All @@ -36,11 +43,17 @@ CoroTask<Expected<int>> coroutine() {
std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - parbegin),
std::this_thread::get_id()
);
parbegin = std::chrono::steady_clock::now();
std::vector<ll::coro::CoroTask<int>> tasks{};
for (size_t i = 0; i < 1000000; i++) {
parbegin = std::chrono::steady_clock::now();
for (auto&& i : generator(1000000)) {
(void)i;
tasks.emplace_back(val1());
}
getLogger().info(
"coroutine: generator use {}",
std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - parbegin),
std::this_thread::get_id()
);
auto vec = co_await collectAll(std::move(tasks));
getLogger().info(
"coroutine: collectAll use {}",
Expand Down
6 changes: 3 additions & 3 deletions src/ll/api/coro/CoroPromise.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ struct CoroPromiseBase {

constexpr ForwardAwaiter<NonNullExecutorRef> await_transform(CurrentExecutor) { return {exec.value()}; }

constexpr YieldAwaiter await_transform(Yield) { return {exec}; }
constexpr YieldAwaiter await_transform(Yield const&) { return {exec}; }

template <class R, class P>
constexpr SleepWaiter await_transform(std::chrono::duration<R, P> dur) {
constexpr SleepWaiter await_transform(std::chrono::duration<R, P> const& dur) {
return {dur, exec};
}
template <class C, class D>
constexpr SleepWaiter await_transform(std::chrono::time_point<C, D> time) {
constexpr SleepWaiter await_transform(std::chrono::time_point<C, D> const& time) {
return {time, exec};
}
};
Expand Down
122 changes: 122 additions & 0 deletions src/ll/api/coro/Generator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#pragma once

#include <coroutine>
#include <exception>
#include <type_traits>

namespace ll::coro {

template <class T>
struct Generator {
using value = std::remove_cvref_t<T>;
using reference = T&&;
using yielded = std::conditional_t<std::is_reference_v<reference>, reference, reference const&>;


struct promise_type {
std::add_pointer_t<yielded> ptr;
std::exception_ptr exception;

Generator get_return_object() noexcept { return Generator{*this}; }

std::suspend_always initial_suspend() noexcept { return {}; }

std::suspend_always final_suspend() noexcept { return {}; }

void unhandled_exception() noexcept { exception = std::current_exception(); }

void rethrow() {
if (exception) {
std::rethrow_exception(exception);
}
}
std::suspend_always yield_value(yielded val) noexcept {
ptr = std::addressof(val);
return {};
}
auto yield_value(std::remove_reference_t<yielded> const& lval)
requires std::is_rvalue_reference_v<yielded>
&& std::constructible_from<std::remove_cvref_t<yielded>, std::remove_reference_t<yielded> const&>
{
struct YieldCopied {
std::remove_cvref_t<yielded> storage;
constexpr YieldCopied(std::remove_reference_t<yielded> const& v) : storage(v) {}
constexpr bool await_ready() const noexcept { return false; }
constexpr void await_suspend(std::coroutine_handle<promise_type> h) noexcept {
h.promise().ptr = std::addressof(storage);
}
constexpr void await_resume() const noexcept {}
};
return YieldCopied{lval};
}
void return_void() noexcept {}

template <class U>
U&& await_transform(U&&) = delete;
};

struct iterator {
using iterator_category = std::input_iterator_tag;
using difference_type = ptrdiff_t;
using value_type = value;
using reference = reference;
using pointer = std::add_pointer_t<yielded>;

std::coroutine_handle<promise_type> handle = nullptr;

iterator() = default;
explicit iterator(std::coroutine_handle<promise_type> handle) noexcept : handle(handle) {}

iterator& operator++() {
handle.resume();
if (handle.done()) {
std::exchange(handle, nullptr).promise().rethrow();
}

return *this;
}

void operator++(int) { ++*this; }

[[nodiscard]] bool operator==(iterator const& other) const noexcept { return handle == other.handle; }

[[nodiscard]] bool operator!=(iterator const& other) const noexcept { return !(*this == other); }

[[nodiscard]] reference operator*() const noexcept { return static_cast<reference>(*handle.promise().ptr); }

[[nodiscard]] pointer operator->() const noexcept { return handle.promise().ptr; }
};
[[nodiscard]] iterator begin() {
if (handle) {
handle.resume();
if (handle.done()) {
handle.promise().rethrow();
return {};
}
}
return iterator{handle};
}

[[nodiscard]] iterator end() noexcept { return {}; }

constexpr Generator(Generator&& other) noexcept : handle(std::exchange(other.handle, nullptr)) {}

constexpr ~Generator() {
if (handle) {
handle.destroy();
}
}
constexpr Generator() = default;

Generator& operator=(Generator&& other) noexcept {
std::swap(other.handle, handle);
return *this;
}

private:
constexpr explicit Generator(promise_type& promise) noexcept
: handle(std::coroutine_handle<promise_type>::from_promise(promise)) {}

std::coroutine_handle<promise_type> handle = nullptr;
};
} // namespace ll::coro

0 comments on commit 7f318d8

Please sign in to comment.