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

211 lines
5.1 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/SysCalls/SysCalls.h"
2014-12-22 01:56:04 +01:00
#include "Emu/Memory/atomic_type.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"
2014-12-23 00:31:11 +01:00
#include "sleep_queue_type.h"
2014-12-22 01:56:04 +01:00
#include "sys_time.h"
#include "sys_lwmutex.h"
2014-08-23 16:51:51 +02:00
SysCallBase sys_lwmutex("sys_lwmutex");
2014-09-19 02:19:22 +02:00
s32 lwmutex_create(sys_lwmutex_t& lwmutex, u32 protocol, u32 recursive, u64 name_u64)
{
LV2_LOCK(0);
2014-12-23 00:31:11 +01:00
lwmutex.owner.write_relaxed(be_t<u32>::make(0));
2014-12-22 01:56:04 +01:00
lwmutex.waiter.write_relaxed(be_t<u32>::make(~0));
2014-09-19 02:19:22 +02:00
lwmutex.attribute = protocol | recursive;
lwmutex.recursive_count = 0;
2014-12-23 00:31:11 +01:00
u32 sq_id = sys_lwmutex.GetNewId(new sleep_queue_t(name_u64), TYPE_LWMUTEX);
2014-09-19 02:19:22 +02:00
lwmutex.sleep_queue = sq_id;
std::string name((const char*)&name_u64, 8);
sys_lwmutex.Notice("*** lwmutex created [%s] (attribute=0x%x): sq_id = %d", name.c_str(), protocol | recursive, sq_id);
2014-12-23 00:31:11 +01:00
Emu.GetSyncPrimManager().AddLwMutexData(sq_id, name, 0);
2014-09-19 02:19:22 +02:00
return CELL_OK;
}
2014-12-23 00:31:11 +01:00
s32 sys_lwmutex_create(PPUThread& CPU, vm::ptr<sys_lwmutex_t> lwmutex, vm::ptr<sys_lwmutex_attribute_t> attr)
{
2014-09-19 02:19:22 +02:00
sys_lwmutex.Warning("sys_lwmutex_create(lwmutex_addr=0x%x, attr_addr=0x%x)", lwmutex.addr(), attr.addr());
2014-09-19 02:19:22 +02:00
switch (attr->recursive.ToBE())
{
case se32(SYS_SYNC_RECURSIVE): break;
case se32(SYS_SYNC_NOT_RECURSIVE): break;
2014-09-19 02:19:22 +02:00
default: sys_lwmutex.Error("Unknown recursive attribute(0x%x)", (u32)attr->recursive); return CELL_EINVAL;
}
2014-09-19 02:19:22 +02:00
switch (attr->protocol.ToBE())
2014-01-19 22:19:37 +01:00
{
case se32(SYS_SYNC_PRIORITY): break;
case se32(SYS_SYNC_RETRY): break;
2014-08-23 16:51:51 +02:00
case se32(SYS_SYNC_PRIORITY_INHERIT): sys_lwmutex.Error("Invalid SYS_SYNC_PRIORITY_INHERIT protocol attr"); return CELL_EINVAL;
case se32(SYS_SYNC_FIFO): break;
2014-09-19 02:19:22 +02:00
default: sys_lwmutex.Error("Unknown protocol attribute(0x%x)", (u32)attr->protocol); return CELL_EINVAL;
2014-01-19 22:19:37 +01:00
}
2014-09-19 02:19:22 +02:00
return lwmutex_create(*lwmutex, attr->protocol, attr->recursive, attr->name_u64);
}
2014-12-23 00:31:11 +01:00
s32 sys_lwmutex_destroy(PPUThread& CPU, vm::ptr<sys_lwmutex_t> lwmutex)
{
2014-09-02 03:05:13 +02:00
sys_lwmutex.Warning("sys_lwmutex_destroy(lwmutex_addr=0x%x)", lwmutex.addr());
2014-09-20 02:08:12 +02:00
LV2_LOCK(0);
u32 sq_id = lwmutex->sleep_queue;
if (!Emu.GetIdManager().CheckID(sq_id)) return CELL_ESRCH;
2014-02-05 12:55:32 +01:00
// try to make it unable to lock
2014-12-22 01:56:04 +01:00
switch (int res = lwmutex->trylock(be_t<u32>::make(~0)))
{
case CELL_OK:
2014-02-26 12:27:06 +01:00
lwmutex->all_info() = 0;
2014-03-06 12:40:50 +01:00
lwmutex->attribute = 0xDEADBEEF;
lwmutex->sleep_queue = 0;
Emu.GetIdManager().RemoveID(sq_id);
2014-09-13 22:40:12 +02:00
Emu.GetSyncPrimManager().EraseLwMutexData(sq_id);
default: return res;
}
}
2014-12-23 00:31:11 +01:00
s32 sys_lwmutex_lock(PPUThread& CPU, vm::ptr<sys_lwmutex_t> lwmutex, u64 timeout)
{
2014-09-02 03:05:13 +02:00
sys_lwmutex.Log("sys_lwmutex_lock(lwmutex_addr=0x%x, timeout=%lld)", lwmutex.addr(), timeout);
2014-01-19 22:19:37 +01:00
2014-12-23 00:31:11 +01:00
return lwmutex->lock(be_t<u32>::make(CPU.GetId()), timeout);
}
2014-12-23 00:31:11 +01:00
s32 sys_lwmutex_trylock(PPUThread& CPU, vm::ptr<sys_lwmutex_t> lwmutex)
{
2014-09-02 03:05:13 +02:00
sys_lwmutex.Log("sys_lwmutex_trylock(lwmutex_addr=0x%x)", lwmutex.addr());
2014-12-23 00:31:11 +01:00
return lwmutex->trylock(be_t<u32>::make(CPU.GetId()));
}
2014-12-23 00:31:11 +01:00
s32 sys_lwmutex_unlock(PPUThread& CPU, vm::ptr<sys_lwmutex_t> lwmutex)
{
2014-09-02 03:05:13 +02:00
sys_lwmutex.Log("sys_lwmutex_unlock(lwmutex_addr=0x%x)", lwmutex.addr());
2014-01-19 22:19:37 +01:00
2014-12-23 00:31:11 +01:00
return lwmutex->unlock(be_t<u32>::make(CPU.GetId()));
}
2014-12-22 01:56:04 +01:00
s32 sys_lwmutex_t::trylock(be_t<u32> tid)
{
2014-03-06 12:40:50 +01:00
if (attribute.ToBE() == se32(0xDEADBEEF)) return CELL_EINVAL;
2014-12-23 00:31:11 +01:00
const be_t<u32> old_owner = owner.read_sync();
2014-12-23 00:31:11 +01:00
if (old_owner == tid)
{
if (attribute.ToBE() & se32(SYS_SYNC_RECURSIVE))
{
recursive_count += 1;
2014-12-23 00:31:11 +01:00
if (!recursive_count.ToBE())
{
return CELL_EKRESOURCE;
}
return CELL_OK;
}
else
{
return CELL_EDEADLK;
}
}
2014-12-23 00:31:11 +01:00
if (!owner.compare_and_swap_test(be_t<u32>::make(0), tid))
{
2014-12-22 01:56:04 +01:00
return CELL_EBUSY;
}
2014-12-22 01:56:04 +01:00
recursive_count = 1;
return CELL_OK;
}
2014-12-22 01:56:04 +01:00
s32 sys_lwmutex_t::unlock(be_t<u32> tid)
{
2014-12-23 00:31:11 +01:00
if (owner.read_sync() != tid)
{
return CELL_EPERM;
}
2014-12-23 00:31:11 +01:00
if (!recursive_count || (recursive_count.ToBE() != se32(1) && (attribute.ToBE() & se32(SYS_SYNC_NOT_RECURSIVE))))
{
2014-12-23 00:31:11 +01:00
sys_lwmutex.Error("sys_lwmutex_t::unlock(%d): wrong recursive value fixed (%d)", (u32)sleep_queue, (u32)recursive_count);
recursive_count = 1;
}
recursive_count -= 1;
if (!recursive_count.ToBE())
{
sleep_queue_t* sq;
if (!Emu.GetIdManager().GetIDData(sleep_queue, sq))
{
2014-12-23 00:31:11 +01:00
return CELL_ESRCH;
}
2014-12-22 01:56:04 +01:00
2014-12-23 00:31:11 +01:00
if (!owner.compare_and_swap_test(tid, be_t<u32>::make(sq->pop(attribute))))
{
2014-12-23 00:31:11 +01:00
assert(!"sys_lwmutex_t::unlock() failed");
}
}
2014-12-23 00:31:11 +01:00
return CELL_OK;
}
2014-12-22 01:56:04 +01:00
s32 sys_lwmutex_t::lock(be_t<u32> tid, u64 timeout)
{
2014-12-22 01:56:04 +01:00
switch (s32 res = trylock(tid))
{
2014-12-22 01:56:04 +01:00
case static_cast<s32>(CELL_EBUSY): break;
default: return res;
}
2014-12-23 00:31:11 +01:00
sleep_queue_t* sq;
2014-12-22 01:56:04 +01:00
if (!Emu.GetIdManager().GetIDData(sleep_queue, sq))
{
return CELL_ESRCH;
}
2014-12-23 00:31:11 +01:00
sq->push(tid, attribute);
2014-12-22 01:56:04 +01:00
const u64 time_start = get_system_time();
while (true)
{
2014-12-23 00:31:11 +01:00
auto old_owner = owner.compare_and_swap(be_t<u32>::make(0), tid);
2014-12-22 01:56:04 +01:00
if (!old_owner.ToBE())
{
sq->invalidate(tid);
break;
}
if (old_owner == tid)
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
if (timeout && get_system_time() - time_start > timeout)
{
sq->invalidate(tid);
return CELL_ETIMEDOUT;
}
if (Emu.IsStopped())
{
sys_lwmutex.Warning("sys_lwmutex_t::lock(sq=%d) aborted", (u32)sleep_queue);
return CELL_OK;
}
}
2014-12-22 01:56:04 +01:00
recursive_count = 1;
return CELL_OK;
}