2#include <asyncpp/detail/promise_allocator_base.h>
3#include <asyncpp/scope_guard.h>
20 template<ByteAllocator Allocator = default_allocator_type>
23 struct promise_type : promise_allocator_base<Allocator> {
24 constexpr launch_task get_return_object() noexcept {
return {}; }
25 constexpr suspend_never initial_suspend() noexcept {
return {}; }
26 constexpr suspend_never final_suspend() noexcept {
return {}; }
27 constexpr void return_void() noexcept {}
28 void unhandled_exception() noexcept { std::terminate(); }
41 template<
typename Awaitable, ByteAllocator Allocator = default_allocator_type>
42 void launch(Awaitable&& awaitable,
const Allocator& allocator = {}) {
43 [](std::decay_t<Awaitable> awaitable,
const Allocator&) -> detail::launch_task<Allocator> {
44 co_await std::move(awaitable);
45 }(std::forward<decltype(awaitable)>(awaitable), allocator);
52 std::atomic<size_t> m_count{0U};
53 std::atomic<void*> m_continuation{
nullptr};
66 template<
typename Awaitable, ByteAllocator Allocator = default_allocator_type>
67 void launch(Awaitable&& awaitable,
const Allocator& allocator = {}) {
69 const Allocator&) -> detail::launch_task<Allocator> {
70 scope->m_count.fetch_add(1);
73 if (scope->m_count.fetch_sub(1) == 1) {
75 auto hdl = scope->m_continuation.exchange(
nullptr);
77 if (hdl !=
nullptr) coroutine_handle<>::from_address(hdl).resume();
81 }(
this, std::forward<decltype(awaitable)>(awaitable), allocator);
91 template<
typename Callable,
typename... Args, ByteAllocator Allocator = default_allocator_type>
92 requires(std::is_invocable_v<Callable, Args...>)
93 void invoke_tuple(Callable&& callable, std::tuple<Args...>&& args,
const Allocator& allocator = {}) {
95 const Allocator&) -> detail::launch_task<Allocator> {
96 scope->m_count.fetch_add(1);
99 if (scope->m_count.fetch_sub(1) == 1) {
101 auto hdl = scope->m_continuation.exchange(
nullptr);
103 if (hdl !=
nullptr) coroutine_handle<>::from_address(hdl).resume();
106 co_await std::apply(callable, std::move(args));
107 }(
this, std::forward<Callable>(callable), std::move(args), allocator);
117 template<
typename Callable,
typename... Args>
118 requires(std::is_invocable_v<Callable, Args...>)
119 void invoke(Callable&& callable, Args&&... args) {
120 [](
async_launch_scope* scope, Callable callable, Args&&... args) -> detail::launch_task<> {
121 scope->m_count.fetch_add(1);
124 if (scope->m_count.fetch_sub(1) == 1) {
126 auto hdl = scope->m_continuation.exchange(
nullptr);
128 if (hdl !=
nullptr) coroutine_handle<>::from_address(hdl).resume();
131 co_await std::invoke(callable, std::forward<Args>(args)...);
132 }(
this, std::forward<Callable>(callable), std::forward<Args>(args)...);
139 [[nodiscard]]
auto join() noexcept {
142 [[nodiscard]]
bool await_ready()
const noexcept {
144 return m_scope->m_count.load() == 0;
146 [[nodiscard]]
bool await_suspend(coroutine_handle<> hdl)
const {
148 void* expected =
nullptr;
149 if (!m_scope->m_continuation.compare_exchange_strong(expected, hdl.address()))
150 throw std::logic_error(
"duplicate join");
152 return m_scope->m_count.load() != 0;
154 constexpr void await_resume()
const noexcept {}
156 return awaiter{
this};
164 template<ByteAllocator Allocator = default_allocator_type>
165 std::future<void>
join_future(
const Allocator& allocator = {})
noexcept {
166 std::promise<void> done;
167 auto res = done.get_future();
168 [](
async_launch_scope* that, std::promise<void> done,
const Allocator&) -> detail::launch_task<Allocator> {
170 co_await that->
join();
172 }
catch (...) { done.set_exception(std::current_exception()); }
173 }(
this, std::move(done), allocator);
181 [[nodiscard]]
size_t inflight_coroutines() const noexcept {
return m_count.load(std::memory_order::relaxed); }
Holder class for spawning child tasks. Allows waiting for all of them to finish.
Definition launch.h:51
void invoke(Callable &&callable, Args &&... args)
Invoke the provided callable in a new task. The callable is copied into the task, making it save for ...
Definition launch.h:119
void launch(Awaitable &&awaitable, const Allocator &allocator={})
Spawn a new task for the given awaitable.
Definition launch.h:67
size_t inflight_coroutines() const noexcept
Returns the number of active task on this scope.
Definition launch.h:181
bool all_done() const noexcept
Returns true if there is no active task currently running on this scope.
Definition launch.h:186
auto join() noexcept
Wait for all active tasks to finish.
Definition launch.h:139
void invoke_tuple(Callable &&callable, std::tuple< Args... > &&args, const Allocator &allocator={})
Invoke the provided callable in a new task. The callable is copied into the task, making it save for ...
Definition launch.h:93
std::future< void > join_future(const Allocator &allocator={}) noexcept
Create a future that finishes once all tasks are done. This is equivalent to awaiting join() in a pro...
Definition launch.h:165
Execute a callback function when this scope gets destroyed.
Definition scope_guard.h:12