Skip to content

Commit 92579a7

Browse files
[libc] add a simple TTAS spin lock
1 parent 0fc4e30 commit 92579a7

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

libc/src/__support/threads/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ add_header_library(
1010
sleep.h
1111
)
1212

13+
add_header_library(
14+
spin_lock
15+
HDRS
16+
spin_lock.h
17+
DEPENDS
18+
.sleep
19+
libc.src.__support.CPP.atomic
20+
)
21+
1322
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
1423
add_subdirectory(${LIBC_TARGET_OS})
1524
endif()
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//===-- TTAS Spin Lock ----------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_SPIN_LOCK_H
10+
#define LLVM_LIBC_SRC___SUPPORT_THREADS_SPIN_LOCK_H
11+
12+
#include "src/__support/CPP/atomic.h"
13+
#include "src/__support/macros/attributes.h"
14+
#include "src/__support/threads/sleep.h"
15+
namespace LIBC_NAMESPACE_DECL {
16+
class SpinLock {
17+
cpp::Atomic<bool> flag;
18+
19+
public:
20+
LIBC_INLINE constexpr SpinLock() : flag{false} {}
21+
LIBC_INLINE bool try_lock() {
22+
return !flag.exchange(true, cpp::MemoryOrder::ACQUIRE);
23+
}
24+
LIBC_INLINE void lock() {
25+
// clang-format off
26+
// this compiles to the following on armv9a and x86_64:
27+
// mov w8, #1 | .LBB0_1:
28+
// .LBB0_1: | mov al, 1
29+
// swpab w8, w9, [x0] | xchg byte ptr [rdi], al
30+
// tbnz w9, #0, .LBB0_3 | test al, 1
31+
// b .LBB0_4 | jne .LBB0_3
32+
// .LBB0_2: | jmp .LBB0_4
33+
// isb | .LBB0_2:
34+
// .LBB0_3: | pause
35+
// ldrb w9, [x0] | .LBB0_3:
36+
// tbnz w9, #0, .LBB0_2 | movzx eax, byte ptr [rdi]
37+
// b .LBB0_1 | test al, 1
38+
// .LBB0_4: | jne .LBB0_2
39+
// ret | jmp .LBB0_1
40+
// | .LBB0_4:
41+
// | ret
42+
// clang-format on
43+
// Notice that inside the busy loop .LBB0_2 and .LBB0_3, only instructions
44+
// with load semantics are used. swpab/xchg is only issued in outer loop
45+
// .LBB0_1. This is useful to avoid extra write traffic. The cache
46+
// coherence guarantees "write propagation", so even if the inner loop only
47+
// reads with relaxed ordering, the thread will evetually see the write.
48+
while (!try_lock())
49+
while (flag.load(cpp::MemoryOrder::RELAXED))
50+
sleep_briefly();
51+
}
52+
LIBC_INLINE void unlock() { flag.store(false, cpp::MemoryOrder::RELEASE); }
53+
};
54+
} // namespace LIBC_NAMESPACE_DECL
55+
56+
#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_SPIN_LOCK_H

0 commit comments

Comments
 (0)