diff --git a/Utilities/SSemaphore.cpp b/Utilities/SSemaphore.cpp index 91754b764f..60c0331971 100644 --- a/Utilities/SSemaphore.cpp +++ b/Utilities/SSemaphore.cpp @@ -43,29 +43,22 @@ bool SSemaphore::try_wait() } } -void SSemaphore::post(u32 value) +void SSemaphore::post() { std::lock_guard lock(m_mutex); if (m_count >= m_max) { - value = 0; - } - else if (value > (m_max - m_count)) - { - value = m_max - m_count; + return; } - while (value) - { - m_count++; - value--; - m_cond.notify_one(); - } + m_count++; + m_cond.notify_one(); } bool SSemaphore::post_and_wait() { + // TODO: ??? if (try_wait()) return false; post(); diff --git a/Utilities/SSemaphore.h b/Utilities/SSemaphore.h index af46997bbf..4c96a9c5a3 100644 --- a/Utilities/SSemaphore.h +++ b/Utilities/SSemaphore.h @@ -28,7 +28,7 @@ public: bool try_wait(); - void post(u32 value = 1); + void post(); bool post_and_wait(); }; \ No newline at end of file diff --git a/rpcs3/Emu/CPU/CPUThreadManager.cpp b/rpcs3/Emu/CPU/CPUThreadManager.cpp index 353d44b7b9..b99573e6a6 100644 --- a/rpcs3/Emu/CPU/CPUThreadManager.cpp +++ b/rpcs3/Emu/CPU/CPUThreadManager.cpp @@ -108,6 +108,22 @@ CPUThread* CPUThreadManager::GetThread(u32 id) return res; } +void CPUThreadManager::NotifyThread(const u32 id) +{ + if (!id) return; + + std::lock_guard lock(m_mtx_thread); + + for (u32 i = 0; i < m_threads.size(); i++) + { + if (m_threads[i]->GetId() == id) + { + m_threads[i]->Notify(); + return; + } + } +} + void CPUThreadManager::Exec() { std::lock_guard lock(m_mtx_thread); diff --git a/rpcs3/Emu/CPU/CPUThreadManager.h b/rpcs3/Emu/CPU/CPUThreadManager.h index f4b307a82a..f40a9e936d 100644 --- a/rpcs3/Emu/CPU/CPUThreadManager.h +++ b/rpcs3/Emu/CPU/CPUThreadManager.h @@ -17,6 +17,7 @@ public: CPUThread& AddThread(CPUThreadType type); void RemoveThread(const u32 id); + void NotifyThread(const u32 id); std::vector& GetThreads() { return m_threads; } s32 GetThreadNumById(CPUThreadType type, u32 id); diff --git a/rpcs3/Emu/SysCalls/SysCalls.h b/rpcs3/Emu/SysCalls/SysCalls.h index 57dc717219..bb438c33f2 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.h +++ b/rpcs3/Emu/SysCalls/SysCalls.h @@ -11,6 +11,7 @@ #include "lv2/SC_Event_flag.h" #include "lv2/SC_Condition.h" #include "lv2/SC_Spinlock.h" +#include "lv2/SC_Semaphore.h" #include "Emu/event.h" #include "Static.h" //#define SYSCALLS_DEBUG @@ -144,12 +145,12 @@ extern int sys_event_flag_cancel(u32 eflag_id, mem32_t num); extern int sys_event_flag_get(u32 eflag_id, mem64_t flags); //sys_semaphore -extern int sys_semaphore_create(u32 sem_addr, u32 attr_addr, int initial_val, int max_val); -extern int sys_semaphore_destroy(u32 sem); -extern int sys_semaphore_wait(u32 sem, u64 timeout); -extern int sys_semaphore_trywait(u32 sem); -extern int sys_semaphore_post(u32 sem, int count); -extern int sys_semaphore_get_value(u32 sem, u32 count_addr); +extern int sys_semaphore_create(mem32_t sem, mem_ptr_t attr, int initial_count, int max_count); +extern int sys_semaphore_destroy(u32 sem_id); +extern int sys_semaphore_wait(u32 sem_id, u64 timeout); +extern int sys_semaphore_trywait(u32 sem_id); +extern int sys_semaphore_post(u32 sem_id, int count); +extern int sys_semaphore_get_value(u32 sem_id, mem32_t count); //sys_lwcond extern int sys_lwcond_create(mem_ptr_t lwcond, mem_ptr_t lwmutex, mem_ptr_t attr); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp index a5b97d88f3..f759f3f1b4 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp @@ -81,6 +81,7 @@ int sys_cond_signal(u32 cond_id) { cond->signal_stamp = get_system_time(); cond->signal.lock(target); + Emu.GetCPU().NotifyThread(target); if (Emu.IsStopped()) { @@ -105,15 +106,19 @@ int sys_cond_signal_all(u32 cond_id) while (u32 target = (mutex->protocol == SYS_SYNC_PRIORITY ? cond->m_queue.pop_prio() : cond->m_queue.pop())) { + cond->signaler = GetCurrentCPUThread()->GetId(); cond->signal_stamp = get_system_time(); cond->signal.lock(target); + Emu.GetCPU().NotifyThread(target); if (Emu.IsStopped()) { ConLog.Warning("sys_cond_signal_all(id=%d) aborted", cond_id); + break; } } + cond->signaler = 0; return CELL_OK; } @@ -143,6 +148,7 @@ int sys_cond_signal_to(u32 cond_id, u32 thread_id) { cond->signal_stamp = get_system_time(); cond->signal.lock(target); + Emu.GetCPU().NotifyThread(target); } if (Emu.IsStopped()) @@ -188,6 +194,7 @@ int sys_cond_wait(u32 cond_id, u64 timeout) { if (cond->signal.unlock(tid, tid) == SMR_OK) { + const u64 stamp2 = get_system_time(); if (SMutexResult res = mutex->m_mutex.trylock(tid)) { if (res != SMR_FAILED) @@ -209,11 +216,12 @@ int sys_cond_wait(u32 cond_id, u64 timeout) mutex->recursive = 1; const volatile u64 stamp = cond->signal_stamp; cond->signal.unlock(tid); - //ConLog.Write("sys_cond_wait(): signal latency %d", get_system_time() - stamp); + Emu.GetCPU().NotifyThread(cond->signaler); + //ConLog.Write("sys_cond_wait(): signal latency %lld (minimum %lld)", get_system_time() - stamp, stamp2 - stamp); return CELL_OK; } - Sleep(1); + SM_Sleep(); if (counter++ > max_counter) { diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Condition.h b/rpcs3/Emu/SysCalls/lv2/SC_Condition.h index 145a20868a..d4b64c12cd 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Condition.h +++ b/rpcs3/Emu/SysCalls/lv2/SC_Condition.h @@ -17,12 +17,15 @@ struct Cond { Mutex* mutex; // associated with mutex SMutex signal; + u32 signaler; // signaler thread id (for signal_all) SleepQueue m_queue; + u64 signal_stamp; Cond(Mutex* mutex, u64 name) : mutex(mutex) , m_queue(name) + , signaler(0) { } }; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp index 20a5f64f54..4461ce9dfe 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp @@ -190,6 +190,13 @@ bool SleepQueue::invalidate(u32 tid) return false; } +u32 SleepQueue::count() +{ + std::lock_guard lock(m_mutex); + + return list.size(); +} + bool SleepQueue::finalize() { if (!m_mutex.try_lock()) return false; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h index 1239593bee..ed24e07047 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h +++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h @@ -60,6 +60,7 @@ struct SleepQueue u32 pop_prio(); // SYS_SYNC_PRIORITY u32 pop_prio_inherit(); // (TODO) bool invalidate(u32 tid); + u32 count(); bool finalize(); }; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp index f4cf2785ff..c176dcc8fa 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp @@ -93,7 +93,7 @@ int sys_mutex_destroy(u32 mutex_id) int sys_mutex_lock(u32 mutex_id, u64 timeout) { - sys_mtx.Log("sys_mutex_lock(mutex_id=%d, timeout=0x%llx)", mutex_id, timeout); + sys_mtx.Log("sys_mutex_lock(mutex_id=%d, timeout=%lld)", mutex_id, timeout); Mutex* mutex; if (!Emu.GetIdManager().GetIDData(mutex_id, mutex)) diff --git a/rpcs3/Emu/SysCalls/lv2/SC_RSX.cpp b/rpcs3/Emu/SysCalls/lv2/SC_RSX.cpp index 3020518815..1fce0dc7d0 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_RSX.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_RSX.cpp @@ -8,72 +8,72 @@ SysCallBase sys_rsx("sys_rsx"); int sys_rsx_device_open() { - sys_rsx.Warning("Unimplemented function: sys_rsx_device_open()"); + sys_rsx.Error("TODO: sys_rsx_device_open()"); return CELL_OK; } int sys_rsx_device_close() { - sys_rsx.Warning("Unimplemented function: sys_rsx_device_close()"); + sys_rsx.Error("TODO: sys_rsx_device_close()"); return CELL_OK; } int sys_rsx_memory_allocate() { - sys_rsx.Warning("Unimplemented function: sys_rsx_memory_allocate()"); + sys_rsx.Error("TODO: sys_rsx_memory_allocate()"); return CELL_OK; } int sys_rsx_memory_free() { - sys_rsx.Warning("Unimplemented function: sys_rsx_memory_free()"); + sys_rsx.Error("TODO: sys_rsx_memory_free()"); return CELL_OK; } int sys_rsx_context_allocate() { - sys_rsx.Warning("Unimplemented function: sys_rsx_context_allocate()"); + sys_rsx.Error("TODO: sys_rsx_context_allocate()"); return CELL_OK; } int sys_rsx_context_free() { - sys_rsx.Warning("Unimplemented function: sys_rsx_context_free()"); + sys_rsx.Error("TODO: sys_rsx_context_free()"); return CELL_OK; } int sys_rsx_context_iomap() { - sys_rsx.Warning("Unimplemented function: sys_rsx_context_iomap()"); + sys_rsx.Error("TODO: sys_rsx_context_iomap()"); return CELL_OK; } int sys_rsx_context_iounmap() { - sys_rsx.Warning("Unimplemented function: sys_rsx_context_iounmap()"); + sys_rsx.Error("TODO: sys_rsx_context_iounmap()"); return CELL_OK; } int sys_rsx_context_attribute(s32 context_id, u64 a2, u64 a3, u64 a4, u64 a5, u64 a6) { - sys_rsx.Warning("Unimplemented function: sys_rsx_context_attribute(context_id=%d, a2=%llu, a3=%llu, a4=%llu, a5=%llu, a6=%llu)", context_id, a2, a3, a4, a5, a6); + sys_rsx.Error("TODO: sys_rsx_context_attribute(context_id=%d, a2=%llu, a3=%llu, a4=%llu, a5=%llu, a6=%llu)", context_id, a2, a3, a4, a5, a6); return CELL_OK; } int sys_rsx_device_map(mem32_t a1, mem32_t a2, u32 a3) { - sys_rsx.Warning("Unimplemented function: sys_rsx_device_map(a1_addr=0x%x, a2_addr=0x%x, a3=%d)", a1.GetAddr(), a2.GetAddr(), a3); + sys_rsx.Error("TODO: sys_rsx_device_map(a1_addr=0x%x, a2_addr=0x%x, a3=%d)", a1.GetAddr(), a2.GetAddr(), a3); return CELL_OK; } int sys_rsx_device_unmap() { - sys_rsx.Warning("Unimplemented function: sys_rsx_device_unmap()"); + sys_rsx.Error("TODO: sys_rsx_device_unmap()"); return CELL_OK; } int sys_rsx_attribute() { - sys_rsx.Warning("Unimplemented function: sys_rsx_attribute()"); + sys_rsx.Error("TODO: sys_rsx_attribute()"); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Semaphore.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Semaphore.cpp index 174dfd839a..2ef88daefd 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Semaphore.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Semaphore.cpp @@ -3,116 +3,203 @@ #include "Emu/Memory/Memory.h" #include "Emu/System.h" #include "Emu/SysCalls/SysCalls.h" +#include "SC_Semaphore.h" SysCallBase sys_sem("sys_semaphore"); -struct semaphore_attr -{ - u32 protocol; - u32 pshared; - u64 ipc_key; - int flags; - u32 pad; - char name[8]; -}; - -struct semaphore -{ - rSemaphore sem; - semaphore_attr attr; - int sem_count; - - semaphore(int initial_count, int max_count, semaphore_attr attr) - : sem(initial_count, max_count) - , attr(attr) - { - } -}; - -int sys_semaphore_create(u32 sem_addr, u32 attr_addr, int initial_count, int max_count) +int sys_semaphore_create(mem32_t sem, mem_ptr_t attr, int initial_count, int max_count) { sys_sem.Warning("sys_semaphore_create(sem_addr=0x%x, attr_addr=0x%x, initial_count=%d, max_count=%d)", - sem_addr, attr_addr, initial_count, max_count); + sem.GetAddr(), attr.GetAddr(), initial_count, max_count); - if(!Memory.IsGoodAddr(sem_addr) || !Memory.IsGoodAddr(attr_addr)) return CELL_EFAULT; - - semaphore_attr attr = (semaphore_attr&)Memory[attr_addr]; - attr.protocol = re(attr.protocol); - attr.pshared = re(attr.pshared); - attr.ipc_key = re(attr.ipc_key); - attr.flags = re(attr.flags); - - sys_sem.Log("*** protocol = %d", attr.protocol); - sys_sem.Log("*** pshared = %d", attr.pshared); - sys_sem.Log("*** ipc_key = 0x%llx", attr.ipc_key); - sys_sem.Log("*** flags = 0x%x", attr.flags); - sys_sem.Log("*** name = %s", attr.name); - - Memory.Write32(sem_addr, sys_sem.GetNewId(new semaphore(initial_count, max_count, attr))); - - return CELL_OK; -} - -int sys_semaphore_destroy(u32 sem) -{ - sys_sem.Log("sys_semaphore_destroy(sem=%d)", sem); - - if(!sys_sem.CheckId(sem)) return CELL_ESRCH; - - Emu.GetIdManager().RemoveID(sem); - return CELL_OK; -} - -int sys_semaphore_wait(u32 sem, u64 timeout) -{ - sys_sem.Log("sys_semaphore_wait(sem=0x%x, timeout=0x%llx)", sem, timeout); - - semaphore* sem_data = nullptr; - if(!sys_sem.CheckId(sem, sem_data)) return CELL_ESRCH; - - sem_data->sem_count = 0; // Reset internal counter for sys_semaphore_get_value. - sem_data->sem.WaitTimeout(timeout ? timeout : INFINITE); - - return CELL_OK; -} - -int sys_semaphore_trywait(u32 sem) -{ - sys_sem.Log("sys_semaphore_trywait(sem=%d)", sem); - - semaphore* sem_data = nullptr; - if(!sys_sem.CheckId(sem, sem_data)) return CELL_ESRCH; - - sem_data->sem_count = 0; // Reset internal counter for sys_semaphore_get_value. - if(sem_data->sem.TryWait()) return 1; - - return CELL_OK; -} - -int sys_semaphore_post(u32 sem, int count) -{ - sys_sem.Log("sys_semaphore_post(sem=%d, count=%d)", sem, count); - - semaphore* sem_data = nullptr; - if(!sys_sem.CheckId(sem, sem_data)) return CELL_ESRCH; - - while(count--) + if (!sem.IsGood() || !attr.IsGood()) { - sem_data->sem_count++; // Increment internal counter for sys_semaphore_get_value. - sem_data->sem.Post(); + return CELL_EFAULT; + } + + if (max_count <= 0) + { + return CELL_EINVAL; + } + + if (attr->pshared.ToBE() != se32(0x200)) + { + sys_sem.Error("Invalid pshared attribute(0x%x)", (u32)attr->pshared); + return CELL_EINVAL; + } + + switch (attr->protocol.ToBE()) + { + case se32(SYS_SYNC_FIFO): break; + case se32(SYS_SYNC_PRIORITY): break; + case se32(SYS_SYNC_PRIORITY_INHERIT): sys_sem.Warning("TODO: SYS_SYNC_PRIORITY_INHERIT protocol"); break; + case se32(SYS_SYNC_RETRY): sys_sem.Error("Invalid SYS_SYNC_RETRY protocol"); return CELL_EINVAL; + default: sys_sem.Error("Unknown protocol attribute(0x%x)", (u32)attr->protocol); return CELL_EINVAL; + } + + sem = sys_sem.GetNewId(new Semaphore(initial_count, max_count, attr->protocol, attr->name_u64)); + ConLog.Write("*** semaphore created [%s] (protocol=0x%x): id = %d", + std::string(attr->name, 8).c_str(), (u32)attr->protocol, sem.GetValue()); + + return CELL_OK; +} + +int sys_semaphore_destroy(u32 sem_id) +{ + sys_sem.Warning("sys_semaphore_destroy(sem_id=%d)", sem_id); + + Semaphore* sem; + if (!Emu.GetIdManager().GetIDData(sem_id, sem)) + { + return CELL_ESRCH; + } + + if (!sem->m_queue.finalize()) + { + return CELL_EBUSY; + } + + Emu.GetIdManager().RemoveID(sem_id); + return CELL_OK; +} + +int sys_semaphore_wait(u32 sem_id, u64 timeout) +{ + sys_sem.Log("sys_semaphore_wait(sem_id=%d, timeout=%lld)", sem_id, timeout); + + Semaphore* sem; + if (!Emu.GetIdManager().GetIDData(sem_id, sem)) + { + return CELL_ESRCH; + } + + const u32 tid = GetCurrentPPUThread().GetId(); + const u64 start_time = get_system_time(); + + { + std::lock_guard lock(sem->m_mutex); + if (sem->m_value > 0) + { + sem->m_value--; + return CELL_OK; + } + sem->m_queue.push(tid); + } + + while (true) + { + if (Emu.IsStopped()) + { + ConLog.Warning("sys_semaphore_wait(%d) aborted", sem_id); + return CELL_OK; + } + + if (timeout && get_system_time() - start_time > timeout) + { + return CELL_ETIMEDOUT; + } + + if (tid == sem->signal) + { + std::lock_guard lock(sem->m_mutex); + + sem->signal = 0; + // TODO: notify signaler + return CELL_OK; + } + + SM_Sleep(); + } +} + +int sys_semaphore_trywait(u32 sem_id) +{ + sys_sem.Log("sys_semaphore_trywait(sem_id=%d)", sem_id); + + Semaphore* sem; + if (!Emu.GetIdManager().GetIDData(sem_id, sem)) + { + return CELL_ESRCH; + } + + std::lock_guard lock(sem->m_mutex); + + if (sem->m_value > 0) + { + sem->m_value--; + return CELL_OK; + } + else + { + return CELL_EBUSY; + } +} + +int sys_semaphore_post(u32 sem_id, int count) +{ + sys_sem.Log("sys_semaphore_post(sem_id=%d, count=%d)", sem_id, count); + + Semaphore* sem; + if (!Emu.GetIdManager().GetIDData(sem_id, sem)) + { + return CELL_ESRCH; + } + + if (count < 0) + { + return CELL_EINVAL; + } + + if (count + sem->m_value - sem->m_queue.count() > sem->max) + { + return CELL_EINVAL; + } + + while (count > 0) + { + if (Emu.IsStopped()) + { + ConLog.Warning("sys_semaphore_post(%d) aborted", sem_id); + return CELL_OK; + } + + std::lock_guard lock(sem->m_mutex); + + if (sem->signal && sem->m_queue.count()) + { + SM_Sleep(); + continue; + } + + if (u32 target = (sem->protocol == SYS_SYNC_FIFO) ? sem->m_queue.pop() : sem->m_queue.pop_prio()) + { + count--; + sem->signal = target; + Emu.GetCPU().NotifyThread(target); + } + else + { + sem->m_value += count; + count = 0; + } } return CELL_OK; } -int sys_semaphore_get_value(u32 sem, u32 count_addr) +int sys_semaphore_get_value(u32 sem_id, mem32_t count) { - sys_sem.Log("sys_semaphore_get_value(sem=%d, count_addr=0x%x)", sem, count_addr); + sys_sem.Log("sys_semaphore_get_value(sem_id=%d, count_addr=0x%x)", sem_id, count.GetAddr()); - semaphore* sem_data = nullptr; - if(!sys_sem.CheckId(sem, sem_data)) return CELL_ESRCH; + Semaphore* sem; + if (!Emu.GetIdManager().GetIDData(sem_id, sem)) + { + return CELL_ESRCH; + } - Memory.Write32(count_addr, sem_data->sem_count); + std::lock_guard lock(sem->m_mutex); + + count = sem->m_value; return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Semaphore.h b/rpcs3/Emu/SysCalls/lv2/SC_Semaphore.h new file mode 100644 index 0000000000..4531041312 --- /dev/null +++ b/rpcs3/Emu/SysCalls/lv2/SC_Semaphore.h @@ -0,0 +1,36 @@ +#pragma once + +struct sys_semaphore_attribute +{ + be_t protocol; + be_t pshared; // undefined + be_t ipc_key; // undefined + be_t flags; // undefined + be_t pad; // not used + union + { + char name[8]; + u64 name_u64; + }; +}; + +struct Semaphore +{ + std::mutex m_mutex; + SleepQueue m_queue; + int m_value; + u32 signal; + + const int max; + const u32 protocol; + const u64 name; + + Semaphore(int initial_count, int max_count, u32 protocol, u64 name) + : m_value(initial_count) + , signal(0) + , max(max_count) + , protocol(protocol) + , name(name) + { + } +}; \ No newline at end of file diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 25be8d5571..031e1039df 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -321,6 +321,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 4f530fafa5..5ddf7ac06e 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1048,5 +1048,8 @@ Utilities + + Emu\SysCalls\lv2 + \ No newline at end of file