HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
unfair_mutex.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2020-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
9#pragma once
10
12#include "../utility/module.hpp"
13#include <atomic>
14#include <memory>
15
16namespace hi { inline namespace v1 {
17
35template<bool UseDeadLockDetector>
37public:
38 constexpr unfair_mutex_impl() noexcept {}
39 unfair_mutex_impl(unfair_mutex_impl const&) = delete;
41 unfair_mutex_impl& operator=(unfair_mutex_impl const&) = delete;
42 unfair_mutex_impl& operator=(unfair_mutex_impl&&) = delete;
43
45 {
46 if constexpr (UseDeadLockDetector) {
48 }
49 }
50
51 bool is_locked() const noexcept
52 {
53 return semaphore.load(std::memory_order::relaxed) != 0;
54 }
55
56 void lock() noexcept
57 {
58 if constexpr (UseDeadLockDetector) {
60 // *this mutex is already locked.
61 hi_assert(other != this);
62 // Potential dead-lock because of different ordering with other.
63 hi_assert(other == nullptr);
64 }
65
66 hi_axiom(holds_invariant());
67
68 // Switch to 1 means there are no waiters.
69 semaphore_value_type expected = 0;
70 if (not semaphore.compare_exchange_strong(expected, 1, std::memory_order::acquire)) {
71 [[unlikely]] lock_contended(expected);
72 }
73
74 hi_axiom(holds_invariant());
75 }
76
84 [[nodiscard]] bool try_lock() noexcept
85 {
86 if constexpr (UseDeadLockDetector) {
88 // *this mutex is already locked.
89 hi_assert(other != this);
90 // Potential dead-lock because of different ordering with other.
91 hi_assert(other == nullptr);
92 }
93
94 hi_axiom(holds_invariant());
95
96 // Switch to 1 means there are no waiters.
97 semaphore_value_type expected = 0;
98 if (!semaphore.compare_exchange_strong(expected, 1, std::memory_order::acquire)) {
99 hi_axiom(holds_invariant());
100
101 if constexpr (UseDeadLockDetector) {
102 // *this mutex is locked out-of-order from the order of being locked.
104 }
105
106 [[unlikely]] return false;
107 }
108
109 hi_axiom(holds_invariant());
110 return true;
111 }
112
113 void unlock() noexcept
114 {
115 if constexpr (UseDeadLockDetector) {
116 // *this mutex is locked out-of-order from the order of being locked.
118 }
119
120 hi_axiom(holds_invariant());
121
122 if (semaphore.fetch_sub(1, std::memory_order::relaxed) != 1) {
123 [[unlikely]] semaphore.store(0, std::memory_order::release);
124
125 semaphore.notify_one();
126 } else {
127 atomic_thread_fence(std::memory_order::release);
128 }
129
130 hi_axiom(holds_invariant());
131 }
132
133private:
134 /*
135 * semaphore value:
136 * 0 - Unlocked, no other thread is waiting.
137 * 1 - Locked, no other thread is waiting.
138 * 2 - Locked, zero or more threads are waiting.
139 */
140 std::atomic_unsigned_lock_free semaphore = 0;
141 using semaphore_value_type = typename decltype(semaphore)::value_type;
142
143 bool holds_invariant() const noexcept
144 {
145 return semaphore.load(std::memory_order::relaxed) <= 2;
146 }
147
148 hi_no_inline void lock_contended(semaphore_value_type expected) noexcept
149 {
150 hi_axiom(holds_invariant());
151
152 do {
153 hilet should_wait = expected == 2;
154
155 // Set to 2 when we are waiting.
156 expected = 1;
157 if (should_wait or semaphore.compare_exchange_strong(expected, 2)) {
158 hi_axiom(holds_invariant());
159 semaphore.wait(2);
160 }
161
162 hi_axiom(holds_invariant());
163 // Set to 2 when acquiring the lock, so that during unlock we wake other waiting threads.
164 expected = 0;
165 } while (not semaphore.compare_exchange_strong(expected, 2));
166 }
167};
168
169#ifndef NDEBUG
170using unfair_mutex = unfair_mutex_impl<true>;
171#else
172using unfair_mutex = unfair_mutex_impl<false>;
173#endif
174using unfair_mutex_without_deadlock_detector = unfair_mutex_impl<false>;
175
176}} // namespace hi::v1
A deadlock detector service for unfair_mutex.
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:199
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:253
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
@ other
The gui_event does not have associated data.
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11
static bool unlock(void *object) noexcept
Unlock an object on this thread.
static void remove_object(void *object) noexcept
Remove the object from the detection.
static void * lock(void *object) noexcept
Lock an object on this thread.
An unfair mutex This is a fast implementation of a mutex which does not fairly arbitrate between mult...
Definition unfair_mutex.hpp:36
bool try_lock() noexcept
When try_lock() is called from a thread that already owns the lock it will return false.
Definition unfair_mutex.hpp:84