mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-10 17:01:24 +12:00
95 lines
1.5 KiB
C++
95 lines
1.5 KiB
C++
#pragma once
|
|
|
|
#include "util/types.hpp"
|
|
#include "util/atomic.hpp"
|
|
#include "Utilities/StrFmt.h"
|
|
|
|
// Pessimistic mutex for slow operation, does not spin wait, occupies only one byte
|
|
class slow_mutex
|
|
{
|
|
atomic_t<u8> m_value{0};
|
|
|
|
public:
|
|
constexpr slow_mutex() noexcept = default;
|
|
|
|
void lock() noexcept
|
|
{
|
|
// Two-stage locking: increment, then wait
|
|
while (true)
|
|
{
|
|
const u8 prev = m_value.fetch_op([](u8& val)
|
|
{
|
|
if ((val & 0x7f) == 0x7f) [[unlikely]]
|
|
return;
|
|
|
|
val++;
|
|
});
|
|
|
|
if ((prev & 0x7f) == 0x7f) [[unlikely]]
|
|
{
|
|
// Keep trying until counter can be incremented
|
|
m_value.wait(0x7f, 0x7f);
|
|
}
|
|
else if (prev == 0)
|
|
{
|
|
// Locked
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Normal waiting
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Wait for signal bit
|
|
m_value.wait(0, 0x80);
|
|
m_value &= ~0x80;
|
|
}
|
|
|
|
bool try_lock() noexcept
|
|
{
|
|
return m_value.compare_and_swap_test(0, 1);
|
|
}
|
|
|
|
void unlock() noexcept
|
|
{
|
|
const u8 prev = m_value.fetch_op([](u8& val)
|
|
{
|
|
if (val) [[likely]]
|
|
val--;
|
|
});
|
|
|
|
if (prev == 0) [[unlikely]]
|
|
{
|
|
fmt::throw_exception("I tried to unlock unlocked mutex.");
|
|
}
|
|
|
|
// Set signal and notify
|
|
if (prev & 0x7f)
|
|
{
|
|
m_value |= 0x80;
|
|
m_value.notify_one(0x80);
|
|
}
|
|
|
|
if ((prev & 0x7f) == 0x7f) [[unlikely]]
|
|
{
|
|
// Overflow notify: value can be incremented
|
|
m_value.notify_one(0x7f);
|
|
}
|
|
}
|
|
|
|
bool is_free() const noexcept
|
|
{
|
|
return !m_value;
|
|
}
|
|
|
|
void lock_unlock() noexcept
|
|
{
|
|
if (m_value)
|
|
{
|
|
lock();
|
|
unlock();
|
|
}
|
|
}
|
|
};
|