Skip to content

Commit

Permalink
..
Browse files Browse the repository at this point in the history
  • Loading branch information
kelbon committed Aug 5, 2023
1 parent 4690a7a commit 29f1e21
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 94 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
/.cache
/.vscode
/Testing
/msvc_build

36 changes: 15 additions & 21 deletions include/channel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace dd {
// TODO для таски и канала добавить возможность запустить их синхронно, но это явно unsafe.. Хотя это
// эквивалентно просто резуму хендла

// TODO exception into consumer?
template <typename>
struct channel;

Expand All @@ -29,11 +30,11 @@ struct channel_promise : enable_memory_resource_support {
friend channel_promise;
friend channel<Yield>;

Yield* current_result = nullptr;
std::coroutine_handle<> handle = nullptr;
handle_type top = nullptr;
Yield* current_result;
std::coroutine_handle<> handle;
handle_type top;

constexpr consumer_t(handle_type top) noexcept : top(top) {
constexpr consumer_t(handle_type top) noexcept : current_result(nullptr), handle(nullptr), top(top) {
assume_not_null(top);
}

Expand All @@ -46,15 +47,9 @@ struct channel_promise : enable_memory_resource_support {
top.promise().consumer = this;
return top.promise().current_worker;
}
// TODO return proxy with * operator bool etc? для одинакового интерфейса с генератором... И возможно
// исключений
[[nodiscard]] Yield* await_resume() const noexcept {
return current_result;
}

constexpr bool ended_with_exception() const noexcept {
return !!top.promise().exception;
}
};
// invariant: if running, then consumer != nullptr, setted when channel co_awaited
consumer_t* consumer;
Expand Down Expand Up @@ -107,8 +102,7 @@ struct channel_promise : enable_memory_resource_support {
public:
std::coroutine_handle<> await_suspend(handle_type owner) const noexcept {
channel_promise& leaf_p = leaf.promise();
channel_promise& owner_p = owner.promise();
consumer_t& consumer = *owner_p.consumer;
consumer_t& consumer = *owner.promise().consumer;
leaf_p.consumer = &consumer;
leaf_p.owner = owner;
consumer.top.promise().current_worker = leaf_p.current_worker;
Expand All @@ -125,8 +119,8 @@ struct channel_promise : enable_memory_resource_support {
};

public:
transfer_control_to yield_value(Yield&& lvalue) noexcept {
consumer->current_result = std::addressof(lvalue);
transfer_control_to yield_value(Yield&& rvalue) noexcept {
consumer->current_result = std::addressof(rvalue);
return transfer_control_to{consumer->handle};
}
hold_value_until_resume yield_value(const Yield& clvalue) noexcept(
Expand All @@ -143,10 +137,10 @@ struct channel_promise : enable_memory_resource_support {
static_assert(![] {});
if constexpr (std::is_same_v<typename rng_t::value_type, Yield>) {
handle_type h = e.rng.release();
if (h)
return attach_leaf{h};
else // TODO empty generator/channel smth like...
return attach_leaf{[]() -> channel<Yield> { co_return; }().release()};
if (!h)
h = []() -> channel<Yield> { co_return; }().release();
assume_not_null(h);
return attach_leaf{h};
} else {
auto make_channel = [](auto& r) -> channel<Yield> {
auto next = r.next();
Expand All @@ -168,8 +162,8 @@ struct channel_promise : enable_memory_resource_support {
if (owner) {
owner.promise().consumer = consumer;
return transfer_control_to{owner};
} else
return transfer_control_to{consumer->handle};
}
return transfer_control_to{consumer->handle};
}
static constexpr void return_void() noexcept {
}
Expand Down Expand Up @@ -244,7 +238,7 @@ struct channel {
// analogue for 'begin' of ranges
// usage:
// while(auto* x = co_await channel.next())
[[nodiscard("co_await it")]] auto next() & noexcept KELCORO_LIFETIMEBOUND {
[[nodiscard]] auto next() & noexcept KELCORO_LIFETIMEBOUND {
if (!handle) [[unlikely]]
*this = []() -> channel<Yield> { co_return; }();
assume_not_null(handle);
Expand Down
8 changes: 6 additions & 2 deletions include/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,9 +356,13 @@ struct elements_of {
template <typename R>
elements_of(R&&) -> elements_of<R&&>;

KELCORO_ALWAYS_INLINE void assume(bool b) noexcept {
assert(b);
KELCORO_ASSUME(b);
}

KELCORO_ALWAYS_INLINE void assume_not_null(std::coroutine_handle<> h) noexcept {
void* addr = h.address();
KELCORO_ASSUME(addr != nullptr);
assume(h.address() != nullptr);
}

} // namespace dd
106 changes: 55 additions & 51 deletions include/generator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@
#endif

namespace dd {

template <typename>
struct generator;

// TODO usage .begin as output iterator hmm что то типа .out хммм
// TODO sent(x) yield overload
// TODO send(x) yield overload
template <typename Yield>
struct generator_promise : enable_memory_resource_support {
static_assert(!std::is_reference_v<Yield>);
Expand All @@ -22,7 +26,7 @@ struct generator_promise : enable_memory_resource_support {
// invariant: root != nullptr
generator_promise* root = this;
// invariant: never nullptr, initialized in first co_yield
Yield* current_result;
Yield* current_result = nullptr;
std::coroutine_handle<> current_worker = get_return_object();
std::coroutine_handle<> owner = std::noop_coroutine();

Expand Down Expand Up @@ -56,12 +60,10 @@ struct generator_promise : enable_memory_resource_support {
};

struct attach_leaf {
// precondition: leaf != nullptr
handle_type leaf;
const handle_type leaf;

bool await_ready() const noexcept {
assume_not_null(leaf);
return leaf.done();
return !leaf || leaf.done();
}

private:
Expand All @@ -79,28 +81,27 @@ struct generator_promise : enable_memory_resource_support {
}

public:
void await_suspend(handle_type owner) const noexcept {
std::coroutine_handle<> await_suspend(handle_type owner) const noexcept {
generator_promise& leaf_p = leaf.promise();
generator_promise& root = *owner.promise().root;
// TODO сделать так чтобы это было только в final suspend?
if (leaf_p.current_worker != leaf) [[unlikely]]
set_root_for_each_leaf_subleaf(root);
leaf_p.root = &root;
leaf_p.owner = owner;
root.current_worker = leaf_p.current_worker;
root.current_result = leaf_p.current_result; // first value was created by leaf
return root.current_worker;
}
static constexpr void await_resume() noexcept {
}
~attach_leaf() {
// make sure compiler, that its destroyed here (end of yield expression)
leaf.destroy();
if (leaf)
leaf.destroy();
}
};

public:
std::suspend_always yield_value(Yield&& lvalue) noexcept {
root->current_result = std::addressof(lvalue);
std::suspend_always yield_value(Yield&& rvalue) noexcept {
root->current_result = std::addressof(rvalue);
return {};
}
hold_value_until_resume yield_value(const Yield& clvalue) noexcept(
Expand All @@ -111,29 +112,44 @@ struct generator_promise : enable_memory_resource_support {
}
// attaches leaf-generator
template <typename R>
attach_leaf yield_value(elements_of<R>) noexcept;
attach_leaf yield_value(elements_of<R> e) noexcept {
if constexpr (std::is_same_v<std::remove_cvref_t<R>, generator<Yield>>) {
return attach_leaf{e.rng.release()};
} else {
auto make_gen = [](auto& r) -> generator<Yield> {
for (auto&& x : r) {
using val_t = std::remove_reference_t<decltype(x)>;
if constexpr (std::is_same_v<val_t, Yield>)
co_yield x;
else
co_yield Yield(x);
}
};
handle_type h = make_gen(e.rng).release();
assume_not_null(h);
return attach_leaf{h};
}
}

static constexpr std::suspend_never initial_suspend() noexcept {
static constexpr std::suspend_always initial_suspend() noexcept {
return {};
}
transfer_control_to final_suspend() const noexcept {
root->current_worker = owner;
// i dont have value here now, so i ask 'owner' to create it
root->current_result = nullptr;
return transfer_control_to{owner}; // noop coro if done
}
static constexpr void return_void() noexcept {
}
[[noreturn]] static void unhandled_exception() {
// TODO ? может ли быть неконсистентное состояние?..
throw;
}

// interface for iterator, used only on top-level generator

bool done() noexcept {
return get_return_object().done();
}
void produce_next() {
assert(root == this && !done());
assume(root == this);
assume(!get_return_object().done());
current_worker.resume();
}
};
Expand All @@ -156,7 +172,7 @@ struct generator_iterator {
}
// precondition: h != nullptr
generator_iterator(std::coroutine_handle<generator_promise<Yield>> h) noexcept : handle(h) {
assert(h != nullptr);
assume_not_null(h);
}

using iterator_category = std::input_iterator_tag;
Expand Down Expand Up @@ -205,9 +221,11 @@ struct generator {
public:
// postcondition: empty(), 'for' loop produces 0 values
constexpr generator() noexcept = default;
// precondition: 'handle' != nullptr
// precondition: 'handle' != nullptr && !handle.done()
// TODO must be unusable for invariant, that generator created from non-started non null handle
constexpr generator(handle_type handle) noexcept : handle(handle) {
assume_not_null(handle);
assume(!handle.done());
}
// postcondition: other.empty()
constexpr generator(generator&& other) noexcept : handle(std::exchange(other.handle, nullptr)) {
Expand Down Expand Up @@ -238,42 +256,28 @@ struct generator {
constexpr explicit operator bool() const noexcept {
return !empty();
}
// TODO hmm, я повторяю одно и то же число в начале при реюзе

// * if .empty(), then begin() == end()
iterator begin() const noexcept [[clang::lifetimebound]] {
if (!handle) [[unlikely]]
// produces next value(often first)
iterator begin() KELCORO_LIFETIMEBOUND {
if (empty()) [[unlikely]]
return iterator{};
return iterator(handle);
iterator result(handle);
++result;
return result;
}
static constexpr std::default_sentinel_t end() noexcept {
return std::default_sentinel;
}
};

template <typename Yield>
template <typename R>
typename generator_promise<Yield>::attach_leaf generator_promise<Yield>::yield_value(
elements_of<R> e) noexcept {
if constexpr (std::is_same_v<std::remove_cvref_t<R>, generator<Yield>>) {
std::coroutine_handle h = e.rng.release();
// handle case when default constructed generator yielded,
// (it must always behave as empty range)
if (!h) [[unlikely]]
return attach_leaf{[]() -> generator<Yield> { co_return; }().release()};
return attach_leaf{h};
} else {
auto make_gen = [](auto& r) -> generator<Yield> {
for (auto&& x : r) {
using val_t = std::remove_reference_t<decltype(x)>;
if constexpr (std::is_same_v<val_t, Yield>)
co_yield x;
else
co_yield Yield(x);
}
};
return attach_leaf{make_gen(e.rng).release()};
Yield* next() noexcept {
if (empty())
return nullptr;
auto& p = handle.promise();
p.produce_next();
return p.current_result;
}
}
};

} // namespace dd

Expand Down
Loading

0 comments on commit 29f1e21

Please sign in to comment.