Sunshine0.0.5917
Sunshine 0.0.5917
Self-hosted game stream host for Moonlight.
Loading...
Searching...
No Matches
Go to the documentation of this file.
1
5#pragma once
6
7// standard includes
8#include <chrono>
9#include <deque>
10#include <functional>
11#include <future>
12#include <mutex>
13#include <optional>
14#include <type_traits>
15#include <utility>
16#include <vector>
17
18// local includes
19#include "move_by_copy.h"
20#include "utility.h"
21
22namespace task_pool_util {
23
27 class _ImplBase {
28 public:
29 // _unique_base_type _this_ptr;
30
34 virtual ~_ImplBase() = default;
35
39 virtual void run() = 0;
40 };
41
45 template<class Function>
46 class _Impl: public _ImplBase {
47 Function _func;
48
49 public:
55 explicit _Impl(Function &&f):
56 _func(std::forward<Function>(f)) {
57 }
58
62 void run() override {
63 _func();
64 }
65 };
66
70 class TaskPool {
71 public:
75 using __task = std::unique_ptr<_ImplBase>;
80
84 using __time_point = std::chrono::steady_clock::time_point;
85
89 template<class R>
91 public:
93 std::future<R> future;
94
103 future {std::move(future)} {
104 }
105 };
106
107 protected:
108 std::deque<__task> _tasks;
109 std::vector<std::pair<__time_point, __task>> _timer_tasks;
110 std::mutex _task_mutex;
111
112 public:
113 TaskPool() = default;
114
120 TaskPool(TaskPool &&other) noexcept:
121 _tasks {std::move(other._tasks)},
122 _timer_tasks {std::move(other._timer_tasks)} {
123 }
124
131 TaskPool &operator=(TaskPool &&other) noexcept {
132 std::swap(_tasks, other._tasks);
133 std::swap(_timer_tasks, other._timer_tasks);
134
135 return *this;
136 }
137
145 template<class Function, class... Args>
146 auto push(Function &&newTask, Args &&...args) {
147 static_assert(std::is_invocable_v<Function, Args &&...>, "arguments don't match the function");
148
149 using __return = std::invoke_result_t<Function, Args &&...>;
150 using task_t = std::packaged_task<__return()>;
151
152 auto bind = [task = std::forward<Function>(newTask), tuple_args = std::make_tuple(std::forward<Args>(args)...)]() mutable {
153 return std::apply(task, std::move(tuple_args));
154 };
155
156 task_t task(std::move(bind));
157
158 auto future = task.get_future();
159
160 std::scoped_lock lg(_task_mutex);
161 _tasks.emplace_back(toRunnable(std::move(task)));
162
163 return future;
164 }
165
171 void pushDelayed(std::pair<__time_point, __task> &&task) {
172 std::scoped_lock lg(_task_mutex);
173
174 auto it = _timer_tasks.cbegin();
175 for (; it < _timer_tasks.cend(); ++it) {
176 if (std::get<0>(*it) < task.first) {
177 break;
178 }
179 }
180
181 _timer_tasks.emplace(it, task.first, std::move(task.second));
182 }
183
191 template<class Function, class X, class Y, class... Args>
192 auto pushDelayed(Function &&newTask, std::chrono::duration<X, Y> duration, Args &&...args) {
193 static_assert(std::is_invocable_v<Function, Args &&...>, "arguments don't match the function");
194
195 using __return = std::invoke_result_t<Function, Args &&...>;
196 using task_t = std::packaged_task<__return()>;
197
198 __time_point time_point;
199 if constexpr (std::is_floating_point_v<X>) {
200 time_point = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
201 } else {
202 time_point = std::chrono::steady_clock::now() + duration;
203 }
204
205 auto bind = [task = std::forward<Function>(newTask), tuple_args = std::make_tuple(std::forward<Args>(args)...)]() mutable {
206 return std::apply(task, std::move(tuple_args));
207 };
208
209 task_t task(std::move(bind));
210
211 auto future = task.get_future();
212 auto runnable = toRunnable(std::move(task));
213
214 task_id_t task_id = &*runnable;
215
216 pushDelayed(std::pair {time_point, std::move(runnable)});
217
218 return timer_task_t<__return> {task_id, future};
219 }
220
225 template<class X, class Y>
226 void delay(task_id_t task_id, std::chrono::duration<X, Y> duration) {
227 std::scoped_lock lg(_task_mutex);
228
229 auto it = _timer_tasks.begin();
230 for (; it < _timer_tasks.cend(); ++it) {
231 const __task &task = std::get<1>(*it);
232
233 if (&*task == task_id) {
234 std::get<0>(*it) = std::chrono::steady_clock::now() + duration;
235
236 break;
237 }
238 }
239
240 if (it == _timer_tasks.cend()) {
241 return;
242 }
243
244 // smaller time goes to the back
245 auto prev = it - 1;
246 while (it > _timer_tasks.cbegin()) {
247 if (std::get<0>(*it) > std::get<0>(*prev)) {
248 std::swap(*it, *prev);
249 }
250
251 --prev;
252 --it;
253 }
254 }
255
262 bool cancel(task_id_t task_id) {
263 std::scoped_lock lg(_task_mutex);
264
265 auto it = _timer_tasks.begin();
266 for (; it < _timer_tasks.cend(); ++it) {
267 const __task &task = std::get<1>(*it);
268
269 if (&*task == task_id) {
270 _timer_tasks.erase(it);
271
272 return true;
273 }
274 }
275
276 return false;
277 }
278
285 std::optional<std::pair<__time_point, __task>> pop(task_id_t task_id) {
286 std::scoped_lock lg(_task_mutex);
287
288 auto pos = std::ranges::find_if(_timer_tasks, [&task_id](const auto &t) {
289 return t.second.get() == task_id;
290 });
291
292 if (pos == std::end(_timer_tasks)) {
293 return std::nullopt;
294 }
295
296 return std::move(*pos);
297 }
298
304 std::optional<__task> pop() {
305 std::scoped_lock lg(_task_mutex);
306
307 if (!_tasks.empty()) {
308 __task task = std::move(_tasks.front());
309 _tasks.pop_front();
310 return task;
311 }
312
313 if (!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now()) {
314 __task task = std::move(std::get<1>(_timer_tasks.back()));
315 _timer_tasks.pop_back();
316 return task;
317 }
318
319 return std::nullopt;
320 }
321
327 bool ready() {
328 std::scoped_lock lg(_task_mutex);
329
330 return !_tasks.empty() || (!_timer_tasks.empty() && std::get<0>(_timer_tasks.back()) <= std::chrono::steady_clock::now());
331 }
332
338 std::optional<__time_point> next() {
339 std::scoped_lock lg(_task_mutex);
340
341 if (_timer_tasks.empty()) {
342 return std::nullopt;
343 }
344
345 return std::get<0>(_timer_tasks.back());
346 }
347
348 private:
349 template<class Function>
350 std::unique_ptr<_ImplBase> toRunnable(Function &&f) {
351 return std::make_unique<_Impl<Function>>(std::forward<Function &&>(f));
352 }
353 };
354} // namespace task_pool_util
Queued delayed task identifier paired with its future result.
Definition task_pool.h:90
std::future< R > future
Future that resolves with the timer task result.
Definition task_pool.h:93
task_id_t task_id
Task ID.
Definition task_pool.h:92
timer_task_t(task_id_t task_id, std::future< R > &future)
Track a queued timer task and the future for its result.
Definition task_pool.h:101
bool cancel(task_id_t task_id)
Cancel a queued or delayed task before it runs.
Definition task_pool.h:262
std::optional< __task > pop()
Remove and return the next queued item, waiting when requested.
Definition task_pool.h:304
TaskPool & operator=(TaskPool &&other) noexcept
Assign state from another instance while preserving ownership semantics.
Definition task_pool.h:131
TaskPool(TaskPool &&other) noexcept
Move queued tasks and timers from another task pool.
Definition task_pool.h:120
std::chrono::steady_clock::time_point __time_point
Steady-clock timestamp used to schedule delayed tasks.
Definition task_pool.h:84
_ImplBase * task_id_t
Stable pointer used to identify a queued task.
Definition task_pool.h:79
void pushDelayed(std::pair< __time_point, __task > &&task)
Queue a task that becomes runnable after a delay.
Definition task_pool.h:171
std::optional< std::pair< __time_point, __task > > pop(task_id_t task_id)
Remove and return the next queued item, waiting when requested.
Definition task_pool.h:285
std::unique_ptr< _ImplBase > __task
Type-erased task owned by the task pool.
Definition task_pool.h:75
std::optional< __time_point > next()
Return the next timer task ready for execution.
Definition task_pool.h:338
std::vector< std::pair< __time_point, __task > > _timer_tasks
Delayed tasks sorted by their due time.
Definition task_pool.h:109
std::deque< __task > _tasks
Immediate tasks waiting for worker execution.
Definition task_pool.h:108
auto push(Function &&newTask, Args &&...args)
Queue work for asynchronous execution.
Definition task_pool.h:146
void delay(task_id_t task_id, std::chrono::duration< X, Y > duration)
Definition task_pool.h:226
auto pushDelayed(Function &&newTask, std::chrono::duration< X, Y > duration, Args &&...args)
Definition task_pool.h:192
std::mutex _task_mutex
Mutex protecting task queues and worker wakeups.
Definition task_pool.h:110
bool ready()
Check whether the task pool has active workers.
Definition task_pool.h:327
Type-erased task interface stored by the task pool.
Definition task_pool.h:27
virtual void run()=0
Execute the queued task body.
virtual ~_ImplBase()=default
Destroy a queued callable through its polymorphic base.
void run() override
Execute the queued task body.
Definition task_pool.h:62
_Impl(Function &&f)
Explicitly store a concrete callable behind the task type-erasure interface.
Definition task_pool.h:55
Declarations for the MoveByCopy utility class.
Functions for handling command line arguments.
Definition entry_handler.cpp:41
Declarations for utility functions.