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

132 lines
2.2 KiB
C
Raw Normal View History

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