Sunshine latest
Self-hosted game stream host for Moonlight.
thread_safe.h
Go to the documentation of this file.
1
5#pragma once
6
7// standard includes
8#include <array>
9#include <atomic>
10#include <condition_variable>
11#include <functional>
12#include <map>
13#include <mutex>
14#include <vector>
15
16// local includes
17#include "utility.h"
18
19namespace safe {
20 template<class T>
21 class event_t {
22 public:
23 using status_t = util::optional_t<T>;
24
25 template<class... Args>
26 void raise(Args &&...args) {
27 std::lock_guard lg {_lock};
28 if (!_continue) {
29 return;
30 }
31
32 if constexpr (std::is_same_v<std::optional<T>, status_t>) {
33 _status = std::make_optional<T>(std::forward<Args>(args)...);
34 } else {
35 _status = status_t {std::forward<Args>(args)...};
36 }
37
38 _cv.notify_all();
39 }
40
41 // pop and view should not be used interchangeably
42 status_t pop() {
43 std::unique_lock ul {_lock};
44
45 if (!_continue) {
46 return util::false_v<status_t>;
47 }
48
49 while (!_status) {
50 _cv.wait(ul);
51
52 if (!_continue) {
53 return util::false_v<status_t>;
54 }
55 }
56
57 auto val = std::move(_status);
58 _status = util::false_v<status_t>;
59 return val;
60 }
61
62 // pop and view should not be used interchangeably
63 template<class Rep, class Period>
64 status_t pop(std::chrono::duration<Rep, Period> delay) {
65 std::unique_lock ul {_lock};
66
67 if (!_continue) {
68 return util::false_v<status_t>;
69 }
70
71 while (!_status) {
72 if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
73 return util::false_v<status_t>;
74 }
75 }
76
77 auto val = std::move(_status);
78 _status = util::false_v<status_t>;
79 return val;
80 }
81
82 // pop and view should not be used interchangeably
83 status_t view() {
84 std::unique_lock ul {_lock};
85
86 if (!_continue) {
87 return util::false_v<status_t>;
88 }
89
90 while (!_status) {
91 _cv.wait(ul);
92
93 if (!_continue) {
94 return util::false_v<status_t>;
95 }
96 }
97
98 return _status;
99 }
100
101 // pop and view should not be used interchangeably
102 template<class Rep, class Period>
103 status_t view(std::chrono::duration<Rep, Period> delay) {
104 std::unique_lock ul {_lock};
105
106 if (!_continue) {
107 return util::false_v<status_t>;
108 }
109
110 while (!_status) {
111 if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
112 return util::false_v<status_t>;
113 }
114 }
115
116 return _status;
117 }
118
119 bool peek() {
120 return _continue && (bool) _status;
121 }
122
123 void stop() {
124 std::lock_guard lg {_lock};
125
126 _continue = false;
127
128 _cv.notify_all();
129 }
130
131 void reset() {
132 std::lock_guard lg {_lock};
133
134 _continue = true;
135
136 _status = util::false_v<status_t>;
137 }
138
139 [[nodiscard]] bool running() const {
140 return _continue;
141 }
142
143 private:
144 bool _continue {true};
145 status_t _status {util::false_v<status_t>};
146
147 std::condition_variable _cv;
148 std::mutex _lock;
149 };
150
151 template<class T>
153 public:
154 using status_t = util::optional_t<T>;
155
156 void ring(const status_t &status) {
157 std::lock_guard lg(_lock);
158
159 _status = status;
160 _rang = true;
161 _cv.notify_one();
162 }
163
164 void ring(status_t &&status) {
165 std::lock_guard lg(_lock);
166
167 _status = std::move(status);
168 _rang = true;
169 _cv.notify_one();
170 }
171
172 template<class Rep, class Period>
173 auto wait_for(const std::chrono::duration<Rep, Period> &rel_time) {
174 std::unique_lock ul(_lock);
175
176 return _cv.wait_for(ul, rel_time, [this]() {
177 return _rang;
178 });
179 }
180
181 template<class Rep, class Period, class Pred>
182 auto wait_for(const std::chrono::duration<Rep, Period> &rel_time, Pred &&pred) {
183 std::unique_lock ul(_lock);
184
185 return _cv.wait_for(ul, rel_time, [this, &pred]() {
186 return _rang || pred();
187 });
188 }
189
190 template<class Rep, class Period>
191 auto wait_until(const std::chrono::duration<Rep, Period> &rel_time) {
192 std::unique_lock ul(_lock);
193
194 return _cv.wait_until(ul, rel_time, [this]() {
195 return _rang;
196 });
197 }
198
199 template<class Rep, class Period, class Pred>
200 auto wait_until(const std::chrono::duration<Rep, Period> &rel_time, Pred &&pred) {
201 std::unique_lock ul(_lock);
202
203 return _cv.wait_until(ul, rel_time, [this, &pred]() {
204 return _rang || pred();
205 });
206 }
207
208 auto wait() {
209 std::unique_lock ul(_lock);
210 _cv.wait(ul, [this]() {
211 return _rang;
212 });
213 }
214
215 template<class Pred>
216 auto wait(Pred &&pred) {
217 std::unique_lock ul(_lock);
218 _cv.wait(ul, [this, &pred]() {
219 return _rang || pred();
220 });
221 }
222
223 const status_t &status() const {
224 return _status;
225 }
226
227 status_t &status() {
228 return _status;
229 }
230
231 void reset() {
232 _status = status_t {};
233 _rang = false;
234 }
235
236 private:
237 std::mutex _lock;
238 std::condition_variable _cv;
239
240 status_t _status {util::false_v<status_t>};
241 bool _rang {false};
242 };
243
244 template<class T>
245 using alarm_t = std::shared_ptr<alarm_raw_t<T>>;
246
247 template<class T>
248 alarm_t<T> make_alarm() {
249 return std::make_shared<alarm_raw_t<T>>();
250 }
251
252 template<class T>
253 class queue_t {
254 public:
255 using status_t = util::optional_t<T>;
256
257 queue_t(std::uint32_t max_elements = 32):
258 _max_elements {max_elements} {
259 }
260
261 template<class... Args>
262 void raise(Args &&...args) {
263 std::lock_guard ul {_lock};
264
265 if (!_continue) {
266 return;
267 }
268
269 if (_queue.size() == _max_elements) {
270 _queue.clear();
271 }
272
273 _queue.emplace_back(std::forward<Args>(args)...);
274
275 _cv.notify_all();
276 }
277
278 bool peek() {
279 return _continue && !_queue.empty();
280 }
281
282 template<class Rep, class Period>
283 status_t pop(std::chrono::duration<Rep, Period> delay) {
284 std::unique_lock ul {_lock};
285
286 if (!_continue) {
287 return util::false_v<status_t>;
288 }
289
290 while (_queue.empty()) {
291 if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) {
292 return util::false_v<status_t>;
293 }
294 }
295
296 auto val = std::move(_queue.front());
297 _queue.erase(std::begin(_queue));
298
299 return val;
300 }
301
302 status_t pop() {
303 std::unique_lock ul {_lock};
304
305 if (!_continue) {
306 return util::false_v<status_t>;
307 }
308
309 while (_queue.empty()) {
310 _cv.wait(ul);
311
312 if (!_continue) {
313 return util::false_v<status_t>;
314 }
315 }
316
317 auto val = std::move(_queue.front());
318 _queue.erase(std::begin(_queue));
319
320 return val;
321 }
322
323 std::vector<T> &unsafe() {
324 return _queue;
325 }
326
327 void stop() {
328 std::lock_guard lg {_lock};
329
330 _continue = false;
331
332 _cv.notify_all();
333 }
334
335 [[nodiscard]] bool running() const {
336 return _continue;
337 }
338
339 private:
340 bool _continue {true};
341 std::uint32_t _max_elements;
342
343 std::mutex _lock;
344 std::condition_variable _cv;
345
346 std::vector<T> _queue;
347 };
348
349 template<class T>
350 class shared_t {
351 public:
352 using element_type = T;
353
354 using construct_f = std::function<int(element_type &)>;
355 using destruct_f = std::function<void(element_type &)>;
356
357 struct ptr_t {
358 shared_t *owner;
359
360 ptr_t():
361 owner {nullptr} {
362 }
363
364 explicit ptr_t(shared_t *owner):
365 owner {owner} {
366 }
367
368 ptr_t(ptr_t &&ptr) noexcept:
369 owner {ptr.owner} {
370 ptr.owner = nullptr;
371 }
372
373 ptr_t(const ptr_t &ptr) noexcept:
374 owner {ptr.owner} {
375 if (!owner) {
376 return;
377 }
378
379 auto tmp = ptr.owner->ref();
380 tmp.owner = nullptr;
381 }
382
383 ptr_t &operator=(const ptr_t &ptr) noexcept {
384 if (!ptr.owner) {
385 release();
386
387 return *this;
388 }
389
390 return *this = std::move(*ptr.owner->ref());
391 }
392
393 ptr_t &operator=(ptr_t &&ptr) noexcept {
394 if (owner) {
395 release();
396 }
397
398 std::swap(owner, ptr.owner);
399
400 return *this;
401 }
402
403 ~ptr_t() {
404 if (owner) {
405 release();
406 }
407 }
408
409 operator bool() const {
410 return owner != nullptr;
411 }
412
413 void release() {
414 std::lock_guard lg {owner->_lock};
415
416 if (!--owner->_count) {
417 owner->_destruct(*get());
418 (*this)->~element_type();
419 }
420
421 owner = nullptr;
422 }
423
424 element_type *get() const {
425 return reinterpret_cast<element_type *>(owner->_object_buf.data());
426 }
427
428 element_type *operator->() {
429 return reinterpret_cast<element_type *>(owner->_object_buf.data());
430 }
431 };
432
433 template<class FC, class FD>
434 shared_t(FC &&fc, FD &&fd):
435 _construct {std::forward<FC>(fc)},
436 _destruct {std::forward<FD>(fd)} {
437 }
438
439 [[nodiscard]] ptr_t ref() {
440 std::lock_guard lg {_lock};
441
442 if (!_count) {
443 new (_object_buf.data()) element_type;
444 if (_construct(*reinterpret_cast<element_type *>(_object_buf.data()))) {
445 return ptr_t {nullptr};
446 }
447 }
448
449 ++_count;
450
451 return ptr_t {this};
452 }
453
454 private:
455 construct_f _construct;
456 destruct_f _destruct;
457
458 std::array<std::uint8_t, sizeof(element_type)> _object_buf;
459
460 std::uint32_t _count;
461 std::mutex _lock;
462 };
463
464 template<class T, class F_Construct, class F_Destruct>
465 auto make_shared(F_Construct &&fc, F_Destruct &&fd) {
466 return shared_t<T> {
467 std::forward<F_Construct>(fc),
468 std::forward<F_Destruct>(fd)
469 };
470 }
471
472 using signal_t = event_t<bool>;
473
474 class mail_raw_t;
475 using mail_t = std::shared_ptr<mail_raw_t>;
476
477 void cleanup(mail_raw_t *);
478
479 template<class T>
480 class post_t: public T {
481 public:
482 template<class... Args>
483 post_t(mail_t mail, Args &&...args):
484 T(std::forward<Args>(args)...),
485 mail {std::move(mail)} {
486 }
487
488 mail_t mail;
489
490 ~post_t() {
491 cleanup(mail.get());
492 }
493 };
494
495 template<class T>
496 inline auto lock(const std::weak_ptr<void> &wp) {
497 return std::reinterpret_pointer_cast<typename T::element_type>(wp.lock());
498 }
499
500 class mail_raw_t: public std::enable_shared_from_this<mail_raw_t> {
501 public:
502 template<class T>
503 using event_t = std::shared_ptr<post_t<event_t<T>>>;
504
505 template<class T>
506 using queue_t = std::shared_ptr<post_t<queue_t<T>>>;
507
508 template<class T>
509 event_t<T> event(const std::string_view &id) {
510 std::lock_guard lg {mutex};
511
512 auto it = id_to_post.find(id);
513 if (it != std::end(id_to_post)) {
514 return lock<event_t<T>>(it->second);
515 }
516
517 auto post = std::make_shared<typename event_t<T>::element_type>(shared_from_this());
518 id_to_post.emplace(std::pair<std::string, std::weak_ptr<void>> {std::string {id}, post});
519
520 return post;
521 }
522
523 template<class T>
524 queue_t<T> queue(const std::string_view &id) {
525 std::lock_guard lg {mutex};
526
527 auto it = id_to_post.find(id);
528 if (it != std::end(id_to_post)) {
529 return lock<queue_t<T>>(it->second);
530 }
531
532 auto post = std::make_shared<typename queue_t<T>::element_type>(shared_from_this(), 32);
533 id_to_post.emplace(std::pair<std::string, std::weak_ptr<void>> {std::string {id}, post});
534
535 return post;
536 }
537
538 void cleanup() {
539 std::lock_guard lg {mutex};
540
541 for (auto it = std::begin(id_to_post); it != std::end(id_to_post); ++it) {
542 auto &weak = it->second;
543
544 if (weak.expired()) {
545 id_to_post.erase(it);
546
547 return;
548 }
549 }
550 }
551
552 std::mutex mutex;
553
554 std::map<std::string, std::weak_ptr<void>, std::less<>> id_to_post;
555 };
556
557 inline void cleanup(mail_raw_t *mail) {
558 mail->cleanup();
559 }
560} // namespace safe
Definition thread_safe.h:152
Definition thread_safe.h:21
Definition thread_safe.h:500
Definition thread_safe.h:480
Definition thread_safe.h:253
Definition thread_safe.h:350
Functions for handling command line arguments.
Definition entry_handler.cpp:39
Handles process-wide communication.
Definition globals.h:34
Definition thread_safe.h:357
Declarations for utility functions.