rpcsx/rpcs3/util/slow_mutex.hpp
Nekotekina 8b6d615aa6 atomic.hpp: remove/deprecate incomplete notify features
Notification with "phantom value" could be useful in theory.
As an alternative way of sending signal from the notifier.
But current implementation is just useless.
Also fixed slow_mutex (not used anywhere).
2020-11-26 06:31:36 +03:00

95 lines
1.4 KiB
C++

#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 & 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::raw_error("I tried to unlock unlocked mutex." HERE);
}
// 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() noexcept
{
return !m_value;
}
void lock_unlock() noexcept
{
if (m_value)
{
lock();
unlock();
}
}
};