11#include "unfair_mutex_intf.hpp"
13#include "../utility/utility.hpp"
14#include "../macros.hpp"
19hi_export_module(hikogui.concurrency.unfair_mutex : impl);
21namespace hi {
inline namespace v1 {
24inline unfair_mutex_impl<false> unfair_mutex_deadlock_mutex;
26thread_local inline std::vector<void *> unfair_mutex_deadlock_stack;
33inline std::vector<std::pair<void *, void *>> unfair_mutex_deadlock_lock_graph;
35[[nodiscard]]
inline void *unfair_mutex_deadlock_check_graph(
void *
object)
noexcept
37 hi_assert_not_null(
object);
39 hilet
lock = std::scoped_lock(detail::unfair_mutex_deadlock_mutex);
41 for (hilet before : unfair_mutex_deadlock_stack) {
46 unfair_mutex_deadlock_lock_graph.cbegin(), unfair_mutex_deadlock_lock_graph.cend(), correct_order)) {
52 unfair_mutex_deadlock_lock_graph.cbegin(), unfair_mutex_deadlock_lock_graph.cend(), reverse_order)) {
59 std::upper_bound(unfair_mutex_deadlock_lock_graph.cbegin(), unfair_mutex_deadlock_lock_graph.cend(), correct_order);
60 unfair_mutex_deadlock_lock_graph.insert(it,
std::move(correct_order));
79 hi_assert_not_null(
object);
81 if (
std::count(detail::unfair_mutex_deadlock_stack.
begin(), detail::unfair_mutex_deadlock_stack.
end(),
object) != 0) {
86 if (
auto before = detail::unfair_mutex_deadlock_check_graph(
object)) {
91 detail::unfair_mutex_deadlock_stack.push_back(
object);
106 hi_assert_not_null(
object);
109 if (detail::unfair_mutex_deadlock_stack.empty()) {
114 if (detail::unfair_mutex_deadlock_stack.back() !=
object) {
118 detail::unfair_mutex_deadlock_stack.pop_back();
130 hi_assert_not_null(
object);
137 hilet lock = std::scoped_lock(detail::unfair_mutex_deadlock_mutex);
138 std::erase_if(detail::unfair_mutex_deadlock_lock_graph, [
object](hilet& item) {
139 return item.first ==
object or item.second == object;
153 detail::unfair_mutex_deadlock_stack.clear();
166 hilet lock = std::scoped_lock(detail::unfair_mutex_deadlock_mutex);
167 detail::unfair_mutex_deadlock_lock_graph.clear();
170template<
bool UseDeadLockDetector>
171inline unfair_mutex_impl<UseDeadLockDetector>::~unfair_mutex_impl()
173 hi_axiom(not is_locked());
174 if constexpr (UseDeadLockDetector) {
179template<
bool UseDeadLockDetector>
180inline bool unfair_mutex_impl<UseDeadLockDetector>::is_locked() const noexcept
182 return semaphore.load(std::memory_order::relaxed) != 0;
185template<
bool UseDeadLockDetector>
186inline void unfair_mutex_impl<UseDeadLockDetector>::lock() noexcept
188 if constexpr (UseDeadLockDetector) {
190 hi_assert(
other !=
this,
"This mutex is already locked.");
191 hi_assert(
other ==
nullptr,
"Potential dead-lock because of different lock ordering of mutexes.");
194 hi_axiom(holds_invariant());
197 semaphore_value_type expected = 0;
198 if (not semaphore.compare_exchange_strong(expected, 1, std::memory_order::acquire)) {
199 [[unlikely]] lock_contended(expected);
202 hi_axiom(holds_invariant());
212template<
bool UseDeadLockDetector>
215 if constexpr (UseDeadLockDetector) {
217 hi_assert(
other !=
this,
"This mutex is already locked.");
218 hi_assert(
other ==
nullptr,
"Potential dead-lock because of different lock ordering of mutexes.");
221 hi_axiom(holds_invariant());
224 semaphore_value_type expected = 0;
225 if (not semaphore.compare_exchange_strong(expected, 1, std::memory_order::acquire)) {
226 hi_axiom(holds_invariant());
228 if constexpr (UseDeadLockDetector) {
232 [[unlikely]]
return false;
235 hi_axiom(holds_invariant());
239template<
bool UseDeadLockDetector>
240inline void unfair_mutex_impl<UseDeadLockDetector>::unlock() noexcept
242 if constexpr (UseDeadLockDetector) {
246 hi_axiom(holds_invariant());
248 if (semaphore.fetch_sub(1, std::memory_order::relaxed) != 1) {
249 [[unlikely]] semaphore.store(0, std::memory_order::release);
251 semaphore.notify_one();
253 atomic_thread_fence(std::memory_order::release);
256 hi_axiom(holds_invariant());
259template<
bool UseDeadLockDetector>
260[[nodiscard]]
inline bool unfair_mutex_impl<UseDeadLockDetector>::holds_invariant() const noexcept
262 return semaphore.load(std::memory_order::relaxed) <= 2;
265template<
bool UseDeadLockDetector>
266hi_no_inline
inline void unfair_mutex_impl<UseDeadLockDetector>::lock_contended(semaphore_value_type expected)
noexcept
268 hi_axiom(holds_invariant());
271 hilet should_wait = expected == 2;
275 if (should_wait || semaphore.compare_exchange_strong(expected, 2)) {
276 hi_axiom(holds_invariant());
280 hi_axiom(holds_invariant());
283 }
while (!semaphore.compare_exchange_strong(expected, 2));
An atomic access global variable for quick access to state of the system.
bool is_system_shutting_down() noexcept
Check if the HikoGUI system is being shut down.
Definition global_state.hpp:220
@ end
Start from the end of the file.
Definition seek_whence.hpp:17
@ begin
Start from the beginning of the file.
Definition seek_whence.hpp:15
@ other
The gui_event does not have associated data.
Definition gui_event_variant.hpp:22
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
The HikoGUI API version 1.
Definition lookahead_iterator.hpp:6
hi_export void unfair_mutex_deadlock_remove_object(void *object) noexcept
Remove the object from the detection.
Definition unfair_mutex_impl.hpp:128
hi_export void unfair_mutex_deadlock_clear_stack() noexcept
Clear the stack.
Definition unfair_mutex_impl.hpp:146
hi_export void * unfair_mutex_deadlock_lock(void *object) noexcept
Lock an object on this thread.
Definition unfair_mutex_impl.hpp:72
hi_export void unfair_mutex_deadlock_clear_graph() noexcept
Clear the graph.
Definition unfair_mutex_impl.hpp:159
hi_export bool unfair_mutex_deadlock_unlock(void *object) noexcept
Unlock an object on this thread.
Definition unfair_mutex_impl.hpp:99
bool try_lock() noexcept
When try_lock() is called from a thread that already owns the lock it will return false.
Definition unfair_mutex_impl.hpp:213
T binary_search(T... args)