summaryrefslogtreecommitdiff
path: root/src/common/polyfill_thread.h
diff options
context:
space:
mode:
authorGravatar Liam2023-01-26 17:38:34 -0500
committerGravatar Liam2023-01-27 21:34:49 -0500
commit619c0e70f08c88346e467f54cb1c265e8b41e80e (patch)
tree050cf641eaa102d33795352256805e35dd6999ee /src/common/polyfill_thread.h
parentMerge pull request #9677 from Morph1984/sleep-one (diff)
downloadyuzu-619c0e70f08c88346e467f54cb1c265e8b41e80e.tar.gz
yuzu-619c0e70f08c88346e467f54cb1c265e8b41e80e.tar.xz
yuzu-619c0e70f08c88346e467f54cb1c265e8b41e80e.zip
polyfill_thread: satisfy execution ordering requirements of stop_callback
Diffstat (limited to 'src/common/polyfill_thread.h')
-rw-r--r--src/common/polyfill_thread.h83
1 files changed, 46 insertions, 37 deletions
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
index b2c929d2f..b4c94a5fc 100644
--- a/src/common/polyfill_thread.h
+++ b/src/common/polyfill_thread.h
@@ -41,17 +41,18 @@ bool StoppableTimedWait(std::stop_token token, const std::chrono::duration<Rep,
41#include <chrono> 41#include <chrono>
42#include <condition_variable> 42#include <condition_variable>
43#include <functional> 43#include <functional>
44#include <list> 44#include <map>
45#include <memory> 45#include <memory>
46#include <mutex> 46#include <mutex>
47#include <optional> 47#include <optional>
48#include <thread> 48#include <thread>
49#include <type_traits> 49#include <type_traits>
50#include <utility>
50 51
51namespace std { 52namespace std {
52namespace polyfill { 53namespace polyfill {
53 54
54using stop_state_callbacks = list<function<void()>>; 55using stop_state_callback = size_t;
55 56
56class stop_state { 57class stop_state {
57public: 58public:
@@ -59,61 +60,69 @@ public:
59 ~stop_state() = default; 60 ~stop_state() = default;
60 61
61 bool request_stop() { 62 bool request_stop() {
62 stop_state_callbacks callbacks; 63 unique_lock lk{m_lock};
63 64
64 { 65 if (m_stop_requested) {
65 scoped_lock lk{m_lock}; 66 // Already set, nothing to do.
67 return false;
68 }
66 69
67 if (m_stop_requested.load()) { 70 // Mark stop requested.
68 // Already set, nothing to do 71 m_stop_requested = true;
69 return false;
70 }
71 72
72 // Set as requested 73 while (!m_callbacks.empty()) {
73 m_stop_requested = true; 74 // Get an iterator to the first element.
75 const auto it = m_callbacks.begin();
74 76
75 // Copy callback list 77 // Move the callback function out of the map.
76 callbacks = m_callbacks; 78 function<void()> f;
77 } 79 swap(it->second, f);
80
81 // Erase the now-empty map element.
82 m_callbacks.erase(it);
78 83
79 for (auto callback : callbacks) { 84 // Run the callback.
80 callback(); 85 if (f) {
86 f();
87 }
81 } 88 }
82 89
83 return true; 90 return true;
84 } 91 }
85 92
86 bool stop_requested() const { 93 bool stop_requested() const {
87 return m_stop_requested.load(); 94 unique_lock lk{m_lock};
95 return m_stop_requested;
88 } 96 }
89 97
90 stop_state_callbacks::const_iterator insert_callback(function<void()> f) { 98 stop_state_callback insert_callback(function<void()> f) {
91 stop_state_callbacks::const_iterator ret{}; 99 unique_lock lk{m_lock};
92 bool should_run{};
93
94 {
95 scoped_lock lk{m_lock};
96 should_run = m_stop_requested.load();
97 m_callbacks.push_front(f);
98 ret = m_callbacks.begin();
99 }
100 100
101 if (should_run) { 101 if (m_stop_requested) {
102 f(); 102 // Stop already requested. Don't insert anything,
103 // just run the callback synchronously.
104 if (f) {
105 f();
106 }
107 return 0;
103 } 108 }
104 109
110 // Insert the callback.
111 stop_state_callback ret = ++m_next_callback;
112 m_callbacks.emplace(ret, move(f));
105 return ret; 113 return ret;
106 } 114 }
107 115
108 void remove_callback(stop_state_callbacks::const_iterator it) { 116 void remove_callback(stop_state_callback cb) {
109 scoped_lock lk{m_lock}; 117 unique_lock lk{m_lock};
110 m_callbacks.erase(it); 118 m_callbacks.erase(cb);
111 } 119 }
112 120
113private: 121private:
114 mutex m_lock; 122 mutable recursive_mutex m_lock;
115 atomic<bool> m_stop_requested; 123 map<stop_state_callback, function<void()>> m_callbacks;
116 stop_state_callbacks m_callbacks; 124 stop_state_callback m_next_callback{0};
125 bool m_stop_requested{false};
117}; 126};
118 127
119} // namespace polyfill 128} // namespace polyfill
@@ -223,7 +232,7 @@ public:
223 } 232 }
224 ~stop_callback() { 233 ~stop_callback() {
225 if (m_stop_state && m_callback) { 234 if (m_stop_state && m_callback) {
226 m_stop_state->remove_callback(*m_callback); 235 m_stop_state->remove_callback(m_callback);
227 } 236 }
228 } 237 }
229 238
@@ -234,7 +243,7 @@ public:
234 243
235private: 244private:
236 shared_ptr<polyfill::stop_state> m_stop_state; 245 shared_ptr<polyfill::stop_state> m_stop_state;
237 optional<polyfill::stop_state_callbacks::const_iterator> m_callback; 246 polyfill::stop_state_callback m_callback;
238}; 247};
239 248
240template <typename Callback> 249template <typename Callback>