mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-13 02:08:49 +12:00
270 lines
5.5 KiB
C++
270 lines
5.5 KiB
C++
#include "stdafx.h"
|
|
#include "Emu/Memory/Memory.h"
|
|
#include "Emu/System.h"
|
|
#include "Emu/SysCalls/SysCalls.h"
|
|
#include "Emu/Memory/atomic_type.h"
|
|
|
|
#include "Emu/Cell/PPUThread.h"
|
|
#include "sleep_queue_type.h"
|
|
#include "sys_time.h"
|
|
#include "sys_rwlock.h"
|
|
|
|
SysCallBase sys_rwlock("sys_rwlock");
|
|
|
|
s32 sys_rwlock_create(vm::ptr<u32> rw_lock_id, vm::ptr<sys_rwlock_attribute_t> attr)
|
|
{
|
|
sys_rwlock.Warning("sys_rwlock_create(rw_lock_id_addr=0x%x, attr_addr=0x%x)", rw_lock_id.addr(), attr.addr());
|
|
|
|
switch (attr->protocol.ToBE())
|
|
{
|
|
case se32(SYS_SYNC_PRIORITY): break;
|
|
case se32(SYS_SYNC_RETRY): sys_rwlock.Error("SYS_SYNC_RETRY"); return CELL_EINVAL;
|
|
case se32(SYS_SYNC_PRIORITY_INHERIT): sys_rwlock.Todo("SYS_SYNC_PRIORITY_INHERIT"); break;
|
|
case se32(SYS_SYNC_FIFO): break;
|
|
default: return CELL_EINVAL;
|
|
}
|
|
|
|
if (attr->pshared.ToBE() != se32(0x200))
|
|
{
|
|
sys_rwlock.Error("Invalid pshared value (0x%x)", (u32)attr->pshared);
|
|
return CELL_EINVAL;
|
|
}
|
|
|
|
std::shared_ptr<RWLock> rw(new RWLock((u32)attr->protocol, attr->name_u64));
|
|
const u32 id = sys_rwlock.GetNewId(rw, TYPE_RWLOCK);
|
|
*rw_lock_id = id;
|
|
rw->wqueue.set_full_name(fmt::Format("Rwlock(%d)", id));
|
|
|
|
sys_rwlock.Warning("*** rwlock created [%s] (protocol=0x%x): id = %d", std::string(attr->name, 8).c_str(), rw->protocol, id);
|
|
return CELL_OK;
|
|
}
|
|
|
|
s32 sys_rwlock_destroy(u32 rw_lock_id)
|
|
{
|
|
sys_rwlock.Warning("sys_rwlock_destroy(rw_lock_id=%d)", rw_lock_id);
|
|
|
|
std::shared_ptr<RWLock> rw;
|
|
if (!sys_rwlock.CheckId(rw_lock_id, rw))
|
|
{
|
|
return CELL_ESRCH;
|
|
}
|
|
|
|
if (!rw->sync.compare_and_swap_test({ 0, 0 }, { ~0u, ~0u })) // check if locked and make unusable
|
|
{
|
|
return CELL_EBUSY;
|
|
}
|
|
|
|
Emu.GetIdManager().RemoveID(rw_lock_id);
|
|
return CELL_OK;
|
|
}
|
|
|
|
s32 sys_rwlock_rlock(u32 rw_lock_id, u64 timeout)
|
|
{
|
|
sys_rwlock.Log("sys_rwlock_rlock(rw_lock_id=%d, timeout=%lld)", rw_lock_id, timeout);
|
|
|
|
const u64 start_time = get_system_time();
|
|
|
|
std::shared_ptr<RWLock> rw;
|
|
if (!sys_rwlock.CheckId(rw_lock_id, rw))
|
|
{
|
|
return CELL_ESRCH;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
bool succeeded;
|
|
rw->sync.atomic_op_sync([&succeeded](RWLock::sync_var_t& sync)
|
|
{
|
|
assert(~sync.readers);
|
|
if ((succeeded = !sync.writer))
|
|
{
|
|
sync.readers++;
|
|
}
|
|
});
|
|
|
|
if (succeeded)
|
|
{
|
|
break;
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
|
|
|
|
if (timeout && get_system_time() - start_time > timeout)
|
|
{
|
|
return CELL_ETIMEDOUT;
|
|
}
|
|
|
|
if (Emu.IsStopped())
|
|
{
|
|
sys_rwlock.Warning("sys_rwlock_rlock(id=%d) aborted", rw_lock_id);
|
|
return CELL_OK;
|
|
}
|
|
}
|
|
|
|
return CELL_OK;
|
|
}
|
|
|
|
s32 sys_rwlock_tryrlock(u32 rw_lock_id)
|
|
{
|
|
sys_rwlock.Log("sys_rwlock_tryrlock(rw_lock_id=%d)", rw_lock_id);
|
|
|
|
std::shared_ptr<RWLock> rw;
|
|
if (!sys_rwlock.CheckId(rw_lock_id, rw))
|
|
{
|
|
return CELL_ESRCH;
|
|
}
|
|
|
|
bool succeeded;
|
|
rw->sync.atomic_op_sync([&succeeded](RWLock::sync_var_t& sync)
|
|
{
|
|
assert(~sync.readers);
|
|
if ((succeeded = !sync.writer))
|
|
{
|
|
sync.readers++;
|
|
}
|
|
});
|
|
|
|
if (succeeded)
|
|
{
|
|
return CELL_OK;
|
|
}
|
|
|
|
return CELL_EBUSY;
|
|
}
|
|
|
|
s32 sys_rwlock_runlock(u32 rw_lock_id)
|
|
{
|
|
sys_rwlock.Log("sys_rwlock_runlock(rw_lock_id=%d)", rw_lock_id);
|
|
|
|
std::shared_ptr<RWLock> rw;
|
|
if (!sys_rwlock.CheckId(rw_lock_id, rw))
|
|
{
|
|
return CELL_ESRCH;
|
|
}
|
|
|
|
bool succeeded;
|
|
rw->sync.atomic_op_sync([&succeeded](RWLock::sync_var_t& sync)
|
|
{
|
|
if ((succeeded = sync.readers != 0))
|
|
{
|
|
assert(!sync.writer);
|
|
sync.readers--;
|
|
}
|
|
});
|
|
|
|
if (succeeded)
|
|
{
|
|
return CELL_OK;
|
|
}
|
|
|
|
return CELL_EPERM;
|
|
}
|
|
|
|
s32 sys_rwlock_wlock(PPUThread& CPU, u32 rw_lock_id, u64 timeout)
|
|
{
|
|
sys_rwlock.Log("sys_rwlock_wlock(rw_lock_id=%d, timeout=%lld)", rw_lock_id, timeout);
|
|
|
|
const u64 start_time = get_system_time();
|
|
|
|
std::shared_ptr<RWLock> rw;
|
|
if (!sys_rwlock.CheckId(rw_lock_id, rw))
|
|
{
|
|
return CELL_ESRCH;
|
|
}
|
|
|
|
const u32 tid = CPU.GetId();
|
|
|
|
if (rw->sync.compare_and_swap_test({ 0, 0 }, { 0, tid }))
|
|
{
|
|
return CELL_OK;
|
|
}
|
|
|
|
if (rw->sync.read_relaxed().writer == tid)
|
|
{
|
|
return CELL_EDEADLK;
|
|
}
|
|
|
|
rw->wqueue.push(tid, rw->protocol);
|
|
|
|
while (true)
|
|
{
|
|
auto old_sync = rw->sync.compare_and_swap({ 0, 0 }, { 0, tid });
|
|
if (!old_sync.readers && (!old_sync.writer || old_sync.writer == tid))
|
|
{
|
|
break;
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
|
|
|
|
if (timeout && get_system_time() - start_time > timeout)
|
|
{
|
|
if (!rw->wqueue.invalidate(tid, rw->protocol))
|
|
{
|
|
assert(!"sys_rwlock_wlock() failed (timeout)");
|
|
}
|
|
return CELL_ETIMEDOUT;
|
|
}
|
|
|
|
if (Emu.IsStopped())
|
|
{
|
|
sys_rwlock.Warning("sys_rwlock_wlock(id=%d) aborted", rw_lock_id);
|
|
return CELL_OK;
|
|
}
|
|
}
|
|
|
|
if (!rw->wqueue.invalidate(tid, rw->protocol) && !rw->wqueue.pop(tid, rw->protocol))
|
|
{
|
|
assert(!"sys_rwlock_wlock() failed (locking)");
|
|
}
|
|
return CELL_OK;
|
|
}
|
|
|
|
s32 sys_rwlock_trywlock(PPUThread& CPU, u32 rw_lock_id)
|
|
{
|
|
sys_rwlock.Log("sys_rwlock_trywlock(rw_lock_id=%d)", rw_lock_id);
|
|
|
|
std::shared_ptr<RWLock> rw;
|
|
if (!sys_rwlock.CheckId(rw_lock_id, rw))
|
|
{
|
|
return CELL_ESRCH;
|
|
}
|
|
|
|
const u32 tid = CPU.GetId();
|
|
|
|
if (rw->sync.compare_and_swap_test({ 0, 0 }, { 0, tid }))
|
|
{
|
|
return CELL_OK;
|
|
}
|
|
|
|
if (rw->sync.read_relaxed().writer == tid)
|
|
{
|
|
return CELL_EDEADLK;
|
|
}
|
|
|
|
return CELL_EBUSY;
|
|
}
|
|
|
|
s32 sys_rwlock_wunlock(PPUThread& CPU, u32 rw_lock_id)
|
|
{
|
|
sys_rwlock.Log("sys_rwlock_wunlock(rw_lock_id=%d)", rw_lock_id);
|
|
|
|
std::shared_ptr<RWLock> rw;
|
|
if (!sys_rwlock.CheckId(rw_lock_id, rw))
|
|
{
|
|
return CELL_ESRCH;
|
|
}
|
|
|
|
const u32 tid = CPU.GetId();
|
|
const u32 target = rw->wqueue.signal(rw->protocol);
|
|
|
|
if (rw->sync.compare_and_swap_test({ 0, tid }, { 0, target }))
|
|
{
|
|
if (!target)
|
|
{
|
|
// TODO: signal readers
|
|
}
|
|
return CELL_OK;
|
|
}
|
|
|
|
return CELL_EPERM;
|
|
}
|