11#include "unfair_mutex_intf.hpp"
13#include "../utility/utility.hpp"
14#include "../macros.hpp"
22hi_export_module(hikogui.concurrency.unfair_mutex : impl);
24hi_export
namespace hi {
inline namespace v1 {
27inline unfair_mutex_impl<false> unfair_mutex_deadlock_mutex;
38[[nodiscard]]
inline void *unfair_mutex_deadlock_check_graph(
void *
object)
noexcept
40 hi_assert_not_null(
object);
42 auto const lock = std::scoped_lock(detail::unfair_mutex_deadlock_mutex);
44 for (
auto const before : unfair_mutex_deadlock_stack) {
49 unfair_mutex_deadlock_lock_graph.cbegin(), unfair_mutex_deadlock_lock_graph.cend(), correct_order)) {
55 unfair_mutex_deadlock_lock_graph.cbegin(), unfair_mutex_deadlock_lock_graph.cend(), reverse_order)) {
62 std::upper_bound(unfair_mutex_deadlock_lock_graph.cbegin(), unfair_mutex_deadlock_lock_graph.cend(), correct_order);
63 unfair_mutex_deadlock_lock_graph.insert(it,
std::move(correct_order));
82 hi_assert_not_null(
object);
84 if (
std::count(detail::unfair_mutex_deadlock_stack.
begin(), detail::unfair_mutex_deadlock_stack.
end(),
object) != 0) {
89 if (
auto before = detail::unfair_mutex_deadlock_check_graph(
object)) {
94 detail::unfair_mutex_deadlock_stack.push_back(
object);
109 hi_assert_not_null(
object);
112 if (detail::unfair_mutex_deadlock_stack.empty()) {
117 if (detail::unfair_mutex_deadlock_stack.back() !=
object) {
121 detail::unfair_mutex_deadlock_stack.pop_back();
133 hi_assert_not_null(
object);
140 auto const lock = std::scoped_lock(detail::unfair_mutex_deadlock_mutex);
141 std::erase_if(detail::unfair_mutex_deadlock_lock_graph, [
object](
auto const& item) {
142 return item.first ==
object or item.second == object;
156 detail::unfair_mutex_deadlock_stack.clear();
169 auto const lock = std::scoped_lock(detail::unfair_mutex_deadlock_mutex);
170 detail::unfair_mutex_deadlock_lock_graph.clear();
173template<
bool UseDeadLockDetector>
174inline unfair_mutex_impl<UseDeadLockDetector>::~unfair_mutex_impl()
176 hi_axiom(not is_locked());
177 if constexpr (UseDeadLockDetector) {
182template<
bool UseDeadLockDetector>
183inline bool unfair_mutex_impl<UseDeadLockDetector>::is_locked() const noexcept
185 return semaphore.load(std::memory_order::relaxed) != 0;
188template<
bool UseDeadLockDetector>
189inline void unfair_mutex_impl<UseDeadLockDetector>::lock() noexcept
191 if constexpr (UseDeadLockDetector) {
193 hi_assert(
other !=
this,
"This mutex is already locked.");
194 hi_assert(
other ==
nullptr,
"Potential dead-lock because of different lock ordering of mutexes.");
197 hi_axiom(holds_invariant());
200 semaphore_value_type expected = 0;
201 if (not semaphore.compare_exchange_strong(expected, 1, std::memory_order::acquire)) {
202 [[unlikely]] lock_contended(expected);
205 hi_axiom(holds_invariant());
215template<
bool UseDeadLockDetector>
218 if constexpr (UseDeadLockDetector) {
220 hi_assert(
other !=
this,
"This mutex is already locked.");
221 hi_assert(
other ==
nullptr,
"Potential dead-lock because of different lock ordering of mutexes.");
224 hi_axiom(holds_invariant());
227 semaphore_value_type expected = 0;
228 if (not semaphore.compare_exchange_strong(expected, 1, std::memory_order::acquire)) {
229 hi_axiom(holds_invariant());
231 if constexpr (UseDeadLockDetector) {
235 [[unlikely]]
return false;
238 hi_axiom(holds_invariant());
242template<
bool UseDeadLockDetector>
245 if constexpr (UseDeadLockDetector) {
249 hi_axiom(holds_invariant());
251 if (semaphore.fetch_sub(1, std::memory_order::relaxed) != 1) {
252 [[unlikely]] semaphore.store(0, std::memory_order::release);
254 semaphore.notify_one();
256 atomic_thread_fence(std::memory_order::release);
259 hi_axiom(holds_invariant());
262template<
bool UseDeadLockDetector>
263[[nodiscard]]
inline bool unfair_mutex_impl<UseDeadLockDetector>::holds_invariant() const noexcept
265 return semaphore.load(std::memory_order::relaxed) <= 2;
268template<
bool UseDeadLockDetector>
269hi_no_inline
inline void unfair_mutex_impl<UseDeadLockDetector>::lock_contended(semaphore_value_type expected)
noexcept
271 hi_axiom(holds_invariant());
274 auto const should_wait = expected == 2;
278 if (should_wait || semaphore.compare_exchange_strong(expected, 2)) {
279 hi_axiom(holds_invariant());
283 hi_axiom(holds_invariant());
286 }
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:222
@ end
Start from the end of the file.
@ begin
Start from the beginning of the file.
@ other
The gui_event does not have associated data.
The HikoGUI namespace.
Definition array_generic.hpp:20
hi_export void unfair_mutex_deadlock_remove_object(void *object) noexcept
Remove the object from the detection.
Definition unfair_mutex_impl.hpp:131
hi_export void unfair_mutex_deadlock_clear_stack() noexcept
Clear the stack.
Definition unfair_mutex_impl.hpp:149
hi_export void * unfair_mutex_deadlock_lock(void *object) noexcept
Lock an object on this thread.
Definition unfair_mutex_impl.hpp:75
hi_export void unfair_mutex_deadlock_clear_graph() noexcept
Clear the graph.
Definition unfair_mutex_impl.hpp:162
hi_export bool unfair_mutex_deadlock_unlock(void *object) noexcept
Unlock an object on this thread.
Definition unfair_mutex_impl.hpp:102
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
An unfair mutex This is a fast implementation of a mutex which does not fairly arbitrate between mult...
Definition unfair_mutex_intf.hpp:38
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:216
T binary_search(T... args)