diff --git a/rpcs3/Emu/Memory/atomic.h b/rpcs3/Emu/Memory/atomic.h index 385a3477db..26e8e29c16 100644 --- a/rpcs3/Emu/Memory/atomic.h +++ b/rpcs3/Emu/Memory/atomic.h @@ -33,11 +33,11 @@ template struct _to_atomic_subtype template using atomic_subtype_t = typename _to_atomic_subtype::type; // result wrapper to deal with void result type -template struct atomic_op_result_t +template struct atomic_op_result_t { RT result; - template inline atomic_op_result_t(T func, Args&&... args) + template inline atomic_op_result_t(T func, Args&&... args) : result(std::move(func(std::forward(args)...))) { } @@ -48,16 +48,20 @@ template struct atomic_op_result_t } }; -// void specialization -template<> struct atomic_op_result_t +// void specialization: result is the initial value of the first arg +template struct atomic_op_result_t { - template inline atomic_op_result_t(T func, Args&&... args) + RT result; + + template inline atomic_op_result_t(T func, RT& var, Args&&... args) + : result(var) { - func(std::forward(args)...); + func(var, std::forward(args)...); } - inline void move() + inline RT move() { + return std::move(result); } }; @@ -144,7 +148,7 @@ public: } // perform an atomic operation on data (callable object version, first arg is a reference to atomic type) - template auto atomic_op(F func, Args&&... args) volatile -> decltype(func(std::declval(), args...)) + template> auto atomic_op(F func, Args&&... args) volatile -> decltype(std::declval>().result) { while (true) { @@ -155,7 +159,7 @@ public: subtype _new = old; // call atomic op for the local copy of the old value and save the return value of the function - atomic_op_result_t> result(func, to_type(_new), args...); + atomic_op_result_t result(func, to_type(_new), args...); // atomically compare value with `old`, replace with `_new` and return on success if (sync_bool_compare_and_swap(&sub_data, old, _new)) return result.move(); @@ -163,7 +167,12 @@ public: } // perform an atomic operation on data (member function version) - template::value>> auto atomic_op(RT(CT::* func)(FArgs...), Args&&... args) volatile -> decltype((std::declval().*func)(args...)) + template RT atomic_op(RT(CT::* func)(FArgs...), Args&&... args) volatile + { + return atomic_op(std::mem_fn(func), std::forward(args)...); + } + + template T atomic_op(void(CT::* func)(FArgs...), Args&&... args) volatile { return atomic_op(std::mem_fn(func), std::forward(args)...); } diff --git a/rpcs3/Emu/SysCalls/lv2/sys_cond.cpp b/rpcs3/Emu/SysCalls/lv2/sys_cond.cpp index 1f77a1ebe6..8001ededbc 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_cond.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_cond.cpp @@ -12,12 +12,10 @@ SysCallBase sys_cond("sys_cond"); extern u64 get_system_time(); -void lv2_cond_t::notify(lv2_lock_t& lv2_lock, sleep_queue_t::iterator it) +void lv2_cond_t::notify(lv2_lock_t& lv2_lock, sleep_queue_t::value_type& thread) { CHECK_LV2_LOCK(lv2_lock); - auto& thread = *it; - if (mutex->owner) { // add thread to the mutex sleep queue if cannot lock immediately @@ -107,7 +105,7 @@ s32 sys_cond_signal(u32 cond_id) // signal one waiting thread; protocol is ignored in current implementation if (!cond->sq.empty()) { - cond->notify(lv2_lock, cond->sq.begin()); + cond->notify(lv2_lock, cond->sq.front()); cond->sq.pop_front(); } @@ -128,9 +126,9 @@ s32 sys_cond_signal_all(u32 cond_id) } // signal all waiting threads; protocol is ignored in current implementation - for (auto it = cond->sq.begin(); it != cond->sq.end(); it++) + for (auto& thread : cond->sq) { - cond->notify(lv2_lock, it); + cond->notify(lv2_lock, thread); } cond->sq.clear(); @@ -151,20 +149,22 @@ s32 sys_cond_signal_to(u32 cond_id, u32 thread_id) return CELL_ESRCH; } - // TODO: check if CELL_ESRCH is returned if thread_id is invalid - - // signal specified thread (protocol is not required) - for (auto it = cond->sq.begin(); it != cond->sq.end(); it++) + const auto found = std::find_if(cond->sq.begin(), cond->sq.end(), [=](sleep_queue_t::value_type& thread) { - if ((*it)->get_id() == thread_id) - { - cond->notify(lv2_lock, it); - cond->sq.erase(it); - return CELL_OK; - } + return thread->get_id() == thread_id; + }); + + // TODO: check if CELL_ESRCH is returned if thread_id is invalid + if (found == cond->sq.end()) + { + return CELL_EPERM; } - return CELL_EPERM; + // signal specified thread + cond->notify(lv2_lock, *found); + cond->sq.erase(found); + + return CELL_OK; } s32 sys_cond_wait(PPUThread& ppu, u32 cond_id, u64 timeout) diff --git a/rpcs3/Emu/SysCalls/lv2/sys_cond.h b/rpcs3/Emu/SysCalls/lv2/sys_cond.h index 10038d8a9c..64d1460468 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_cond.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_cond.h @@ -32,7 +32,7 @@ struct lv2_cond_t { } - void notify(lv2_lock_t& lv2_lock, sleep_queue_t::iterator it); + void notify(lv2_lock_t& lv2_lock, sleep_queue_t::value_type& thread); }; REG_ID_TYPE(lv2_cond_t, 0x86); // SYS_COND_OBJECT diff --git a/rpcs3/Emu/SysCalls/lv2/sys_lwcond.cpp b/rpcs3/Emu/SysCalls/lv2/sys_lwcond.cpp index 8f9b52585a..4fd6f08b9f 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_lwcond.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_lwcond.cpp @@ -12,11 +12,11 @@ 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& mutex, bool mode2) +void lv2_lwcond_t::notify(lv2_lock_t & lv2_lock, sleep_queue_t::value_type& thread, const std::shared_ptr& mutex, bool mode2) { CHECK_LV2_LOCK(lv2_lock); - auto& ppu = static_cast(*it->get()); + auto& ppu = static_cast(*thread); ppu.GPR[3] = mode2; // set to return CELL_EBUSY @@ -24,7 +24,7 @@ void lv2_lwcond_t::notify(lv2_lock_t & lv2_lock, sleep_queue_t::iterator it, con { if (!mutex->signaled) { - return mutex->sq.emplace_back(*it); + return mutex->sq.emplace_back(thread); } mutex->signaled--; @@ -114,7 +114,7 @@ s32 _sys_lwcond_signal(u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, u32 mod } // signal specified waiting thread - cond->notify(lv2_lock, found, mutex, mode == 2); + cond->notify(lv2_lock, *found, mutex, mode == 2); cond->sq.erase(found); @@ -144,9 +144,9 @@ s32 _sys_lwcond_signal_all(u32 lwcond_id, u32 lwmutex_id, u32 mode) // mode 2: lightweight mutex was not owned by the calling thread and waiter hasn't been increased // signal all waiting threads; protocol is ignored in current implementation - for (auto it = cond->sq.begin(); it != cond->sq.end(); it++) + for (auto& thread : cond->sq) { - cond->notify(lv2_lock, it, mutex, mode == 2); + cond->notify(lv2_lock, thread, mutex, mode == 2); } // in mode 1, return the amount of threads signaled diff --git a/rpcs3/Emu/SysCalls/lv2/sys_lwcond.h b/rpcs3/Emu/SysCalls/lv2/sys_lwcond.h index 8541ec3486..d898bcb416 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_lwcond.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_lwcond.h @@ -32,7 +32,7 @@ struct lv2_lwcond_t { } - void notify(lv2_lock_t& lv2_lock, sleep_queue_t::iterator it, const std::shared_ptr& mutex, bool mode2); + void notify(lv2_lock_t& lv2_lock, sleep_queue_t::value_type& thread, const std::shared_ptr& mutex, bool mode2); }; REG_ID_TYPE(lv2_lwcond_t, 0x97); // SYS_LWCOND_OBJECT diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index d4228ea8af..fa2a95b389 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -200,15 +200,20 @@ public: force_inline bool IsReady() const { return m_status == Ready; } }; +extern Emulator Emu; + using lv2_lock_t = std::unique_lock; +inline bool check_lv2_lock(lv2_lock_t& lv2_lock) +{ + return lv2_lock.owns_lock() && lv2_lock.mutex() == &Emu.GetCoreMutex(); +} + #define LV2_LOCK lv2_lock_t lv2_lock(Emu.GetCoreMutex()) #define LV2_DEFER_LOCK lv2_lock_t lv2_lock -#define CHECK_LV2_LOCK(x) if (!(x).owns_lock() || (x).mutex() != &Emu.GetCoreMutex()) throw EXCEPTION("Invalid LV2_LOCK (locked=%d)", (x).owns_lock()) +#define CHECK_LV2_LOCK(x) if (!check_lv2_lock(x)) throw EXCEPTION("lv2_lock is invalid or not locked") #define CHECK_EMU_STATUS if (Emu.IsStopped()) throw EXCEPTION("Aborted (emulation stopped)") -extern Emulator Emu; - typedef void(*CallAfterCbType)(std::function func); void CallAfter(std::function func);