sys_lwmutex, sys_lwcond syscalls improved

This commit is contained in:
Nekotekina 2015-07-21 19:35:55 +03:00
parent 6462201aac
commit cdf38d15d2
5 changed files with 145 additions and 136 deletions

View File

@ -217,7 +217,7 @@ s32 sys_lwmutex_lock(PPUThread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64 timeout
}
// lock using the syscall
const s32 res = _sys_lwmutex_lock(lwmutex->sleep_queue, timeout);
const s32 res = _sys_lwmutex_lock(ppu, lwmutex->sleep_queue, timeout);
lwmutex->all_info--;

View File

@ -5,8 +5,6 @@
#include "Emu/SysCalls/SysCalls.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/SysCalls/Modules/sysPrxForUser.h"
#include "sleep_queue.h"
#include "sys_lwmutex.h"
#include "sys_lwcond.h"
@ -14,6 +12,30 @@ SysCallBase sys_lwcond("sys_lwcond");
extern u64 get_system_time();
void lv2_lwcond_t::notify(lv2_lock_t & lv2_lock, sleep_queue_t::iterator it, const std::shared_ptr<lv2_lwmutex_t>& mutex, bool mode2)
{
CHECK_LV2_LOCK(lv2_lock);
auto& ppu = static_cast<PPUThread&>(*it->get());
ppu.GPR[3] = mode2; // set to return CELL_EBUSY
if (!mode2)
{
if (!mutex->signaled)
{
return mutex->sq.emplace_back(*it);
}
mutex->signaled--;
}
if (!ppu.signal())
{
throw EXCEPTION("Thread already signaled");
}
}
s32 _sys_lwcond_create(vm::ptr<u32> lwcond_id, u32 lwmutex_id, vm::ptr<sys_lwcond_t> control, u64 name, u32 arg5)
{
sys_lwcond.Warning("_sys_lwcond_create(lwcond_id=*0x%x, lwmutex_id=0x%x, control=*0x%x, name=0x%llx, arg5=0x%x)", lwcond_id, lwmutex_id, control, name, arg5);
@ -36,7 +58,7 @@ s32 _sys_lwcond_destroy(u32 lwcond_id)
return CELL_ESRCH;
}
if (!cond->waiters.empty() || cond->signaled1 || cond->signaled2)
if (!cond->sq.empty())
{
return CELL_EBUSY;
}
@ -62,47 +84,39 @@ s32 _sys_lwcond_signal(u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, u32 mod
if (mode != 1 && mode != 2 && mode != 3)
{
sys_lwcond.Error("_sys_lwcond_signal(%d): invalid mode (%d)", lwcond_id, mode);
throw EXCEPTION("Unknown mode (%d)", mode);
}
const auto found = ~ppu_thread_id ? cond->waiters.find(ppu_thread_id) : cond->waiters.begin();
// mode 1: lightweight mutex was initially owned by the calling thread
// mode 2: lightweight mutex was not owned by the calling thread and waiter hasn't been increased
// mode 3: lightweight mutex was forcefully owned by the calling thread
if (mode == 1)
// pick waiter; protocol is ignored in current implementation
const auto found = !~ppu_thread_id ? cond->sq.begin() : std::find_if(cond->sq.begin(), cond->sq.end(), [=](sleep_queue_t::value_type& thread)
{
// mode 1: lightweight mutex was initially owned by the calling thread
return thread->get_id() == ppu_thread_id;
});
if (found == cond->waiters.end())
if (found == cond->sq.end())
{
if (mode == 1)
{
return CELL_EPERM;
}
cond->signaled1++;
}
else if (mode == 2)
{
// mode 2: lightweight mutex was not owned by the calling thread and waiter hasn't been increased
if (found == cond->waiters.end())
else if (mode == 2)
{
return CELL_OK;
}
cond->signaled2++;
}
else
{
// in mode 3, lightweight mutex was forcefully owned by the calling thread
if (found == cond->waiters.end())
else
{
return ~ppu_thread_id ? CELL_ENOENT : CELL_EPERM;
}
cond->signaled1++;
}
cond->waiters.erase(found);
cond->cv.notify_one();
// signal specified waiting thread
cond->notify(lv2_lock, found, mutex, mode == 2);
cond->sq.erase(found);
return CELL_OK;
}
@ -123,36 +137,27 @@ s32 _sys_lwcond_signal_all(u32 lwcond_id, u32 lwmutex_id, u32 mode)
if (mode != 1 && mode != 2)
{
sys_lwcond.Error("_sys_lwcond_signal_all(%d): invalid mode (%d)", lwcond_id, mode);
throw EXCEPTION("Unknown mode (%d)", mode);
}
const u32 count = (u32)cond->waiters.size();
// mode 1: lightweight mutex was initially owned by the calling thread
// mode 2: lightweight mutex was not owned by the calling thread and waiter hasn't been increased
if (count)
// signal all waiting threads; protocol is ignored in current implementation
for (auto it = cond->sq.begin(); it != cond->sq.end(); it++)
{
cond->waiters.clear();
cond->cv.notify_all();
cond->notify(lv2_lock, it, mutex, mode == 2);
}
if (mode == 1)
{
// in mode 1, lightweight mutex was initially owned by the calling thread
// in mode 1, return the amount of threads signaled
const s32 result = mode == 2 ? CELL_OK : static_cast<s32>(cond->sq.size());
cond->signaled1 += count;
cond->sq.clear();
return count;
}
else
{
// in mode 2, lightweight mutex was not owned by the calling thread and waiter hasn't been increased
cond->signaled2 += count;
return CELL_OK;
}
return result;
}
s32 _sys_lwcond_queue_wait(PPUThread& CPU, u32 lwcond_id, u32 lwmutex_id, u64 timeout)
s32 _sys_lwcond_queue_wait(PPUThread& ppu, u32 lwcond_id, u32 lwmutex_id, u64 timeout)
{
sys_lwcond.Log("_sys_lwcond_queue_wait(lwcond_id=0x%x, lwmutex_id=0x%x, timeout=0x%llx)", lwcond_id, lwmutex_id, timeout);
@ -169,64 +174,45 @@ s32 _sys_lwcond_queue_wait(PPUThread& CPU, u32 lwcond_id, u32 lwmutex_id, u64 ti
}
// finalize unlocking the mutex
mutex->signaled++;
if (mutex->waiters)
{
mutex->cv.notify_one();
}
mutex->unlock(lv2_lock);
// add waiter; protocol is ignored in current implementation
cond->waiters.emplace(CPU.get_id());
sleep_queue_entry_t waiter(ppu, cond->sq);
while ((!(cond->signaled1 && mutex->signaled) && !cond->signaled2) || cond->waiters.count(CPU.get_id()))
// potential mutex waiter (not added immediately)
sleep_queue_entry_t mutex_waiter(ppu, cond->sq, defer_sleep);
while (!ppu.unsignal())
{
CHECK_EMU_STATUS;
const bool is_timedout = timeout && get_system_time() - start_time > timeout;
// check timeout
if (is_timedout)
if (timeout && waiter)
{
// cancel waiting
if (!cond->waiters.erase(CPU.get_id()))
const u64 passed = get_system_time() - start_time;
if (passed >= timeout)
{
if (cond->signaled1 && !mutex->signaled)
// try to reown the mutex if timed out
if (mutex->signaled)
{
cond->signaled1--;
mutex->signaled--;
return CELL_EDEADLK;
}
else
{
throw EXCEPTION("Unexpected values");
return CELL_ETIMEDOUT;
}
}
if (mutex->signaled)
{
mutex->signaled--;
return CELL_EDEADLK;
}
else
{
return CELL_ETIMEDOUT;
}
ppu.cv.wait_for(lv2_lock, std::chrono::microseconds(timeout - passed));
}
else
{
ppu.cv.wait(lv2_lock);
}
(cond->signaled1 ? mutex->cv : cond->cv).wait_for(lv2_lock, std::chrono::milliseconds(1));
}
if (cond->signaled1 && mutex->signaled)
{
mutex->signaled--;
cond->signaled1--;
return CELL_OK;
}
else
{
cond->signaled2--;
return CELL_EBUSY;
}
// return cause
return ppu.GPR[3] ? CELL_EBUSY : CELL_OK;
}

View File

@ -1,5 +1,7 @@
#pragma once
#include "sleep_queue.h"
namespace vm { using namespace ps3; }
struct sys_lwmutex_t;
@ -23,20 +25,14 @@ struct lv2_lwcond_t
{
const u64 name;
std::atomic<u32> signaled1; // mode 1 signals
std::atomic<u32> signaled2; // mode 2 signals
// TODO: use sleep queue
std::condition_variable cv;
std::unordered_set<u32> waiters;
sleep_queue_t sq;
lv2_lwcond_t(u64 name)
: name(name)
, signaled1(0)
, signaled2(0)
//, waiters(0)
{
}
void notify(lv2_lock_t& lv2_lock, sleep_queue_t::iterator it, const std::shared_ptr<lv2_lwmutex_t>& mutex, bool mode2);
};
REG_ID_TYPE(lv2_lwcond_t, 0x97); // SYS_LWCOND_OBJECT
@ -49,4 +45,4 @@ s32 _sys_lwcond_create(vm::ptr<u32> lwcond_id, u32 lwmutex_id, vm::ptr<sys_lwcon
s32 _sys_lwcond_destroy(u32 lwcond_id);
s32 _sys_lwcond_signal(u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, u32 mode);
s32 _sys_lwcond_signal_all(u32 lwcond_id, u32 lwmutex_id, u32 mode);
s32 _sys_lwcond_queue_wait(PPUThread& CPU, u32 lwcond_id, u32 lwmutex_id, u64 timeout);
s32 _sys_lwcond_queue_wait(PPUThread& ppu, u32 lwcond_id, u32 lwmutex_id, u64 timeout);

View File

@ -5,13 +5,36 @@
#include "Emu/SysCalls/SysCalls.h"
#include "Emu/Cell/PPUThread.h"
#include "sleep_queue.h"
#include "sys_lwmutex.h"
SysCallBase sys_lwmutex("sys_lwmutex");
extern u64 get_system_time();
void lv2_lwmutex_t::unlock(lv2_lock_t& lv2_lock)
{
CHECK_LV2_LOCK(lv2_lock);
if (signaled)
{
throw EXCEPTION("Unexpected");
}
if (sq.size())
{
if (!sq.front()->signal())
{
throw EXCEPTION("Thread already signaled");
}
sq.pop_front();
}
else
{
signaled++;
}
}
s32 _sys_lwmutex_create(vm::ptr<u32> lwmutex_id, u32 protocol, vm::ptr<sys_lwmutex_t> control, u32 arg4, u64 name, u32 arg6)
{
sys_lwmutex.Warning("_sys_lwmutex_create(lwmutex_id=*0x%x, protocol=0x%x, control=*0x%x, arg4=0x%x, name=0x%llx, arg6=0x%x)", lwmutex_id, protocol, control, arg4, name, arg6);
@ -45,7 +68,7 @@ s32 _sys_lwmutex_destroy(u32 lwmutex_id)
return CELL_ESRCH;
}
if (mutex->waiters)
if (!mutex->sq.empty())
{
return CELL_EBUSY;
}
@ -55,7 +78,7 @@ s32 _sys_lwmutex_destroy(u32 lwmutex_id)
return CELL_OK;
}
s32 _sys_lwmutex_lock(u32 lwmutex_id, u64 timeout)
s32 _sys_lwmutex_lock(PPUThread& ppu, u32 lwmutex_id, u64 timeout)
{
sys_lwmutex.Log("_sys_lwmutex_lock(lwmutex_id=0x%x, timeout=0x%llx)", lwmutex_id, timeout);
@ -70,25 +93,36 @@ s32 _sys_lwmutex_lock(u32 lwmutex_id, u64 timeout)
return CELL_ESRCH;
}
// protocol is ignored in current implementation
mutex->waiters++;
if (mutex->signaled)
{
mutex->signaled--;
while (!mutex->signaled)
return CELL_OK;
}
// add waiter; protocol is ignored in current implementation
sleep_queue_entry_t waiter(ppu, mutex->sq);
while (!ppu.unsignal())
{
CHECK_EMU_STATUS;
if (timeout && get_system_time() - start_time > timeout)
if (timeout)
{
mutex->waiters--;
return CELL_ETIMEDOUT;
const u64 passed = get_system_time() - start_time;
if (passed >= timeout)
{
return CELL_ETIMEDOUT;
}
ppu.cv.wait_for(lv2_lock, std::chrono::microseconds(timeout - passed));
}
else
{
ppu.cv.wait(lv2_lock);
}
mutex->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
}
mutex->signaled--;
mutex->waiters--;
return CELL_OK;
}
@ -106,7 +140,7 @@ s32 _sys_lwmutex_trylock(u32 lwmutex_id)
return CELL_ESRCH;
}
if (mutex->waiters || !mutex->signaled)
if (!mutex->sq.empty() || !mutex->signaled)
{
return CELL_EBUSY;
}
@ -129,17 +163,7 @@ s32 _sys_lwmutex_unlock(u32 lwmutex_id)
return CELL_ESRCH;
}
if (mutex->signaled)
{
throw EXCEPTION("Already signaled (lwmutex_id=0x%x)", lwmutex_id);
}
mutex->signaled++;
if (mutex->waiters)
{
mutex->cv.notify_one();
}
mutex->unlock(lv2_lock);
return CELL_OK;
}

View File

@ -1,5 +1,7 @@
#pragma once
#include "sleep_queue.h"
namespace vm { using namespace ps3; }
struct sys_lwmutex_attribute_t
@ -54,27 +56,28 @@ struct lv2_lwmutex_t
const u32 protocol;
const u64 name;
// this object is not truly a mutex and its syscall names are wrong, it's probabably sleep queue or something
std::atomic<u32> signaled;
// this object is not truly a mutex and its syscall names may be wrong, it's probably a sleep queue or something
std::atomic<u32> signaled{ 0 };
// TODO: use sleep queue, possibly remove condition variable
std::condition_variable cv;
std::atomic<u32> waiters;
sleep_queue_t sq;
lv2_lwmutex_t(u32 protocol, u64 name)
: protocol(protocol)
, name(name)
, signaled(0)
, waiters(0)
{
}
void unlock(lv2_lock_t& lv2_lock);
};
REG_ID_TYPE(lv2_lwmutex_t, 0x95); // SYS_LWMUTEX_OBJECT
// Aux
class PPUThread;
// SysCalls
s32 _sys_lwmutex_create(vm::ptr<u32> lwmutex_id, u32 protocol, vm::ptr<sys_lwmutex_t> control, u32 arg4, u64 name, u32 arg6);
s32 _sys_lwmutex_destroy(u32 lwmutex_id);
s32 _sys_lwmutex_lock(u32 lwmutex_id, u64 timeout);
s32 _sys_lwmutex_lock(PPUThread& ppu, u32 lwmutex_id, u64 timeout);
s32 _sys_lwmutex_trylock(u32 lwmutex_id);
s32 _sys_lwmutex_unlock(u32 lwmutex_id);