sys_lwcond, sys_mutex, sys_cond rewritten

Some bugs fixed
This commit is contained in:
Nekotekina 2014-02-14 15:40:41 +04:00
parent 209155d71d
commit e94ea409fe
14 changed files with 489 additions and 315 deletions

View file

@ -449,7 +449,6 @@ int cellAudioSetNotifyEventQueue(u64 key)
return CELL_AUDIO_ERROR_PARAM; return CELL_AUDIO_ERROR_PARAM;
} }
eq->events.push(0, 0, 0, 0);
eq->events.push(0, 0, 0, 0); eq->events.push(0, 0, 0, 0);
return CELL_OK; return CELL_OK;

View file

@ -725,7 +725,7 @@ int cellRescSetBufferAddress(mem32_t colorBuffers, mem32_t vertexArray, mem32_t
if(!s_rescInternalInstance->m_bInitialized) if(!s_rescInternalInstance->m_bInitialized)
return CELL_RESC_ERROR_NOT_INITIALIZED; return CELL_RESC_ERROR_NOT_INITIALIZED;
if(!colorBuffers.GetAddr() || !vertexArray.GetAddr() || !fragmentShader.GetAddr()) if(!colorBuffers.IsGood() || !vertexArray.IsGood() || !fragmentShader.IsGood())
return CELL_RESC_ERROR_BAD_ARGUMENT; return CELL_RESC_ERROR_BAD_ARGUMENT;
if(colorBuffers.GetAddr() % COLOR_BUFFER_ALIGNMENT || if(colorBuffers.GetAddr() % COLOR_BUFFER_ALIGNMENT ||
vertexArray.GetAddr() % VERTEX_BUFFER_ALIGNMENT || vertexArray.GetAddr() % VERTEX_BUFFER_ALIGNMENT ||

View file

@ -74,7 +74,7 @@ static func_caller* sc_table[1024] =
bind_func(sys_cond_wait), //107 (0x06B) bind_func(sys_cond_wait), //107 (0x06B)
bind_func(sys_cond_signal), //108 (0x06C) bind_func(sys_cond_signal), //108 (0x06C)
bind_func(sys_cond_signal_all), //109 (0x06D) bind_func(sys_cond_signal_all), //109 (0x06D)
null_func, null_func, null_func, null_func, //113 (0x071) bind_func(sys_cond_signal_to), null_func, null_func, null_func, //113 (0x071)
bind_func(sys_semaphore_get_value), //114 (0x072) bind_func(sys_semaphore_get_value), //114 (0x072)
null_func, null_func, null_func, bind_func(sys_event_flag_clear), null_func, //119 (0x077) null_func, null_func, null_func, bind_func(sys_event_flag_clear), null_func, //119 (0x077)
bind_func(sys_rwlock_create), //120 (0x078) bind_func(sys_rwlock_create), //120 (0x078)

View file

@ -179,7 +179,7 @@ extern int sys_cond_signal_all(u32 cond_id);
extern int sys_cond_signal_to(u32 cond_id, u32 thread_id); extern int sys_cond_signal_to(u32 cond_id, u32 thread_id);
//sys_mutex //sys_mutex
extern int sys_mutex_create(u32 mutex_id_addr, u32 attr_addr); extern int sys_mutex_create(mem32_t mutex_id, mem_ptr_t<sys_mutex_attribute> attr);
extern int sys_mutex_destroy(u32 mutex_id); extern int sys_mutex_destroy(u32 mutex_id);
extern int sys_mutex_lock(u32 mutex_id, u64 timeout); extern int sys_mutex_lock(u32 mutex_id, u64 timeout);
extern int sys_mutex_trylock(u32 mutex_id); extern int sys_mutex_trylock(u32 mutex_id);

View file

@ -1,6 +1,5 @@
#include "stdafx.h" #include "stdafx.h"
#include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SysCalls.h"
#include "SC_Mutex.h"
#include "Emu/SysCalls/lv2/SC_Condition.h" #include "Emu/SysCalls/lv2/SC_Condition.h"
SysCallBase sys_cond("sys_cond"); SysCallBase sys_cond("sys_cond");
@ -21,13 +20,16 @@ int sys_cond_create(mem32_t cond_id, u32 mutex_id, mem_ptr_t<sys_cond_attribute>
return CELL_EINVAL; return CELL_EINVAL;
} }
mutex* mtx_data; Mutex* mutex;
if (!Emu.GetIdManager().GetIDData(mutex_id, mtx_data)) if (!Emu.GetIdManager().GetIDData(mutex_id, mutex))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
cond_id = sys_cond.GetNewId(new condition(mtx_data->mtx, attr->name_u64)); Cond* cond = new Cond(mutex, attr->name_u64);
u32 id = sys_cond.GetNewId(cond);
cond_id = id;
mutex->cond_count++;
sys_cond.Warning("*** condition created [%s]: id = %d", wxString(attr->name, 8).wx_str(), cond_id.GetValue()); sys_cond.Warning("*** condition created [%s]: id = %d", wxString(attr->name, 8).wx_str(), cond_id.GetValue());
return CELL_OK; return CELL_OK;
@ -35,88 +37,165 @@ int sys_cond_create(mem32_t cond_id, u32 mutex_id, mem_ptr_t<sys_cond_attribute>
int sys_cond_destroy(u32 cond_id) int sys_cond_destroy(u32 cond_id)
{ {
sys_cond.Error("sys_cond_destroy(cond_id=%d)", cond_id); sys_cond.Warning("sys_cond_destroy(cond_id=%d)", cond_id);
condition* cond; Cond* cond;
if (!Emu.GetIdManager().GetIDData(cond_id, cond)) if (!Emu.GetIdManager().GetIDData(cond_id, cond))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
if (true) // TODO if (!cond->m_queue.finalize())
{ {
return CELL_EBUSY; return CELL_EBUSY;
} }
cond->mutex->cond_count--;
Emu.GetIdManager().RemoveID(cond_id); Emu.GetIdManager().RemoveID(cond_id);
return CELL_OK; return CELL_OK;
} }
int sys_cond_wait(u32 cond_id, u64 timeout) int sys_cond_wait(u32 cond_id, u64 timeout)
{ {
sys_cond.Warning("sys_cond_wait(cond_id=%d, timeout=%lld)", cond_id, timeout); sys_cond.Log("sys_cond_wait(cond_id=%d, timeout=%lld)", cond_id, timeout);
condition* cond_data = nullptr; Cond* cond;
if(!sys_cond.CheckId(cond_id, cond_data)) return CELL_ESRCH; if (!Emu.GetIdManager().GetIDData(cond_id, cond))
{
return CELL_ESRCH;
}
Mutex* mutex = cond->mutex;
u32 tid = GetCurrentPPUThread().GetId();
if (mutex->m_mutex.GetOwner() != tid)
{
return CELL_EPERM;
}
cond->m_queue.push(tid);
mutex->m_mutex.unlock(tid);
u32 counter = 0; u32 counter = 0;
const u32 max_counter = timeout ? (timeout / 1000) : 20000; const u32 max_counter = timeout ? (timeout / 1000) : ~0;
do
while (true)
{ {
if (Emu.IsStopped()) /* switch (mutex->m_mutex.trylock(tid))
{ {
ConLog.Warning("sys_cond_wait(cond_id=%d, ...) aborted", cond_id); case SMR_OK: mutex->m_mutex.unlock(tid); break;
return CELL_ETIMEDOUT; case SMR_SIGNAL: return CELL_OK;
} */
if (mutex->m_mutex.GetOwner() == tid)
{
_mm_mfence();
return CELL_OK;
} }
switch (cond_data->cond.WaitTimeout(1)) Sleep(1);
{
case wxCOND_NO_ERROR: return CELL_OK;
case wxCOND_TIMEOUT: break;
default: return CELL_EPERM;
}
if (counter++ > max_counter) if (counter++ > max_counter)
{ {
if (!timeout) cond->m_queue.invalidate(tid);
{ return CELL_ETIMEDOUT;
counter = 0; }
} if (Emu.IsStopped())
else {
{ ConLog.Warning("sys_cond_wait(id=%d) aborted", cond_id);
return CELL_ETIMEDOUT; return CELL_OK;
} }
} }
} while (true);
} }
int sys_cond_signal(u32 cond_id) int sys_cond_signal(u32 cond_id)
{ {
sys_cond.Warning("sys_cond_signal(cond_id=%d)", cond_id); sys_cond.Log("sys_cond_signal(cond_id=%d)", cond_id);
condition* cond_data = nullptr; Cond* cond;
if(!sys_cond.CheckId(cond_id, cond_data)) return CELL_ESRCH; if (!Emu.GetIdManager().GetIDData(cond_id, cond))
{
return CELL_ESRCH;
}
cond_data->cond.Signal(); Mutex* mutex = cond->mutex;
u32 tid = GetCurrentPPUThread().GetId();
if (u32 target = mutex->protocol == SYS_SYNC_PRIORITY ? mutex->m_queue.pop_prio() : mutex->m_queue.pop())
{
if (mutex->m_mutex.trylock(target) != SMR_OK)
{
mutex->m_mutex.lock(tid);
mutex->m_mutex.unlock(tid, target);
}
}
if (Emu.IsStopped())
{
ConLog.Warning("sys_cond_signal(id=%d) aborted", cond_id);
}
return CELL_OK; return CELL_OK;
} }
int sys_cond_signal_all(u32 cond_id) int sys_cond_signal_all(u32 cond_id)
{ {
sys_cond.Warning("sys_cond_signal_all(cond_id=%d)", cond_id); sys_cond.Log("sys_cond_signal_all(cond_id=%d)", cond_id);
condition* cond_data = nullptr; Cond* cond;
if(!sys_cond.CheckId(cond_id, cond_data)) return CELL_ESRCH; if (!Emu.GetIdManager().GetIDData(cond_id, cond))
{
return CELL_ESRCH;
}
cond_data->cond.Broadcast(); Mutex* mutex = cond->mutex;
u32 tid = GetCurrentPPUThread().GetId();
while (u32 target = mutex->protocol == SYS_SYNC_PRIORITY ? mutex->m_queue.pop_prio() : mutex->m_queue.pop())
{
if (mutex->m_mutex.trylock(target) != SMR_OK)
{
mutex->m_mutex.lock(tid);
mutex->m_mutex.unlock(tid, target);
}
}
if (Emu.IsStopped())
{
ConLog.Warning("sys_cond_signal_all(id=%d) aborted", cond_id);
}
return CELL_OK; return CELL_OK;
} }
int sys_cond_signal_to(u32 cond_id, u32 thread_id) int sys_cond_signal_to(u32 cond_id, u32 thread_id)
{ {
sys_cond.Error("sys_cond_signal_to(cond_id=%d, thread_id=%d)", cond_id, thread_id); sys_cond.Log("sys_cond_signal_to(cond_id=%d, thread_id=%d)", cond_id, thread_id);
Cond* cond;
if (!Emu.GetIdManager().GetIDData(cond_id, cond))
{
return CELL_ESRCH;
}
if (!cond->m_queue.invalidate(thread_id))
{
return CELL_EPERM;
}
Mutex* mutex = cond->mutex;
u32 tid = GetCurrentPPUThread().GetId();
if (mutex->m_mutex.trylock(thread_id) != SMR_OK)
{
mutex->m_mutex.lock(tid);
mutex->m_mutex.unlock(tid, thread_id);
}
if (Emu.IsStopped())
{
ConLog.Warning("sys_cond_signal_to(id=%d, to=%d) aborted", cond_id, thread_id);
}
return CELL_OK; return CELL_OK;
} }

View file

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "SC_Mutex.h"
struct sys_cond_attribute struct sys_cond_attribute
{ {
@ -12,14 +13,14 @@ struct sys_cond_attribute
}; };
}; };
struct condition struct Cond
{ {
wxCondition cond; Mutex* mutex; // associated with mutex
u64 name_u64; SleepQueue m_queue;
condition(wxMutex& mtx, u64 name) Cond(Mutex* mutex, u64 name)
: cond(mtx) : mutex(mutex)
, name_u64(name) , m_queue(name)
{ {
} }
}; };

View file

@ -59,7 +59,7 @@ int sys_event_queue_create(mem32_t equeue_id, mem_ptr_t<sys_event_queue_attr> at
int sys_event_queue_destroy(u32 equeue_id, int mode) int sys_event_queue_destroy(u32 equeue_id, int mode)
{ {
sys_event.Warning("sys_event_queue_destroy(equeue_id=%d, mode=0x%x)", equeue_id, mode); sys_event.Error("sys_event_queue_destroy(equeue_id=%d, mode=0x%x)", equeue_id, mode);
EventQueue* eq; EventQueue* eq;
if (!Emu.GetIdManager().GetIDData(equeue_id, eq)) if (!Emu.GetIdManager().GetIDData(equeue_id, eq))

View file

@ -10,13 +10,22 @@ int sys_lwcond_create(mem_ptr_t<sys_lwcond_t> lwcond, mem_ptr_t<sys_lwmutex_t> l
sys_lwcond.Warning("sys_lwcond_create(lwcond_addr=0x%x, lwmutex_addr=0x%x, attr_addr=0x%x)", sys_lwcond.Warning("sys_lwcond_create(lwcond_addr=0x%x, lwmutex_addr=0x%x, attr_addr=0x%x)",
lwcond.GetAddr(), lwmutex.GetAddr(), attr.GetAddr()); lwcond.GetAddr(), lwmutex.GetAddr(), attr.GetAddr());
if (!lwcond.IsGood() || !lwmutex.IsGood() || !attr.IsGood()) return CELL_EFAULT; if (!lwcond.IsGood() || !lwmutex.IsGood() || !attr.IsGood())
{
return CELL_EFAULT;
}
lwcond->lwmutex = lwmutex.GetAddr(); lwcond->lwmutex = lwmutex.GetAddr();
lwcond->lwcond_queue = sys_lwcond.GetNewId(new LWCond(attr->name_u64)); lwcond->lwcond_queue = sys_lwcond.GetNewId(new SleepQueue(attr->name_u64));
if (lwmutex->attribute.ToBE() == se32(SYS_SYNC_RETRY))
{
sys_lwcond.Warning("Unsupported SYS_SYNC_RETRY lwmutex protocol");
}
sys_lwcond.Warning("*** lwcond created [%s] (lwmutex.attr=0x%x): id = %d",
wxString(attr->name, 8).wx_str(), (u32)lwmutex->attribute, (u32)lwcond->lwcond_queue);
sys_lwcond.Warning("*** lwcond created [%s] (attr=0x%x, lwmutex.sq=0x%x): id = %d",
wxString(attr->name, 8).wx_str(), (u32)lwmutex->attribute, (u32)lwmutex->sleep_queue, (u32)lwcond->lwcond_queue);
return CELL_OK; return CELL_OK;
} }
@ -24,12 +33,25 @@ int sys_lwcond_destroy(mem_ptr_t<sys_lwcond_t> lwcond)
{ {
sys_lwcond.Warning("sys_lwcond_destroy(lwcond_addr=0x%x)", lwcond.GetAddr()); sys_lwcond.Warning("sys_lwcond_destroy(lwcond_addr=0x%x)", lwcond.GetAddr());
if (!lwcond.IsGood()) return CELL_EFAULT; if (!lwcond.IsGood())
LWCond* lwc; {
u32 id = (u32)lwcond->lwcond_queue; return CELL_EFAULT;
if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH; }
Emu.GetIdManager().RemoveID(id); u32 lwc = lwcond->lwcond_queue;
SleepQueue* sq;
if (!Emu.GetIdManager().GetIDData(lwc, sq))
{
return CELL_ESRCH;
}
if (!sq->finalize())
{
return CELL_EBUSY;
}
Emu.GetIdManager().RemoveID(lwc);
return CELL_OK; return CELL_OK;
} }
@ -37,12 +59,33 @@ int sys_lwcond_signal(mem_ptr_t<sys_lwcond_t> lwcond)
{ {
sys_lwcond.Log("sys_lwcond_signal(lwcond_addr=0x%x)", lwcond.GetAddr()); sys_lwcond.Log("sys_lwcond_signal(lwcond_addr=0x%x)", lwcond.GetAddr());
if (!lwcond.IsGood()) return CELL_EFAULT; if (!lwcond.IsGood())
LWCond* lwc; {
u32 id = (u32)lwcond->lwcond_queue; return CELL_EFAULT;
if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH; }
lwc->signal(mem_ptr_t<sys_lwmutex_t>(lwcond->lwmutex)->attribute); SleepQueue* sq;
if (!Emu.GetIdManager().GetIDData((u32)lwcond->lwcond_queue, sq))
{
return CELL_ESRCH;
}
mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex);
be_t<u32> tid = GetCurrentPPUThread().GetId();
if (be_t<u32> target = mutex->attribute.ToBE() == se32(SYS_SYNC_PRIORITY) ? sq->pop_prio() : sq->pop())
{
if (mutex->owner.trylock(target) != SMR_OK)
{
mutex->owner.lock(tid);
mutex->owner.unlock(tid, target);
}
}
if (Emu.IsStopped())
{
ConLog.Warning("sys_lwcond_signal(sq=%d) aborted", (u32)lwcond->lwcond_queue);
}
return CELL_OK; return CELL_OK;
} }
@ -51,12 +94,33 @@ int sys_lwcond_signal_all(mem_ptr_t<sys_lwcond_t> lwcond)
{ {
sys_lwcond.Log("sys_lwcond_signal_all(lwcond_addr=0x%x)", lwcond.GetAddr()); sys_lwcond.Log("sys_lwcond_signal_all(lwcond_addr=0x%x)", lwcond.GetAddr());
if (!lwcond.IsGood()) return CELL_EFAULT; if (!lwcond.IsGood())
LWCond* lwc; {
u32 id = (u32)lwcond->lwcond_queue; return CELL_EFAULT;
if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH; }
lwc->signal_all(); SleepQueue* sq;
if (!Emu.GetIdManager().GetIDData((u32)lwcond->lwcond_queue, sq))
{
return CELL_ESRCH;
}
mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex);
be_t<u32> tid = GetCurrentPPUThread().GetId();
while (be_t<u32> target = mutex->attribute.ToBE() == se32(SYS_SYNC_PRIORITY) ? sq->pop_prio() : sq->pop())
{
if (mutex->owner.trylock(target) != SMR_OK)
{
mutex->owner.lock(tid);
mutex->owner.unlock(tid, target);
}
}
if (Emu.IsStopped())
{
ConLog.Warning("sys_lwcond_signal_all(sq=%d) aborted", (u32)lwcond->lwcond_queue);
}
return CELL_OK; return CELL_OK;
} }
@ -65,12 +129,37 @@ int sys_lwcond_signal_to(mem_ptr_t<sys_lwcond_t> lwcond, u32 ppu_thread_id)
{ {
sys_lwcond.Log("sys_lwcond_signal_to(lwcond_addr=0x%x, ppu_thread_id=%d)", lwcond.GetAddr(), ppu_thread_id); sys_lwcond.Log("sys_lwcond_signal_to(lwcond_addr=0x%x, ppu_thread_id=%d)", lwcond.GetAddr(), ppu_thread_id);
if (!lwcond.IsGood()) return CELL_EFAULT; if (!lwcond.IsGood())
LWCond* lwc; {
u32 id = (u32)lwcond->lwcond_queue; return CELL_EFAULT;
if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH; }
if (!lwc->signal_to(ppu_thread_id)) return CELL_EPERM; SleepQueue* sq;
if (!Emu.GetIdManager().GetIDData((u32)lwcond->lwcond_queue, sq))
{
return CELL_ESRCH;
}
if (!sq->invalidate(ppu_thread_id))
{
return CELL_EPERM;
}
mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex);
be_t<u32> tid = GetCurrentPPUThread().GetId();
be_t<u32> target = ppu_thread_id;
if (mutex->owner.trylock(target) != SMR_OK)
{
mutex->owner.lock(tid);
mutex->owner.unlock(tid, target);
}
if (Emu.IsStopped())
{
ConLog.Warning("sys_lwcond_signal_to(sq=%d, to=%d) aborted", (u32)lwcond->lwcond_queue, ppu_thread_id);
}
return CELL_OK; return CELL_OK;
} }
@ -79,49 +168,56 @@ int sys_lwcond_wait(mem_ptr_t<sys_lwcond_t> lwcond, u64 timeout)
{ {
sys_lwcond.Log("sys_lwcond_wait(lwcond_addr=0x%x, timeout=%lld)", lwcond.GetAddr(), timeout); sys_lwcond.Log("sys_lwcond_wait(lwcond_addr=0x%x, timeout=%lld)", lwcond.GetAddr(), timeout);
if (!lwcond.IsGood()) return CELL_EFAULT; if (!lwcond.IsGood())
LWCond* lwc; {
u32 id = (u32)lwcond->lwcond_queue; return CELL_EFAULT;
if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH; }
const u32 tid = GetCurrentPPUThread().GetId();
mem_ptr_t<sys_lwmutex_t> lwmutex(lwcond->lwmutex); SleepQueue* sq;
if (!Emu.GetIdManager().GetIDData((u32)lwcond->lwcond_queue, sq))
{
return CELL_ESRCH;
}
if ((u32)lwmutex->owner.GetOwner() != tid) return CELL_EPERM; // caller must own this lwmutex mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex);
lwc->begin_waiting(tid); u32 tid_le = GetCurrentPPUThread().GetId();
be_t<u32> tid = tid_le;
if (mutex->owner.GetOwner() != tid)
{
return CELL_EPERM; // caller must own this lwmutex
}
sq->push(tid_le);
mutex->owner.unlock(tid);
u32 counter = 0; u32 counter = 0;
const u32 max_counter = timeout ? (timeout / 1000) : 20000; const u32 max_counter = timeout ? (timeout / 1000) : ~0;
bool was_locked = true; while (true)
do
{ {
if (Emu.IsStopped()) /* switch (mutex->trylock(tid))
{ {
ConLog.Warning("sys_lwcond_wait(sq id=%d, ...) aborted", id); case SMR_OK: mutex->unlock(tid); break;
return CELL_ETIMEDOUT; case SMR_SIGNAL: return CELL_OK;
} */
if (mutex->owner.GetOwner() == tid)
{
_mm_mfence();
return CELL_OK;
} }
if (was_locked) lwmutex->unlock(tid);
Sleep(1); Sleep(1);
if (was_locked = (lwmutex->trylock(tid) == CELL_OK))
{
if (lwc->check(tid))
{
return CELL_OK;
}
}
if (counter++ > max_counter) if (counter++ > max_counter)
{ {
if (!timeout) sq->invalidate(tid_le);
{ return CELL_ETIMEDOUT;
sys_lwcond.Warning("sys_lwcond_wait(lwcond_addr=0x%x): TIMEOUT", lwcond.GetAddr()); }
counter = 0; if (Emu.IsStopped())
} {
else ConLog.Warning("sys_lwcond_wait(sq=%d) aborted", (u32)lwcond->lwcond_queue);
{ return CELL_OK;
lwc->stop_waiting(tid); }
return CELL_ETIMEDOUT; }
}
}
} while (true);
} }

View file

@ -13,108 +13,4 @@ struct sys_lwcond_t
{ {
be_t<u32> lwmutex; be_t<u32> lwmutex;
be_t<u32> lwcond_queue; be_t<u32> lwcond_queue;
};
#pragma pack()
struct LWCond
{
std::mutex m_lock;
Array<u32> waiters; // list of waiting threads
Array<u32> signaled; // list of signaled threads
u64 m_name; // not used
LWCond(u64 name)
: m_name(name)
{
}
void signal(u32 _protocol)
{
std::lock_guard<std::mutex> lock(m_lock);
if (waiters.GetCount())
{
if ((_protocol & SYS_SYNC_ATTR_PROTOCOL_MASK) == SYS_SYNC_PRIORITY)
{
u64 max_prio = 0;
u32 sel = 0;
for (u32 i = 0; i < waiters.GetCount(); i++)
{
CPUThread* t = Emu.GetCPU().GetThread(waiters[i]);
if (!t) continue;
u64 prio = t->GetPrio();
if (prio > max_prio)
{
max_prio = prio;
sel = i;
}
}
signaled.AddCpy(waiters[sel]);
waiters.RemoveAt(sel);
}
else // SYS_SYNC_FIFO
{
signaled.AddCpy(waiters[0]);
waiters.RemoveAt(0);
}
}
}
void signal_all()
{
std::lock_guard<std::mutex> lock(m_lock);
signaled.AppendFrom(waiters); // "nobody cares" protocol (!)
waiters.Clear();
}
bool signal_to(u32 id) // returns false if not found
{
std::lock_guard<std::mutex> lock(m_lock);
for (u32 i = waiters.GetCount() - 1; ~i; i--)
{
if (waiters[i] == id)
{
waiters.RemoveAt(i);
signaled.AddCpy(id);
return true;
}
}
return false;
}
void begin_waiting(u32 id)
{
std::lock_guard<std::mutex> lock(m_lock);
waiters.AddCpy(id);
}
void stop_waiting(u32 id)
{
std::lock_guard<std::mutex> lock(m_lock);
for (u32 i = waiters.GetCount() - 1; ~i; i--)
{
if (waiters[i] == id)
{
waiters.RemoveAt(i);
break;
}
}
}
bool check(u32 id) // returns true if signaled
{
std::lock_guard<std::mutex> lock(m_lock);
for (u32 i = signaled.GetCount() - 1; ~i; i--)
{
if (signaled[i] == id)
{
signaled.RemoveAt(i);
return true;
}
}
return false;
}
}; };

View file

@ -6,7 +6,7 @@ SysCallBase sc_lwmutex("sys_lwmutex");
int sys_lwmutex_create(mem_ptr_t<sys_lwmutex_t> lwmutex, mem_ptr_t<sys_lwmutex_attribute_t> attr) int sys_lwmutex_create(mem_ptr_t<sys_lwmutex_t> lwmutex, mem_ptr_t<sys_lwmutex_attribute_t> attr)
{ {
sc_lwmutex.Log("sys_lwmutex_create(lwmutex_addr=0x%x, lwmutex_attr_addr=0x%x)", sc_lwmutex.Warning("sys_lwmutex_create(lwmutex_addr=0x%x, lwmutex_attr_addr=0x%x)",
lwmutex.GetAddr(), attr.GetAddr()); lwmutex.GetAddr(), attr.GetAddr());
if (!lwmutex.IsGood() || !attr.IsGood()) return CELL_EFAULT; if (!lwmutex.IsGood() || !attr.IsGood()) return CELL_EFAULT;
@ -15,7 +15,7 @@ int sys_lwmutex_create(mem_ptr_t<sys_lwmutex_t> lwmutex, mem_ptr_t<sys_lwmutex_a
{ {
case se32(SYS_SYNC_RECURSIVE): break; case se32(SYS_SYNC_RECURSIVE): break;
case se32(SYS_SYNC_NOT_RECURSIVE): break; case se32(SYS_SYNC_NOT_RECURSIVE): break;
default: sc_lwmutex.Error("Unknown 0x%x recursive attr", (u32)attr->attr_recursive); return CELL_EINVAL; default: sc_lwmutex.Error("Unknown recursive attribute(0x%x)", (u32)attr->attr_recursive); return CELL_EINVAL;
} }
switch (attr->attr_protocol.ToBE()) switch (attr->attr_protocol.ToBE())
@ -24,7 +24,7 @@ int sys_lwmutex_create(mem_ptr_t<sys_lwmutex_t> lwmutex, mem_ptr_t<sys_lwmutex_a
case se32(SYS_SYNC_RETRY): break; case se32(SYS_SYNC_RETRY): break;
case se32(SYS_SYNC_PRIORITY_INHERIT): sc_lwmutex.Error("Invalid SYS_SYNC_PRIORITY_INHERIT protocol attr"); return CELL_EINVAL; case se32(SYS_SYNC_PRIORITY_INHERIT): sc_lwmutex.Error("Invalid SYS_SYNC_PRIORITY_INHERIT protocol attr"); return CELL_EINVAL;
case se32(SYS_SYNC_FIFO): break; case se32(SYS_SYNC_FIFO): break;
default: sc_lwmutex.Error("Unknown 0x%x protocol attr", (u32)attr->attr_protocol); return CELL_EINVAL; default: sc_lwmutex.Error("Unknown protocol attribute(0x%x)", (u32)attr->attr_protocol); return CELL_EINVAL;
} }
lwmutex->attribute = attr->attr_protocol | attr->attr_recursive; lwmutex->attribute = attr->attr_protocol | attr->attr_recursive;
@ -35,7 +35,7 @@ int sys_lwmutex_create(mem_ptr_t<sys_lwmutex_t> lwmutex, mem_ptr_t<sys_lwmutex_a
u32 sq_id = sc_lwmutex.GetNewId(new SleepQueue(attr->name_u64)); u32 sq_id = sc_lwmutex.GetNewId(new SleepQueue(attr->name_u64));
lwmutex->sleep_queue = sq_id; lwmutex->sleep_queue = sq_id;
sc_lwmutex.Log("*** lwmutex created [%s] (attribute=0x%x): sq_id = %d", sc_lwmutex.Warning("*** lwmutex created [%s] (attribute=0x%x): sq_id = %d",
wxString(attr->name, 8).wx_str(), (u32)lwmutex->attribute, sq_id); wxString(attr->name, 8).wx_str(), (u32)lwmutex->attribute, sq_id);
return CELL_OK; return CELL_OK;
@ -43,7 +43,7 @@ int sys_lwmutex_create(mem_ptr_t<sys_lwmutex_t> lwmutex, mem_ptr_t<sys_lwmutex_a
int sys_lwmutex_destroy(mem_ptr_t<sys_lwmutex_t> lwmutex) int sys_lwmutex_destroy(mem_ptr_t<sys_lwmutex_t> lwmutex)
{ {
sc_lwmutex.Log("sys_lwmutex_destroy(lwmutex_addr=0x%x)", lwmutex.GetAddr()); sc_lwmutex.Warning("sys_lwmutex_destroy(lwmutex_addr=0x%x)", lwmutex.GetAddr());
if (!lwmutex.IsGood()) return CELL_EFAULT; if (!lwmutex.IsGood()) return CELL_EFAULT;
@ -160,18 +160,38 @@ u32 SleepQueue::pop_prio_inherit() // (TODO)
return 0; return 0;
} }
void SleepQueue::invalidate(u32 tid) bool SleepQueue::invalidate(u32 tid)
{ {
SMutexLocker lock(m_mutex); SMutexLocker lock(m_mutex);
for (u32 i = 0; i < list.GetCount(); i++) if (tid) for (u32 i = 0; i < list.GetCount(); i++)
{ {
if (list[i] = tid) if (list[i] = tid)
{ {
list[i] = 0; list[i] = 0;
return; return true;
} }
} }
return false;
}
bool SleepQueue::finalize()
{
u32 tid = GetCurrentPPUThread().GetId();
m_mutex.lock(tid);
for (u32 i = 0; i < list.GetCount(); i++)
{
if (list[i])
{
m_mutex.unlock(tid);
return false;
}
}
return true;
} }
int sys_lwmutex_t::trylock(be_t<u32> tid) int sys_lwmutex_t::trylock(be_t<u32> tid)

View file

@ -8,7 +8,7 @@ enum
SYS_SYNC_FIFO = 1, SYS_SYNC_FIFO = 1,
// Priority Order // Priority Order
SYS_SYNC_PRIORITY = 2, SYS_SYNC_PRIORITY = 2,
// Basic Priority Inheritance Protocol // Basic Priority Inheritance Protocol (probably not implemented)
SYS_SYNC_PRIORITY_INHERIT = 3, SYS_SYNC_PRIORITY_INHERIT = 3,
// Not selected while unlocking // Not selected while unlocking
SYS_SYNC_RETRY = 4, SYS_SYNC_RETRY = 4,
@ -59,7 +59,8 @@ struct SleepQueue
u32 pop(); // SYS_SYNC_FIFO u32 pop(); // SYS_SYNC_FIFO
u32 pop_prio(); // SYS_SYNC_PRIORITY u32 pop_prio(); // SYS_SYNC_PRIORITY
u32 pop_prio_inherit(); // (TODO) u32 pop_prio_inherit(); // (TODO)
void invalidate(u32 tid); bool invalidate(u32 tid);
bool finalize();
}; };
struct sys_lwmutex_t struct sys_lwmutex_t
@ -84,26 +85,4 @@ struct sys_lwmutex_t
int trylock(be_t<u32> tid); int trylock(be_t<u32> tid);
int unlock(be_t<u32> tid); int unlock(be_t<u32> tid);
int lock(be_t<u32> tid, u64 timeout); int lock(be_t<u32> tid, u64 timeout);
}; };
/*
class lwmutex_locker
{
mem_ptr_t<sys_lwmutex_t> m_mutex;
be_t<u32> m_id;
lwmutex_locker(mem_ptr_t<sys_lwmutex_t> lwmutex, be_t<u32> tid, u64 timeout = 0)
: m_id(tid)
, m_mutex(lwmutex)
{
if (int res = m_mutex->lock(m_id, timeout))
{
ConLog.Error("lwmutex_locker: m_mutex->lock failed(res=0x%x)", res);
Emu.Pause();
}
}
~lwmutex_locker()
{
m_mutex->unlock(m_id);
}
};*/

View file

@ -5,39 +5,74 @@
SysCallBase sys_mtx("sys_mutex"); SysCallBase sys_mtx("sys_mutex");
int sys_mutex_create(u32 mutex_id_addr, u32 attr_addr) int sys_mutex_create(mem32_t mutex_id, mem_ptr_t<sys_mutex_attribute> attr)
{ {
sys_mtx.Warning("sys_mutex_create(mutex_id_addr=0x%x, attr_addr=0x%x)", sys_mtx.Warning("sys_mutex_create(mutex_id_addr=0x%x, attr_addr=0x%x)", mutex_id.GetAddr(), attr.GetAddr());
mutex_id_addr, attr_addr);
if(!Memory.IsGoodAddr(mutex_id_addr) || !Memory.IsGoodAddr(attr_addr)) return CELL_EFAULT; if (!mutex_id.IsGood() || !attr.IsGood())
{
return CELL_EFAULT;
}
mutex_attr attr = (mutex_attr&)Memory[attr_addr]; switch (attr->protocol.ToBE())
attr.protocol = re(attr.protocol); {
attr.recursive = re(attr.recursive); case se32(SYS_SYNC_FIFO): break;
attr.pshared = re(attr.pshared); case se32(SYS_SYNC_PRIORITY): break;
attr.adaptive = re(attr.adaptive); case se32(SYS_SYNC_PRIORITY_INHERIT): sys_mtx.Warning("TODO: SYS_SYNC_PRIORITY_INHERIT protocol"); break;
attr.ipc_key = re(attr.ipc_key); case se32(SYS_SYNC_RETRY): sys_mtx.Error("Invalid SYS_SYNC_RETRY protocol"); return CELL_EINVAL;
attr.flags = re(attr.flags); default: sys_mtx.Error("Unknown protocol attribute(0x%x)", (u32)attr->protocol); return CELL_EINVAL;
}
sys_mtx.Log("*** protocol = %d", attr.protocol);
sys_mtx.Log("*** recursive = %d", attr.recursive);
sys_mtx.Log("*** pshared = %d", attr.pshared);
sys_mtx.Log("*** ipc_key = 0x%llx", attr.ipc_key);
sys_mtx.Log("*** flags = 0x%x", attr.flags);
sys_mtx.Log("*** name = %s", attr.name);
Memory.Write32(mutex_id_addr, sys_mtx.GetNewId(new mutex(attr))); bool is_recursive;
switch (attr->recursive.ToBE())
{
case se32(SYS_SYNC_RECURSIVE): is_recursive = true; break;
case se32(SYS_SYNC_NOT_RECURSIVE): is_recursive = false; break;
default: sys_mtx.Error("Unknown recursive attribute(0x%x)", (u32)attr->recursive); return CELL_EINVAL;
}
if (attr->pshared.ToBE() != se32(0x200))
{
sys_mtx.Error("Unknown pshared attribute(0x%x)", (u32)attr->pshared);
return CELL_EINVAL;
}
mutex_id = sys_mtx.GetNewId(new Mutex((u32)attr->protocol, is_recursive, attr->name_u64));
sys_mtx.Warning("*** mutex created [%s] (protocol=0x%x, recursive=%d): id = %d",
wxString(attr->name, 8).wx_str(), (u32)attr->protocol, is_recursive, mutex_id.GetValue());
return CELL_OK; return CELL_OK;
} }
int sys_mutex_destroy(u32 mutex_id) int sys_mutex_destroy(u32 mutex_id)
{ {
sys_mtx.Log("sys_mutex_destroy(mutex_id=0x%x)", mutex_id); sys_mtx.Warning("sys_mutex_destroy(mutex_id=0x%x)", mutex_id);
if(!sys_mtx.CheckId(mutex_id)) return CELL_ESRCH; Mutex* mutex;
if (!Emu.GetIdManager().GetIDData(mutex_id, mutex))
{
return CELL_ESRCH;
}
if ((u32&)mutex->cond_count) // check if associated condition variable exists
{
return CELL_EPERM;
}
u32 tid = GetCurrentPPUThread().GetId();
if (mutex->m_mutex.trylock(tid)) // check if locked
{
return CELL_EBUSY;
}
if (!mutex->m_queue.finalize())
{
mutex->m_mutex.unlock(tid);
return CELL_EBUSY;
}
mutex->m_mutex.unlock(tid, ~0);
Emu.GetIdManager().RemoveID(mutex_id); Emu.GetIdManager().RemoveID(mutex_id);
return CELL_OK; return CELL_OK;
} }
@ -46,56 +81,113 @@ int sys_mutex_lock(u32 mutex_id, u64 timeout)
{ {
sys_mtx.Log("sys_mutex_lock(mutex_id=0x%x, timeout=0x%llx)", mutex_id, timeout); sys_mtx.Log("sys_mutex_lock(mutex_id=0x%x, timeout=0x%llx)", mutex_id, timeout);
mutex* mtx_data = nullptr; Mutex* mutex;
if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH; if (!Emu.GetIdManager().GetIDData(mutex_id, mutex))
u32 counter = 0;
const u32 max_counter = timeout ? (timeout / 1000) : 20000;
do
{ {
if (Emu.IsStopped()) return CELL_ESRCH;
}
u32 tid = GetCurrentPPUThread().GetId();
if (mutex->m_mutex.GetOwner() == tid)
{
if (mutex->is_recursive)
{ {
ConLog.Warning("sys_mutex_lock(mutex_id=%d, ...) aborted", mutex_id); if (mutex->recursive++ == 0)
return CELL_ETIMEDOUT; {
return CELL_EKRESOURCE;
}
return CELL_OK;
} }
else
if (mtx_data->mtx.TryLock() == wxMUTEX_NO_ERROR) return CELL_OK;
Sleep(1);
if (counter++ > max_counter)
{ {
if (!timeout) return CELL_EDEADLK;
{ }
counter = 0; }
}
else switch (mutex->m_mutex.trylock(tid))
{ {
return CELL_ETIMEDOUT; case SMR_OK: return CELL_OK;
} case SMR_FAILED: break;
} default: goto abort;
} while (true); }
mutex->m_queue.push(tid);
switch (mutex->m_mutex.lock(tid, timeout ? ((timeout < 1000) ? 1 : (timeout / 1000)) : 0))
{
case SMR_OK: mutex->m_queue.invalidate(tid);
case SMR_SIGNAL: mutex->recursive = 1; return CELL_OK;
case SMR_TIMEOUT: return CELL_ETIMEDOUT;
default: goto abort;
}
abort:
if (Emu.IsStopped())
{
ConLog.Warning("sys_mutex_lock(id=%d) aborted", mutex_id);
return CELL_OK;
}
return CELL_ESRCH;
} }
int sys_mutex_trylock(u32 mutex_id) int sys_mutex_trylock(u32 mutex_id)
{ {
sys_mtx.Log("sys_mutex_trylock(mutex_id=0x%x)", mutex_id); sys_mtx.Log("sys_mutex_trylock(mutex_id=0x%x)", mutex_id);
mutex* mtx_data = nullptr; Mutex* mutex;
if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH; if (!Emu.GetIdManager().GetIDData(mutex_id, mutex))
{
return CELL_ESRCH;
}
if (mtx_data->mtx.TryLock() != wxMUTEX_NO_ERROR) return CELL_EBUSY; u32 tid = GetCurrentPPUThread().GetId();
return CELL_OK; if (mutex->m_mutex.GetOwner() == tid)
{
if (mutex->is_recursive)
{
if (++mutex->recursive == 0)
{
return CELL_EKRESOURCE;
}
return CELL_OK;
}
else
{
return CELL_EDEADLK;
}
}
switch (mutex->m_mutex.trylock(tid))
{
case SMR_OK: return CELL_OK;
}
return CELL_EBUSY;
} }
int sys_mutex_unlock(u32 mutex_id) int sys_mutex_unlock(u32 mutex_id)
{ {
sys_mtx.Log("sys_mutex_unlock(mutex_id=0x%x)", mutex_id); sys_mtx.Log("sys_mutex_unlock(mutex_id=0x%x)", mutex_id);
mutex* mtx_data = nullptr; Mutex* mutex;
if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH; if (!Emu.GetIdManager().GetIDData(mutex_id, mutex))
{
return CELL_ESRCH;
}
mtx_data->mtx.Unlock(); u32 tid = GetCurrentPPUThread().GetId();
return CELL_OK; if (mutex->m_mutex.GetOwner() == tid)
{
mutex->recursive--;
if (!mutex->recursive)
{
mutex->m_mutex.unlock(tid, mutex->protocol == SYS_SYNC_PRIORITY ? mutex->m_queue.pop_prio() : mutex->m_queue.pop());
}
return CELL_OK;
}
return CELL_EPERM;
} }

View file

@ -1,25 +1,36 @@
#pragma once #pragma once
struct mutex_attr struct sys_mutex_attribute
{ {
u32 protocol; be_t<u32> protocol; // SYS_SYNC_FIFO, SYS_SYNC_PRIORITY or SYS_SYNC_PRIORITY_INHERIT
u32 recursive; be_t<u32> recursive; // SYS_SYNC_RECURSIVE or SYS_SYNC_NOT_RECURSIVE
u32 pshared; be_t<u32> pshared; // always 0x200 (not shared)
u32 adaptive; be_t<u32> adaptive;
u64 ipc_key; be_t<u64> ipc_key;
int flags; be_t<int> flags;
u32 pad; be_t<u32> pad;
char name[8]; union
{
char name[8];
u64 name_u64;
};
}; };
struct mutex struct Mutex
{ {
wxMutex mtx; SMutex m_mutex;
mutex_attr attr; SleepQueue m_queue;
u32 recursive; // recursive locks count
std::atomic<u32> cond_count; // count of condition variables associated
mutex(const mutex_attr& attr) const u32 protocol;
: mtx() const bool is_recursive;
, attr(attr)
Mutex(u32 protocol, bool is_recursive, u64 name)
: protocol(protocol)
, is_recursive(is_recursive)
, m_queue(name)
, cond_count(0)
{ {
} }
}; };

View file

@ -32,7 +32,8 @@ void sys_ppu_thread_exit(int errorcode)
int sys_ppu_thread_yield() int sys_ppu_thread_yield()
{ {
sysPrxForUser.Log("sys_ppu_thread_yield()"); sysPrxForUser.Log("sys_ppu_thread_yield()");
Sleep(1);
return CELL_OK; return CELL_OK;
} }