34 bool is_locked()
const noexcept
36 return to_bool(lock_count(_v.load(std::memory_order::relaxed)));
48 value_type expected = 0;
49 if (not _exclusive_try_lock(expected)) {
50 [[unlikely]]
return exclusive_lock_contended<false>(expected);
56 hi_force_inline
void exclusive_lock() noexcept
58 value_type expected = 0;
59 if (not _exclusive_try_lock(expected)) {
60 [[unlikely]] exclusive_lock_contended<true>(expected);
72 if (_v.fetch_sub(exclusive_one, std::memory_order::release) - exclusive_one) {
88 value_type expected = 0;
89 if (not _shared_try_lock(expected)) {
90 [[unlikely]] shared_lock_contended(expected);
94 [[nodiscard]] hi_force_inline
bool shared_try_lock() noexcept
96 value_type expected = 0;
97 return _shared_try_lock(expected);
100 hi_force_inline
void shared_unlock() noexcept
104 hilet tmp = _v.fetch_sub(shared_one, std::memory_order::release) - shared_one;
105 if (tmp != 0 and tmp < shared_one) {
116 using atomic_value_type = std::atomic_unsigned_lock_free;
117 using value_type = atomic_value_type::value_type;
119 constexpr static value_type total_bit =
sizeof(value_type) * CHAR_BIT;
120 constexpr static value_type waiter_bit = total_bit / 2;
121 constexpr static value_type exclusive_bit = 1;
122 constexpr static value_type shared_bit = total_bit - exclusive_bit - waiter_bit;
124 constexpr static value_type exclusive_off = 0;
125 constexpr static value_type shared_off = exclusive_off + exclusive_bit;
126 constexpr static value_type waiter_off = shared_off + shared_bit;
128 constexpr static value_type exclusive_mask = ((value_type{1} << exclusive_bit) - 1) << exclusive_off;
129 constexpr static value_type shared_mask = ((value_type{1} << shared_bit) - 1) << shared_off;
130 constexpr static value_type waiter_mask = ((value_type{1} << waiter_bit) - 1) << waiter_off;
132 constexpr static value_type exclusive_one = value_type{1} << exclusive_off;
133 constexpr static value_type shared_one = value_type{1} << shared_off;
134 constexpr static value_type waiter_one = value_type{1} << waiter_off;
151 atomic_value_type _v = 0;
153 [[nodiscard]]
static value_type exclusive_count(value_type value)
noexcept
155 return value & exclusive_mask;
158 [[nodiscard]]
static value_type shared_count(value_type value)
noexcept
160 static_assert(waiter_off + waiter_bit == total_bit);
161 static_assert(exclusive_off == 0);
162 static_assert(shared_off == exclusive_off + exclusive_bit);
164 value <<= total_bit - waiter_off;
165 value >>= (total_bit - waiter_off) + shared_off;
169 [[nodiscard]]
static bool _is_locked(value_type value)
noexcept
171 static_assert(waiter_off + waiter_bit == total_bit);
173 return to_bool(value << (total_bit - waiter_off));
176 [[nodiscard]]
static value_type increment_exclusive(value_type value)
noexcept
178 hi_axiom(value & exclusive_mask != exclusive_mask);
179 return value + exclusive_one;
182 [[nodiscard]]
static value_type increment_shared(value_type value)
noexcept
184 hi_axiom(value & shared_mask != shared_mask);
185 return value + shared_one;
188 [[nodiscard]]
static value_type increment_wait(value_type value)
noexcept
190 hi_axiom(value & waiter_mask != waiter_mask);
191 return value + waiter_one;
194 [[nodiscard]]
static value_type clear_exclusive_and_waiter(value_type value)
noexcept
196 return value & shared_mask;
199 [[nodiscard]]
bool holds_invariant() const noexcept
201 hilet tmp = _v.load(std::memory_order::relaxed);
202 return not(to_bool(shared_count(tmp)) and to_bool(exclusive_count(tmp)));
205 hi_force_inline
void wait(value_type& expected)
noexcept
210 hilet desired = increment_wait(expected);
211 if (_v.compare_exchange_strong(expected, desired, std::memory_order::relaxed)) {
216 expected = _v.fetch_sub(waiter_one, std::memory_order::relaxed) - waiter_one;
222 [[nodiscard]] hi_force_inline
bool _exclusive_try_lock(value_type& expected)
noexcept
228 value_type expected = 0;
229 return _v.compare_exchange_strong(
230 expected, increment_exclusive(expected), std::memory_order::acquire, std::memory_order::relaxed);
234 hi_no_inline
bool exclusive_lock_contended(value_type expected)
noexcept
239 if (_is_locked(expected)) {
240 if constexpr (Wait) {
247 if (_v.compare_exchange_strong(
248 expected, increment_exclusive(expected), std::memory_order::acquire, std::memory_order::relaxed)) {
256 [[nodiscard]]
bool _shared_try_lock(value_type& expected)
noexcept
259 expected = clear_exclusive_and_waiter(_v.load(std::memory_order::relaxed));
261 return _v.compare_exchange_strong(
262 expected, increment_shared(expected), std::memory_order::acquire, std::memory_order::relaxed);
265 hi_no_inline
void shared_lock_contended(value_type expected)
noexcept
270 if (exclusive_count(expected)) {
274 if (_v.compare_exchange_strong(
275 expected, increment_shared(expected), std::memory_order::acquire, std::memory_order::relaxed)) {