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
3 changes: 2 additions & 1 deletion include/condy/awaiters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ class [[nodiscard]] RangedParallelAwaiterBase {
}

public:
bool await_ready() const noexcept { return false; }
bool await_ready() const noexcept { return awaiters_.empty(); }

template <typename PromiseType>
void await_suspend(std::coroutine_handle<PromiseType> h) {
Expand Down Expand Up @@ -249,6 +249,7 @@ using RangedWhenAllAwaiter = RangedParallelAwaiterBase<
* @brief Awaiter to wait for any operation in a range to complete.
* @details An awaiter that waits for any operation in a range to complete.
* @tparam Awaiter The type of the awaiters in the range.
* @throws std::out_of_range if the range is empty.
* @return std::pair<size_t, ...> A pair containing the index of the completed
* awaiter and its result.
*/
Expand Down
5 changes: 4 additions & 1 deletion include/condy/finish_handles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,10 @@ class RangedWhenAnyFinishHandle : public RangedParallelAnyFinishHandle<Handle> {
ReturnType extract_result() {
auto r = Base::extract_result();
auto &[order, results] = r;
return std::make_pair(order[0], std::move(results[order[0]]));
// May throw out_of_range if the range is empty, which is expected and
// should be handled by caller.
auto idx = order.at(0);
return std::make_pair(idx, std::move(results[idx]));
}
};

Expand Down
55 changes: 55 additions & 0 deletions tests/test_awaiter_operations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <cstddef>
#include <cstring>
#include <doctest/doctest.h>
#include <stdexcept>

using namespace condy::operators;

Expand Down Expand Up @@ -162,6 +163,33 @@ TEST_CASE("test awaiter_operations - test ranged when_all") {
context.reset();
}

TEST_CASE("test awaiter_operations - test when_all with empty range") {
condy::Ring ring;
io_uring_params params{};
std::memset(&params, 0, sizeof(params));
ring.init(8, &params);
auto &context = condy::detail::Context::current();
context.init(&ring, &runtime);

size_t unfinished = 1;
auto func = [&]() -> condy::Coro<void> {
using OpAwaiter =
decltype(condy::detail::make_op_awaiter(io_uring_prep_nop));
std::vector<OpAwaiter> awaiters;
auto r = co_await condy::when_all(std::move(awaiters));
REQUIRE(r.empty());
--unfinished;
};

auto coro = func();
REQUIRE(unfinished == 1);

coro.release().resume();
REQUIRE(unfinished == 0);

context.reset();
}

TEST_CASE("test awaiter_operations - test ranged when_any") {
condy::Ring ring;
io_uring_params params{};
Expand Down Expand Up @@ -207,6 +235,33 @@ TEST_CASE("test awaiter_operations - test ranged when_any") {
context.reset();
}

TEST_CASE("test awaiter_operations - test when_any with empty range") {
condy::Ring ring;
io_uring_params params{};
std::memset(&params, 0, sizeof(params));
ring.init(8, &params);
auto &context = condy::detail::Context::current();
context.init(&ring, &runtime);

size_t unfinished = 1;
auto func = [&]() -> condy::Coro<void> {
using OpAwaiter =
decltype(condy::detail::make_op_awaiter(io_uring_prep_nop));
std::vector<OpAwaiter> awaiters;
REQUIRE_THROWS_AS(co_await condy::when_any(std::move(awaiters)),
std::out_of_range);
--unfinished;
};

auto coro = func();
REQUIRE(unfinished == 1);

coro.release().resume();
REQUIRE(unfinished == 0);

context.reset();
}

TEST_CASE("test awaiter_operations - test &&") {
condy::Ring ring;
io_uring_params params{};
Expand Down
Loading