1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-23 11:13:19 +01:00
rpcs3/Utilities/SMutex.h

178 lines
3.0 KiB
C
Raw Normal View History

2014-02-05 12:55:32 +01:00
#pragma once
#include "BEType.h"
2014-02-05 12:55:32 +01:00
extern void SM_Sleep();
2014-02-26 12:27:06 +01:00
extern size_t SM_GetCurrentThreadId();
2014-02-05 12:55:32 +01:00
extern u32 SM_GetCurrentCPUThreadId();
extern be_t<u32> SM_GetCurrentCPUThreadIdBE();
enum SMutexResult
{
SMR_OK = 0, // succeeded (lock, trylock, unlock)
SMR_FAILED, // failed (trylock, unlock)
SMR_DEADLOCK, // mutex reached deadlock (lock, trylock)
SMR_SIGNAL = SMR_DEADLOCK, // unlock can be used for signaling specific thread
SMR_PERMITTED, // not owner of the mutex (unlock)
SMR_ABORT, // emulator has been stopped (lock, trylock, unlock)
SMR_DESTROYED, // mutex has been destroyed (lock, trylock, unlock)
SMR_TIMEOUT, // timed out (lock)
};
template
<
typename T,
2014-02-26 12:27:06 +01:00
u64 free_value = 0,
u64 dead_value = 0xffffffff,
void (*wait)() = SM_Sleep
2014-02-05 12:55:32 +01:00
>
class SMutexBase
{
2014-03-02 00:50:47 +01:00
static_assert(sizeof(T) == sizeof(std::atomic<T>), "Invalid SMutexBase type");
2014-02-05 12:55:32 +01:00
std::atomic<T> owner;
public:
SMutexBase()
: owner((T)free_value)
{
}
void initialize()
{
(T&)owner = free_value;
}
2014-02-05 12:55:32 +01:00
~SMutexBase()
{
lock((T)dead_value);
owner = (T)dead_value;
}
__forceinline T GetOwner() const
{
return (T&)owner;
}
__forceinline T GetFreeValue() const
{
return (T)free_value;
}
__forceinline T GetDeadValue() const
{
return (T)dead_value;
}
2014-02-05 12:55:32 +01:00
SMutexResult trylock(T tid)
{
if (Emu.IsStopped())
{
return SMR_ABORT;
}
2014-02-05 12:55:32 +01:00
T old = (T)free_value;
if (!owner.compare_exchange_strong(old, tid))
{
if (old == tid)
{
return SMR_DEADLOCK;
}
if (old == (T)dead_value)
{
return SMR_DESTROYED;
}
return SMR_FAILED;
}
return SMR_OK;
}
SMutexResult unlock(T tid, T to = (T)free_value)
{
if (Emu.IsStopped())
{
return SMR_ABORT;
}
2014-02-05 12:55:32 +01:00
T old = tid;
if (!owner.compare_exchange_strong(old, to))
{
if (old == (T)free_value)
{
return SMR_FAILED;
}
if (old == (T)dead_value)
{
return SMR_DESTROYED;
}
return SMR_PERMITTED;
}
return SMR_OK;
}
SMutexResult lock(T tid, u64 timeout = 0)
{
u64 counter = 0;
while (true)
{
switch (SMutexResult res = trylock(tid))
{
case SMR_FAILED: break;
default: return res;
}
if (wait) wait();
2014-02-05 12:55:32 +01:00
if (timeout && counter++ > timeout)
{
return SMR_TIMEOUT;
}
}
}
};
template<typename T, typename Tid, Tid (get_tid)()>
2014-02-05 12:55:32 +01:00
class SMutexLockerBase
{
T& sm;
public:
const Tid tid;
2014-02-05 12:55:32 +01:00
__forceinline SMutexLockerBase(T& _sm)
2014-02-05 12:55:32 +01:00
: sm(_sm)
, tid(get_tid())
{
if (!tid)
{
if (!Emu.IsStopped())
{
LOG_ERROR(HLE, "SMutexLockerBase: thread id == 0");
Emu.Pause();
}
return;
}
2014-02-05 12:55:32 +01:00
sm.lock(tid);
}
__forceinline ~SMutexLockerBase()
2014-02-05 12:55:32 +01:00
{
if (tid) sm.unlock(tid);
2014-02-05 12:55:32 +01:00
}
};
2014-02-26 12:27:06 +01:00
typedef SMutexBase<size_t>
2014-02-05 12:55:32 +01:00
SMutexGeneral;
typedef SMutexBase<u32>
SMutex;
typedef SMutexBase<be_t<u32>>
SMutexBE;
typedef SMutexLockerBase<SMutexGeneral, size_t, SM_GetCurrentThreadId>
2014-02-05 12:55:32 +01:00
SMutexGeneralLocker;
typedef SMutexLockerBase<SMutex, u32, SM_GetCurrentCPUThreadId>
2014-02-05 12:55:32 +01:00
SMutexLocker;
typedef SMutexLockerBase<SMutexBE, be_t<u32>, SM_GetCurrentCPUThreadIdBE>
SMutexBELocker;