rpcs3/Utilities/SharedMutex.h

181 lines
3.4 KiB
C
Raw Normal View History

#pragma once
#include "types.h"
2016-02-01 21:55:43 +00:00
#include "Atomic.h"
#include "Platform.h"
2016-02-01 21:55:43 +00:00
2015-11-30 15:10:17 +00:00
//! An attempt to create effective implementation of "shared mutex", lock-free in optimistic case.
//! All locking and unlocking may be done by a single LOCK XADD or LOCK CMPXCHG instruction.
2015-11-30 15:10:17 +00:00
//! MSVC implementation of std::shared_timed_mutex seems suboptimal.
//! std::shared_mutex is not available until C++17.
class shared_mutex final
{
enum : u32
{
2016-02-01 21:55:43 +00:00
SM_WRITER_LOCK = 1u << 31, // Exclusive lock flag, must be MSB
SM_WAITERS_BIT = 1u << 30, // Flag set if m_wq_size or m_rq_size is non-zero
SM_INVALID_BIT = 1u << 29, // Unreachable reader count bit (may be set by incorrect unlock_shared() call)
2015-11-30 15:10:17 +00:00
2016-02-01 21:55:43 +00:00
SM_READER_MASK = SM_WAITERS_BIT - 1, // Valid reader count bit mask
SM_READER_MAX = 1u << 24, // Max reader count
};
atomic_t<u32> m_ctrl{}; // Control variable: reader count | SM_* flags
2015-11-30 15:10:17 +00:00
struct internal;
2016-02-01 21:55:43 +00:00
atomic_t<internal*> m_data{}; // Internal data
2016-02-01 21:55:43 +00:00
void lock_shared_hard();
void unlock_shared_notify();
2016-02-01 21:55:43 +00:00
void lock_hard();
void unlock_notify();
2016-02-01 21:55:43 +00:00
2016-05-13 14:01:48 +00:00
void lock_upgrade_hard();
void lock_degrade_hard();
public:
constexpr shared_mutex() = default;
2016-02-01 21:55:43 +00:00
// Initialize internal data
void initialize_once();
2016-02-01 21:55:43 +00:00
~shared_mutex();
bool try_lock_shared()
2015-11-30 15:10:17 +00:00
{
2016-05-13 14:01:48 +00:00
const u32 ctrl = m_ctrl.load();
2016-02-01 21:55:43 +00:00
2016-05-13 14:01:48 +00:00
return ctrl < SM_READER_MAX && m_ctrl.compare_and_swap_test(ctrl, ctrl + 1);
2015-11-30 15:10:17 +00:00
}
void lock_shared()
{
// Optimization: unconditional increment, compensated later
if (UNLIKELY(m_ctrl++ >= SM_READER_MAX))
2015-11-30 15:10:17 +00:00
{
2016-02-01 21:55:43 +00:00
lock_shared_hard();
2015-11-30 15:10:17 +00:00
}
}
2015-11-30 15:10:17 +00:00
void unlock_shared()
{
if (UNLIKELY(m_ctrl-- >= SM_READER_MAX))
2015-11-30 15:10:17 +00:00
{
2016-02-01 21:55:43 +00:00
unlock_shared_notify();
2015-11-30 15:10:17 +00:00
}
}
2016-02-01 21:55:43 +00:00
bool try_lock()
2015-11-30 15:10:17 +00:00
{
2016-05-13 14:01:48 +00:00
return !m_ctrl && m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK);
2015-11-30 15:10:17 +00:00
}
2016-02-01 21:55:43 +00:00
void lock()
2015-11-30 15:10:17 +00:00
{
2016-05-13 14:01:48 +00:00
if (UNLIKELY(!m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK)))
{
lock_hard();
}
2015-11-30 15:10:17 +00:00
}
2015-11-30 15:10:17 +00:00
void unlock()
{
2016-05-13 14:01:48 +00:00
m_ctrl &= ~SM_WRITER_LOCK;
if (UNLIKELY(m_ctrl))
2015-11-30 15:10:17 +00:00
{
2016-02-01 21:55:43 +00:00
unlock_notify();
2015-11-30 15:10:17 +00:00
}
}
2016-05-13 14:01:48 +00:00
bool try_lock_upgrade()
{
return m_ctrl == 1 && m_ctrl.compare_and_swap_test(1, SM_WRITER_LOCK);
}
bool try_lock_degrade()
{
return m_ctrl == SM_WRITER_LOCK && m_ctrl.compare_and_swap_test(SM_WRITER_LOCK, 1);
}
void lock_upgrade()
{
if (UNLIKELY(!m_ctrl.compare_and_swap_test(1, SM_WRITER_LOCK)))
{
lock_upgrade_hard();
}
}
void lock_degrade()
{
if (UNLIKELY(!m_ctrl.compare_and_swap_test(SM_WRITER_LOCK, 1)))
{
lock_degrade_hard();
}
}
2015-11-30 15:10:17 +00:00
};
//! Simplified shared (reader) lock implementation.
2015-11-30 15:10:17 +00:00
//! std::shared_lock may be used instead if necessary.
class reader_lock final
{
shared_mutex& m_mutex;
public:
reader_lock(const reader_lock&) = delete;
reader_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
m_mutex.lock_shared();
}
~reader_lock()
{
m_mutex.unlock_shared();
}
};
//! Simplified exclusive (writer) lock implementation.
//! std::lock_guard may or std::unique_lock be used instead if necessary.
class writer_lock final
{
shared_mutex& m_mutex;
public:
writer_lock(const writer_lock&) = delete;
writer_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
m_mutex.lock();
}
~writer_lock()
{
m_mutex.unlock();
}
};
2016-05-13 14:01:48 +00:00
// Exclusive (writer) lock in the scope of shared (reader) lock.
class upgraded_lock final
{
shared_mutex& m_mutex;
public:
upgraded_lock(const writer_lock&) = delete;
upgraded_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
m_mutex.lock_upgrade();
}
~upgraded_lock()
{
m_mutex.lock_degrade();
}
};