#pragma once
#include <atomic>
#include <cassert>
#include <mutex>
#include <thread>
#include "wpi/Compiler.h"
namespace wpi {
class spinlock {
std::atomic_flag lock_flag;
public:
spinlock() noexcept { lock_flag.clear(); }
LLVM_ATTRIBUTE_ALWAYS_INLINE
bool try_lock() { return !lock_flag.test_and_set(std::memory_order_acquire); }
LLVM_ATTRIBUTE_ALWAYS_INLINE
void lock() {
for (unsigned int i = 1; !try_lock(); ++i) {
if ((i & 0xff) == 0) {
std::this_thread::yield();
}
}
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
void unlock() { lock_flag.clear(std::memory_order_release); }
};
class recursive_spinlock1 {
std::atomic<std::thread::id> owner_thread_id{std::thread::id{}};
int32_t recursive_counter{0};
std::atomic_flag lock_flag;
public:
recursive_spinlock1() noexcept { lock_flag.clear(); }
LLVM_ATTRIBUTE_ALWAYS_INLINE
bool try_lock() {
if (!lock_flag.test_and_set(std::memory_order_acquire)) {
owner_thread_id.store(std::this_thread::get_id(),
std::memory_order_release);
} else {
if (owner_thread_id.load(std::memory_order_acquire) !=
std::this_thread::get_id()) {
return false;
}
}
++recursive_counter;
return true;
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
void lock() {
for (unsigned int i = 1; !try_lock(); ++i) {
if ((i & 0xffff) == 0) {
std::this_thread::yield();
}
}
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
void unlock() {
assert(owner_thread_id.load(std::memory_order_acquire) ==
std::this_thread::get_id());
assert(recursive_counter > 0);
if (--recursive_counter == 0) {
owner_thread_id.store(std::thread::id{}, std::memory_order_release);
lock_flag.clear(std::memory_order_release);
}
}
};
class recursive_spinlock2 {
std::atomic<std::thread::id> owner_thread_id{std::thread::id{}};
int32_t recursive_counter{0};
public:
LLVM_ATTRIBUTE_ALWAYS_INLINE
bool try_lock() {
auto owner = std::thread::id{};
auto us = std::this_thread::get_id();
if (!owner_thread_id.compare_exchange_weak(owner, us,
std::memory_order_acquire)) {
if (owner != us) {
return false;
}
}
++recursive_counter;
return true;
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
void lock() {
for (unsigned int i = 1; !try_lock(); ++i) {
if ((i & 0xffff) == 0) {
std::this_thread::yield();
}
}
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
void unlock() {
assert(owner_thread_id.load(std::memory_order_acquire) ==
std::this_thread::get_id());
assert(recursive_counter > 0);
if (--recursive_counter == 0) {
owner_thread_id.store(std::thread::id{}, std::memory_order_release);
}
}
};
#ifdef __arm__
using recursive_spinlock = recursive_spinlock2;
#else
using recursive_spinlock = recursive_spinlock1;
#endif
}