-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwhen_all.hpp
More file actions
104 lines (90 loc) · 2.6 KB
/
Copy pathwhen_all.hpp
File metadata and controls
104 lines (90 loc) · 2.6 KB
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
92
93
94
95
96
97
98
99
100
101
102
103
104
#pragma once
#include <atomic>
#include <exception>
#include <memory>
#include "detail/ranges.hpp"
#include "task.hpp"
namespace dispatch_queue {
namespace detail {
struct when_all_helper {
when_all_helper(size_t count)
: result_count(std::make_shared<std::atomic<size_t>>(count))
{
}
template<typename T>
void operator()(const task<T>& task) const {
if (task.get_state() == task_state::failed) {
any_failed->store(true, std::memory_order_release);
}
if (result_count->fetch_sub(1, std::memory_order_acq_rel) == 1) {
if (any_failed->load(std::memory_order_acquire)) {
future->set_exception(std::make_exception_ptr(task_error("subtask failed")));
}
else {
future->set_value();
}
}
}
std::shared_ptr<std::atomic<size_t>> result_count;
std::shared_ptr<std::atomic<bool>> any_failed = std::make_shared<std::atomic<bool>>(false);
std::shared_ptr<detail::task_future<void>> future = detail::task_future<void>::create_pending();
};
template<typename TaskRange>
task<void> when_all_internal(const TaskRange& tasks) {
auto tasks_size = detail::range_size(tasks);
if (tasks_size == 0) {
return detail::task_future<void>::create_ready();
}
else if (tasks_size == 1) {
return *detail::range_begin(tasks);
}
when_all_helper helper(tasks_size);
for (auto&& task : tasks) {
task.then(helper);
}
return helper.future;
}
} // end namespace detail
/**
* Create a task that finishes whenever all given tasks finish.
*
* If any subtask fails, this task fail with a `task_error`.
* Otherwise, it succeeds.
*/
template<typename T>
task<void> when_all(std::initializer_list<task<T>> tasks) {
return detail::when_all_internal(tasks);
}
/**
* Create a task that finishes whenever all given tasks finish.
*
* If any subtask fails, this task fail with a `task_error`.
* Otherwise, it succeeds.
*/
template<typename TaskRange>
task<void> when_all(const TaskRange& tasks) {
return detail::when_all_internal(tasks);
}
#ifdef __cpp_fold_expressions
/**
* Create a task that finishes whenever all given tasks finish.
*
* If any subtask fails, this task fail with a `task_error`.
* Otherwise, it succeeds.
*/
template<typename... Tasks>
task<void> when_all(const Tasks&... tasks) {
if constexpr (sizeof...(Tasks) == 0) {
return detail::task_future<void>::create_ready();
}
else if constexpr (sizeof...(Tasks) == 1) {
return std::get<0>(std::forward_as_tuple(std::forward<Tasks>(tasks)...));
}
else {
detail::when_all_helper helper(sizeof...(Tasks));
(tasks.then(helper), ...);
return helper.future;
}
}
#endif // __cpp_fold_expressions
} // end namespace dispatch_queue