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