polyfill_thread: satisfy execution ordering requirements of stop_callback

This commit is contained in:
Liam
2023-02-01 16:41:43 +02:00
committed by GPUCode
parent e08e644e73
commit df7f1b13cb

View File

@@ -27,17 +27,18 @@ void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
#include <atomic> #include <atomic>
#include <functional> #include <functional>
#include <list> #include <map>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <thread> #include <thread>
#include <type_traits> #include <type_traits>
#include <utility>
namespace std { namespace std {
namespace polyfill { namespace polyfill {
using stop_state_callbacks = list<function<void()>>; using stop_state_callback = size_t;
class stop_state { class stop_state {
public: public:
@@ -45,66 +46,74 @@ public:
~stop_state() = default; ~stop_state() = default;
bool request_stop() { bool request_stop() {
stop_state_callbacks callbacks; unique_lock lk{m_lock};
{ if (m_stop_requested) {
scoped_lock lk{m_lock}; // Already set, nothing to do.
return false;
if (m_stop_requested.load()) {
// Already set, nothing to do
return false;
}
// Set as requested
m_stop_requested = true;
// Copy callback list
callbacks = m_callbacks;
} }
for (auto callback : callbacks) { // Mark stop requested.
callback(); m_stop_requested = true;
while (!m_callbacks.empty()) {
// Get an iterator to the first element.
const auto it = m_callbacks.begin();
// Move the callback function out of the map.
function<void()> f;
swap(it->second, f);
// Erase the now-empty map element.
m_callbacks.erase(it);
// Run the callback.
if (f) {
f();
}
} }
return true; return true;
} }
bool stop_requested() const { bool stop_requested() const {
return m_stop_requested.load(); unique_lock lk{m_lock};
return m_stop_requested;
} }
stop_state_callbacks::const_iterator insert_callback(function<void()> f) { stop_state_callback insert_callback(function<void()> f) {
stop_state_callbacks::const_iterator ret{}; unique_lock lk{m_lock};
bool should_run{};
{ if (m_stop_requested) {
scoped_lock lk{m_lock}; // Stop already requested. Don't insert anything,
should_run = m_stop_requested.load(); // just run the callback synchronously.
m_callbacks.push_front(f); if (f) {
ret = m_callbacks.begin(); f();
} }
return 0;
if (should_run) {
f();
} }
// Insert the callback.
stop_state_callback ret = ++m_next_callback;
m_callbacks.emplace(ret, move(f));
return ret; return ret;
} }
void remove_callback(stop_state_callbacks::const_iterator it) { void remove_callback(stop_state_callback cb) {
scoped_lock lk{m_lock}; unique_lock lk{m_lock};
m_callbacks.erase(it); m_callbacks.erase(cb);
} }
private: private:
mutex m_lock; mutable recursive_mutex m_lock;
atomic<bool> m_stop_requested; map<stop_state_callback, function<void()>> m_callbacks;
stop_state_callbacks m_callbacks; stop_state_callback m_next_callback{0};
bool m_stop_requested{false};
}; };
} // namespace polyfill } // namespace polyfill
#if ANDROID #ifndef __cpp_lib_concepts
template <class T, class... Args> template <class T, class... Args>
concept constructible_from = is_nothrow_destructible_v<T> && is_constructible_v<T, Args...>; concept constructible_from = is_nothrow_destructible_v<T> && is_constructible_v<T, Args...>;
#endif #endif
@@ -214,7 +223,7 @@ public:
} }
~stop_callback() { ~stop_callback() {
if (m_stop_state && m_callback) { if (m_stop_state && m_callback) {
m_stop_state->remove_callback(*m_callback); m_stop_state->remove_callback(m_callback);
} }
} }
@@ -225,7 +234,7 @@ public:
private: private:
shared_ptr<polyfill::stop_state> m_stop_state; shared_ptr<polyfill::stop_state> m_stop_state;
optional<polyfill::stop_state_callbacks::const_iterator> m_callback; polyfill::stop_state_callback m_callback;
}; };
template <typename Callback> template <typename Callback>