139 #include "../version.h"
141 #ifdef VLINK_ENABLE_CXX_STD_20
142 #if __has_include(<coroutine>)
145 #if defined(__cpp_lib_coroutine)
146 #ifndef VLINK_ENABLE_COROUTINE
147 #define VLINK_ENABLE_COROUTINE
152 #ifdef VLINK_ENABLE_COROUTINE
164 #include <type_traits>
186 namespace Coroutine {
188 template <
typename TypeT =
void>
202 template <
typename TypeT>
220 VLINK_EXPORT void deallocate_frame(
void* ptr,
size_t size) noexcept;
231 struct FinalAwaiter final {
238 bool await_ready() const noexcept;
248 template <typename PromiseT>
249 std::coroutine_handle<> await_suspend(std::coroutine_handle<PromiseT> handle) noexcept;
255 void await_resume() const noexcept;
268 template <typename TypeT>
269 struct TaskPromiseBase {
270 std::coroutine_handle<> continuation;
271 std::exception_ptr exception;
278 std::suspend_always initial_suspend() noexcept;
285 FinalAwaiter final_suspend() noexcept;
290 void unhandled_exception() noexcept;
303 template <typename TypeT>
304 struct TaskPromise final : TaskPromiseBase<TypeT> {
305 std::optional<TypeT> result;
313 static void*
operator new(
size_t size);
321 static void operator delete(
void* ptr,
size_t size) noexcept;
328 Task<TypeT> get_return_object() noexcept;
336 template <typename UpT>
337 void return_value(UpT&& v) noexcept(std::is_nothrow_constructible_v<TypeT, UpT&&>);
349 struct TaskPromise<
void> final : TaskPromiseBase<
void> {
356 static void*
operator new(
size_t size);
364 static void operator delete(
void* ptr,
size_t size) noexcept;
371 Task<void> get_return_object() noexcept;
376 void return_void() noexcept;
407 static void*
operator new(
size_t size);
415 static void operator delete(
void* ptr,
size_t size) noexcept;
422 DetachedTask get_return_object() noexcept;
429 std::suspend_always initial_suspend() noexcept;
436 std::suspend_never final_suspend() noexcept;
441 void return_void() noexcept;
450 void unhandled_exception() noexcept;
453 using Handle = std::coroutine_handle<promise_type>;
458 DetachedTask() noexcept;
470 explicit DetachedTask(Handle h) noexcept;
477 DetachedTask(DetachedTask&& other) noexcept;
485 DetachedTask& operator=(DetachedTask&& other) noexcept;
498 VLINK_EXPORT DetachedTask co_spawn_void_impl(Task<
void> task);
509 template <typename TypeT, typename CallbackT>
510 DetachedTask co_spawn_value_impl(Task<TypeT> task, CallbackT on_complete);
520 template <typename CallbackT>
521 DetachedTask co_spawn_void_with_cb_impl(Task<
void> task, CallbackT on_complete);
532 enum class ResumePostResult : uint8_t {
538 struct AwaiterResumeState;
549 inline constexpr uint32_t kMaxResumePostRetry = 30000U;
572 VLINK_EXPORT ResumePostResult post_callback_if_alive(MessageLoop* loop,
573 const std::shared_ptr<MessageLoop::AliveState>& alive_state,
574 MoveFunction<
void()>&& resume_callback,
575 MoveFunction<
void()>&& drop_callback, uint16_t priority = 0);
589 VLINK_EXPORT void co_spawn_detached_handle(MessageLoop& loop, DetachedTask::Handle handle, uint16_t priority = 0);
601 VLINK_EXPORT void register_future_wait(MoveFunction<
bool()>&& poll);
617 template <
typename TypeT>
620 using promise_type = detail::TaskPromise<TypeT>;
621 using Handle = std::coroutine_handle<promise_type>;
626 Task() noexcept = default;
633 explicit Task(Handle handle) noexcept;
640 Task(Task&& other) noexcept;
648 Task& operator=(Task&& other) noexcept;
660 [[nodiscard]]
bool valid() const noexcept;
667 [[nodiscard]]
bool done() const noexcept;
674 bool await_ready() const noexcept;
682 std::coroutine_handle<> await_suspend(std::coroutine_handle<> awaiter) noexcept;
693 TypeT await_resume();
700 Handle release() noexcept;
707 [[nodiscard]] Handle native_handle() const noexcept;
735 ScheduleAwaiter() noexcept;
745 ScheduleAwaiter(MessageLoop* loop, std::shared_ptr<MessageLoop::AliveState> alive_state,
746 std::shared_ptr<detail::AwaiterResumeState> state, uint16_t priority) noexcept;
751 ScheduleAwaiter(ScheduleAwaiter&&) noexcept;
756 ScheduleAwaiter& operator=(ScheduleAwaiter&&) noexcept;
758 MessageLoop* loop{
nullptr};
759 std::shared_ptr<MessageLoop::AliveState> alive_state;
760 std::shared_ptr<detail::AwaiterResumeState> state;
761 uint16_t priority{0};
769 static bool await_ready() noexcept;
787 bool await_suspend(std::coroutine_handle<> handle);
816 YieldAwaiter() noexcept;
826 YieldAwaiter(MessageLoop* loop, std::shared_ptr<MessageLoop::AliveState> alive_state,
827 std::shared_ptr<detail::AwaiterResumeState> state, uint16_t priority) noexcept;
832 YieldAwaiter(YieldAwaiter&&) noexcept;
837 YieldAwaiter& operator=(YieldAwaiter&&) noexcept;
839 MessageLoop* loop{
nullptr};
840 std::shared_ptr<MessageLoop::AliveState> alive_state;
841 std::shared_ptr<detail::AwaiterResumeState> state;
842 uint16_t priority{0};
850 static bool await_ready() noexcept;
863 bool await_suspend(std::coroutine_handle<> handle);
894 DelayAwaiter() noexcept;
905 DelayAwaiter(MessageLoop* loop, std::shared_ptr<MessageLoop::AliveState> alive_state,
906 std::shared_ptr<detail::AwaiterResumeState> state, uint32_t ms, uint16_t priority) noexcept;
911 DelayAwaiter(DelayAwaiter&&) noexcept;
916 DelayAwaiter& operator=(DelayAwaiter&&) noexcept;
918 MessageLoop* loop{
nullptr};
919 std::shared_ptr<MessageLoop::AliveState> alive_state;
920 std::shared_ptr<detail::AwaiterResumeState> state;
922 uint16_t priority{0};
930 static bool await_ready() noexcept;
943 bool await_suspend(std::coroutine_handle<> handle);
978 template <typename TypeT>
979 class FutureAwaiter final {
987 FutureAwaiter(MessageLoop* loop, std::future<TypeT> fut) noexcept;
992 FutureAwaiter(FutureAwaiter&&) noexcept = default;
997 FutureAwaiter& operator=(FutureAwaiter&&) noexcept = default;
1009 bool await_ready() const noexcept;
1016 void await_suspend(std::coroutine_handle<> handle);
1025 TypeT await_resume();
1030 MessageLoop* loop_{
nullptr};
1031 std::shared_ptr<State> state_;
1062 GraphAwaiter(MessageLoop* loop,
GraphTaskPtr graph) noexcept;
1067 GraphAwaiter(GraphAwaiter&&) noexcept;
1072 GraphAwaiter& operator=(GraphAwaiter&&) noexcept;
1084 [[nodiscard]]
bool await_ready() const noexcept;
1091 void await_suspend(std::coroutine_handle<> handle);
1098 void await_resume() const;
1103 MessageLoop* loop_{
nullptr};
1105 std::shared_ptr<State> state_;
1147 VLINK_EXPORT DelayAwaiter delay_ms(MessageLoop& loop, uint32_t ms,
1158 template <
typename TypeT>
1159 FutureAwaiter<TypeT> await_future(MessageLoop& loop, std::future<TypeT> fut) noexcept;
1188 VLINK_EXPORT void co_spawn(MessageLoop& loop, Task<void>&& task);
1197 VLINK_EXPORT void co_spawn_with_priority(MessageLoop& loop, Task<void>&& task, uint16_t priority);
1234 template <
typename TypeT,
typename CallbackT,
1236 typename = std::enable_if_t<!std::is_integral_v<std::remove_reference_t<CallbackT>>>>
1237 void co_spawn(MessageLoop& loop, Task<TypeT>&& task, CallbackT&& on_complete);
1249 template <
typename TypeT,
typename CallbackT,
1251 typename = std::enable_if_t<!std::is_integral_v<std::remove_reference_t<CallbackT>>>>
1252 void co_spawn_with_priority(MessageLoop& loop, Task<TypeT>&& task, CallbackT&& on_complete, uint16_t priority);
1281 template <
typename CallbackT>
1282 Task<void> exec(MessageLoop& loop,
const Schedule::Config& config, CallbackT&& callback);
1303 template <
typename TypeT>
1304 Task<std::vector<TypeT>> when_all(MessageLoop& loop, std::vector<Task<TypeT>> tasks);
1318 VLINK_EXPORT Task<void> when_all(MessageLoop& loop, std::vector<Task<void>> tasks);
1340 template <
typename TypeT>
1341 Task<std::pair<size_t, TypeT>> when_any(MessageLoop& loop, std::vector<Task<TypeT>> tasks);
1360 VLINK_EXPORT Task<size_t> when_any(MessageLoop& loop, std::vector<Task<void>> tasks);
1377 VLINK_EXPORT Task<void> sequence(MessageLoop& loop, std::vector<Task<void>> tasks);
1386 inline bool FinalAwaiter::await_ready() const noexcept {
return false; }
1388 template <
typename PromiseT>
1389 inline std::coroutine_handle<> FinalAwaiter::await_suspend(std::coroutine_handle<PromiseT> handle) noexcept {
1390 auto next = handle.promise().continuation;
1391 return next ? next : std::noop_coroutine();
1395 inline void FinalAwaiter::await_resume() const noexcept {}
1397 template <
typename TypeT>
1398 inline std::suspend_always TaskPromiseBase<TypeT>::initial_suspend() noexcept {
1402 template <
typename TypeT>
1403 inline FinalAwaiter TaskPromiseBase<TypeT>::final_suspend() noexcept {
1407 template <
typename TypeT>
1408 inline void TaskPromiseBase<TypeT>::unhandled_exception() noexcept {
1409 exception = std::current_exception();
1412 template <
typename TypeT>
1413 inline void* TaskPromise<TypeT>::operator
new(
size_t size) {
1414 return detail::allocate_frame(size);
1417 template <
typename TypeT>
1418 inline void TaskPromise<TypeT>::operator
delete(
void* ptr,
size_t size) noexcept {
1419 detail::deallocate_frame(ptr, size);
1422 template <
typename TypeT>
1423 inline Task<TypeT> TaskPromise<TypeT>::get_return_object() noexcept {
1424 return Task<TypeT>{std::coroutine_handle<TaskPromise<TypeT>>::from_promise(*
this)};
1427 template <
typename TypeT>
1428 template <
typename UpT>
1429 inline void TaskPromise<TypeT>::return_value(UpT&& v) noexcept(std::is_nothrow_constructible_v<TypeT, UpT&&>) {
1430 result.emplace(std::forward<UpT>(v));
1433 inline void* TaskPromise<void>::operator
new(
size_t size) {
return detail::allocate_frame(size); }
1435 inline void TaskPromise<void>::operator
delete(
void* ptr,
size_t size) noexcept { detail::deallocate_frame(ptr, size); }
1437 inline Task<void> TaskPromise<void>::get_return_object() noexcept {
1438 return Task<void>{std::coroutine_handle<TaskPromise<void>>::from_promise(*
this)};
1441 inline void TaskPromise<void>::return_void() noexcept {}
1443 template <
typename TypeT,
typename CallbackT>
1444 inline DetachedTask co_spawn_value_impl(Task<TypeT> task, CallbackT on_complete) {
1445 on_complete(co_await std::move(task));
1448 template <
typename CallbackT>
1449 inline DetachedTask co_spawn_void_with_cb_impl(Task<void> task, CallbackT on_complete) {
1450 co_await std::move(task);
1456 template <
typename TypeT>
1457 inline Task<TypeT>::Task(Handle handle) noexcept : handle_(handle) {}
1459 template <
typename TypeT>
1460 inline Task<TypeT>::Task(Task&& other) noexcept : handle_(std::exchange(other.handle_, {})) {}
1462 template <
typename TypeT>
1463 inline Task<TypeT>& Task<TypeT>::operator=(Task&& other) noexcept {
1468 handle_ = std::exchange(other.handle_, {});
1474 template <
typename TypeT>
1475 inline Task<TypeT>::~Task() {
1481 template <
typename TypeT>
1482 inline bool Task<TypeT>::valid() const noexcept {
1483 return handle_ !=
nullptr;
1486 template <
typename TypeT>
1487 inline bool Task<TypeT>::done() const noexcept {
1488 return handle_ && handle_.done();
1491 template <
typename TypeT>
1492 inline bool Task<TypeT>::await_ready() const noexcept {
1497 return handle_.done();
1500 template <
typename TypeT>
1501 inline std::coroutine_handle<> Task<TypeT>::await_suspend(std::coroutine_handle<> awaiter) noexcept {
1502 handle_.promise().continuation = awaiter;
1506 template <
typename TypeT>
1507 inline TypeT Task<TypeT>::await_resume() {
1509 throw std::logic_error(
"vlink::Coroutine::Task::await_resume on invalid Task (moved-from or default-constructed)");
1512 if VUNLIKELY (handle_.promise().exception) {
1513 std::rethrow_exception(handle_.promise().exception);
1516 if constexpr (!std::is_void_v<TypeT>) {
1517 return std::move(*handle_.promise().result);
1521 template <
typename TypeT>
1522 inline typename Task<TypeT>::Handle Task<TypeT>::release() noexcept {
1523 return std::exchange(handle_, {});
1526 template <
typename TypeT>
1527 inline typename Task<TypeT>::Handle Task<TypeT>::native_handle() const noexcept {
1531 template <
typename TypeT>
1532 struct FutureAwaiter<TypeT>::State final {
1533 explicit State(std::future<TypeT> future) noexcept : fut(std::move(future)) {}
1535 void set_handle(std::coroutine_handle<> h) {
1536 std::lock_guard lock(mtx);
1538 if (!abandoned.load(std::memory_order_acquire)) {
1543 std::coroutine_handle<> take_handle() {
1544 std::lock_guard lock(mtx);
1545 return std::exchange(handle, {});
1548 void clear_handle() {
1549 std::lock_guard lock(mtx);
1553 void resume_ready() {
1554 auto h = take_handle();
1561 void cancel_and_resume() {
1562 target_closed.store(
true, std::memory_order_release);
1563 auto h = take_handle();
1570 std::future<TypeT> fut;
1572 std::coroutine_handle<> handle;
1573 std::atomic_bool abandoned{
false};
1574 std::atomic_bool target_closed{
false};
1577 template <
typename TypeT>
1578 inline FutureAwaiter<TypeT>::FutureAwaiter(MessageLoop* loop, std::future<TypeT> fut) noexcept
1579 : loop_(loop), state_(MemoryResource::make_shared<State>(std::move(fut))) {}
1581 template <
typename TypeT>
1582 inline FutureAwaiter<TypeT>::~FutureAwaiter() {
1584 state_->abandoned.store(
true, std::memory_order_release);
1585 state_->clear_handle();
1589 template <
typename TypeT>
1590 inline bool FutureAwaiter<TypeT>::await_ready() const noexcept {
1591 return !state_->fut.valid() || state_->fut.wait_for(std::chrono::nanoseconds::zero()) == std::future_status::ready;
1594 template <
typename TypeT>
1595 inline void FutureAwaiter<TypeT>::await_suspend(std::coroutine_handle<> handle) {
1596 auto* loop_ptr = loop_;
1597 auto loop_alive = loop_ptr->get_alive_state();
1598 auto state = state_;
1599 state->set_handle(handle);
1601 detail::register_future_wait(
1602 MoveFunction<
bool()>([loop_ptr, loop_alive, state = std::move(state),
1603 retry_count = 0U]()
mutable ->
bool {
1604 if VUNLIKELY (state->abandoned.load(std::memory_order_acquire)) {
1608 if (state->fut.valid() && state->fut.wait_for(std::chrono::nanoseconds::zero()) != std::future_status::ready) {
1612 const auto result = detail::post_callback_if_alive(
1613 loop_ptr, loop_alive, MoveFunction<
void()>([state]() { state->resume_ready(); }),
1614 MoveFunction<
void()>([state]() {
1615 detail::register_future_wait(MoveFunction<
bool()>([state]() {
1616 if (!state->abandoned.load(std::memory_order_acquire)) {
1617 state->cancel_and_resume();
1624 if (result == detail::ResumePostResult::kPosted) {
1628 if (result == detail::ResumePostResult::kRetry) {
1629 if (++retry_count >= detail::kMaxResumePostRetry) {
1630 detail::register_future_wait(MoveFunction<
bool()>([state]() {
1631 if (!state->abandoned.load(std::memory_order_acquire)) {
1632 state->cancel_and_resume();
1646 template <
typename TypeT>
1647 inline TypeT FutureAwaiter<TypeT>::await_resume() {
1648 state_->abandoned.store(
true, std::memory_order_release);
1649 state_->clear_handle();
1651 if VUNLIKELY (state_->target_closed.load(std::memory_order_acquire)) {
1652 throw Exception::OperationCancelled{};
1655 if constexpr (std::is_void_v<TypeT>) {
1658 return state_->fut.get();
1662 template <
typename TypeT>
1663 inline FutureAwaiter<TypeT> await_future(MessageLoop& loop, std::future<TypeT> fut) noexcept {
1664 return FutureAwaiter<TypeT>{&loop, std::move(fut)};
1667 template <
typename TypeT,
typename CallbackT,
typename>
1668 inline void co_spawn(MessageLoop& loop, Task<TypeT>&& task, CallbackT&& on_complete) {
1669 co_spawn_with_priority<TypeT, CallbackT>(loop, std::move(task), std::forward<CallbackT>(on_complete), 0);
1672 template <
typename TypeT,
typename CallbackT,
typename>
1673 inline void co_spawn_with_priority(MessageLoop& loop, Task<TypeT>&& task, CallbackT&& on_complete, uint16_t priority) {
1674 if constexpr (std::is_void_v<TypeT>) {
1675 auto detached = detail::co_spawn_void_with_cb_impl<std::decay_t<CallbackT>>(std::move(task),
1676 std::forward<CallbackT>(on_complete));
1677 auto handle = detached.handle;
1678 detached.handle = {};
1679 detail::co_spawn_detached_handle(loop, handle, priority);
1681 auto detached = detail::co_spawn_value_impl<TypeT, std::decay_t<CallbackT>>(std::move(task),
1682 std::forward<CallbackT>(on_complete));
1683 auto handle = detached.handle;
1684 detached.handle = {};
1685 detail::co_spawn_detached_handle(loop, handle, priority);
1689 template <
typename CallbackT>
1690 inline Task<void> exec(MessageLoop& loop,
const Schedule::Config& config, CallbackT&& callback) {
1691 auto promise_ptr = MemoryResource::make_shared<std::promise<void>>();
1692 auto fut = promise_ptr->get_future();
1694 auto status = loop.exec_task(config, [cb = std::forward<CallbackT>(callback), promise_ptr]()
mutable {
1697 promise_ptr->set_value();
1699 promise_ptr->set_exception(std::current_exception());
1704 promise_ptr->set_exception(std::make_exception_ptr(std::runtime_error(
"Coroutine::exec: exec_task post failed")));
1707 co_await await_future(loop, std::move(fut));
1714 template <
typename TypeT>
1715 struct WhenAllState final {
1716 std::vector<TypeT> results;
1717 std::atomic<size_t> remaining{0};
1719 std::exception_ptr first_exc;
1720 std::promise<void> promise;
1723 template <
typename TypeT>
1724 struct WhenAllGuard final {
1725 std::shared_ptr<WhenAllState<TypeT>> state;
1726 bool completed_normally{
false};
1728 explicit WhenAllGuard(std::shared_ptr<WhenAllState<TypeT>> s) noexcept : state(std::move(s)) {}
1730 WhenAllGuard(WhenAllGuard&& other) noexcept
1731 : state(std::move(other.state)), completed_normally(other.completed_normally) {}
1733 WhenAllGuard(
const WhenAllGuard&) =
delete;
1735 WhenAllGuard& operator=(
const WhenAllGuard&) =
delete;
1737 WhenAllGuard& operator=(WhenAllGuard&&) =
delete;
1744 if (!completed_normally) {
1746 std::lock_guard lock(state->exc_mtx);
1748 if (!state->first_exc) {
1749 state->first_exc = std::make_exception_ptr(
1750 std::runtime_error(
"vlink::Coroutine::when_all: sub-task aborted before completion"));
1755 if (state->remaining.fetch_sub(1, std::memory_order_acq_rel) == 1) {
1756 if (state->first_exc) {
1757 state->promise.set_exception(state->first_exc);
1759 state->promise.set_value();
1765 template <
typename TypeT>
1766 inline Task<void> when_all_runner(Task<TypeT> task,
size_t i, WhenAllGuard<TypeT> guard) {
1768 auto value = co_await std::move(task);
1769 guard.state->results[i] = std::move(value);
1771 std::lock_guard lock(guard.state->exc_mtx);
1773 if (!guard.state->first_exc) {
1774 guard.state->first_exc = std::current_exception();
1778 guard.completed_normally =
true;
1781 template <
typename TypeT>
1782 struct WhenAnyState final {
1783 std::atomic_bool fired{
false};
1784 std::atomic<size_t> remaining{0};
1786 std::exception_ptr first_exc;
1787 std::optional<std::pair<size_t, TypeT>> winner;
1788 std::promise<std::pair<size_t, TypeT>> promise;
1791 template <
typename TypeT>
1792 struct WhenAnyGuard final {
1793 std::shared_ptr<WhenAnyState<TypeT>> state;
1794 bool completed_normally{
false};
1796 explicit WhenAnyGuard(std::shared_ptr<WhenAnyState<TypeT>> s) noexcept : state(std::move(s)) {}
1798 WhenAnyGuard(WhenAnyGuard&& other) noexcept
1799 : state(std::move(other.state)), completed_normally(other.completed_normally) {}
1801 WhenAnyGuard(
const WhenAnyGuard&) =
delete;
1803 WhenAnyGuard& operator=(
const WhenAnyGuard&) =
delete;
1805 WhenAnyGuard& operator=(WhenAnyGuard&&) =
delete;
1812 if (!completed_normally) {
1814 std::lock_guard lock(state->exc_mtx);
1816 if (!state->first_exc) {
1817 state->first_exc = std::make_exception_ptr(
1818 std::runtime_error(
"vlink::Coroutine::when_any: sub-task aborted before completion"));
1823 if (state->remaining.fetch_sub(1, std::memory_order_acq_rel) == 1) {
1824 if (state->winner) {
1825 state->promise.set_value(std::move(*state->winner));
1827 state->promise.set_exception(state->first_exc);
1833 template <
typename TypeT>
1834 inline Task<void> when_any_runner(Task<TypeT> task,
size_t i, WhenAnyGuard<TypeT> guard) {
1836 auto value = co_await std::move(task);
1837 bool expected =
false;
1839 if (guard.state->fired.compare_exchange_strong(expected,
true, std::memory_order_acq_rel,
1840 std::memory_order_relaxed)) {
1841 guard.state->winner.emplace(i, std::move(value));
1844 std::lock_guard lock(guard.state->exc_mtx);
1846 if (!guard.state->first_exc) {
1847 guard.state->first_exc = std::current_exception();
1851 guard.completed_normally =
true;
1856 template <
typename TypeT>
1857 inline Task<std::vector<TypeT>> when_all(MessageLoop& loop, std::vector<Task<TypeT>> tasks) {
1858 static_assert(std::is_default_constructible_v<TypeT>,
1859 "Co::when_all<T>: T must be default-constructible (results vector pre-sized).");
1862 co_return std::vector<TypeT>{};
1865 const size_t count = tasks.size();
1866 auto state = MemoryResource::make_shared<detail::WhenAllState<TypeT>>();
1867 state->results.resize(count);
1868 state->remaining.store(count, std::memory_order_relaxed);
1869 auto fut = state->promise.get_future();
1871 for (
size_t i = 0; i < count; ++i) {
1872 co_spawn(loop, detail::when_all_runner<TypeT>(std::move(tasks[i]), i, detail::WhenAllGuard<TypeT>{state}));
1875 co_await await_future(loop, std::move(fut));
1877 co_return std::move(state->results);
1880 template <
typename TypeT>
1881 inline Task<std::pair<size_t, TypeT>> when_any(MessageLoop& loop, std::vector<Task<TypeT>> tasks) {
1883 throw std::invalid_argument(
"vlink::Coroutine::when_any: tasks must not be empty");
1886 auto state = MemoryResource::make_shared<detail::WhenAnyState<TypeT>>();
1887 state->remaining.store(tasks.size(), std::memory_order_relaxed);
1888 auto fut = state->promise.get_future();
1890 for (
size_t i = 0; i < tasks.size(); ++i) {
1891 co_spawn(loop, detail::when_any_runner<TypeT>(std::move(tasks[i]), i, detail::WhenAnyGuard<TypeT>{state}));
1894 co_return co_await await_future(loop, std::move(fut));
1907 namespace Co = Coroutine;
@ kNoPriority
FIFO sentinel.
Definition: message_loop.h:162
Thin final wrappers around the standard exception hierarchy used by VLink.
Pool-backed type-erased callables: copyable vlink::Function and move-only vlink::MoveFunction.
Directed acyclic task graph with condition branching, cycle guard and DOT export.
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLINK_EXPORT
Definition: macros.h:81
#define VLIKELY(...)
Short alias for VLINK_LIKELY.
Definition: macros.h:284
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition: macros.h:174
PMR adapter that lets standard pmr-aware containers allocate through vlink::MemoryPool.
Single-threaded task dispatcher with three queue backends, timers and scheduling envelopes.
std::shared_ptr< GraphTask > GraphTaskPtr
Convenience alias for the canonical shared_ptr<GraphTask> handle.
Definition: graph_task.h:467
Fluent task-scheduling wrapper used by MessageLoop::exec_task() and family.