uvco 0.1
Loading...
Searching...
No Matches
uvco::SelectSet< Ts > Class Template Reference

#include <select.h>

Public Types

using Variant = std::variant<Promise<Ts> *...>
using Tuple = std::tuple<Promise<Ts> *...>

Public Member Functions

 SelectSet (Promise< Ts > &...promises)
 ~SelectSet ()
bool await_ready () noexcept
std::coroutine_handle await_suspend (std::coroutine_handle<> handle)
std::vector< Variantawait_resume ()

Private Member Functions

template<size_t Ix = 0>
void checkPromises (std::vector< Variant > &readyPromises)

Private Attributes

Tuple promises_
std::coroutine_handle suspended_
bool resumed_ = false

Detailed Description

template<typename... Ts>
class uvco::SelectSet< Ts >

A SelectSet is a set of promises that are awaited simultaneously. The first promise that is ready is returned. If no promise is ready, the coroutine is suspended until one of the promises is ready.

See also waitAny() for a more convenient approach that internally utilizes this class.

The SelectSet is directly awaitable. For example:

Promise<int> promise1 = []() -> Promise<int> { co_return 1; }();
Promise<int> promise2 = []() -> Promise<int> { co_return 2; }();
std::vector<std::variant<Promise<int>, Promise<int>>> results = co_await
SelectSet{promise1, promise2};
ASSERT_EQ(2, results.size());
EXPECT_EQ(0, results[0].index()); // Order is unspecified usually
EXPECT_EQ(1, std::get<0>(results[0]).unwrap()); // or co_await
// Easy approach, no second co_await:
std::vector<std::variant<int, int>> results2 = co_await waitAny(promise1,
promise2); ASSERT_EQ(2, results.size()); EXPECT_EQ(0, results[0].index());
// Order is unspecified usually
Definition promise.h:49
SelectSet(Promise< Ts > &...promises)
Definition select.h:61
Promise< std::vector< std::variant< PromiseTypes... > > > waitAny(Promise< PromiseTypes > &...promises)
Definition combinators.h:43

It is okay to add an already finished promise to a SelectSet.

A given SelectSet can only be awaited once. Usually, as shown above, it's best used as a temporary; it only takes references to the promises being selected from, and is therefore cheaply constructible.

It is possible that no events are returned ("spurious wakeup"); make sure that you can handle an empty result vector.

Member Typedef Documentation

◆ Tuple

template<typename... Ts>
using uvco::SelectSet< Ts >::Tuple = std::tuple<Promise<Ts> *...>

◆ Variant

template<typename... Ts>
using uvco::SelectSet< Ts >::Variant = std::variant<Promise<Ts> *...>

Constructor & Destructor Documentation

◆ SelectSet()

template<typename... Ts>
uvco::SelectSet< Ts >::SelectSet ( Promise< Ts > &... promises)
inlineexplicit
61: promises_{&promises...} {}
Definition select.h:56
Tuple promises_
Definition select.h:131

◆ ~SelectSet()

template<typename... Ts>
uvco::SelectSet< Ts >::~SelectSet ( )
inline
63 {
64 if (!resumed_) {
66 [](auto *...promise) { (promise->core()->resetHandle(), ...); },
67 promises_);
68 }
69 }
bool resumed_
Definition select.h:133

Member Function Documentation

◆ await_ready()

template<typename... Ts>
bool uvco::SelectSet< Ts >::await_ready ( )
inlinenodiscardnoexcept
71 {
72 BOOST_ASSERT_MSG(!resumed_, "A select set can only be used once");
73 const bool isReady = std::apply(
74 [](auto *...promise) -> bool { return (promise->ready() || ...); },
75 promises_);
77 return isReady;
78 }

◆ await_resume()

template<typename... Ts>
std::vector< Variant > uvco::SelectSet< Ts >::await_resume ( )
inline

Returns all promises that are ready. It is possible that no promise is ready, and the returned vector is empty, in the case that two promises were ready at once; one promise scheduled the SelectSet for resumption, we delivered both events, and will be woken up a second time.

TODO: provide a no-/rare-allocation API by reusing a vector or span.

101 {
102 resumed_ = true;
105 // Prevent the current coroutine from being resumed again, if another
106 // promise in the SelectSet became ready in the meantime.
107 if (suspended_ != nullptr) {
109 suspended_ = nullptr;
110 }
111 return readyPromises;
112 }
static void cancel(std::coroutine_handle<> handle)
Definition loop.cc:104
std::coroutine_handle suspended_
Definition select.h:132
void checkPromises(std::vector< Variant > &readyPromises)
Definition select.h:116

◆ await_suspend()

template<typename... Ts>
std::coroutine_handle uvco::SelectSet< Ts >::await_suspend ( std::coroutine_handle<> handle)
inline

Register the current coroutine to be resumed when one of the promises is ready.

82 {
84 [handle](auto *...promise) {
85 ((!promise->core()->stale() ? promise->core()->setHandle(handle)
86 : (void)0),
87 ...);
88 },
89 promises_);
91 return Loop::getNext();
92 }
static std::coroutine_handle getNext()
Definition loop.cc:108

◆ checkPromises()

template<typename... Ts>
template<size_t Ix = 0>
void uvco::SelectSet< Ts >::checkPromises ( std::vector< Variant > & readyPromises)
inlineprivate
116 {
117 if constexpr (Ix < sizeof...(Ts)) {
120 if (promise->ready()) {
122 } else {
123 // Reset promises to `init` state and remove the current coroutine's
124 // handle from them so they don't resume us later.
125 promise->core()->resetHandle();
126 }
128 }
129 }

Member Data Documentation

◆ promises_

template<typename... Ts>
Tuple uvco::SelectSet< Ts >::promises_
private

◆ resumed_

template<typename... Ts>
bool uvco::SelectSet< Ts >::resumed_ = false
private

◆ suspended_

template<typename... Ts>
std::coroutine_handle uvco::SelectSet< Ts >::suspended_
private

The documentation for this class was generated from the following file: