Sunshine v2025.118.151840
Self-hosted game stream host for Moonlight.
task_pool.h
Go to the documentation of this file.
1
5#pragma once
6
7#include <chrono>
8#include <deque>
9#include <functional>
10#include <future>
11#include <mutex>
12#include <optional>
13#include <type_traits>
14#include <utility>
15#include <vector>
16
17#include "move_by_copy.h"
18#include "utility.h"
19namespace task_pool_util {
20
21 class _ImplBase {
22 public:
23 // _unique_base_type _this_ptr;
24
25 inline virtual ~_ImplBase() = default;
26
27 virtual void
28 run() = 0;
29 };
30
31 template <class Function>
32 class _Impl: public _ImplBase {
33 Function _func;
34
35 public:
36 _Impl(Function &&f):
37 _func(std::forward<Function>(f)) {}
38
39 void
40 run() override {
41 _func();
42 }
43 };
44
45 class TaskPool {
46 public:
47 typedef std::unique_ptr<_ImplBase> __task;
48 typedef _ImplBase *task_id_t;
49
50 typedef std::chrono::steady_clock::time_point __time_point;
51
52 template <class R>
54 public:
55 task_id_t task_id;
56 std::future<R> future;
57
58 timer_task_t(task_id_t task_id, std::future<R> &future):
59 task_id { task_id }, future { std::move(future) } {}
60 };
61
62 protected:
63 std::deque<__task> _tasks;
64 std::vector<std::pair<__time_point, __task>> _timer_tasks;
65 std::mutex _task_mutex;
66
67 public:
68 TaskPool() = default;
69 TaskPool(TaskPool &&other) noexcept:
70 _tasks { std::move(other._tasks) }, _timer_tasks { std::move(other._timer_tasks) } {}
71
72 TaskPool &
73 operator=(TaskPool &&other) noexcept {
74 std::swap(_tasks, other._tasks);
75 std::swap(_timer_tasks, other._timer_tasks);
76
77 return *this;
78 }
79
80 template <class Function, class... Args>
81 auto
82 push(Function &&newTask, Args &&...args) {
83 static_assert(std::is_invocable_v<Function, Args &&...>, "arguments don't match the function");
84
85 using __return = std::invoke_result_t<Function, Args &&...>;
86 using task_t = std::packaged_task<__return()>;
87
88 auto bind = [task = std::forward<Function>(newTask), tuple_args = std::make_tuple(std::forward<Args>(args)...)]() mutable {
89 return std::apply(task, std::move(tuple_args));
90 };
91
92 task_t task(std::move(bind));
93
94 auto future = task.get_future();
95
96 std::lock_guard<std::mutex> lg(_task_mutex);
97 _tasks.emplace_back(toRunnable(std::move(task)));
98
99 return future;
100 }
101
102 void
103 pushDelayed(std::pair<__time_point, __task> &&task) {
104 std::lock_guard lg(_task_mutex);
105
106 auto it = _timer_tasks.cbegin();
107 for (; it < _timer_tasks.cend(); ++it) {
108 if (std::get<0>(*it) < task.first) {
109 break;
110 }
111 }
112
113 _timer_tasks.emplace(it, task.first, std::move(task.second));
114 }
115
119 template <class Function, class X, class Y, class... Args>
120 auto
121 pushDelayed(Function &&newTask, std::chrono::duration<X, Y> duration, Args &&...args) {
122 static_assert(std::is_invocable_v<Function, Args &&...>, "arguments don't match the function");
123
124 using __return = std::invoke_result_t<Function, Args &&...>;
125 using task_t = std::packaged_task<__return()>;
126
127 __time_point time_point;
128 if constexpr (std::is_floating_point_v<X>) {
129 time_point = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
130 }
131 else {
132 time_point = std::chrono::steady_clock::now() + duration;
133 }
134
135 auto bind = [task = std::forward<Function>(newTask), tuple_args = std::make_tuple(std::forward<Args>(args)...)]() mutable {
136 return std::apply(task, std::move(tuple_args));
137 };
138
139 task_t task(std::move(bind));
140
141 auto future = task.get_future();
142 auto runnable = toRunnable(std::move(task));
143
144 task_id_t task_id = &*runnable;
145
146 pushDelayed(std::pair { time_point, std::move(runnable) });
147
148 return timer_task_t<__return> { task_id, future };
149 }
150
155 template <class X, class Y>
156 void
157 delay(task_id_t task_id, std::chrono::duration<X, Y> duration) {
158 std::lock_guard<std::mutex> lg(_task_mutex);
159
160 auto it = _timer_tasks.begin();
161 for (; it < _timer_tasks.cend(); ++it) {
162 const __task &task = std::get<1>(*it);
163
164 if (&*task == task_id) {
165 std::get<0>(*it) = std::chrono::steady_clock::now() + duration;
166
167 break;
168 }
169 }
170
171 if (it == _timer_tasks.cend()) {
172 return;
173 }
174
175 // smaller time goes to the back
176 auto prev = it - 1;
177 while (it > _timer_tasks.cbegin()) {
178 if (std::get<0>(*it) > std::get<0>(*prev)) {
179 std::swap(*it, *prev);
180 }
181
182 --prev;
183 --it;
184 }
185 }
186
187 bool
188 cancel(task_id_t task_id) {
189 std::lock_guard lg(_task_mutex);
190
191 auto it = _timer_tasks.begin();
192 for (; it < _timer_tasks.cend(); ++it) {
193 const __task &task = std::get<1>(*it);
194
195 if (&*task == task_id) {
196 _timer_tasks.erase(it);
197
198 return true;
199 }
200 }
201
202 return false;
203 }
204
205 std::optional<std::pair<__time_point, __task>>
206 pop(task_id_t task_id) {
207 std::lock_guard lg(_task_mutex);
208
209 auto pos = std::find_if(std::begin(_timer_tasks), std::end(_timer_tasks), [&task_id](const auto &t) { return t.second.get() == task_id; });
210
211 if (pos == std::end(_timer_tasks)) {
212 return std::nullopt;
213 }
214
215 return std::move(*pos);
216 }
217
218 std::optional<__task>
219 pop() {
220 std::lock_guard lg(_task_mutex);
221
222 if (!_tasks.empty()) {
223 __task task = std::move(_tasks.front());
224 _tasks.pop_front();
225 return task;
226 }
227
228 if (!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now()) {
229 __task task = std::move(std::get<1>(_timer_tasks.back()));
230 _timer_tasks.pop_back();
231 return task;
232 }
233
234 return std::nullopt;
235 }
236
237 bool
238 ready() {
239 std::lock_guard<std::mutex> lg(_task_mutex);
240
241 return !_tasks.empty() || (!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now());
242 }
243
244 std::optional<__time_point>
245 next() {
246 std::lock_guard<std::mutex> lg(_task_mutex);
247
248 if (_timer_tasks.empty()) {
249 return std::nullopt;
250 }
251
252 return std::get<0>(_timer_tasks.back());
253 }
254
255 private:
256 template <class Function>
257 std::unique_ptr<_ImplBase>
258 toRunnable(Function &&f) {
259 return std::make_unique<_Impl<Function>>(std::forward<Function &&>(f));
260 }
261 };
262} // namespace task_pool_util
Definition task_pool.h:45
void delay(task_id_t task_id, std::chrono::duration< X, Y > duration)
Definition task_pool.h:157
auto pushDelayed(Function &&newTask, std::chrono::duration< X, Y > duration, Args &&...args)
Definition task_pool.h:121
Definition task_pool.h:21
Definition task_pool.h:32
Declarations for the MoveByCopy utility class.
Functions for handling command line arguments.
Definition entry_handler.cpp:41
Declarations for utility functions.