xrpld
Loading...
Searching...
No Matches
spinlock.h
1// Copyright (c) 2022, Nikolaos D. Bougalis <nikb@bougalis.net>
2
3#pragma once
4
5#include <xrpl/beast/utility/instrumentation.h>
6
7#include <atomic>
8#include <limits>
9#include <type_traits>
10
11#ifndef __aarch64__
12#include <immintrin.h>
13#endif
14
15namespace xrpl {
16
17namespace detail {
29inline void
30spinPause() noexcept
31{
32#ifdef __aarch64__
33 asm volatile("yield");
34#else
35 _mm_pause();
36#endif
37}
38
39} // namespace detail
40
69
76template <class T>
78{
79 // clang-format off
80 static_assert(std::is_unsigned_v<T>);
82 static_assert(
83 std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_or(0)), T> &&
84 std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_and(0)), T>,
85 "std::atomic<T>::fetch_and(T) and std::atomic<T>::fetch_and(T) are required by packed_spinlock");
86 // clang-format on
87
88private:
90 T const mask_;
91
92public:
95 operator=(PackedSpinlock const&) = delete;
96
106 PackedSpinlock(std::atomic<T>& lock, int index) : bits_(lock), mask_(static_cast<T>(1) << index)
107 {
108 XRPL_ASSERT(
109 index >= 0 && (mask_ != 0),
110 "xrpl::PackedSpinlock::PackedSpinlock : valid index and mask");
111 }
112
113 [[nodiscard]] bool
114 try_lock() // NOLINT(readability-identifier-naming)
115 {
116 return (bits_.fetch_or(mask_, std::memory_order_acquire) & mask_) == 0;
117 }
118
119 void
121 {
122 while (!try_lock())
123 {
124 // The use of relaxed memory ordering here is intentional and
125 // serves to help reduce cache coherency traffic during times
126 // of contention by avoiding writes that would definitely not
127 // result in the lock being acquired.
128 while ((bits_.load(std::memory_order_relaxed) & mask_) != 0)
130 }
131 }
132
133 void
135 {
136 bits_.fetch_and(~mask_, std::memory_order_release);
137 }
138};
139
153template <class T>
155{
156 static_assert(std::is_unsigned_v<T>);
158
159private:
161
162public:
163 Spinlock(Spinlock const&) = delete;
164 Spinlock&
165 operator=(Spinlock const&) = delete;
166
178
179 [[nodiscard]] bool
180 try_lock() // NOLINT(readability-identifier-naming)
181 {
182 T expected = 0;
183
184 return lock_.compare_exchange_weak(
185 expected,
187 std::memory_order_acquire,
188 std::memory_order_relaxed);
189 }
190
191 void
193 {
194 while (!try_lock())
195 {
196 // The use of relaxed memory ordering here is intentional and
197 // serves to help reduce cache coherency traffic during times
198 // of contention by avoiding writes that would definitely not
199 // result in the lock being acquired.
200 while (lock_.load(std::memory_order_relaxed) != 0)
202 }
203 }
204
205 void
207 {
208 lock_.store(0, std::memory_order_release);
209 }
210};
211
212
213} // namespace xrpl
PackedSpinlock(PackedSpinlock const &)=delete
std::atomic< T > & bits_
Definition spinlock.h:89
PackedSpinlock(std::atomic< T > &lock, int index)
A single spinlock packed inside the specified atomic.
Definition spinlock.h:106
PackedSpinlock & operator=(PackedSpinlock const &)=delete
Spinlock(Spinlock const &)=delete
void unlock()
Definition spinlock.h:206
Spinlock(std::atomic< T > &lock)
Grabs the.
Definition spinlock.h:175
std::atomic< T > & lock_
Definition spinlock.h:160
Spinlock & operator=(Spinlock const &)=delete
bool try_lock()
Definition spinlock.h:180
T declval(T... args)
T is_same_v
T is_unsigned_v
T max(T... args)
void spinPause() noexcept
Inform the processor that we are in a tight spin-wait loop.
Definition spinlock.h:30
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5