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
72 changes: 42 additions & 30 deletions cth_coro/incl/cth/coro/executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,55 +26,65 @@ class executor {


/**
* steals the execution context to the scheduler, keeps it as awaitable
* steals the execution context to the scheduler after the coroutine finished, keeps it as awaitable
* @tparam Pyld payload type; the scheduler is injected automatically when it is a scheduler_payload
* @tparam Awaitable must be compatible with the this_coro framework
* @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
*/
template<this_coro_awaitable Awaitable>
auto steal(Awaitable&& awaitable) -> awaiter_t<Awaitable> { return co::steal(scheduler(), awaitable); }
template<payload Pyld = this_coro::default_payload, this_coro_awaitable Awaitable, class... Args>
[[nodiscard]] 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
return co::steal(std::forward<Awaitable>(awaitable), Pyld{std::forward<Args>(args)...});
}

/**
* steals the execution context to the scheduler
* steals the execution context to the scheduler after the coroutine finished
* @note converts the awaitable to a task
* @tparam Pyld payload type; the scheduler is injected automatically when it is a scheduler_payload
* @tparam Awaitable must not be compatible with the this_coro framework
* @param awaitable to steal context from
* @param args extra arguments to construct the payload with (besides the auto-injected scheduler)
* @return task with context stolen
*/
template<non_this_coro_awaitable Awaitable>
auto steal(Awaitable awaitable) -> capture_task<awaited_t<Awaitable>> {
return co::steal(scheduler(), std::move(awaitable));
template<payload Pyld = this_coro::default_payload, non_this_coro_awaitable Awaitable, class... Args>
[[nodiscard]] auto steal(Awaitable awaitable, Args&&... args) const -> capture_task<awaited_t<Awaitable>> {
if constexpr(this_coro::scheduler_payload<Pyld>)
return co::steal(std::move(awaitable), Pyld{scheduler(), std::forward<Args>(args)...});
else
return co::steal(std::move(awaitable), Pyld{std::forward<Args>(args)...});
}


/**
* runs the task on the scheduler once started
* @tparam T type to return
* @param task to run
* runs the coroutine on the scheduler once started, then captures execution back
* @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
*/
template<class T>
auto spawn(scheduled_task<T> task) -> scheduled_task<T> { return executor::steal(std::move(task)); }

/**
* wraps the awaitable with a task running on the scheduler once started
* @param awaitable to wrap
* @return task
*/
template<awaitable Awaitable>
auto spawn(Awaitable awaitable) -> scheduled_task<awaited_t<Awaitable>> {
return executor::spawn(this_coro::payload{scheduler()}, *this, std::move(awaitable));
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> {
if constexpr(this_coro::scheduler_payload<Pyld>)
return spawn_impl(Pyld{scheduler(), std::forward<Args>(args)...}, *this, std::move(task));
else
return spawn_impl(Pyld{std::forward<Args>(args)...}, *this, std::move(task));
}

private:
template<awaitable Awaitable>
static auto spawn(
[[maybe_unused]] this_coro::payload p,
executor& s,
template<payload Pyld, awaitable Awaitable>
static auto spawn_impl(
Pyld payload,
executor self,
Awaitable task
)
-> scheduled_task<awaited_t<Awaitable>> {
co_await s.schedule();
co_return co_await s.steal(std::move(task));
-> scheduled_task<awaited_t<Awaitable>, Pyld> {
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);
}

scheduler const* _sched;
Expand All @@ -99,7 +109,9 @@ class executor {

namespace cth::co::this_coro {
struct [[nodiscard]] executor_tag : tag_base {
static [[nodiscard]] constexpr auto operator()(payload const& p) { return data_awaiter{executor{p.scheduler()}}; }
static [[nodiscard]] constexpr auto operator()(default_payload const& p) {
return data_awaiter{executor{p.scheduler()}};
}
};


Expand Down
53 changes: 42 additions & 11 deletions cth_coro/incl/cth/coro/func/steal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,60 @@
namespace cth::co {


template<non_this_coro_awaitable Awaitable>
[[nodiscard]] auto steal(scheduler const& scheduler, Awaitable awaitable)
-> capture_task<awaited_t<Awaitable>> {

template<this_coro::scheduler_payload Pyld, non_this_coro_awaitable Awaitable>
[[nodiscard]] auto steal(Awaitable awaitable, Pyld const& 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>;

if constexpr(std::same_as<void, result_t>) {
co_await awaitable;
co_await schedule_awaiter{scheduler};
co_await std::move(awaitable);
co_await schedule_awaiter{payload.scheduler()};
co_return;
} else {
decltype(auto) result = co_await awaitable;
co_await schedule_awaiter{scheduler};
decltype(auto) result = co_await std::move(awaitable);
co_await schedule_awaiter{payload.scheduler()};
co_return result;
}
}

template<this_coro_awaitable Awaitable>
constexpr auto steal(scheduler const& scheduler, Awaitable&& awaitable) -> awaiter_t<Awaitable> {

template<payload Pyld, this_coro_awaitable Awaitable>
constexpr auto steal(Awaitable&& awaitable, Pyld const& payload) -> awaiter_t<Awaitable> {
static_assert(this_coro_awaitable_with<Awaitable, Pyld>, "awaitable not compatible with payload type");

auto awaiter = co::extract_awaiter(std::forward<Awaitable>(awaitable));
awaiter.inject(this_coro::payload{scheduler});
awaiter.inject(payload);

return awaiter;
}


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)...});
}

template<payload Pyld, non_this_coro_awaitable Awaitable, class... Args>
[[nodiscard]] auto steal(Awaitable awaitable, Args&&... args) -> capture_task<awaited_t<Awaitable>> {
return co::steal(std::move(awaitable), Pyld{std::forward<Args>(args)...});
}



template<non_this_coro_awaitable Awaitable>
[[nodiscard]] auto steal(
Awaitable awaitable,
scheduler const& scheduler
) -> capture_task<awaited_t<Awaitable>> {
return co::steal(std::move(awaitable), this_coro::default_payload{scheduler});
}

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});
}


}
4 changes: 3 additions & 1 deletion cth_coro/incl/cth/coro/scheduler.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#pragma once

#include "cth/io/log.hpp"
#include <cth/io/log.hpp>
#include <cth/chrono.hpp>

#include "os/native_handle.hpp"

#include "utility/fwd.hpp"


#include <functional>
#include <thread>
#include <vector>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
namespace cth::co {
template<cth_promise Promise>
struct this_coro_promise_awaiter
: this_coro_promise_awaiter_base
, private dev::basic_promise_awaiter<Promise> {
: this_coro_promise_awaiter_base<typename Promise::payload_type>,
private dev::basic_promise_awaiter<Promise> {
private:
using base_t = dev::basic_promise_awaiter<Promise>;
using payload_t = Promise::payload_type;

public:
this_coro_promise_awaiter() = delete;
Expand All @@ -26,7 +27,7 @@ struct this_coro_promise_awaiter

template<class T>
decltype(auto) await_suspend(std::coroutine_handle<T> caller) noexcept {
this_coro_promise_awaiter_base::inject_payload_into(base_t::handle);
this_coro_promise_awaiter_base<payload_t>::inject_payload_into(base_t::handle);
return base_t::await_suspend(caller);
}
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
#pragma once
#include "cth/coro/this_coro/payload.hpp"

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

#include <cth/data/optional.hpp>

#include <coroutine>
#include <optional>



namespace cth::co {
/**
* Base for awaitables which support executor injection
* @tparam Pyld payload to work with defaulted to @ref this_coro::default_payload
*/
template<payload Pyld>
struct this_coro_promise_awaiter_base {
using payload_type = Pyld;

constexpr this_coro_promise_awaiter_base() = default;

/**
Expand All @@ -21,11 +27,11 @@ struct this_coro_promise_awaiter_base {
*/
template<promise Promise>
void inject_payload_into(std::coroutine_handle<Promise> h) {
constexpr bool injectable = this_coro_supported_promise<Promise>;
constexpr bool injectable = this_coro_supported_promise_with<Promise, Pyld>;

static_assert(
injectable,
"this coro supporting awaitables may not be awaited by awaitables without support"
"this coro supporting awaitables may not be awaited by awaitables without a compatible payload"
);


Expand All @@ -37,13 +43,13 @@ struct this_coro_promise_awaiter_base {
* injects the this_coro payload into this awaiter unless it already has one
* @param payload to inject
*/
constexpr void inject(this_coro::payload payload) {
constexpr void inject(Pyld const& payload) {
if(!_payload)
_payload.emplace(payload);
}

private:
std::optional<this_coro::payload> _payload{};
cth::dt::optional<Pyld> _payload{};
};


Expand Down
22 changes: 11 additions & 11 deletions cth_coro/incl/cth/coro/tasks/dev/scheduled_task.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@

namespace cth::co::dev {

template<class T>
struct scheduled_promise : basic_promise<T>, this_coro_promise_base {
explicit scheduled_promise(this_coro::payload payload) : this_coro_promise_base{std::move(payload)} {}
template<class T, payload Pyld = this_coro::default_payload>
struct scheduled_promise : basic_promise<T>, this_coro_promise_base<Pyld> {
explicit scheduled_promise(Pyld const& payload) : this_coro_promise_base<Pyld>{payload} {}

template<class... Args>
explicit scheduled_promise(this_coro::payload const& payload, Args&&...) : scheduled_promise{payload} {}
explicit scheduled_promise(Pyld const& payload, Args&&...) : scheduled_promise{payload} {}


scheduled_task<T> get_return_object() noexcept;
scheduled_task<T, Pyld> get_return_object() noexcept;
};

}

namespace cth::co {

template<class T>
template<class T, payload Pyld>
class [[nodiscard]] scheduled_task
: public task_base<dev::scheduled_promise<T>, dev::capture_promise_awaiter> {
using base_t = task_base<dev::scheduled_promise<T>, dev::capture_promise_awaiter>;
: public task_base<dev::scheduled_promise<T, Pyld>, dev::capture_promise_awaiter> {
using base_t = task_base<dev::scheduled_promise<T, Pyld>, dev::capture_promise_awaiter>;

using base_t::base_t;
friend base_t::promise_type;
Expand All @@ -40,9 +40,9 @@ class [[nodiscard]] scheduled_task
scheduled_task& operator=(scheduled_task&& other) noexcept = default;
};

template<class T>
scheduled_task<T> dev::scheduled_promise<T>::get_return_object() noexcept {
return scheduled_task<T>{std::coroutine_handle<scheduled_promise>::from_promise(*this)};
template<class T, payload Pyld>
scheduled_task<T, Pyld> dev::scheduled_promise<T, Pyld>::get_return_object() noexcept {
return scheduled_task<T, Pyld>{std::coroutine_handle<scheduled_promise>::from_promise(*this)};
}

}
29 changes: 13 additions & 16 deletions cth_coro/incl/cth/coro/tasks/executor_task.hpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
#pragma once
#include "cth/coro/tasks/task_base.hpp"
#include "cth/coro/tasks/awaiters/this_coro_promise_awaiter.hpp"
#include "promises/basic_promise.hpp"
#include "promises/this_coro_promise_base.hpp"
#include "cth/coro/tasks/promises/basic_promise.hpp"
#include "cth/coro/tasks/promises/this_coro_promise_base.hpp"

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

#include <coroutine>

namespace cth::co {
template<class T>
class executor_task;
}

namespace cth::co::dev {
template<class T>
struct executor_promise : basic_promise<T>, this_coro_promise_base {
executor_task<T> get_return_object() noexcept;
template<class T, payload Pyld = this_coro::default_payload>
struct executor_promise : basic_promise<T>, this_coro_promise_base<Pyld> {
executor_task<T, Pyld> get_return_object() noexcept;
};
}

Expand All @@ -25,18 +22,18 @@ namespace cth::co {
* Task to be spawned on the @ref executor
* @tparam T task return type
*/
template<class T>
class [[nodiscard]] executor_task : public task_base<dev::executor_promise<T>, this_coro_promise_awaiter> {
using base_t = task_base<dev::executor_promise<T>, this_coro_promise_awaiter>;
template<class T, payload Pyld>
class [[nodiscard]] executor_task : public task_base<dev::executor_promise<T, Pyld>, this_coro_promise_awaiter> {
using base_t = task_base<dev::executor_promise<T, Pyld>, this_coro_promise_awaiter>;
using base_t::base_t;

friend base_t::promise_type;
};


template<class T>
auto dev::executor_promise<T>::get_return_object() noexcept -> executor_task<T> {
return executor_task<T>{std::coroutine_handle<executor_promise<T>>::from_promise(*this)};
template<class T, payload Pyld>
auto dev::executor_promise<T, Pyld>::get_return_object() noexcept -> executor_task<T, Pyld> {
return executor_task<T, Pyld>{std::coroutine_handle<executor_promise<T, Pyld>>::from_promise(*this)};
}

using executor_void_task = executor_task<void>;
Expand Down
Loading
Loading