1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 10:42:36 +01:00

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;
}
eq->events.push(0, 0, 0, 0);
eq->events.push(0, 0, 0, 0);
return CELL_OK;

View File

@ -725,7 +725,7 @@ int cellRescSetBufferAddress(mem32_t colorBuffers, mem32_t vertexArray, mem32_t
if(!s_rescInternalInstance->m_bInitialized)
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;
if(colorBuffers.GetAddr() % COLOR_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_signal), //108 (0x06C)
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)
null_func, null_func, null_func, bind_func(sys_event_flag_clear), null_func, //119 (0x077)
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);
//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_lock(u32 mutex_id, u64 timeout);
extern int sys_mutex_trylock(u32 mutex_id);

View File

@ -1,6 +1,5 @@
#include "stdafx.h"
#include "Emu/SysCalls/SysCalls.h"
#include "SC_Mutex.h"
#include "Emu/SysCalls/lv2/SC_Condition.h"
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;
}
mutex* mtx_data;
if (!Emu.GetIdManager().GetIDData(mutex_id, mtx_data))
Mutex* mutex;
if (!Emu.GetIdManager().GetIDData(mutex_id, mutex))
{
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());
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)
{
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))
{
return CELL_ESRCH;
}
if (true) // TODO
if (!cond->m_queue.finalize())
{
return CELL_EBUSY;
}
cond->mutex->cond_count--;
Emu.GetIdManager().RemoveID(cond_id);
return CELL_OK;
}
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;
if(!sys_cond.CheckId(cond_id, cond_data)) return CELL_ESRCH;
Cond* cond;
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;
const u32 max_counter = timeout ? (timeout / 1000) : 20000;
do
const u32 max_counter = timeout ? (timeout / 1000) : ~0;
while (true)
{
if (Emu.IsStopped())
/* switch (mutex->m_mutex.trylock(tid))
{
ConLog.Warning("sys_cond_wait(cond_id=%d, ...) aborted", cond_id);
return CELL_ETIMEDOUT;
case SMR_OK: mutex->m_mutex.unlock(tid); break;
case SMR_SIGNAL: return CELL_OK;
} */
if (mutex->m_mutex.GetOwner() == tid)
{
_mm_mfence();
return CELL_OK;
}
switch (cond_data->cond.WaitTimeout(1))
{
case wxCOND_NO_ERROR: return CELL_OK;
case wxCOND_TIMEOUT: break;
default: return CELL_EPERM;
}
Sleep(1);
if (counter++ > max_counter)
{
if (!timeout)
{
counter = 0;
}
else
{
return CELL_ETIMEDOUT;
}
}
} while (true);
cond->m_queue.invalidate(tid);
return CELL_ETIMEDOUT;
}
if (Emu.IsStopped())
{
ConLog.Warning("sys_cond_wait(id=%d) aborted", cond_id);
return CELL_OK;
}
}
}
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;
if(!sys_cond.CheckId(cond_id, cond_data)) return CELL_ESRCH;
Cond* cond;
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;
}
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;
if(!sys_cond.CheckId(cond_id, cond_data)) return CELL_ESRCH;
Cond* cond;
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;
}
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;
}

View File

@ -1,4 +1,5 @@
#pragma once
#include "SC_Mutex.h"
struct sys_cond_attribute
{
@ -12,14 +13,14 @@ struct sys_cond_attribute
};
};
struct condition
struct Cond
{
wxCondition cond;
u64 name_u64;
Mutex* mutex; // associated with mutex
SleepQueue m_queue;
condition(wxMutex& mtx, u64 name)
: cond(mtx)
, name_u64(name)
Cond(Mutex* mutex, u64 name)
: mutex(mutex)
, 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)
{
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;
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)",
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->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;
}
@ -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());
if (!lwcond.IsGood()) return CELL_EFAULT;
LWCond* lwc;
u32 id = (u32)lwcond->lwcond_queue;
if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH;
if (!lwcond.IsGood())
{
return CELL_EFAULT;
}
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;
}
@ -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());
if (!lwcond.IsGood()) return CELL_EFAULT;
LWCond* lwc;
u32 id = (u32)lwcond->lwcond_queue;
if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH;
if (!lwcond.IsGood())
{
return CELL_EFAULT;
}
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;
}
@ -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());
if (!lwcond.IsGood()) return CELL_EFAULT;
LWCond* lwc;
u32 id = (u32)lwcond->lwcond_queue;
if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH;
if (!lwcond.IsGood())
{
return CELL_EFAULT;
}
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;
}
@ -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);
if (!lwcond.IsGood()) return CELL_EFAULT;
LWCond* lwc;
u32 id = (u32)lwcond->lwcond_queue;
if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH;
if (!lwcond.IsGood())
{
return CELL_EFAULT;
}
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;
}
@ -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);
if (!lwcond.IsGood()) return CELL_EFAULT;
LWCond* lwc;
u32 id = (u32)lwcond->lwcond_queue;
if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH;
const u32 tid = GetCurrentPPUThread().GetId();
if (!lwcond.IsGood())
{
return CELL_EFAULT;
}
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
lwc->begin_waiting(tid);
mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex);
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;
const u32 max_counter = timeout ? (timeout / 1000) : 20000;
bool was_locked = true;
do
const u32 max_counter = timeout ? (timeout / 1000) : ~0;
while (true)
{
if (Emu.IsStopped())
/* switch (mutex->trylock(tid))
{
ConLog.Warning("sys_lwcond_wait(sq id=%d, ...) aborted", id);
return CELL_ETIMEDOUT;
case SMR_OK: mutex->unlock(tid); break;
case SMR_SIGNAL: return CELL_OK;
} */
if (mutex->owner.GetOwner() == tid)
{
_mm_mfence();
return CELL_OK;
}
if (was_locked) lwmutex->unlock(tid);
Sleep(1);
if (was_locked = (lwmutex->trylock(tid) == CELL_OK))
{
if (lwc->check(tid))
{
return CELL_OK;
}
}
if (counter++ > max_counter)
{
if (!timeout)
{
sys_lwcond.Warning("sys_lwcond_wait(lwcond_addr=0x%x): TIMEOUT", lwcond.GetAddr());
counter = 0;
}
else
{
lwc->stop_waiting(tid);
return CELL_ETIMEDOUT;
}
}
} while (true);
sq->invalidate(tid_le);
return CELL_ETIMEDOUT;
}
if (Emu.IsStopped())
{
ConLog.Warning("sys_lwcond_wait(sq=%d) aborted", (u32)lwcond->lwcond_queue);
return CELL_OK;
}
}
}

View File

@ -13,108 +13,4 @@ struct sys_lwcond_t
{
be_t<u32> lwmutex;
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)
{
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());
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_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())
@ -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_PRIORITY_INHERIT): sc_lwmutex.Error("Invalid SYS_SYNC_PRIORITY_INHERIT protocol attr"); return CELL_EINVAL;
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;
@ -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));
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);
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)
{
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;
@ -160,18 +160,38 @@ u32 SleepQueue::pop_prio_inherit() // (TODO)
return 0;
}
void SleepQueue::invalidate(u32 tid)
bool SleepQueue::invalidate(u32 tid)
{
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)
{
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)

View File

@ -8,7 +8,7 @@ enum
SYS_SYNC_FIFO = 1,
// Priority Order
SYS_SYNC_PRIORITY = 2,
// Basic Priority Inheritance Protocol
// Basic Priority Inheritance Protocol (probably not implemented)
SYS_SYNC_PRIORITY_INHERIT = 3,
// Not selected while unlocking
SYS_SYNC_RETRY = 4,
@ -59,7 +59,8 @@ struct SleepQueue
u32 pop(); // SYS_SYNC_FIFO
u32 pop_prio(); // SYS_SYNC_PRIORITY
u32 pop_prio_inherit(); // (TODO)
void invalidate(u32 tid);
bool invalidate(u32 tid);
bool finalize();
};
struct sys_lwmutex_t
@ -84,26 +85,4 @@ struct sys_lwmutex_t
int trylock(be_t<u32> tid);
int unlock(be_t<u32> tid);
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");
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)",
mutex_id_addr, attr_addr);
sys_mtx.Warning("sys_mutex_create(mutex_id_addr=0x%x, attr_addr=0x%x)", mutex_id.GetAddr(), attr.GetAddr());
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];
attr.protocol = re(attr.protocol);
attr.recursive = re(attr.recursive);
attr.pshared = re(attr.pshared);
attr.adaptive = re(attr.adaptive);
attr.ipc_key = re(attr.ipc_key);
attr.flags = re(attr.flags);
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);
switch (attr->protocol.ToBE())
{
case se32(SYS_SYNC_FIFO): break;
case se32(SYS_SYNC_PRIORITY): break;
case se32(SYS_SYNC_PRIORITY_INHERIT): sys_mtx.Warning("TODO: SYS_SYNC_PRIORITY_INHERIT protocol"); break;
case se32(SYS_SYNC_RETRY): sys_mtx.Error("Invalid SYS_SYNC_RETRY protocol"); return CELL_EINVAL;
default: sys_mtx.Error("Unknown protocol attribute(0x%x)", (u32)attr->protocol); return CELL_EINVAL;
}
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;
}
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);
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);
mutex* mtx_data = nullptr;
if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH;
u32 counter = 0;
const u32 max_counter = timeout ? (timeout / 1000) : 20000;
do
Mutex* mutex;
if (!Emu.GetIdManager().GetIDData(mutex_id, mutex))
{
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);
return CELL_ETIMEDOUT;
if (mutex->recursive++ == 0)
{
return CELL_EKRESOURCE;
}
return CELL_OK;
}
if (mtx_data->mtx.TryLock() == wxMUTEX_NO_ERROR) return CELL_OK;
Sleep(1);
if (counter++ > max_counter)
else
{
if (!timeout)
{
counter = 0;
}
else
{
return CELL_ETIMEDOUT;
}
}
} while (true);
return CELL_EDEADLK;
}
}
switch (mutex->m_mutex.trylock(tid))
{
case SMR_OK: return CELL_OK;
case SMR_FAILED: break;
default: goto abort;
}
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)
{
sys_mtx.Log("sys_mutex_trylock(mutex_id=0x%x)", mutex_id);
mutex* mtx_data = nullptr;
if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH;
Mutex* mutex;
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)
{
sys_mtx.Log("sys_mutex_unlock(mutex_id=0x%x)", mutex_id);
mutex* mtx_data = nullptr;
if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH;
Mutex* mutex;
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
struct mutex_attr
struct sys_mutex_attribute
{
u32 protocol;
u32 recursive;
u32 pshared;
u32 adaptive;
u64 ipc_key;
int flags;
u32 pad;
char name[8];
be_t<u32> protocol; // SYS_SYNC_FIFO, SYS_SYNC_PRIORITY or SYS_SYNC_PRIORITY_INHERIT
be_t<u32> recursive; // SYS_SYNC_RECURSIVE or SYS_SYNC_NOT_RECURSIVE
be_t<u32> pshared; // always 0x200 (not shared)
be_t<u32> adaptive;
be_t<u64> ipc_key;
be_t<int> flags;
be_t<u32> pad;
union
{
char name[8];
u64 name_u64;
};
};
struct mutex
struct Mutex
{
wxMutex mtx;
mutex_attr attr;
SMutex m_mutex;
SleepQueue m_queue;
u32 recursive; // recursive locks count
std::atomic<u32> cond_count; // count of condition variables associated
mutex(const mutex_attr& attr)
: mtx()
, attr(attr)
const u32 protocol;
const bool is_recursive;
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()
{
sysPrxForUser.Log("sys_ppu_thread_yield()");
sysPrxForUser.Log("sys_ppu_thread_yield()");
Sleep(1);
return CELL_OK;
}