WPILibC++  2019.2.1-24-g74f7ba0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
spinlock.h
1 /*----------------------------------------------------------------------------*/
2 /* Copyright (c) 2018 FIRST. All Rights Reserved. */
3 /* Open Source Software - may be modified and shared by FRC teams. The code */
4 /* must be accompanied by the FIRST BSD license file in the root directory of */
5 /* the project. */
6 /*----------------------------------------------------------------------------*/
7 
8 #pragma once
9 
10 #include <atomic>
11 #include <cassert>
12 #include <mutex>
13 #include <thread>
14 
15 #include "Compiler.h"
16 
17 namespace wpi {
18 
22 class spinlock {
23  std::atomic_flag lock_flag;
24 
25  public:
26  spinlock() noexcept { lock_flag.clear(); }
27 
28  LLVM_ATTRIBUTE_ALWAYS_INLINE
29  bool try_lock() { return !lock_flag.test_and_set(std::memory_order_acquire); }
30 
31  LLVM_ATTRIBUTE_ALWAYS_INLINE
32  void lock() {
33  for (unsigned int i = 1; !try_lock(); ++i)
34  if ((i & 0xff) == 0) std::this_thread::yield();
35  }
36 
37  LLVM_ATTRIBUTE_ALWAYS_INLINE
38  void unlock() { lock_flag.clear(std::memory_order_release); }
39 };
40 
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 
54  LLVM_ATTRIBUTE_ALWAYS_INLINE
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  ++recursive_counter;
65  return true;
66  }
67 
68  LLVM_ATTRIBUTE_ALWAYS_INLINE
69  void lock() {
70  for (unsigned int i = 1; !try_lock(); ++i)
71  if ((i & 0xffff) == 0) std::this_thread::yield();
72  }
73 
74  LLVM_ATTRIBUTE_ALWAYS_INLINE
75  void unlock() {
76  assert(owner_thread_id.load(std::memory_order_acquire) ==
77  std::this_thread::get_id());
78  assert(recursive_counter > 0);
79 
80  if (--recursive_counter == 0) {
81  owner_thread_id.store(std::thread::id{}, std::memory_order_release);
82  lock_flag.clear(std::memory_order_release);
83  }
84  }
85 };
86 
93  std::atomic<std::thread::id> owner_thread_id{std::thread::id{}};
94  int32_t recursive_counter{0};
95 
96  public:
97  LLVM_ATTRIBUTE_ALWAYS_INLINE
98  bool try_lock() {
99  auto owner = std::thread::id{};
100  auto us = std::this_thread::get_id();
101  if (!owner_thread_id.compare_exchange_weak(owner, us,
102  std::memory_order_acquire)) {
103  if (owner != us) return false;
104  }
105  ++recursive_counter;
106  return true;
107  }
108 
109  LLVM_ATTRIBUTE_ALWAYS_INLINE
110  void lock() {
111  for (unsigned int i = 1; !try_lock(); ++i)
112  if ((i & 0xffff) == 0) std::this_thread::yield();
113  }
114 
115  LLVM_ATTRIBUTE_ALWAYS_INLINE
116  void unlock() {
117  assert(owner_thread_id.load(std::memory_order_acquire) ==
118  std::this_thread::get_id());
119  assert(recursive_counter > 0);
120 
121  if (--recursive_counter == 0)
122  owner_thread_id.store(std::thread::id{}, std::memory_order_release);
123  }
124 };
125 
126 #ifdef __arm__
127 // benchmarking has shown this version to be faster on ARM, but slower on
128 // windows, mac, and linux
130 #else
132 #endif
133 
134 } // namespace wpi
A spinlock mutex.
Definition: spinlock.h:22
A recursive spinlock mutex.
Definition: spinlock.h:46
WPILib C++ utilities (wpiutil) namespace.
Definition: SmallString.h:21
A recursive spinlock mutex.
Definition: spinlock.h:92