WPILibC++ 2024.3.2
spinlock.h
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#pragma once
6
7#include <atomic>
8#include <cassert>
9#include <mutex>
10#include <thread>
11
12#include "wpi/Compiler.h"
13
14namespace wpi {
15
16/**
17 * A spinlock mutex. Wraps std::atomic_flag in a std::mutex compatible way.
18 */
19class spinlock {
20 std::atomic_flag lock_flag;
21
22 public:
23 spinlock() noexcept { lock_flag.clear(); }
24
26 bool try_lock() { return !lock_flag.test_and_set(std::memory_order_acquire); }
27
29 void lock() {
30 for (unsigned int i = 1; !try_lock(); ++i) {
31 if ((i & 0xff) == 0) {
32 std::this_thread::yield();
33 }
34 }
35 }
36
38 void unlock() { lock_flag.clear(std::memory_order_release); }
39};
40
41/**
42 * A recursive spinlock mutex. This version uses std::atomic_flag for spin,
43 * then checks the thread id for recursion. It is generally faster on desktop
44 * platforms compared to recursive_spinlock2.
45 */
47 std::atomic<std::thread::id> owner_thread_id{std::thread::id{}};
48 int32_t recursive_counter{0};
49 std::atomic_flag lock_flag;
50
51 public:
52 recursive_spinlock1() noexcept { lock_flag.clear(); }
53
55 bool try_lock() {
56 if (!lock_flag.test_and_set(std::memory_order_acquire)) {
57 owner_thread_id.store(std::this_thread::get_id(),
58 std::memory_order_release);
59 } else {
60 if (owner_thread_id.load(std::memory_order_acquire) !=
61 std::this_thread::get_id()) {
62 return false;
63 }
64 }
65 ++recursive_counter;
66 return true;
67 }
68
70 void lock() {
71 for (unsigned int i = 1; !try_lock(); ++i) {
72 if ((i & 0xffff) == 0) {
73 std::this_thread::yield();
74 }
75 }
76 }
77
79 void unlock() {
80 assert(owner_thread_id.load(std::memory_order_acquire) ==
81 std::this_thread::get_id());
82 assert(recursive_counter > 0);
83
84 if (--recursive_counter == 0) {
85 owner_thread_id.store(std::thread::id{}, std::memory_order_release);
86 lock_flag.clear(std::memory_order_release);
87 }
88 }
89};
90
91/**
92 * A recursive spinlock mutex. This version spins directly on the std::atomic
93 * of the thread id. It is generally faster on embedded ARM platforms such
94 * as the RoboRIO and Raspberry Pi, compared to recursive_spinlock1.
95 */
97 std::atomic<std::thread::id> owner_thread_id{std::thread::id{}};
98 int32_t recursive_counter{0};
99
100 public:
102 bool try_lock() {
103 auto owner = std::thread::id{};
104 auto us = std::this_thread::get_id();
105 if (!owner_thread_id.compare_exchange_weak(owner, us,
106 std::memory_order_acquire)) {
107 if (owner != us) {
108 return false;
109 }
110 }
111 ++recursive_counter;
112 return true;
113 }
114
116 void lock() {
117 for (unsigned int i = 1; !try_lock(); ++i) {
118 if ((i & 0xffff) == 0) {
119 std::this_thread::yield();
120 }
121 }
122 }
123
125 void unlock() {
126 assert(owner_thread_id.load(std::memory_order_acquire) ==
127 std::this_thread::get_id());
128 assert(recursive_counter > 0);
129
130 if (--recursive_counter == 0) {
131 owner_thread_id.store(std::thread::id{}, std::memory_order_release);
132 }
133 }
134};
135
136#ifdef __arm__
137// benchmarking has shown this version to be faster on ARM, but slower on
138// windows, mac, and linux
139using recursive_spinlock = recursive_spinlock2;
140#else
141using recursive_spinlock = recursive_spinlock1;
142#endif
143
144} // namespace wpi
#define LLVM_ATTRIBUTE_ALWAYS_INLINE
LLVM_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do so, mark a method "always...
Definition: Compiler.h:258
A recursive spinlock mutex.
Definition: spinlock.h:46
LLVM_ATTRIBUTE_ALWAYS_INLINE void unlock()
Definition: spinlock.h:79
recursive_spinlock1() noexcept
Definition: spinlock.h:52
LLVM_ATTRIBUTE_ALWAYS_INLINE void lock()
Definition: spinlock.h:70
LLVM_ATTRIBUTE_ALWAYS_INLINE bool try_lock()
Definition: spinlock.h:55
A recursive spinlock mutex.
Definition: spinlock.h:96
LLVM_ATTRIBUTE_ALWAYS_INLINE bool try_lock()
Definition: spinlock.h:102
LLVM_ATTRIBUTE_ALWAYS_INLINE void lock()
Definition: spinlock.h:116
LLVM_ATTRIBUTE_ALWAYS_INLINE void unlock()
Definition: spinlock.h:125
A spinlock mutex.
Definition: spinlock.h:19
LLVM_ATTRIBUTE_ALWAYS_INLINE void lock()
Definition: spinlock.h:29
spinlock() noexcept
Definition: spinlock.h:23
LLVM_ATTRIBUTE_ALWAYS_INLINE bool try_lock()
Definition: spinlock.h:26
LLVM_ATTRIBUTE_ALWAYS_INLINE void unlock()
Definition: spinlock.h:38
Definition: ntcore_cpp.h:26
recursive_spinlock1 recursive_spinlock
Definition: spinlock.h:141