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 {
28inline void
29spinPause() noexcept
30{
31#ifdef __aarch64__
32 asm volatile("yield");
33#else
34 _mm_pause();
35#endif
36}
37
38} // namespace detail
39
67
73template <class T>
75{
76 // clang-format off
77 static_assert(std::is_unsigned_v<T>);
79 static_assert(
80 std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_or(0)), T> &&
81 std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_and(0)), T>,
82 "std::atomic<T>::fetch_and(T) and std::atomic<T>::fetch_and(T) are required by packed_spinlock");
83 // clang-format on
84
85private:
87 T const mask_;
88
89public:
92 operator=(PackedSpinlock const&) = delete;
93
102 PackedSpinlock(std::atomic<T>& lock, int index) : bits_(lock), mask_(static_cast<T>(1) << index)
103 {
104 XRPL_ASSERT(
105 index >= 0 && (mask_ != 0),
106 "xrpl::PackedSpinlock::PackedSpinlock : valid index and mask");
107 }
108
109 [[nodiscard]] bool
110 try_lock() // NOLINT(readability-identifier-naming)
111 {
112 return (bits_.fetch_or(mask_, std::memory_order_acquire) & mask_) == 0;
113 }
114
115 void
117 {
118 while (!try_lock())
119 {
120 // The use of relaxed memory ordering here is intentional and
121 // serves to help reduce cache coherency traffic during times
122 // of contention by avoiding writes that would definitely not
123 // result in the lock being acquired.
124 while ((bits_.load(std::memory_order_relaxed) & mask_) != 0)
126 }
127 }
128
129 void
131 {
132 bits_.fetch_and(~mask_, std::memory_order_release);
133 }
134};
135
148template <class T>
150{
151 static_assert(std::is_unsigned_v<T>);
153
154private:
156
157public:
158 Spinlock(Spinlock const&) = delete;
159 Spinlock&
160 operator=(Spinlock const&) = delete;
161
172
173 [[nodiscard]] bool
174 try_lock() // NOLINT(readability-identifier-naming)
175 {
176 T expected = 0;
177
178 return lock_.compare_exchange_weak(
179 expected,
181 std::memory_order_acquire,
182 std::memory_order_relaxed);
183 }
184
185 void
187 {
188 while (!try_lock())
189 {
190 // The use of relaxed memory ordering here is intentional and
191 // serves to help reduce cache coherency traffic during times
192 // of contention by avoiding writes that would definitely not
193 // result in the lock being acquired.
194 while (lock_.load(std::memory_order_relaxed) != 0)
196 }
197 }
198
199 void
201 {
202 lock_.store(0, std::memory_order_release);
203 }
204};
205
206
207} // namespace xrpl
PackedSpinlock(PackedSpinlock const &)=delete
std::atomic< T > & bits_
Definition spinlock.h:86
PackedSpinlock(std::atomic< T > &lock, int index)
A single spinlock packed inside the specified atomic.
Definition spinlock.h:102
PackedSpinlock & operator=(PackedSpinlock const &)=delete
Spinlock(Spinlock const &)=delete
void unlock()
Definition spinlock.h:200
Spinlock(std::atomic< T > &lock)
Grabs the.
Definition spinlock.h:169
std::atomic< T > & lock_
Definition spinlock.h:155
Spinlock & operator=(Spinlock const &)=delete
bool try_lock()
Definition spinlock.h:174
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:29
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5