carb/thread/Spinlock.h
File members: carb/thread/Spinlock.h
// Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#pragma once
#include "../Defines.h"
#include <atomic>
#include <thread>
namespace carb
{
namespace thread
{
namespace detail
{
#ifndef DOXYGEN_SHOULD_SKIP_THIS
class RecursionPolicyDisallow
{
public:
constexpr RecursionPolicyDisallow() = default;
bool ownsLock() const
{
return std::this_thread::get_id() == m_owner;
}
void enter()
{
auto cur = std::this_thread::get_id();
CARB_FATAL_UNLESS(cur != m_owner, "Recursion is not allowed");
m_owner = cur;
}
bool tryLeave()
{
CARB_FATAL_UNLESS(ownsLock(), "Not owning thread");
m_owner = std::thread::id(); // clear the owner
return true;
}
private:
std::thread::id m_owner{};
};
class RecursionPolicyAllow
{
public:
constexpr RecursionPolicyAllow() = default;
bool ownsLock() const
{
return std::this_thread::get_id() == m_owner;
}
void enter()
{
auto cur = std::this_thread::get_id();
if (cur == m_owner)
++m_recursion;
else
{
CARB_ASSERT(m_owner == std::thread::id()); // owner should be clear
m_owner = cur;
m_recursion = 1;
}
}
bool tryLeave()
{
CARB_FATAL_UNLESS(ownsLock(), "Not owning thread");
if (--m_recursion == 0)
{
m_owner = std::thread::id(); // clear the owner
return true;
}
return false;
}
private:
std::thread::id m_owner{};
size_t m_recursion{ 0 };
};
#endif
template <class RecursionPolicy>
class SpinlockImpl
{
public:
constexpr SpinlockImpl() = default;
~SpinlockImpl() = default;
CARB_PREVENT_COPY(SpinlockImpl);
void lock()
{
if (!m_rp.ownsLock())
{
// Spin trying to set the lock bit
while (CARB_UNLIKELY(!!m_lock.fetch_or(1, std::memory_order_acquire)))
{
CARB_HARDWARE_PAUSE();
}
}
m_rp.enter();
}
void unlock()
{
if (m_rp.tryLeave())
{
// Released the lock
m_lock.store(0, std::memory_order_release);
}
}
bool try_lock()
{
if (!m_rp.ownsLock())
{
// See if we can set the lock bit
if (CARB_UNLIKELY(!!m_lock.fetch_or(1, std::memory_order_acquire)))
{
// Failed!
return false;
}
}
m_rp.enter();
return true;
}
bool isLockedByThisThread() const
{
return m_rp.ownsLock();
}
private:
std::atomic<size_t> m_lock{ 0 };
RecursionPolicy m_rp;
};
} // namespace detail
using RecursiveSpinlock = detail::SpinlockImpl<detail::RecursionPolicyAllow>;
using Spinlock = detail::SpinlockImpl<detail::RecursionPolicyDisallow>;
} // namespace thread
} // namespace carb