Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace cth::co {
template<this_coro::scheduler_payload Pyld = this_coro::default_payload>
struct native_handle_awaiter_base : dev::this_coro_awaiter_base<Pyld> {
explicit native_handle_awaiter_base(os::native_handle_t handle) : _handle{handle} {}
constexpr explicit native_handle_awaiter_base(os::native_handle_t handle) : _handle{handle} {}

[[nodiscard]] bool await_ready() const { return os::check_ready(_handle); }
void await_suspend(std::coroutine_handle<> h) const {
Expand Down
2 changes: 1 addition & 1 deletion cth_coro/incl/cth/coro/awaiters/wait_awaiter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct wait_awaiter {
std::chrono::steady_clock::time_point timePoint;
co::scheduler const& scheduler;

[[nodiscard]] constexpr bool await_ready() const noexcept {
[[nodiscard]] bool await_ready() const noexcept {
return std::chrono::steady_clock::now() >= timePoint;
}

Expand Down
24 changes: 15 additions & 9 deletions cth_coro/incl/cth/coro/executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include "cth/coro/awaiters/data_awaiter.hpp"
#include "cth/coro/awaiters/schedule_awaiter.hpp"
#include "cth/coro/func/steal.hpp"
#include "cth/coro/tasks/dev/scheduled_task.hpp"
#include "cth/coro/tasks/dev/spawn_task.hpp"
#include "cth/coro/utility/concepts.hpp"


Expand Down Expand Up @@ -33,9 +33,10 @@ class executor {
* @param awaitable to steal context from
* @param args extra arguments to construct the payload with (besides the auto-injected scheduler)
* @return awaitable with context stolen
* @attention the awaiter does not own the task, co_await it in the same expression, do not store it
*/
template<payload Pyld = this_coro::default_payload, this_coro_awaitable Awaitable, class... Args>
[[nodiscard]] auto steal(Awaitable&& awaitable, Args&&... args) const -> awaiter_t<Awaitable> {
[[nodiscard]] constexpr auto steal(Awaitable&& awaitable, Args&&... args) const -> awaiter_t<Awaitable> {
if constexpr(this_coro::scheduler_payload<Pyld>)
return co::steal(std::forward<Awaitable>(awaitable), Pyld{scheduler(), std::forward<Args>(args)...});
else
Expand All @@ -60,14 +61,16 @@ class executor {
}

/**
* runs the coroutine on the scheduler once started, then captures execution back
* runs the coroutine on the scheduler once started
* @details the caller continues on its own scheduler afterwards, with one post if the coroutine ended elsewhere,
* a caller without this_coro support continues where the coroutine ended
* @tparam Pyld payload type; the scheduler is injected automatically when it is a scheduler_payload
* @param task coroutine to run on the scheduler
* @param args extra arguments to construct the payload with (besides the auto-injected scheduler)
* @return cold scheduled task; starts on the scheduler when awaited
* @return cold spawn task; starts on the scheduler when awaited
*/
template<payload Pyld = this_coro::default_payload, awaitable Awaitable, class... Args>
[[nodiscard]] auto spawn(Awaitable task, Args&&... args) const -> scheduled_task<awaited_t<Awaitable>, Pyld> {
[[nodiscard]] auto spawn(Awaitable task, Args&&... args) const -> spawn_task<awaited_t<Awaitable>> {
if constexpr(this_coro::scheduler_payload<Pyld>)
return spawn_impl(Pyld{scheduler(), std::forward<Args>(args)...}, *this, std::move(task));
else
Expand All @@ -81,11 +84,14 @@ class executor {
executor self,
Awaitable task
)
-> scheduled_task<awaited_t<Awaitable>, Pyld> {
co_await self.schedule<Pyld>();
-> spawn_task<awaited_t<Awaitable>> {
co_await self.schedule();

// inject the payload into the child and finish back on this executor
co_return co_await co::steal(std::move(task), payload);
// the final awaiter of the spawn_task resumes the caller on its own scheduler
if constexpr(this_coro_awaitable<Awaitable>)
co_return co_await co::steal(std::move(task), payload); // injects the payload, no hop
else
co_return co_await task;
}

co::scheduler const* _sched;
Expand Down
14 changes: 13 additions & 1 deletion cth_coro/incl/cth/coro/func/steal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace cth::co {


template<this_coro::scheduler_payload Pyld, non_this_coro_awaitable Awaitable>
[[nodiscard]] auto steal(Awaitable awaitable, Pyld const& payload) -> capture_task<awaited_t<Awaitable>> {
[[nodiscard]] auto steal(Awaitable awaitable, Pyld payload) -> capture_task<awaited_t<Awaitable>> {
static_assert(has_scheduler<Pyld>, "foreign awaitables can only be stolen with a scheduler in the payload");

using result_t = awaited_t<Awaitable>;
Expand All @@ -29,6 +29,10 @@ template<this_coro::scheduler_payload Pyld, non_this_coro_awaitable Awaitable>
}


/**
* injects the payload into the awaiter of the awaitable
* @attention the awaiter does not own the task, co_await it in the same expression, do not store it
*/
template<payload Pyld, this_coro_awaitable Awaitable>
constexpr auto steal(Awaitable&& awaitable, Pyld const& payload) -> awaiter_t<Awaitable> {
static_assert(
Expand All @@ -43,6 +47,10 @@ constexpr auto steal(Awaitable&& awaitable, Pyld const& payload) -> awaiter_t<Aw
}


/**
* injects a payload constructed from the arguments into the awaiter of the awaitable
* @attention the awaiter does not own the task, co_await it in the same expression, do not store it
*/
template<payload Pyld, this_coro_awaitable Awaitable, class... Args>
constexpr auto steal(Awaitable&& awaitable, Args&&... args) -> awaiter_t<Awaitable> {
return co::steal(std::forward<Awaitable>(awaitable), Pyld{std::forward<Args>(args)...});
Expand All @@ -63,6 +71,10 @@ template<non_this_coro_awaitable Awaitable>
return co::steal(std::move(awaitable), this_coro::default_payload{scheduler});
}

/**
* injects the default payload of the scheduler into the awaiter of the awaitable
* @attention the awaiter does not own the task, co_await it in the same expression, do not store it
*/
template<this_coro_awaitable Awaitable>
constexpr auto steal(Awaitable&& awaitable, scheduler const& scheduler) -> awaiter_t<Awaitable> {
return co::steal(std::forward<Awaitable>(awaitable), this_coro::default_payload{scheduler});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ struct basic_final_promise_awaiter {
constexpr bool await_ready() noexcept { return false; }

template<class Promise>
[[nodiscard]] constexpr auto await_suspend(std::coroutine_handle<Promise> h) noexcept
[[nodiscard]] auto await_suspend(std::coroutine_handle<Promise> h) noexcept
-> std::coroutine_handle<> {
if(auto continuation = h.promise().continuation)
return continuation;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#pragma once
#include "basic_promise_awaiter.hpp"
#include "capture_promise_awaiter_base.hpp"

#include "cth/coro/utility/fwd.hpp"

namespace cth::co::dev {

template<class Promise>
struct capture_promise_awaiter
: basic_promise_awaiter<Promise>
, capture_promise_awaiter_base {
, capture_awaiter_base {
using dev::basic_promise_awaiter<Promise>::basic_promise_awaiter;
};
}

This file was deleted.

34 changes: 34 additions & 0 deletions cth_coro/incl/cth/coro/tasks/awaiters/dev/spawn_final_awaiter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once
#include "cth/coro/scheduler.hpp"

#include <coroutine>


namespace cth::co::dev {
/**
* final awaiter of @ref spawn_promise, resumes the awaiting coroutine on its own scheduler
* @details transfers directly if the scheduler of the caller owns the thread, else posts the resume onto it,
* a caller without a known scheduler continues where the spawned coroutine ended
* @attention terminates if the post throws, the final awaiter is noexcept
*/
struct spawn_final_awaiter {
[[nodiscard]] constexpr bool await_ready() const noexcept { return false; }

template<class Promise>
[[nodiscard]] auto await_suspend(std::coroutine_handle<Promise> h) const noexcept -> std::coroutine_handle<> {
auto const continuation = h.promise().continuation;
if(!continuation)
return std::noop_coroutine();

auto const* caller = h.promise().caller;
if(caller == nullptr || caller->owns_thread())
return continuation;

// the caller may destroy this coroutine as soon as it resumes, nothing touches the frame after the post
caller->post([continuation] { continuation.resume(); });
return std::noop_coroutine();
}

constexpr void await_resume() const noexcept {}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once
#include "basic_promise_awaiter.hpp"

#include "cth/coro/this_coro/payload.hpp"
#include "cth/coro/this_coro/this_coro_base.hpp"
#include "cth/coro/utility/fwd.hpp"

#include <cth/meta/coro.hpp>

#include <coroutine>


namespace cth::co::dev {
/**
* awaiter of @ref spawn_task, takes the scheduler of the caller from the injected payload
* @details the payload of the caller is not stored and not rebound,
* a caller without this_coro support injects nothing
* @tparam Promise of the spawned coroutine, needs a `caller` member
*/
template<cth_promise Promise>
struct spawn_promise_awaiter : this_coro_base<this_coro::default_payload>, private basic_promise_awaiter<Promise> {
private:
using base_t = basic_promise_awaiter<Promise>;

public:
using promise_type = base_t::promise_type;

using base_t::await_ready;
using base_t::await_resume;
using base_t::base_t;

/**
* stores the scheduler of the caller
* @param payload of the caller
*/
template<this_coro::scheduler_payload Pyld>
constexpr void inject(Pyld const& payload) noexcept { _caller = &payload.scheduler(); }

[[nodiscard]] auto await_suspend(std::coroutine_handle<> caller) noexcept -> std::coroutine_handle<promise_type> {
base_t::handle.promise().caller = _caller;
return base_t::await_suspend(caller);
}

private:
co::scheduler const* _caller = nullptr;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ struct this_coro_awaiter_base : this_coro_base<Pyld> {
}

protected:
Pyld& payload() { return *_payload; }
Pyld const& payload() const { return *_payload; }
constexpr Pyld& payload() { return *_payload; }
constexpr Pyld const& payload() const { return *_payload; }

private:
cth::dt::optional<Pyld> _payload{};
Expand Down
48 changes: 0 additions & 48 deletions cth_coro/incl/cth/coro/tasks/dev/scheduled_task.hpp

This file was deleted.

54 changes: 54 additions & 0 deletions cth_coro/incl/cth/coro/tasks/dev/spawn_task.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#pragma once
#include "cth/coro/tasks/task_base.hpp"

#include "cth/coro/tasks/awaiters/dev/spawn_final_awaiter.hpp"
#include "cth/coro/tasks/awaiters/dev/spawn_promise_awaiter.hpp"
#include "cth/coro/tasks/promises/basic_promise.hpp"
#include "cth/coro/utility/fwd.hpp"

#include <coroutine>


namespace cth::co::dev {

template<class T>
struct spawn_promise : basic_promise<T, std::suspend_always{}, spawn_final_awaiter{}> {
/**
* scheduler of the awaiting coroutine, nullptr -> unknown
*/
co::scheduler const* caller = nullptr;

spawn_task<T> get_return_object() noexcept;
};

}

namespace cth::co {

/**
* task of @ref executor::spawn()
* @details this_coro awaitable: the caller continues on its own scheduler after the spawned coroutine ended,
* a caller without this_coro support continues where the spawned coroutine ended
* @tparam T result type
*/
template<class T>
class [[nodiscard]] spawn_task : public task_base<dev::spawn_promise<T>, dev::spawn_promise_awaiter> {
using base_t = task_base<dev::spawn_promise<T>, dev::spawn_promise_awaiter>;

using base_t::base_t;
friend base_t::promise_type;

public:
spawn_task(spawn_task const& other) = delete;
spawn_task& operator=(spawn_task const& other) = delete;

spawn_task(spawn_task&& other) noexcept = default;
spawn_task& operator=(spawn_task&& other) noexcept = default;
};

template<class T>
spawn_task<T> dev::spawn_promise<T>::get_return_object() noexcept {
return spawn_task<T>{std::coroutine_handle<spawn_promise>::from_promise(*this)};
}

}
8 changes: 4 additions & 4 deletions cth_coro/incl/cth/coro/tasks/promises/raw_promise.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ struct raw_promise : dev::raw_promise_base {
std::exception_ptr exception;
std::coroutine_handle<> continuation = nullptr;

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

template<class U>
void return_value(U&& value) noexcept {
constexpr void return_value(U&& value) noexcept {
result.emplace(std::forward<U>(value));
}
};
Expand All @@ -31,8 +31,8 @@ struct raw_promise<void> : dev::raw_promise_base {
std::exception_ptr exception;
std::coroutine_handle<> continuation = nullptr;

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

void return_void() noexcept { result.emplace(); }
constexpr void return_void() noexcept { result.emplace(); }
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct this_coro_promise_base : this_coro_base<Pyld> {
[[nodiscard]] constexpr auto await_transform(Awaitable&& captured) { return cth::co::extract_awaiter(captured); }

template<native_handle_waitable Waitable> requires (this_coro::scheduler_payload<Pyld>)
[[nodiscard]] auto await_transform(Waitable const& waitable) {
[[nodiscard]] constexpr auto await_transform(Waitable const& waitable) {
return raw_native_handle_awaiter{_payload->scheduler(), waitable.native_handle()};
}

Expand Down Expand Up @@ -85,8 +85,8 @@ struct this_coro_promise_base : this_coro_base<Pyld> {
_payload->rebind_scheduler(scheduler);
}

this_coro_promise_base(Pyld const& payload) noexcept : _payload{payload} {}
this_coro_promise_base() noexcept = default;
constexpr this_coro_promise_base(Pyld const& payload) noexcept : _payload{payload} {}
constexpr this_coro_promise_base() noexcept = default;

private:
cth::dt::optional<Pyld> _payload{};
Expand Down
Loading
Loading