xref: /llvm-project/libc/src/__support/threads/spin_lock.h (revision db6b7a84e6e4949569e756f46357d9f54ad16a03)
1408a351dSSchrodinger ZHU Yifan //===-- TTAS Spin Lock ----------------------------------------------------===//
2408a351dSSchrodinger ZHU Yifan //
3408a351dSSchrodinger ZHU Yifan // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4408a351dSSchrodinger ZHU Yifan // See https://llvm.org/LICENSE.txt for license information.
5408a351dSSchrodinger ZHU Yifan // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6408a351dSSchrodinger ZHU Yifan //
7408a351dSSchrodinger ZHU Yifan //===----------------------------------------------------------------------===//
8408a351dSSchrodinger ZHU Yifan 
9408a351dSSchrodinger ZHU Yifan #ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_SPIN_LOCK_H
10408a351dSSchrodinger ZHU Yifan #define LLVM_LIBC_SRC___SUPPORT_THREADS_SPIN_LOCK_H
11408a351dSSchrodinger ZHU Yifan 
12408a351dSSchrodinger ZHU Yifan #include "src/__support/CPP/atomic.h"
13408a351dSSchrodinger ZHU Yifan #include "src/__support/macros/attributes.h"
14408a351dSSchrodinger ZHU Yifan #include "src/__support/threads/sleep.h"
15408a351dSSchrodinger ZHU Yifan 
16408a351dSSchrodinger ZHU Yifan namespace LIBC_NAMESPACE_DECL {
17408a351dSSchrodinger ZHU Yifan 
18*03841e7aSSchrodinger ZHU Yifan class SpinLock {
19*03841e7aSSchrodinger ZHU Yifan   cpp::Atomic<unsigned char> flag;
20408a351dSSchrodinger ZHU Yifan 
21408a351dSSchrodinger ZHU Yifan public:
22*03841e7aSSchrodinger ZHU Yifan   LIBC_INLINE constexpr SpinLock() : flag{0} {}
23408a351dSSchrodinger ZHU Yifan   LIBC_INLINE bool try_lock() {
24*03841e7aSSchrodinger ZHU Yifan     return !flag.exchange(1u, cpp::MemoryOrder::ACQUIRE);
25408a351dSSchrodinger ZHU Yifan   }
26408a351dSSchrodinger ZHU Yifan   LIBC_INLINE void lock() {
27408a351dSSchrodinger ZHU Yifan     // clang-format off
28408a351dSSchrodinger ZHU Yifan     // For normal TTAS, this compiles to the following on armv9a and x86_64:
29408a351dSSchrodinger ZHU Yifan     //         mov     w8, #1            |          .LBB0_1:
30408a351dSSchrodinger ZHU Yifan     // .LBB0_1:                          |                  mov     al, 1
31408a351dSSchrodinger ZHU Yifan     //         swpab   w8, w9, [x0]      |                  xchg    byte ptr [rdi], al
32408a351dSSchrodinger ZHU Yifan     //         tbnz    w9, #0, .LBB0_3   |                  test    al, 1
33408a351dSSchrodinger ZHU Yifan     //         b       .LBB0_4           |                  jne     .LBB0_3
34408a351dSSchrodinger ZHU Yifan     // .LBB0_2:                          |                  jmp     .LBB0_4
35408a351dSSchrodinger ZHU Yifan     //         isb                       |         .LBB0_2:
36408a351dSSchrodinger ZHU Yifan     // .LBB0_3:                          |                  pause
37408a351dSSchrodinger ZHU Yifan     //         ldrb    w9, [x0]          |         .LBB0_3:
38408a351dSSchrodinger ZHU Yifan     //         tbnz    w9, #0, .LBB0_2   |                  movzx   eax, byte ptr [rdi]
39408a351dSSchrodinger ZHU Yifan     //         b       .LBB0_1           |                  test    al, 1
40408a351dSSchrodinger ZHU Yifan     // .LBB0_4:                          |                  jne     .LBB0_2
41408a351dSSchrodinger ZHU Yifan     //         ret                       |                  jmp     .LBB0_1
42408a351dSSchrodinger ZHU Yifan     //                                   |          .LBB0_4:
43408a351dSSchrodinger ZHU Yifan     //                                   |                  ret
44408a351dSSchrodinger ZHU Yifan     // clang-format on
45408a351dSSchrodinger ZHU Yifan     // Notice that inside the busy loop .LBB0_2 and .LBB0_3, only instructions
46408a351dSSchrodinger ZHU Yifan     // with load semantics are used. swpab/xchg is only issued in outer loop
47408a351dSSchrodinger ZHU Yifan     // .LBB0_1. This is useful to avoid extra write traffic. The cache
48408a351dSSchrodinger ZHU Yifan     // coherence guarantees "write propagation", so even if the inner loop only
49408a351dSSchrodinger ZHU Yifan     // reads with relaxed ordering, the thread will evetually see the write.
50408a351dSSchrodinger ZHU Yifan     while (!try_lock())
51408a351dSSchrodinger ZHU Yifan       while (flag.load(cpp::MemoryOrder::RELAXED))
52408a351dSSchrodinger ZHU Yifan         sleep_briefly();
53408a351dSSchrodinger ZHU Yifan   }
54*03841e7aSSchrodinger ZHU Yifan   LIBC_INLINE void unlock() { flag.store(0u, cpp::MemoryOrder::RELEASE); }
55*03841e7aSSchrodinger ZHU Yifan   LIBC_INLINE bool is_locked() { return flag.load(cpp::MemoryOrder::ACQUIRE); }
56*03841e7aSSchrodinger ZHU Yifan   LIBC_INLINE bool is_invalid() {
57*03841e7aSSchrodinger ZHU Yifan     return flag.load(cpp::MemoryOrder::ACQUIRE) > 1;
58408a351dSSchrodinger ZHU Yifan   }
59*03841e7aSSchrodinger ZHU Yifan   // poison the lock
60*03841e7aSSchrodinger ZHU Yifan   LIBC_INLINE ~SpinLock() { flag.store(0xffu, cpp::MemoryOrder::RELEASE); }
61408a351dSSchrodinger ZHU Yifan };
62408a351dSSchrodinger ZHU Yifan 
63408a351dSSchrodinger ZHU Yifan } // namespace LIBC_NAMESPACE_DECL
64408a351dSSchrodinger ZHU Yifan 
65408a351dSSchrodinger ZHU Yifan #endif // LLVM_LIBC_SRC___SUPPORT_THREADS_SPIN_LOCK_H
66