mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 19:45:20 +00:00
Implement "slow mutex" without spin-waiting.
Benefits: only 1 byte. Testing atomic wait.
This commit is contained in:
parent
9efedbe76a
commit
00e64920c8
1 changed files with 89 additions and 0 deletions
89
rpcs3/util/slow_mutex.hpp
Normal file
89
rpcs3/util/slow_mutex.hpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
|
||||
#include "atomic.hpp"
|
||||
#include <mutex>
|
||||
|
||||
// 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 == umax) [[unlikely]]
|
||||
return;
|
||||
|
||||
val++;
|
||||
});
|
||||
|
||||
if (prev == umax) [[unlikely]]
|
||||
{
|
||||
// Keep trying until counter can be incremented
|
||||
m_value.wait(0xff, 0x01);
|
||||
}
|
||||
else if (prev == 0)
|
||||
{
|
||||
// Locked
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal waiting
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for 7 bits to become 0, which could only mean one thing
|
||||
m_value.wait<atomic_wait::op_ne>(0, 0xfe);
|
||||
}
|
||||
|
||||
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::raw_error("I tried to unlock unlocked mutex." HERE);
|
||||
}
|
||||
|
||||
// Normal notify with forced value (ignoring real waiter count)
|
||||
m_value.notify_one(0xfe, 0);
|
||||
|
||||
if (prev == umax) [[unlikely]]
|
||||
{
|
||||
// Overflow notify: value can be incremented
|
||||
m_value.notify_one(0x01, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_free() noexcept
|
||||
{
|
||||
return !m_value;
|
||||
}
|
||||
|
||||
void lock_unlock() noexcept
|
||||
{
|
||||
if (m_value)
|
||||
{
|
||||
lock();
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Add table
Reference in a new issue