1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
// vim: ts=8 sw=2 smarttab expandtab
#pragma once
#include <seastar/core/future.hh>
#include "crimson/common/errorator.h"
namespace crimson {
template <class... AllowedErrors>
class parallel_for_each_state final : private seastar::continuation_base<> {
using future_t = typename errorator<AllowedErrors...>::template future<>;
std::vector<future_t> _incomplete;
seastar::promise<> _result;
std::exception_ptr _ex;
private:
void wait_for_one() noexcept {
while (!_incomplete.empty() && _incomplete.back().available()) {
if (_incomplete.back().failed()) {
_ex = _incomplete.back().get_exception();
}
_incomplete.pop_back();
}
if (!_incomplete.empty()) {
seastar::internal::set_callback(std::move(_incomplete.back()),
static_cast<continuation_base<>*>(this));
_incomplete.pop_back();
return;
}
if (__builtin_expect(bool(_ex), false)) {
_result.set_exception(std::move(_ex));
} else {
_result.set_value();
}
delete this;
}
virtual void run_and_dispose() noexcept override {
if (_state.failed()) {
_ex = std::move(_state).get_exception();
}
_state = {};
wait_for_one();
}
task* waiting_task() noexcept override { return _result.waiting_task(); }
public:
parallel_for_each_state(size_t n) {
_incomplete.reserve(n);
}
void add_future(future_t&& f) {
_incomplete.push_back(std::move(f));
}
future_t get_future() {
auto ret = _result.get_future();
wait_for_one();
return ret;
}
};
template <typename Iterator, typename Func, typename... AllowedErrors>
static inline typename errorator<AllowedErrors...>::template future<>
parallel_for_each(Iterator first, Iterator last, Func&& func) noexcept {
parallel_for_each_state<AllowedErrors...>* s = nullptr;
// Process all elements, giving each future the following treatment:
// - available, not failed: do nothing
// - available, failed: collect exception in ex
// - not available: collect in s (allocating it if needed)
for (;first != last; ++first) {
auto f = seastar::futurize_invoke(std::forward<Func>(func), *first);
if (!f.available() || f.failed()) {
if (!s) {
using itraits = std::iterator_traits<Iterator>;
auto n = (seastar::internal::iterator_range_estimate_vector_capacity(
first, last, typename itraits::iterator_category()) + 1);
s = new parallel_for_each_state<AllowedErrors...>(n);
}
s->add_future(std::move(f));
}
}
// If any futures were not available, hand off to parallel_for_each_state::start().
// Otherwise we can return a result immediately.
if (s) {
// s->get_future() takes ownership of s (and chains it to one of the futures it contains)
// so this isn't a leak
return s->get_future();
}
return seastar::make_ready_future<>();
}
} // namespace crimson
|