rpcsx/rpcs3/Emu/Cell/lv2/sys_rwlock.cpp

325 lines
6 KiB
C++
Raw Normal View History

#include "stdafx.h"
2014-08-23 16:51:51 +02:00
#include "Emu/Memory/Memory.h"
2014-08-22 18:36:27 +02:00
#include "Emu/System.h"
2015-03-06 23:58:42 +01:00
#include "Emu/IdManager.h"
2014-08-23 16:51:51 +02:00
2016-04-14 00:23:53 +02:00
#include "Emu/Cell/ErrorCodes.h"
2014-08-23 16:51:51 +02:00
#include "Emu/Cell/PPUThread.h"
#include "sys_rwlock.h"
2016-05-13 15:55:34 +02:00
logs::channel sys_rwlock("sys_rwlock", logs::level::notice);
extern u64 get_system_time();
2016-04-14 00:23:53 +02:00
void lv2_rwlock_t::notify_all(lv2_lock_t)
2015-07-20 18:46:24 +02:00
{
// pick a new writer if possible; protocol is ignored in current implementation
if (!readers && !writer && wsq.size())
{
writer = idm::get<ppu_thread>(wsq.front()->id);
2015-07-20 18:46:24 +02:00
2016-05-13 15:55:34 +02:00
VERIFY(!writer->state.test_and_set(cpu_state::signal));
writer->notify();
2015-07-20 18:46:24 +02:00
return wsq.pop_front();
}
// wakeup all readers if possible
if (!writer && !wsq.size())
{
readers += static_cast<u32>(rsq.size());
for (auto& thread : rsq)
{
2016-05-13 15:55:34 +02:00
VERIFY(!thread->state.test_and_set(cpu_state::signal));
thread->notify();
2015-07-20 18:46:24 +02:00
}
return rsq.clear();
}
}
2014-10-11 19:20:01 +02:00
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=*0x%x, attr=*0x%x)", rw_lock_id, attr);
2015-03-08 03:32:41 +01:00
if (!rw_lock_id || !attr)
2015-01-04 23:45:09 +01:00
{
return CELL_EFAULT;
}
2015-03-08 03:32:41 +01:00
const u32 protocol = attr->protocol;
2015-07-20 18:46:24 +02:00
if (protocol != SYS_SYNC_FIFO && protocol != SYS_SYNC_PRIORITY && protocol != SYS_SYNC_PRIORITY_INHERIT)
{
sys_rwlock.error("sys_rwlock_create(): unknown protocol (0x%x)", protocol);
2015-07-20 18:46:24 +02:00
return CELL_EINVAL;
}
2014-08-13 20:01:09 +02:00
2015-09-15 18:23:17 +02:00
if (attr->pshared != SYS_SYNC_NOT_PROCESS_SHARED || attr->ipc_key || attr->flags)
{
sys_rwlock.error("sys_rwlock_create(): unknown attributes (pshared=0x%x, ipc_key=0x%llx, flags=0x%x)", attr->pshared, attr->ipc_key, attr->flags);
2014-08-13 20:01:09 +02:00
return CELL_EINVAL;
}
*rw_lock_id = idm::make<lv2_rwlock_t>(protocol, attr->name_u64);
return CELL_OK;
}
s32 sys_rwlock_destroy(u32 rw_lock_id)
{
sys_rwlock.warning("sys_rwlock_destroy(rw_lock_id=0x%x)", rw_lock_id);
2015-03-08 03:32:41 +01:00
LV2_LOCK;
const auto rwlock = idm::get<lv2_rwlock_t>(rw_lock_id);
2015-03-11 16:30:50 +01:00
2015-04-12 22:16:30 +02:00
if (!rwlock)
{
return CELL_ESRCH;
}
2015-07-20 18:46:24 +02:00
if (rwlock->readers || rwlock->writer || rwlock->rsq.size() || rwlock->wsq.size())
{
return CELL_EBUSY;
}
idm::remove<lv2_rwlock_t>(rw_lock_id);
2015-03-08 03:32:41 +01:00
return CELL_OK;
}
s32 sys_rwlock_rlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
{
sys_rwlock.trace("sys_rwlock_rlock(rw_lock_id=0x%x, timeout=0x%llx)", rw_lock_id, timeout);
const u64 start_time = get_system_time();
2015-03-08 03:32:41 +01:00
LV2_LOCK;
const auto rwlock = idm::get<lv2_rwlock_t>(rw_lock_id);
2015-03-11 16:30:50 +01:00
2015-04-12 22:16:30 +02:00
if (!rwlock)
{
return CELL_ESRCH;
}
2015-07-20 18:46:24 +02:00
if (!rwlock->writer && rwlock->wsq.empty())
{
if (!++rwlock->readers)
{
throw EXCEPTION("Too many readers");
}
return CELL_OK;
}
// add waiter; protocol is ignored in current implementation
2016-04-14 00:23:53 +02:00
sleep_entry<cpu_thread> waiter(rwlock->rsq, ppu);
2015-03-11 16:30:50 +01:00
2016-04-14 00:23:53 +02:00
while (!ppu.state.test_and_reset(cpu_state::signal))
{
2015-07-04 01:22:24 +02:00
CHECK_EMU_STATUS;
2015-07-20 18:46:24 +02:00
if (timeout)
{
2015-07-20 18:46:24 +02:00
const u64 passed = get_system_time() - start_time;
if (passed >= timeout)
{
return CELL_ETIMEDOUT;
}
get_current_thread_cv().wait_for(lv2_lock, std::chrono::microseconds(timeout - passed));
2015-07-20 18:46:24 +02:00
}
else
{
get_current_thread_cv().wait(lv2_lock);
2015-07-20 18:46:24 +02:00
}
}
2015-07-20 18:46:24 +02:00
if (rwlock->writer || !rwlock->readers)
{
throw EXCEPTION("Unexpected");
}
2015-03-08 03:32:41 +01:00
return CELL_OK;
}
s32 sys_rwlock_tryrlock(u32 rw_lock_id)
{
sys_rwlock.trace("sys_rwlock_tryrlock(rw_lock_id=0x%x)", rw_lock_id);
2015-03-08 03:32:41 +01:00
LV2_LOCK;
const auto rwlock = idm::get<lv2_rwlock_t>(rw_lock_id);
2015-03-11 16:30:50 +01:00
2015-04-12 22:16:30 +02:00
if (!rwlock)
{
return CELL_ESRCH;
}
2015-07-20 18:46:24 +02:00
if (rwlock->writer || rwlock->wsq.size())
{
2015-03-08 03:32:41 +01:00
return CELL_EBUSY;
}
2015-07-20 18:46:24 +02:00
if (!++rwlock->readers)
{
throw EXCEPTION("Too many readers");
}
2015-03-08 03:32:41 +01:00
return CELL_OK;
}
s32 sys_rwlock_runlock(u32 rw_lock_id)
{
sys_rwlock.trace("sys_rwlock_runlock(rw_lock_id=0x%x)", rw_lock_id);
2015-03-08 03:32:41 +01:00
LV2_LOCK;
const auto rwlock = idm::get<lv2_rwlock_t>(rw_lock_id);
2015-03-11 16:30:50 +01:00
2015-04-12 22:16:30 +02:00
if (!rwlock)
{
return CELL_ESRCH;
}
2015-03-08 03:32:41 +01:00
if (!rwlock->readers)
{
2015-03-08 03:32:41 +01:00
return CELL_EPERM;
}
2015-07-20 18:46:24 +02:00
if (!--rwlock->readers)
{
2015-07-20 18:46:24 +02:00
rwlock->notify_all(lv2_lock);
}
2015-03-08 03:32:41 +01:00
return CELL_OK;
}
s32 sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
{
sys_rwlock.trace("sys_rwlock_wlock(rw_lock_id=0x%x, timeout=0x%llx)", rw_lock_id, timeout);
const u64 start_time = get_system_time();
2015-03-08 03:32:41 +01:00
LV2_LOCK;
const auto rwlock = idm::get<lv2_rwlock_t>(rw_lock_id);
2015-03-11 16:30:50 +01:00
2015-04-12 22:16:30 +02:00
if (!rwlock)
{
2015-03-08 03:32:41 +01:00
return CELL_ESRCH;
}
2015-07-20 18:46:24 +02:00
if (rwlock->writer.get() == &ppu)
{
return CELL_EDEADLK;
}
2015-07-20 18:46:24 +02:00
if (!rwlock->readers && !rwlock->writer)
{
rwlock->writer = idm::get<ppu_thread>(ppu.id);
2015-07-20 18:46:24 +02:00
return CELL_OK;
}
// add waiter; protocol is ignored in current implementation
2016-04-14 00:23:53 +02:00
sleep_entry<cpu_thread> waiter(rwlock->wsq, ppu);
2015-07-20 18:46:24 +02:00
2016-04-14 00:23:53 +02:00
while (!ppu.state.test_and_reset(cpu_state::signal))
{
2015-07-04 01:22:24 +02:00
CHECK_EMU_STATUS;
2015-07-20 18:46:24 +02:00
if (timeout)
{
2015-07-20 18:46:24 +02:00
const u64 passed = get_system_time() - start_time;
if (passed >= timeout)
{
// if the last waiter quit the writer sleep queue, readers must acquire the lock
if (!rwlock->writer && rwlock->wsq.size() == 1)
{
2016-04-14 00:23:53 +02:00
if (rwlock->wsq.front() != &ppu)
2015-07-20 18:46:24 +02:00
{
throw EXCEPTION("Unexpected");
}
rwlock->wsq.clear();
rwlock->notify_all(lv2_lock);
}
return CELL_ETIMEDOUT;
}
get_current_thread_cv().wait_for(lv2_lock, std::chrono::microseconds(timeout - passed));
2015-07-20 18:46:24 +02:00
}
else
{
get_current_thread_cv().wait(lv2_lock);
}
}
2015-03-08 03:32:41 +01:00
2015-07-20 18:46:24 +02:00
if (rwlock->readers || rwlock->writer.get() != &ppu)
{
throw EXCEPTION("Unexpected");
}
2015-03-08 03:32:41 +01:00
return CELL_OK;
}
s32 sys_rwlock_trywlock(ppu_thread& ppu, u32 rw_lock_id)
{
sys_rwlock.trace("sys_rwlock_trywlock(rw_lock_id=0x%x)", rw_lock_id);
2015-03-08 03:32:41 +01:00
LV2_LOCK;
const auto rwlock = idm::get<lv2_rwlock_t>(rw_lock_id);
2015-03-11 16:30:50 +01:00
2015-04-12 22:16:30 +02:00
if (!rwlock)
{
return CELL_ESRCH;
}
2015-07-20 18:46:24 +02:00
if (rwlock->writer.get() == &ppu)
{
2015-03-08 03:32:41 +01:00
return CELL_EDEADLK;
}
2015-07-20 18:46:24 +02:00
if (rwlock->readers || rwlock->writer || rwlock->wsq.size())
{
2015-03-08 03:32:41 +01:00
return CELL_EBUSY;
}
rwlock->writer = idm::get<ppu_thread>(ppu.id);
2015-03-08 03:32:41 +01:00
return CELL_OK;
}
s32 sys_rwlock_wunlock(ppu_thread& ppu, u32 rw_lock_id)
{
sys_rwlock.trace("sys_rwlock_wunlock(rw_lock_id=0x%x)", rw_lock_id);
2015-03-08 03:32:41 +01:00
LV2_LOCK;
const auto rwlock = idm::get<lv2_rwlock_t>(rw_lock_id);
2015-03-11 16:30:50 +01:00
2015-04-12 22:16:30 +02:00
if (!rwlock)
{
return CELL_ESRCH;
}
2015-07-20 18:46:24 +02:00
if (rwlock->writer.get() != &ppu)
{
2015-03-08 03:32:41 +01:00
return CELL_EPERM;
}
2015-07-20 18:46:24 +02:00
rwlock->writer.reset();
2015-03-11 16:30:50 +01:00
2015-07-20 18:46:24 +02:00
rwlock->notify_all(lv2_lock);
2015-03-08 03:32:41 +01:00
return CELL_OK;
}