rpcsx/rpcs3/Emu/SysCalls/lv2/sys_mutex.cpp

251 lines
5.7 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/SysCalls/SysCalls.h"
2014-08-23 16:51:51 +02:00
2014-08-26 01:55:37 +02:00
#include "Emu/CPU/CPUThreadManager.h"
2014-08-23 16:51:51 +02:00
#include "Emu/Cell/PPUThread.h"
2015-03-02 22:09:20 +01:00
#include "sleep_queue.h"
2014-12-23 00:31:11 +01:00
#include "sys_time.h"
#include "sys_mutex.h"
2014-08-23 16:51:51 +02:00
SysCallBase sys_mutex("sys_mutex");
2014-08-22 16:21:55 +02:00
Mutex::~Mutex()
{
2014-12-23 00:31:11 +01:00
if (u32 tid = owner.read_sync())
2014-08-22 16:21:55 +02:00
{
sys_mutex.Notice("Mutex(%d) was owned by thread %d (recursive=%d)", id.read_relaxed(), tid, recursive_count.load());
2014-08-22 16:21:55 +02:00
}
2014-12-24 17:09:32 +01:00
if (u32 count = queue.count())
2014-08-22 16:21:55 +02:00
{
sys_mutex.Notice("Mutex(%d) was waited by %d threads", id.read_relaxed(), count);
2014-08-22 16:21:55 +02:00
}
}
2014-12-23 00:31:11 +01:00
s32 sys_mutex_create(PPUThread& CPU, vm::ptr<u32> mutex_id, vm::ptr<sys_mutex_attribute> attr)
{
2014-09-02 03:05:13 +02:00
sys_mutex.Log("sys_mutex_create(mutex_id_addr=0x%x, attr_addr=0x%x)", mutex_id.addr(), attr.addr());
switch (attr->protocol.data())
{
case se32(SYS_SYNC_FIFO): break;
case se32(SYS_SYNC_PRIORITY): break;
case se32(SYS_SYNC_PRIORITY_INHERIT): sys_mutex.Todo("SYS_SYNC_PRIORITY_INHERIT"); break;
case se32(SYS_SYNC_RETRY): sys_mutex.Error("Invalid protocol (SYS_SYNC_RETRY)"); return CELL_EINVAL;
default: sys_mutex.Error("Unknown protocol (0x%x)", attr->protocol); return CELL_EINVAL;
}
bool is_recursive;
switch (attr->recursive.data())
{
case se32(SYS_SYNC_RECURSIVE): is_recursive = true; break;
case se32(SYS_SYNC_NOT_RECURSIVE): is_recursive = false; break;
default: sys_mutex.Error("Unknown recursive attribute (0x%x)", attr->recursive); return CELL_EINVAL;
}
if (attr->pshared.data() != se32(0x200))
{
sys_mutex.Error("Unknown pshared attribute (0x%x)", attr->pshared);
return CELL_EINVAL;
}
std::shared_ptr<Mutex> mutex(new Mutex(attr->protocol, is_recursive, attr->name_u64));
2014-12-28 14:15:22 +01:00
2014-12-23 00:31:11 +01:00
const u32 id = sys_mutex.GetNewId(mutex, TYPE_MUTEX);
mutex->id.exchange(id);
2014-09-01 02:51:48 +02:00
*mutex_id = id;
2014-12-28 14:15:22 +01:00
mutex->queue.set_full_name(fmt::Format("Mutex(%d)", id));
sys_mutex.Warning("*** mutex created [%s] (protocol=0x%x, recursive=%s): id = %d", std::string(attr->name, 8).c_str(), attr->protocol, is_recursive, id);
2014-02-16 16:23:58 +01:00
// TODO: unlock mutex when owner thread does exit
return CELL_OK;
}
2014-12-23 00:31:11 +01:00
s32 sys_mutex_destroy(PPUThread& CPU, u32 mutex_id)
{
2014-08-23 16:51:51 +02:00
sys_mutex.Warning("sys_mutex_destroy(mutex_id=%d)", mutex_id);
2014-12-24 00:38:13 +01:00
std::shared_ptr<Mutex> mutex;
if (!Emu.GetIdManager().GetIDData(mutex_id, mutex))
{
return CELL_ESRCH;
}
2014-12-28 14:15:22 +01:00
// check if associated condition variable exists
if (mutex->cond_count) // TODO: check safety
{
return CELL_EPERM;
}
if (!mutex->owner.compare_and_swap_test(0, ~0)) // check if locked and make unusable
{
return CELL_EBUSY;
}
Emu.GetIdManager().RemoveID(mutex_id);
return CELL_OK;
}
2014-12-23 00:31:11 +01:00
s32 sys_mutex_lock(PPUThread& CPU, u32 mutex_id, u64 timeout)
{
2014-08-23 16:51:51 +02:00
sys_mutex.Log("sys_mutex_lock(mutex_id=%d, timeout=%lld)", mutex_id, timeout);
const u64 start_time = get_system_time();
2014-12-24 00:38:13 +01:00
std::shared_ptr<Mutex> mutex;
if (!Emu.GetIdManager().GetIDData(mutex_id, mutex))
{
return CELL_ESRCH;
}
2014-12-23 00:31:11 +01:00
const u32 tid = CPU.GetId();
const u32 old_owner = mutex->owner.compare_and_swap(0, tid);
if (!~old_owner)
{
return CELL_ESRCH; // mutex is going to be destroyed
}
if (old_owner == tid)
{
if (mutex->is_recursive)
{
2014-12-24 00:38:13 +01:00
if (!~mutex->recursive_count)
{
return CELL_EKRESOURCE;
}
2014-12-24 00:38:13 +01:00
mutex->recursive_count++;
return CELL_OK;
}
else
{
return CELL_EDEADLK;
}
}
else if (!old_owner)
2014-12-23 00:31:11 +01:00
{
2014-12-24 00:38:13 +01:00
mutex->recursive_count = 1;
2014-12-23 00:31:11 +01:00
CPU.owned_mutexes++;
return CELL_OK;
}
mutex->queue.push(tid, mutex->protocol);
while (true)
{
2014-12-23 00:31:11 +01:00
auto old_owner = mutex->owner.compare_and_swap(0, tid);
if (!old_owner || old_owner == tid)
{
2014-12-23 00:31:11 +01:00
break;
}
2014-12-23 00:31:11 +01:00
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
if (timeout && get_system_time() - start_time > timeout)
2014-12-23 00:31:11 +01:00
{
if (!mutex->queue.invalidate(tid, mutex->protocol))
{
assert(!"sys_mutex_lock() failed (timeout)");
}
2014-12-23 00:31:11 +01:00
return CELL_ETIMEDOUT;
}
2014-12-23 00:31:11 +01:00
if (Emu.IsStopped())
{
sys_mutex.Warning("sys_mutex_lock(id=%d) aborted", mutex_id);
return CELL_OK;
}
}
2015-01-02 17:02:31 +01:00
if (!mutex->queue.invalidate(tid, mutex->protocol) && !mutex->queue.pop(tid, mutex->protocol))
{
assert(!"sys_mutex_lock() failed (locking)");
}
2014-12-24 00:38:13 +01:00
mutex->recursive_count = 1;
2014-12-23 00:31:11 +01:00
CPU.owned_mutexes++;
return CELL_OK;
}
2014-12-23 00:31:11 +01:00
s32 sys_mutex_trylock(PPUThread& CPU, u32 mutex_id)
{
2014-08-23 16:51:51 +02:00
sys_mutex.Log("sys_mutex_trylock(mutex_id=%d)", mutex_id);
2014-12-24 00:38:13 +01:00
std::shared_ptr<Mutex> mutex;
if (!Emu.GetIdManager().GetIDData(mutex_id, mutex))
{
return CELL_ESRCH;
}
2014-12-23 00:31:11 +01:00
const u32 tid = CPU.GetId();
const u32 old_owner = mutex->owner.compare_and_swap(0, tid);
if (!~old_owner)
{
return CELL_ESRCH; // mutex is going to be destroyed
}
if (old_owner == tid)
{
if (mutex->is_recursive)
{
2014-12-24 00:38:13 +01:00
if (!~mutex->recursive_count)
{
return CELL_EKRESOURCE;
}
2014-12-24 00:38:13 +01:00
mutex->recursive_count++;
return CELL_OK;
}
else
{
return CELL_EDEADLK;
}
}
else if (!old_owner)
{
mutex->recursive_count = 1;
CPU.owned_mutexes++;
return CELL_OK;
}
2014-12-23 00:31:11 +01:00
return CELL_EBUSY;
}
2014-12-23 00:31:11 +01:00
s32 sys_mutex_unlock(PPUThread& CPU, u32 mutex_id)
{
2014-08-23 16:51:51 +02:00
sys_mutex.Log("sys_mutex_unlock(mutex_id=%d)", mutex_id);
2014-12-24 00:38:13 +01:00
std::shared_ptr<Mutex> mutex;
if (!Emu.GetIdManager().GetIDData(mutex_id, mutex))
{
return CELL_ESRCH;
}
2014-12-23 00:31:11 +01:00
const u32 tid = CPU.GetId();
const u32 owner = mutex->owner.read_sync();
if (!~owner)
{
return CELL_ESRCH; // mutex is going to be destroyed
}
if (owner != tid)
{
2014-12-23 00:31:11 +01:00
return CELL_EPERM;
}
2014-12-24 00:38:13 +01:00
if (!mutex->recursive_count || (mutex->recursive_count != 1 && !mutex->is_recursive))
2014-12-23 00:31:11 +01:00
{
2014-12-24 00:38:13 +01:00
sys_mutex.Error("sys_mutex_unlock(%d): wrong recursive value fixed (%d)", mutex_id, mutex->recursive_count.load());
mutex->recursive_count = 1;
2014-12-23 00:31:11 +01:00
}
2014-12-24 00:38:13 +01:00
if (!--mutex->recursive_count)
2014-12-23 00:31:11 +01:00
{
if (!mutex->owner.compare_and_swap_test(tid, mutex->queue.signal(mutex->protocol)))
{
2014-12-23 00:31:11 +01:00
assert(!"sys_mutex_unlock() failed");
}
2014-12-23 00:31:11 +01:00
CPU.owned_mutexes--;
}
2014-12-23 00:31:11 +01:00
return CELL_OK;
}