2014-02-05 11:55:32 +00:00
|
|
|
#pragma once
|
2014-09-19 19:11:43 +00:00
|
|
|
#include "Emu/Memory/vm_atomic.h"
|
2014-02-05 11:55:32 +00:00
|
|
|
|
2014-08-22 16:36:27 +00:00
|
|
|
bool SM_IsAborted();
|
|
|
|
void SM_Sleep();
|
2014-02-05 11:55:32 +00:00
|
|
|
|
|
|
|
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-07-18 16:55:26 +00:00
|
|
|
const u64 free_value = 0,
|
|
|
|
const u64 dead_value = 0xffffffffffffffffull,
|
2014-02-23 16:52:52 +00:00
|
|
|
void (*wait)() = SM_Sleep
|
2014-02-05 11:55:32 +00:00
|
|
|
>
|
|
|
|
class SMutexBase
|
|
|
|
{
|
2014-09-19 19:11:43 +00:00
|
|
|
static_assert(sizeof(T) == sizeof(vm::atomic_le<T>), "Invalid SMutexBase type");
|
2014-09-19 00:19:22 +00:00
|
|
|
T owner;
|
2014-09-19 19:11:43 +00:00
|
|
|
typedef vm::atomic_le<T> AT;
|
2014-02-05 11:55:32 +00:00
|
|
|
|
|
|
|
public:
|
2014-07-18 16:55:26 +00:00
|
|
|
static const T GetFreeValue()
|
2014-02-05 11:55:32 +00:00
|
|
|
{
|
2014-07-18 16:55:26 +00:00
|
|
|
static const u64 value = free_value;
|
2014-09-23 14:27:18 +00:00
|
|
|
return (T&)value;
|
2014-02-05 11:55:32 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 16:55:26 +00:00
|
|
|
static const T GetDeadValue()
|
2014-02-20 02:16:17 +00:00
|
|
|
{
|
2014-07-18 16:55:26 +00:00
|
|
|
static const u64 value = dead_value;
|
2014-09-23 14:27:18 +00:00
|
|
|
return (T&)value;
|
2014-02-20 02:16:17 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 16:55:26 +00:00
|
|
|
void initialize()
|
2014-02-05 11:55:32 +00:00
|
|
|
{
|
2014-07-18 16:55:26 +00:00
|
|
|
owner = GetFreeValue();
|
2014-02-05 11:55:32 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 16:55:26 +00:00
|
|
|
void finalize()
|
2014-02-20 02:16:17 +00:00
|
|
|
{
|
2014-07-18 16:55:26 +00:00
|
|
|
owner = GetDeadValue();
|
2014-02-20 02:16:17 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 16:55:26 +00:00
|
|
|
__forceinline T GetOwner() const
|
2014-02-20 02:16:17 +00:00
|
|
|
{
|
2014-07-18 16:55:26 +00:00
|
|
|
return (T&)owner;
|
2014-02-20 02:16:17 +00:00
|
|
|
}
|
|
|
|
|
2014-02-05 11:55:32 +00:00
|
|
|
SMutexResult trylock(T tid)
|
|
|
|
{
|
2014-08-22 16:36:27 +00:00
|
|
|
if (SM_IsAborted())
|
2014-02-13 11:13:05 +00:00
|
|
|
{
|
|
|
|
return SMR_ABORT;
|
|
|
|
}
|
2014-09-19 19:11:43 +00:00
|
|
|
T old = reinterpret_cast<AT&>(owner).compare_and_swap(GetFreeValue(), tid);
|
2014-02-05 11:55:32 +00:00
|
|
|
|
2014-09-19 19:11:43 +00:00
|
|
|
if (old != GetFreeValue())
|
2014-02-05 11:55:32 +00:00
|
|
|
{
|
|
|
|
if (old == tid)
|
|
|
|
{
|
|
|
|
return SMR_DEADLOCK;
|
|
|
|
}
|
2014-07-18 16:55:26 +00:00
|
|
|
if (old == GetDeadValue())
|
2014-02-05 11:55:32 +00:00
|
|
|
{
|
|
|
|
return SMR_DESTROYED;
|
|
|
|
}
|
|
|
|
return SMR_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SMR_OK;
|
|
|
|
}
|
|
|
|
|
2014-07-18 16:55:26 +00:00
|
|
|
SMutexResult unlock(T tid, T to = GetFreeValue())
|
2014-02-05 11:55:32 +00:00
|
|
|
{
|
2014-08-22 16:36:27 +00:00
|
|
|
if (SM_IsAborted())
|
2014-02-13 11:13:05 +00:00
|
|
|
{
|
|
|
|
return SMR_ABORT;
|
|
|
|
}
|
2014-09-19 19:11:43 +00:00
|
|
|
T old = reinterpret_cast<AT&>(owner).compare_and_swap(tid, to);
|
2014-02-05 11:55:32 +00:00
|
|
|
|
2014-09-19 19:11:43 +00:00
|
|
|
if (old != tid)
|
2014-02-05 11:55:32 +00:00
|
|
|
{
|
2014-07-18 16:55:26 +00:00
|
|
|
if (old == GetFreeValue())
|
2014-02-05 11:55:32 +00:00
|
|
|
{
|
|
|
|
return SMR_FAILED;
|
|
|
|
}
|
2014-07-18 16:55:26 +00:00
|
|
|
if (old == GetDeadValue())
|
2014-02-05 11:55:32 +00:00
|
|
|
{
|
|
|
|
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 20:54:12 +00:00
|
|
|
if (wait != nullptr) wait();
|
2014-02-05 11:55:32 +00:00
|
|
|
|
|
|
|
if (timeout && counter++ > timeout)
|
|
|
|
{
|
|
|
|
return SMR_TIMEOUT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-09-19 19:11:43 +00:00
|
|
|
typedef SMutexBase<u32> SMutex;
|