ThreadSchedule 3.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
futures.hpp
Go to the documentation of this file.
1#pragma once
2
12#include "../expected.hpp"
13
14#include <chrono>
15#include <exception>
16#include <future>
17#include <random>
18#include <stdexcept>
19#include <thread>
20#include <utility>
21#include <vector>
22
24{
25
37template <typename T>
38auto
39when_all(std::vector<std::future<T>>& futures) -> std::vector<T>
40{
41 std::vector<T> results;
42 results.reserve(futures.size());
43 std::exception_ptr first_error;
44
45 for (auto& f : futures)
46 {
47 try
48 {
49 results.push_back(f.get());
50 }
51 catch (...)
52 {
53 if (!first_error)
54 first_error = std::current_exception();
55 }
56 }
57
58 if (first_error)
59 std::rethrow_exception(first_error);
60
61 return results;
62}
63
69inline void
70when_all(std::vector<std::future<void>>& futures)
71{
72 std::exception_ptr first_error;
73
74 for (auto& f : futures)
75 {
76 try
77 {
78 f.get();
79 }
80 catch (...)
81 {
82 if (!first_error)
83 first_error = std::current_exception();
84 }
85 }
86
87 if (first_error)
88 std::rethrow_exception(first_error);
89}
90
99template <typename T>
100auto
101when_all_settled(std::vector<std::future<T>>& futures) -> std::vector<expected<T, std::exception_ptr>>
102{
103 std::vector<expected<T, std::exception_ptr>> results;
104 results.reserve(futures.size());
105
106 for (auto& f : futures)
107 {
108 try
109 {
110 results.push_back(f.get());
111 }
112 catch (...)
113 {
114 results.push_back(unexpected(std::current_exception()));
115 }
116 }
117
118 return results;
119}
120
125inline auto
126when_all_settled(std::vector<std::future<void>>& futures) -> std::vector<expected<void, std::exception_ptr>>
127{
128 std::vector<expected<void, std::exception_ptr>> results;
129 results.reserve(futures.size());
130
131 for (auto& f : futures)
132 {
133 try
134 {
135 f.get();
136 results.emplace_back();
137 }
138 catch (...)
139 {
140 results.push_back(unexpected(std::current_exception()));
141 }
142 }
143
144 return results;
145}
146
161template <typename T>
162auto
163when_any(std::vector<std::future<T>>& futures) -> std::pair<size_t, T>
164{
165 if (futures.empty())
166 throw std::invalid_argument("when_any: empty futures vector");
167
168 thread_local std::mt19937 rng{ std::random_device{}() };
169 std::uniform_int_distribution<size_t> dist(0, futures.size() - 1);
170 size_t const start = dist(rng);
171 unsigned backoff_ms = 1;
172
173 while (true)
174 {
175 size_t deferred = futures.size();
176 for (size_t k = 0; k < futures.size(); ++k)
177 {
178 size_t const i = (start + k) % futures.size();
179 auto const status = futures[i].wait_for(std::chrono::milliseconds(1));
180 if (status == std::future_status::ready)
181 return { i, futures[i].get() };
182 if (status == std::future_status::deferred && deferred == futures.size())
183 deferred = i;
184 }
185 if (deferred != futures.size())
186 return { deferred, futures[deferred].get() };
187 std::this_thread::sleep_for(std::chrono::milliseconds(backoff_ms));
188 if (backoff_ms < 16)
189 backoff_ms *= 2;
190 }
191}
192
199inline auto
200when_any(std::vector<std::future<void>>& futures) -> size_t
201{
202 if (futures.empty())
203 throw std::invalid_argument("when_any: empty futures vector");
204
205 thread_local std::mt19937 rng{ std::random_device{}() };
206 std::uniform_int_distribution<size_t> dist(0, futures.size() - 1);
207 size_t const start = dist(rng);
208 unsigned backoff_ms = 1;
209
210 while (true)
211 {
212 size_t deferred = futures.size();
213 for (size_t k = 0; k < futures.size(); ++k)
214 {
215 size_t const i = (start + k) % futures.size();
216 auto const status = futures[i].wait_for(std::chrono::milliseconds(1));
217 if (status == std::future_status::ready)
218 {
219 futures[i].get();
220 return i;
221 }
222 if (status == std::future_status::deferred && deferred == futures.size())
223 deferred = i;
224 }
225 if (deferred != futures.size())
226 {
227 futures[deferred].get();
228 return deferred;
229 }
230 std::this_thread::sleep_for(std::chrono::milliseconds(backoff_ms));
231 if (backoff_ms < 16)
232 backoff_ms *= 2;
233 }
234}
235
236} // namespace threadschedule::advanced
auto when_all(std::vector< std::future< T > > &futures) -> std::vector< T >
Block until all futures complete, returning results in submission order.
Definition futures.hpp:39
auto when_any(std::vector< std::future< T > > &futures) -> std::pair< size_t, T >
Block until the first future becomes ready.
Definition futures.hpp:163
auto when_all_settled(std::vector< std::future< T > > &futures) -> std::vector< expected< T, std::exception_ptr > >
Block until all futures complete, returning an expected per slot.
Definition futures.hpp:101