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