VLink 2.0.0
A high-performance communication middleware
载入中...
搜索中...
未找到
spin_lock.h
浏览该文件的文档.
1/*
2 * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 * Author: Thun Lu <thun.lu@zohomail.cn>
4 * Repo: https://github.com/thun-res/vlink
5 * _ __ __ _ __
6 * | | / / / / (_) ____ / /__
7 * | | / / / / / / / __ \ / //_/
8 * | |/ / / /___ / / / / / / / ,<
9 * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23
24/**
25 * @file spin_lock.h
26 * @brief Adaptive, cache-line-aligned spin lock and RAII guard.
27 *
28 * @details
29 * @c SpinLock implements a simple user-space mutex that spins instead of
30 * sleeping. It is intended for very short critical sections (a few CPU
31 * instructions) where the overhead of a @c std::mutex context switch would
32 * dominate. It is used inside @c CpuProfiler and other hot-path VLink internals.
33 *
34 * Locking strategy inside @c lock():
35 * -# Try to acquire with @c exchange(true, acquire); if successful, return.
36 * -# Spin with @c load(relaxed) until the flag appears free, then retry.
37 * -# Apply exponential back-off: after every @c backoff spins, call
38 * @c Utils::yield_cpu() (a CPU-pause instruction).
39 * -# If @c kMaxSpinCount (50000) is exceeded, sleep for 10 us and log an
40 * error. This is a safety valve for pathological contention.
41 *
42 * Cache-line alignment (@c alignas(64)) prevents false sharing when multiple
43 * @c SpinLock objects reside in adjacent memory.
44 *
45 * @note
46 * - @b Do not use this lock for long critical sections or I/O -- use a
47 * @c std::mutex instead.
48 * - @c SpinLock is not recursive; a thread that calls @c lock() twice will
49 * deadlock.
50 * - The lock is not @c VLINK_EXPORT because it is header-only and entirely
51 * inlined.
52 *
53 * @par Example
54 * @code
55 * vlink::SpinLock lock;
56 *
57 * // Manual lock/unlock:
58 * lock.lock();
59 * critical_section();
60 * lock.unlock();
61 *
62 * // RAII guard (preferred):
63 * {
64 * vlink::SpinLockGuard guard(lock);
65 * critical_section();
66 * } // automatically unlocked here
67 * @endcode
68 */
69
70#pragma once
71
72#include <algorithm>
73#include <atomic>
74#include <cstdint>
75#include <thread>
76
77#include "./logger.h"
78#include "./macros.h"
79#include "./utils.h"
80
81#ifdef _MSC_VER
82extern "C" void _mm_pause(void);
83#endif
84
85namespace vlink {
86
87/**
88 * @class SpinLock
89 * @brief Adaptive, cache-line-aligned spin lock.
90 *
91 * @details
92 * Implements the @c Lockable named requirement: @c lock(), @c try_lock(),
93 * and @c unlock(). Can be used directly with @c std::lock_guard.
94 */
95class SpinLock final {
96 public:
97 /**
98 * @brief Default constructor. Initialises the flag to @c false (unlocked).
99 */
100 SpinLock() noexcept = default;
101
102 /**
103 * @brief Destructor.
104 */
105 ~SpinLock() noexcept = default;
106
107 /**
108 * @brief Acquires the lock, spinning until successful.
109 *
110 * @details
111 * Uses an exponential back-off strategy to reduce bus contention:
112 * - Spins with @c load(relaxed) for up to @c backoff iterations.
113 * - Calls @c Utils::yield_cpu() (PAUSE/WFE/yield) when @c backoff is reached.
114 * - Back-off doubles each round up to 1024.
115 * - After 50000 total spins, logs an error and sleeps 10 us.
116 *
117 * @warning
118 * This function must not be called recursively from the same thread; doing so
119 * will cause an infinite deadlock.
120 */
121 void lock() noexcept;
122
123 /**
124 * @brief Attempts to acquire the lock without blocking.
125 *
126 * @details
127 * Performs a single @c exchange(true, acquire). Returns immediately
128 * regardless of whether the lock was acquired.
129 *
130 * @return @c true if the lock was successfully acquired, @c false if it
131 * was already held by another thread.
132 */
133 bool try_lock() noexcept;
134
135 /**
136 * @brief Releases the lock.
137 *
138 * @details
139 * Stores @c false with @c release memory order. Must only be called by
140 * the thread that successfully called @c lock() or @c try_lock().
141 */
142 void unlock() noexcept;
143
144 private:
145 static constexpr size_t kInterferenceSize = 64U;
146
147 alignas(kInterferenceSize) std::atomic<bool> flag_{false};
148
150};
151
152/**
153 * @class SpinLockGuard
154 * @brief RAII guard that acquires a @c SpinLock on construction and releases it
155 * on destruction.
156 *
157 * @details
158 * Analogous to @c std::lock_guard<SpinLock>. Preferred over manual
159 * @c lock() / @c unlock() because it is exception-safe.
160 *
161 * @par Example
162 * @code
163 * SpinLock my_lock;
164 * {
165 * SpinLockGuard guard(my_lock);
166 * // critical section
167 * }
168 * @endcode
169 */
170class SpinLockGuard final {
171 public:
172 /**
173 * @brief Acquires @p lock immediately.
174 *
175 * @param lock The @c SpinLock to acquire. Must outlive this guard.
176 */
177 explicit SpinLockGuard(SpinLock& lock) noexcept;
178
179 /**
180 * @brief Releases the lock held by this guard.
181 */
182 ~SpinLockGuard() noexcept;
183
184 private:
185 SpinLock& lock_;
186
188};
189
190////////////////////////////////////////////////////////////////
191/// Details
192////////////////////////////////////////////////////////////////
193
194inline void SpinLock::lock() noexcept {
195 constexpr static uint32_t kMaxSpinCount = 50000U;
196
197 uint32_t total_spin = 0;
198 uint16_t spin_count = 0;
199 uint16_t backoff = 1;
200
201 // LCOV_EXCL_START
202 // GCOVR_EXCL_START
203
204 for (;;) {
205 if (!flag_.exchange(true, std::memory_order_acquire)) {
206 return;
207 }
208
209 while (flag_.load(std::memory_order_relaxed)) {
210 ++total_spin;
211
212 if (++spin_count >= backoff) {
213 if VUNLIKELY (total_spin > kMaxSpinCount) {
214 VLOG_E("SpinLock: exceeded max spin count.");
215 std::this_thread::sleep_for(std::chrono::microseconds(10));
216 } else {
218 }
219
220 backoff = std::min<uint16_t>(backoff * 2U, 1024U);
221 spin_count = 0;
222 }
223 }
224 }
225
226 // GCOVR_EXCL_STOP
227 // LCOV_EXCL_STOP
228}
229
230inline bool SpinLock::try_lock() noexcept {
231 if (flag_.load(std::memory_order_relaxed)) {
232 return false;
233 }
234
235 return !flag_.exchange(true, std::memory_order_acquire);
236}
237
238inline void SpinLock::unlock() noexcept { flag_.store(false, std::memory_order_release); }
239
240inline SpinLockGuard::SpinLockGuard(SpinLock& lock) noexcept : lock_(lock) { lock_.lock(); }
241
242inline SpinLockGuard::~SpinLockGuard() noexcept { lock_.unlock(); }
243
244} // namespace vlink
Global singleton logger with three output styles and pluggable backends.
#define VLOG_E(...)
定义 logger.h:854
Platform-independent macro definitions for the VLink library.
#define VUNLIKELY(...)
Shorthand alias for VLINK_UNLIKELY. Hints that the expression is unlikely true.
定义 macros.h:302
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
定义 macros.h:184
STL namespace
Platform-agnostic system utilities for process, thread, network and signal management.