From 32bb2b1b5bc172833dc735fe482dbba3a7d87a29 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Tue, 4 Mar 2025 23:08:34 +0100 Subject: [PATCH] libutil: asyncSpread this could be a method of AsyncCollect, but putting there would require another overload that synthesizes the empty key types we use here. even then a separate function is slightly nicer because it spares us writing things like `asyncCollect(someIterableOfPromises).collect()` or similar Change-Id: Ia92abc105016414a2171e237f2a0bc6233d3ccbf --- lix/libutil/async-collect.hh | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lix/libutil/async-collect.hh b/lix/libutil/async-collect.hh index 1c65ac8d6..3cf8a3e8a 100644 --- a/lix/libutil/async-collect.hh +++ b/lix/libutil/async-collect.hh @@ -1,6 +1,8 @@ #pragma once /// @file +#include "lix/libutil/result.hh" +#include #include #include #include @@ -101,4 +103,37 @@ AsyncCollect asyncCollect(kj::Array>> promises return AsyncCollect(std::move(promises)); } +/** + * Run `fn` for every item in the `input` range asynchronously, using + * the same fail-fast semantics as `asyncCollect`. `asyncSpread` is a + * shorthand for calling `asyncCollect` with `std::tuple()` values as + * keys, awaiting that to completion, and propagating all exceptions. + */ +template +kj::Promise> asyncSpread(Input && input, Fn fn) + requires requires { + { + fn(*begin(input)) + } -> std::same_as>>; + } +{ + kj::Vector, kj::Promise>>> children; + if constexpr (requires { input.size(); }) { + children.reserve(input.size()); + } + + for (auto & i : input) { + children.add(std::tuple(), fn(i)); + } + + auto collect = asyncCollect(children.releaseAsArray()); + while (auto r = co_await collect.next()) { + if (!r->second.has_value()) { + co_return std::move(r->second); + } + } + + co_return result::success(); +} + }