2014-02-05 12:55:32 +01:00
|
|
|
#pragma once
|
2014-06-02 19:27:24 +02:00
|
|
|
#include "BEType.h"
|
2014-07-10 22:54:12 +02:00
|
|
|
#include "Emu/System.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,
|
2014-03-31 12:04:34 +02:00
|
|
|
u64 dead_value = 0xffffffff,
|
2014-02-23 17:52:52 +01:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-02-20 03:16:17 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-02-20 03:16:17 +01:00
|
|
|
__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)
|
|
|
|
{
|
2014-02-13 12:13:05 +01:00
|
|
|
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)
|
|
|
|
{
|
2014-02-13 12:13:05 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-07-10 22:54:12 +02:00
|
|
|
if (wait != nullptr) wait();
|
2014-02-05 12:55:32 +01:00
|
|
|
|
|
|
|
if (timeout && counter++ > timeout)
|
|
|
|
{
|
|
|
|
return SMR_TIMEOUT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-06-20 13:00:36 +02:00
|
|
|
template<typename T, typename Tid, Tid (get_tid)()>
|
2014-02-05 12:55:32 +01:00
|
|
|
class SMutexLockerBase
|
|
|
|
{
|
2014-06-20 13:00:36 +02:00
|
|
|
T& sm;
|
2014-02-06 23:55:48 +01:00
|
|
|
public:
|
2014-06-20 13:00:36 +02:00
|
|
|
const Tid tid;
|
2014-02-05 12:55:32 +01:00
|
|
|
|
2014-06-20 13:00:36 +02:00
|
|
|
__forceinline SMutexLockerBase(T& _sm)
|
2014-02-05 12:55:32 +01:00
|
|
|
: sm(_sm)
|
|
|
|
, tid(get_tid())
|
|
|
|
{
|
2014-02-09 12:11:48 +01:00
|
|
|
if (!tid)
|
|
|
|
{
|
2014-03-04 20:18:17 +01:00
|
|
|
if (!Emu.IsStopped())
|
|
|
|
{
|
2014-06-17 17:44:03 +02:00
|
|
|
LOG_ERROR(HLE, "SMutexLockerBase: thread id == 0");
|
2014-03-04 20:18:17 +01:00
|
|
|
Emu.Pause();
|
|
|
|
}
|
|
|
|
return;
|
2014-02-09 12:11:48 +01:00
|
|
|
}
|
2014-02-05 12:55:32 +01:00
|
|
|
sm.lock(tid);
|
|
|
|
}
|
|
|
|
|
2014-03-31 12:04:34 +02:00
|
|
|
__forceinline ~SMutexLockerBase()
|
2014-02-05 12:55:32 +01:00
|
|
|
{
|
2014-03-04 20:18:17 +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;
|
|
|
|
|
2014-06-20 13:00:36 +02:00
|
|
|
typedef SMutexLockerBase<SMutexGeneral, size_t, SM_GetCurrentThreadId>
|
2014-02-05 12:55:32 +01:00
|
|
|
SMutexGeneralLocker;
|
2014-06-20 13:00:36 +02:00
|
|
|
typedef SMutexLockerBase<SMutex, u32, SM_GetCurrentCPUThreadId>
|
2014-02-05 12:55:32 +01:00
|
|
|
SMutexLocker;
|
2014-06-20 13:00:36 +02:00
|
|
|
typedef SMutexLockerBase<SMutexBE, be_t<u32>, SM_GetCurrentCPUThreadIdBE>
|
2014-02-23 17:52:52 +01:00
|
|
|
SMutexBELocker;
|