SimpleKernel 1.17.0
Loading...
Searching...
No Matches
spinlock_test.cpp
Go to the documentation of this file.
1
5#include "spinlock.hpp"
6
7#include <etl/format.h>
8
9#include <atomic>
10#include <cstdint>
11
12#include "basic_info.hpp"
13#include "cpu_io.h"
14#include "system_test.h"
15
16namespace {
17// 测试辅助类:暴露 protected 成员用于测试验证
18class TestSpinLock : public SpinLock {
19 public:
22};
23
24auto test_basic_lock() -> bool {
25 klog::Info("Running test_basic_lock...");
26 TestSpinLock lock("basic");
27 EXPECT_TRUE(lock.Lock(), "Basic lock failed");
28 EXPECT_TRUE(lock.IsLockedByCurrentCore(),
29 "IsLockedByCurrentCore failed after lock");
30 EXPECT_TRUE(lock.UnLock(), "Basic unlock failed");
31 EXPECT_TRUE(!lock.IsLockedByCurrentCore(),
32 "IsLockedByCurrentCore failed after unlock");
33 klog::Info("test_basic_lock passed");
34 return true;
35}
36
37auto test_recursive_lock() -> bool {
38 klog::Info("Running test_recursive_lock...");
39 TestSpinLock lock("recursive");
40 EXPECT_TRUE(lock.Lock(), "Lock failed in recursive test");
41 // Lock() 如果已经被当前核心锁定则返回 false
42 if (lock.Lock()) {
43 klog::Err("FAIL: Recursive lock should return false");
44 (void)lock.UnLock(); // 尝试恢复
45 (void)lock.UnLock();
46 return false;
47 }
48
49 EXPECT_TRUE(lock.UnLock(), "Unlock failed in recursive test");
50 // 再次解锁应该失败
51 if (lock.UnLock()) {
52 klog::Err("FAIL: Double unlock should return false");
53 return false;
54 }
55 klog::Info("test_recursive_lock passed");
56 return true;
57}
58
59auto test_lock_guard() -> bool {
60 klog::Info("Running test_lock_guard...");
61 TestSpinLock lock("guard");
62 {
63 LockGuard<TestSpinLock> guard(lock);
64 EXPECT_TRUE(lock.IsLockedByCurrentCore(), "LockGuard failed to lock");
65 }
66 EXPECT_TRUE(!lock.IsLockedByCurrentCore(), "LockGuard failed to unlock");
67 klog::Info("test_lock_guard passed");
68 return true;
69}
70
71auto test_interrupt_restore() -> bool {
72 klog::Info("Running test_interrupt_restore...");
73 TestSpinLock lock("intr");
74
75 // Case 1: Interrupts enabled
78 klog::Err("FAIL: Failed to enable interrupts");
79 return false;
80 }
81
82 (void)lock.Lock();
84 klog::Err("FAIL: Lock didn't disable interrupts");
85 (void)lock.UnLock();
86 return false;
87 }
88 (void)lock.UnLock();
89
91 klog::Err("FAIL: Unlock didn't restore interrupts (expected enabled)");
92 return false;
93 }
94
95 // Case 2: Interrupts disabled
97 // Ensure disabled
99 klog::Err("FAIL: Failed to disable interrupts for test");
100 return false;
101 }
102
103 (void)lock.Lock();
105 klog::Err("FAIL: Lock enabled interrupts unexpectedly");
106 (void)lock.UnLock();
108 return false;
109 }
110 (void)lock.UnLock();
111
113 klog::Err("FAIL: Unlock enabled interrupts (expected disabled)");
115 return false;
116 }
117
118 cpu_io::EnableInterrupt(); // Cleanup
119 klog::Info("test_interrupt_restore passed");
120 return true;
121}
122
123SpinLock smp_lock("smp_lock");
124int shared_counter = 0;
125std::atomic<int> finished_cores = 0;
126
127auto spinlock_smp_test() -> bool {
128 for (int i = 0; i < 10000; ++i) {
129 (void)smp_lock.Lock();
130 shared_counter++;
131 (void)smp_lock.UnLock();
132 }
133
134 int finished = finished_cores.fetch_add(1) + 1;
135 int total_cores = BasicInfoSingleton::instance().core_count;
136
137 if (finished == total_cores) {
138 bool passed = (shared_counter == total_cores * 10000);
139 if (passed) {
140 klog::Info(" All cores finished. shared_counter = {}. OK.",
141 shared_counter);
142 } else {
143 klog::Err(" All cores finished. shared_counter = {}. EXPECTED {}. FAIL.",
144 shared_counter, total_cores * 10000);
145 }
146 return passed;
147 }
148
149 return true;
150}
151
152constexpr int BUFFER_SIZE = 8192;
153int shared_buffer[BUFFER_SIZE];
154int buffer_index = 0;
155SpinLock buffer_lock("buffer_lock");
156std::atomic<int> buffer_test_finished_cores = 0;
157
158auto spinlock_smp_buffer_test() -> bool {
159 // 每个核心尝试写入一定次数
160 int writes_per_core = 500;
161
162 for (int i = 0; i < writes_per_core; ++i) {
163 (void)buffer_lock.Lock();
164 if (buffer_index < BUFFER_SIZE) {
165 // 写入 Core ID
166 shared_buffer[buffer_index++] = cpu_io::GetCurrentCoreId();
167 }
168 (void)buffer_lock.UnLock();
169 }
170
171 int finished = buffer_test_finished_cores.fetch_add(1) + 1;
172 int total_cores = BasicInfoSingleton::instance().core_count;
173
174 if (finished == total_cores) {
175 // 最后一个完成的核心进行检查
176 klog::Info("All cores finished buffer writes. Checking buffer...");
177
178 // 检查写入总数
179 int expected_writes = writes_per_core * total_cores;
180 if (expected_writes > BUFFER_SIZE) expected_writes = BUFFER_SIZE;
181
182 if (buffer_index != expected_writes) {
183 klog::Err("FAIL: Buffer index {}, expected {}", buffer_index,
184 expected_writes);
185 return false;
186 }
187
188 klog::Info("Buffer test passed. Final index: {}", buffer_index);
189 return true;
190 }
191
192 return true;
193}
194
195constexpr int STR_BUFFER_SIZE = 512 * 1024;
196// 使用更大的缓冲区以容纳所有字符串
197char shared_str_buffer[STR_BUFFER_SIZE];
198int str_buffer_offset = 0;
199// 单独的锁用于此测试
200SpinLock str_lock("str_lock");
201std::atomic<int> str_test_finished_cores = 0;
202std::atomic<int> str_test_start_barrier = 0;
203
204auto spinlock_smp_string_test() -> bool {
206 size_t core_count = BasicInfoSingleton::instance().core_count;
207
208 if (core_count < 2) {
209 if (core_id == 0) {
210 klog::Info("Skipping SMP string test: need more than 1 core.");
211 }
212 return true;
213 }
214
215 int arrived = str_test_start_barrier.fetch_add(1) + 1;
216
217 constexpr int kBarrierSpinLimit = 100000000;
218 int spins = 0;
219 while (str_test_start_barrier.load() < (int)core_count) {
220 if (++spins > kBarrierSpinLimit) {
221 if (core_id == 0) {
222 klog::Err(
223 "SMP string test barrier timeout: {}/{} cores arrived, skipping",
224 str_test_start_barrier.load(), (int)core_count);
225 }
226 return true;
227 }
228 }
229
230 int writes_per_core = 500;
231 char local_buf[128];
232
233 for (int i = 0; i < writes_per_core; ++i) {
234 // 2. 先写入可区分的字符串到本地缓冲区
235 // 增加数据长度以增加临界区持续时间
236 auto* end = etl::format_to_n(local_buf, sizeof(local_buf) - 1,
237 "[C:{}-{}|LongStringPaddingForContention]",
238 (int)core_id, i);
239 *end = '\0';
240 int len = static_cast<int>(end - local_buf);
241
242 (void)str_lock.Lock();
243 if (str_buffer_offset + len < STR_BUFFER_SIZE - 1) {
244 for (int k = 0; k < len; ++k) {
245 shared_str_buffer[str_buffer_offset + k] = local_buf[k];
246 }
247 str_buffer_offset += len;
248 shared_str_buffer[str_buffer_offset] = '\0';
249 }
250 (void)str_lock.UnLock();
251 }
252
253 int finished = str_test_finished_cores.fetch_add(1) + 1;
254 if (finished == (int)core_count) {
255 // 3. 验证
257 "All cores finished string writes. Verifying string integrity...");
258 bool failed = false;
259 int current_idx = 0;
260 int tokens_found = 0;
261
262 while (current_idx < str_buffer_offset) {
263 if (shared_str_buffer[current_idx] != '[') {
264 failed = true;
265 klog::Err("FAIL: Expected '[' at {}, got '{}'", current_idx,
266 shared_str_buffer[current_idx]);
267 break;
268 }
269
270 // 应该找到匹配的 ']'
271 int end_idx = current_idx + 1;
272 bool closed = false;
273 while (end_idx < str_buffer_offset) {
274 if (shared_str_buffer[end_idx] == ']') {
275 closed = true;
276 break;
277 }
278 if (shared_str_buffer[end_idx] == '[') {
279 // 在结束前遇到另一个开始 -> 数据损坏/交错
280 break;
281 }
282 end_idx++;
283 }
284
285 if (!closed) {
286 failed = true;
287 klog::Err("FAIL: Broken token starting at {}", current_idx);
288 break;
289 }
290
291 // 验证内容格式 C:ID-Seq
292 if (shared_str_buffer[current_idx + 1] != 'C' ||
293 shared_str_buffer[current_idx + 2] != ':') {
294 failed = true;
295 klog::Err("FAIL: Invalid content in token at {}", current_idx);
296 break;
297 }
298
299 // 验证填充完整性
300 const char* padding = "|LongStringPaddingForContention";
301 int padding_len = 31; // "|LongStringPaddingForContention" 的长度
302 int token_content_len = end_idx - current_idx - 1;
303 bool padding_ok = true;
304
305 if (token_content_len < padding_len) {
306 padding_ok = false;
307 } else {
308 int padding_start = end_idx - padding_len;
309 for (int p = 0; p < padding_len; ++p) {
310 if (shared_str_buffer[padding_start + p] != padding[p]) {
311 padding_ok = false;
312 break;
313 }
314 }
315 }
316
317 if (!padding_ok) {
318 failed = true;
319 klog::Err("FAIL: Broken padding in token at {}. Content len: {}",
320 current_idx, token_content_len);
321 break;
322 }
323
324 tokens_found++;
325 current_idx = end_idx + 1;
326 }
327
328 int expected_tokens = writes_per_core * core_count;
329 // 如果缓冲区太小可能会耗尽,但通常我们应该期望完全匹配。
330
331 if (tokens_found != expected_tokens) {
332 failed = true;
333 klog::Err("FAIL: Expected {} tokens, found {}", expected_tokens,
334 tokens_found);
335 }
336
337 if (!failed) {
338 klog::Info("String test passed. Length: {}, Tokens: {}",
339 str_buffer_offset, tokens_found);
340 }
341 return !failed;
342 }
343 return true;
344}
345
346} // namespace
347
348auto spinlock_test() -> bool {
349 bool ret = true;
351
352 // 单元测试仅在核心 0 上运行,以避免日志混乱
353 if (core_id == 0) {
354 klog::Info("Starting spinlock_test");
355 ret = ret && test_basic_lock();
356 ret = ret && test_recursive_lock();
357 ret = ret && test_lock_guard();
358 ret = ret && test_interrupt_restore();
359 }
360
361 // SMP (多核) 测试在所有核心上运行
362 // 使用顺序执行以确保如果前一个测试失败,屏障不会死锁
363 if (!spinlock_smp_test()) ret = false;
364 if (!spinlock_smp_buffer_test()) ret = false;
365 if (!spinlock_smp_string_test()) ret = false;
366
367 if (core_id == 0) {
368 if (ret) {
369 klog::Info("spinlock_test passed");
370 } else {
371 klog::Err("spinlock_test failed");
372 }
373 }
374 return ret;
375}
void * end[]
内核结束
RAII 风格的锁守卫模板类
Definition spinlock.hpp:131
自旋锁
Definition spinlock.hpp:27
__always_inline auto IsLockedByCurrentCore() -> bool
检查当前 core 是否获得此锁
Definition spinlock.hpp:115
SpinLock()=default
auto GetCurrentCoreId() -> size_t
Definition cpu_io.h:26
void DisableInterrupt()
Definition cpu_io.h:41
bool GetInterruptStatus()
Definition cpu_io.h:48
void EnableInterrupt()
Definition cpu_io.h:34
auto Err(etl::format_string< Args... > fmt, Args &&... args) -> void
以 ERROR 级别记录日志
auto Info(etl::format_string< Args... > fmt, Args &&... args) -> void
以 INFO 级别记录日志
size_t core_id
核心 ID
Definition per_cpu.hpp:1
auto spinlock_test() -> bool
#define EXPECT_TRUE(cond, msg)