Channel Select¶
select_recv allows a coroutine to wait for messages from multiple channels at once, similar to Go's select.
This enables fan-in patterns, multiplexed consumers, and prioritizing fastest data sources.
Overview¶
select_recv returns:
Where:
size_t— index of the channel that produced a messagevalue_type—std::tuple<Ts...>associated with that channel
If all channels are closed and empty, the result is nullopt.
Requirements¶
- All channels passed to
select_recvmust have the samevalue_type. - Channels may have different capacities and producers.
select_recvinternally uses an event-based wake mechanism (AsyncEvent), not busy polling.
Usage Example¶
AsyncChannel<int> ch1{4};
AsyncChannel<int> ch2{4};
task::Awaitable<void> consumer() {
for (;;) {
auto res = co_await select_recv(ch1, ch2);
if (!res) {
std::cout << "all channels closed\n";
co_return;
}
auto [idx, tup] = *res;
auto& [v] = tup;
std::cout << "received " << v << " from ch" << (idx + 1) << "\n";
}
}
Behavior Summary¶
Ready value¶
If any channel already has data, select_recv returns immediately.
Empty channels¶
If none have data, the coroutine suspends on a shared AsyncEvent.
Wake-up¶
A wake-up occurs when:
- any channel receives a new message
- any channel is closed
Closed channels¶
A channel contributes messages until its buffer is empty.
If all channels are closed and empty, the select returns nullopt.
Example: Multiplexing Producers¶
AsyncChannel<int> A{8};
AsyncChannel<int> B{8};
task::Awaitable<void> multiplexer() {
for (;;) {
auto r = co_await select_recv(A, B);
if (!r) break;
auto [index, tup] = *r;
auto& [value] = tup;
std::cout << "got " << value << " from channel " << index << "\n";
}
}
Notes¶
select_recvdoes not guarantee fairness; it is optimized for throughput.- Only receive-selection is supported (not send-selection).
- Ideal for fan-in patterns, pipeline aggregation, and merging asynchronous streams.