From 78c37ff8b6030fb2ca27e05679ad6d205c9be5ec Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Tue, 3 Mar 2015 23:09:23 +0300 Subject: [PATCH] Some bicycle for PSV --- rpcs3/Emu/ARMv7/Modules/psv_cond.h | 15 +- rpcs3/Emu/ARMv7/Modules/psv_event_flag.h | 15 +- rpcs3/Emu/ARMv7/Modules/psv_mutex.h | 15 +- rpcs3/Emu/ARMv7/Modules/psv_sema.h | 15 +- rpcs3/Emu/ARMv7/Modules/sceLibKernel.cpp | 58 ++++--- rpcs3/Emu/ARMv7/PSVObjectList.cpp | 8 +- rpcs3/Emu/ARMv7/PSVObjectList.h | 79 ++++----- rpcs3/Emu/Cell/SPUThread.cpp | 4 +- rpcs3/Emu/Memory/refcnt.h | 196 +++++++++++++++++++++++ rpcs3/emucore.vcxproj | 1 + rpcs3/emucore.vcxproj.filters | 3 + rpcs3/stdafx.h | 1 + 12 files changed, 300 insertions(+), 110 deletions(-) create mode 100644 rpcs3/Emu/Memory/refcnt.h diff --git a/rpcs3/Emu/ARMv7/Modules/psv_cond.h b/rpcs3/Emu/ARMv7/Modules/psv_cond.h index cd8c518615..812b307695 100644 --- a/rpcs3/Emu/ARMv7/Modules/psv_cond.h +++ b/rpcs3/Emu/ARMv7/Modules/psv_cond.h @@ -6,19 +6,10 @@ struct psv_cond_t u32 attr; s32 mutexId; -private: - psv_cond_t() = delete; - psv_cond_t(const psv_cond_t&) = delete; - psv_cond_t(psv_cond_t&&) = delete; - - psv_cond_t& operator =(const psv_cond_t&) = delete; - psv_cond_t& operator =(psv_cond_t&&) = delete; - public: psv_cond_t(const char* name, u32 attr, s32 mutexId); - void on_init(s32 id) {} - void on_stop() {} - }; -extern psv_object_list_t g_psv_cond_list; +typedef psv_object_list_t psv_cond_list_t; + +extern psv_cond_list_t g_psv_cond_list; diff --git a/rpcs3/Emu/ARMv7/Modules/psv_event_flag.h b/rpcs3/Emu/ARMv7/Modules/psv_event_flag.h index 44f1c8214b..79a3f2ec86 100644 --- a/rpcs3/Emu/ARMv7/Modules/psv_event_flag.h +++ b/rpcs3/Emu/ARMv7/Modules/psv_event_flag.h @@ -6,19 +6,10 @@ struct psv_event_flag_t u32 attr; u32 pattern; -private: - psv_event_flag_t() = delete; - psv_event_flag_t(const psv_event_flag_t&) = delete; - psv_event_flag_t(psv_event_flag_t&&) = delete; - - psv_event_flag_t& operator =(const psv_event_flag_t&) = delete; - psv_event_flag_t& operator =(psv_event_flag_t&&) = delete; - public: psv_event_flag_t(const char* name, u32 attr, u32 pattern); - void on_init(s32 id) {} - void on_stop() {} - }; -extern psv_object_list_t g_psv_ef_list; +typedef psv_object_list_t psv_ef_list_t; + +extern psv_ef_list_t g_psv_ef_list; diff --git a/rpcs3/Emu/ARMv7/Modules/psv_mutex.h b/rpcs3/Emu/ARMv7/Modules/psv_mutex.h index 5da6ad4590..824c0ff96e 100644 --- a/rpcs3/Emu/ARMv7/Modules/psv_mutex.h +++ b/rpcs3/Emu/ARMv7/Modules/psv_mutex.h @@ -6,19 +6,10 @@ struct psv_mutex_t u32 attr; s32 count; -private: - psv_mutex_t() = delete; - psv_mutex_t(const psv_mutex_t&) = delete; - psv_mutex_t(psv_mutex_t&&) = delete; - - psv_mutex_t& operator =(const psv_mutex_t&) = delete; - psv_mutex_t& operator =(psv_mutex_t&&) = delete; - public: psv_mutex_t(const char* name, u32 attr, s32 count); - void on_init(s32 id) {} - void on_stop() {} - }; -extern psv_object_list_t g_psv_mutex_list; +typedef psv_object_list_t psv_mutex_list_t; + +extern psv_mutex_list_t g_psv_mutex_list; diff --git a/rpcs3/Emu/ARMv7/Modules/psv_sema.h b/rpcs3/Emu/ARMv7/Modules/psv_sema.h index 95608c3c04..c4c6715ea2 100644 --- a/rpcs3/Emu/ARMv7/Modules/psv_sema.h +++ b/rpcs3/Emu/ARMv7/Modules/psv_sema.h @@ -7,19 +7,10 @@ struct psv_sema_t s32 value; s32 max; -private: - psv_sema_t() = delete; - psv_sema_t(const psv_sema_t&) = delete; - psv_sema_t(psv_sema_t&&) = delete; - - psv_sema_t& operator =(const psv_sema_t&) = delete; - psv_sema_t& operator =(psv_sema_t&&) = delete; - public: psv_sema_t(const char* name, u32 attr, s32 init_value, s32 max_value); - void on_init(s32 id) {} - void on_stop() {} - }; -extern psv_object_list_t g_psv_sema_list; +typedef psv_object_list_t psv_sema_list_t; + +extern psv_sema_list_t g_psv_sema_list; diff --git a/rpcs3/Emu/ARMv7/Modules/sceLibKernel.cpp b/rpcs3/Emu/ARMv7/Modules/sceLibKernel.cpp index cd17ee6894..90b27abe84 100644 --- a/rpcs3/Emu/ARMv7/Modules/sceLibKernel.cpp +++ b/rpcs3/Emu/ARMv7/Modules/sceLibKernel.cpp @@ -398,11 +398,12 @@ s32 sceKernelCreateEventFlag(vm::psv::ptr pName, u32 attr, u32 initP { sceLibKernel.Error("sceKernelCreateEventFlag(pName=0x%x, attr=0x%x, initPattern=0x%x, pOptParam=0x%x)", pName, attr, initPattern, pOptParam); - std::shared_ptr ef(new psv_event_flag_t(pName.get_ptr(), attr, initPattern)); + if (s32 id = g_psv_ef_list.add(new psv_event_flag_t(pName.get_ptr(), attr, initPattern), 0)) + { + return id; + } - const s32 id = g_psv_ef_list.add(ef); - - return id; + RETURN_ERROR(SCE_KERNEL_ERROR_ERROR); } s32 sceKernelDeleteEventFlag(s32 evfId) @@ -461,23 +462,31 @@ s32 sceKernelCreateSema(vm::psv::ptr pName, u32 attr, s32 initCount, { sceLibKernel.Error("sceKernelCreateSema(pName=0x%x, attr=0x%x, initCount=%d, maxCount=%d, pOptParam=0x%x)", pName, attr, initCount, maxCount, pOptParam); - std::shared_ptr sema(new psv_sema_t(pName.get_ptr(), attr, initCount, maxCount)); + if (s32 id = g_psv_sema_list.add(new psv_sema_t(pName.get_ptr(), attr, initCount, maxCount), 0)) + { + return id; + } - const s32 id = g_psv_sema_list.add(sema); - - return id; + RETURN_ERROR(SCE_KERNEL_ERROR_ERROR); } s32 sceKernelDeleteSema(s32 semaId) { sceLibKernel.Error("sceKernelDeleteSema(semaId=0x%x)", semaId); + ref_t sema = g_psv_sema_list.get(semaId); + + if (!sema) + { + RETURN_ERROR(SCE_KERNEL_ERROR_INVALID_UID); + } + if (!g_psv_sema_list.remove(semaId)) { RETURN_ERROR(SCE_KERNEL_ERROR_INVALID_UID); } - throw SCE_OK; + return SCE_OK; } s32 sceKernelOpenSema(vm::psv::ptr pName) @@ -492,7 +501,18 @@ s32 sceKernelCloseSema(s32 semaId) s32 sceKernelWaitSema(s32 semaId, s32 needCount, vm::psv::ptr pTimeout) { - throw __FUNCTION__; + sceLibKernel.Error("sceKernelWaitSema(semaId=0x%x, needCount=%d, pTimeout=0x%x)", semaId, needCount, pTimeout); + + ref_t sema = g_psv_sema_list.get(semaId); + + if (!sema) + { + RETURN_ERROR(SCE_KERNEL_ERROR_INVALID_UID); + } + + sceLibKernel.Error("*** name = %s", sema->name); + Emu.Pause(); + return SCE_OK; } s32 sceKernelWaitSemaCB(s32 semaId, s32 needCount, vm::psv::ptr pTimeout) @@ -526,11 +546,12 @@ s32 sceKernelCreateMutex(vm::psv::ptr pName, u32 attr, s32 initCount { sceLibKernel.Error("sceKernelCreateMutex(pName=0x%x, attr=0x%x, initCount=%d, pOptParam=0x%x)", pName, attr, initCount, pOptParam); - std::shared_ptr mutex(new psv_mutex_t(pName.get_ptr(), attr, initCount)); + if (s32 id = g_psv_mutex_list.add(new psv_mutex_t(pName.get_ptr(), attr, initCount), 0)) + { + return id; + } - const s32 id = g_psv_mutex_list.add(mutex); - - return id; + RETURN_ERROR(SCE_KERNEL_ERROR_ERROR); } s32 sceKernelDeleteMutex(s32 mutexId) @@ -626,11 +647,12 @@ s32 sceKernelCreateCond(vm::psv::ptr pName, u32 attr, s32 mutexId, v { sceLibKernel.Error("sceKernelCreateCond(pName=0x%x, attr=0x%x, mutexId=0x%x, pOptParam=0x%x)", pName, attr, mutexId, pOptParam); - std::shared_ptr cond(new psv_cond_t(pName.get_ptr(), attr, mutexId)); + if (s32 id = g_psv_cond_list.add(new psv_cond_t(pName.get_ptr(), attr, mutexId), 0)) + { + return id; + } - const s32 id = g_psv_cond_list.add(cond); - - return id; + RETURN_ERROR(SCE_KERNEL_ERROR_ERROR); } s32 sceKernelDeleteCond(s32 condId) diff --git a/rpcs3/Emu/ARMv7/PSVObjectList.cpp b/rpcs3/Emu/ARMv7/PSVObjectList.cpp index 5e89e15c73..d0212876ba 100644 --- a/rpcs3/Emu/ARMv7/PSVObjectList.cpp +++ b/rpcs3/Emu/ARMv7/PSVObjectList.cpp @@ -8,10 +8,10 @@ #include "Modules/psv_mutex.h" #include "Modules/psv_cond.h" -psv_object_list_t g_psv_sema_list; -psv_object_list_t g_psv_ef_list; -psv_object_list_t g_psv_mutex_list; -psv_object_list_t g_psv_cond_list; +psv_sema_list_t g_psv_sema_list; +psv_ef_list_t g_psv_ef_list; +psv_mutex_list_t g_psv_mutex_list; +psv_cond_list_t g_psv_cond_list; void clear_all_psv_objects() { diff --git a/rpcs3/Emu/ARMv7/PSVObjectList.h b/rpcs3/Emu/ARMv7/PSVObjectList.h index 5be66688e0..8d471a2fa7 100644 --- a/rpcs3/Emu/ARMv7/PSVObjectList.h +++ b/rpcs3/Emu/ARMv7/PSVObjectList.h @@ -24,12 +24,26 @@ union psv_uid_t template class psv_object_list_t // Class for managing object data { - std::array, 0x8000> m_data; +public: + typedef refcounter_t rc_type; + typedef ref_t ref_type; + + static const u32 max = 0x8000; + +private: + std::array m_data; std::atomic m_hint; // guessing next free position - std::mutex m_mutex; // TODO: remove it when shared_ptr atomic ops are fully available + + void error(s32 uid) + { + throw fmt::format("Invalid UID requested (type=0x%x, uid=0x%x)", type, uid); + } public: - psv_object_list_t() : m_hint(0) {} + psv_object_list_t() + : m_hint(0) + { + } psv_object_list_t(const psv_object_list_t&) = delete; psv_object_list_t(psv_object_list_t&&) = delete; @@ -40,7 +54,7 @@ public: public: static const u32 uid_class = type; - // check if UID is potentially valid (will return true if the object doesn't exist) + // check if UID is potentially valid (will return true even if the object doesn't exist) bool check(s32 uid) { const psv_uid_t id = psv_uid_t::make(uid); @@ -49,76 +63,65 @@ public: return !id.sign && id.type == uid_class && id.oddness == 1; } - // share object with UID specified (will return empty pointer if the object doesn't exist or the UID is invalid) - std::shared_ptr find(s32 uid) + // share object with UID specified + ref_type get(s32 uid) { if (!check(uid)) { - return nullptr; + return ref_type(); } - return m_data[psv_uid_t::make(uid).number]; + return &m_data[psv_uid_t::make(uid).number]; } - std::shared_ptr operator [](s32 uid) + ref_type operator [](s32 uid) { return find(uid); } // generate UID for newly created object (will return zero if the limit exceeded) - s32 add(std::shared_ptr& data) + s32 add(T* data, s32 error_code) { - std::lock_guard lock(m_mutex); - - for (u32 i = 0, j = m_hint % m_data.size(); i < m_data.size(); i++, j = (j + 1) % m_data.size()) + for (u32 i = 0, j = m_hint; i < m_data.size(); i++, j = (j + 1) % m_data.size()) { // find an empty position and copy the pointer - if (!m_data[j]) + if (m_data[j].try_set(data)) { - m_data[j] = data; - m_hint = j + 1; // guess next position - psv_uid_t id = psv_uid_t::make(1); // odd number - id.type = uid_class; // set type - id.number = j; // set position - data->on_init(id.uid); // save UID + m_hint = (j + 1) % m_data.size(); // guess next position + + psv_uid_t id = psv_uid_t::make(1); // make UID + id.type = uid_class; + id.number = j; + return id.uid; // return UID } } - return 0; + delete data; + return error_code; } - // remove object with UID specified and share it for the last time (will return empty pointer if the object doesn't exists or the UID is invalid) - std::shared_ptr remove(s32 uid) + // remove object with specified UID + bool remove(s32 uid) { if (!check(uid)) { - return nullptr; + return false; } const u32 pos = psv_uid_t::make(uid).number; - std::lock_guard lock(m_mutex); + m_hint = std::min(pos, m_hint); - std::shared_ptr old_ptr = nullptr; - m_data[pos].swap(old_ptr); - m_hint = pos; - return old_ptr; + return m_data[pos].try_remove(); } // remove all objects void clear() { - std::lock_guard lock(m_mutex); - - for (auto& object : m_data) + for (auto& v : m_data) { - if (object) - { - object->on_stop(); - } - - object = nullptr; + v.try_remove(); } m_hint = 0; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 042f2f81bc..572a26f567 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -832,7 +832,7 @@ void SPUThread::set_ch_value(u32 ch, u32 value) if (mfc_queue[i].second.tag == value) { do_dma_list_cmd(mfc_queue[i].first, mfc_queue[i].second); - mfc_queue[i].second.tag = ~0; + mfc_queue[i].second.tag = 0xdead; processed++; } } @@ -841,7 +841,7 @@ void SPUThread::set_ch_value(u32 ch, u32 value) { for (size_t i = 0; i < mfc_queue.size(); i++) { - if (mfc_queue[i].second.tag == ~0) + if (mfc_queue[i].second.tag == 0xdead) { mfc_queue.erase(mfc_queue.begin() + i); processed--; diff --git a/rpcs3/Emu/Memory/refcnt.h b/rpcs3/Emu/Memory/refcnt.h new file mode 100644 index 0000000000..c756d93722 --- /dev/null +++ b/rpcs3/Emu/Memory/refcnt.h @@ -0,0 +1,196 @@ +#pragma once +#include "atomic.h" + +// run endless loop for debugging +__forceinline static void deadlock() +{ + while (true) + { + std::this_thread::yield(); + } +} + +template +class ref_t; + +template +class refcounter_t // non-relocateable "smart" pointer with ref counter +{ +public: + typedef T type, * p_type; + typedef refcounter_t rc_type; + + // counter > 0, ptr != nullptr : object exists and shared + // counter > 0, ptr == nullptr : object exists and shared, but not owned by refcounter_t + // counter == 0, ptr != nullptr : object exists and not shared + // counter == 0, ptr == nullptr : object doesn't exist + // counter < 0 : bad state, used to provoke error for debugging + + struct sync_var_t + { + s64 counter; + p_type ptr; + }; + +private: + atomic_le_t m_var; + + friend class ref_t; + + // try to share object (increment counter), returns nullptr if doesn't exist or cannot be shared + __forceinline p_type ref_inc() + { + p_type out_ptr; + + m_var.atomic_op([&out_ptr](sync_var_t& v) + { + assert(v.counter >= 0); + + if ((out_ptr = v.ptr)) + { + v.counter++; + } + }); + + return out_ptr; + } + + // try to release previously shared object (decrement counter), returns true if should be deleted + __forceinline bool ref_dec() + { + bool do_delete; + + m_var.atomic_op([&do_delete](sync_var_t& v) + { + assert(v.counter > 0); + + do_delete = !--v.counter && !v.ptr; + }); + + return do_delete; + } + +public: + refcounter_t() + { + // initialize ref counter + m_var.write_relaxed({ 0, nullptr }); + } + + ~refcounter_t() + { + // set bad state + auto ref = m_var.exchange({ -1, nullptr }); + + // finalize + if (ref.counter) + { + deadlock(); + } + else if (ref.ptr) + { + delete ref.ptr; + } + } + + refcounter_t(const rc_type& right) = delete; + refcounter_t(rc_type&& right_rv) = delete; + + rc_type& operator =(const rc_type& right) = delete; + rc_type& operator =(rc_type&& right_rv) = delete; + +public: + // try to set new object (if it doesn't exist) + bool try_set(p_type ptr) + { + return m_var.compare_and_swap_test({ 0, nullptr }, { 0, ptr }); + } + + // try to remove object (if exists) + bool try_remove() + { + bool out_res; + p_type out_ptr; + + m_var.atomic_op([&out_res, &out_ptr](sync_var_t& v) + { + out_res = (out_ptr = v.ptr); + + if (v.counter) + { + out_ptr = nullptr; + } + + v.ptr = nullptr; + }); + + if (out_ptr) + { + delete out_ptr; + } + + return out_res; + } +}; + +template +class ref_t +{ +public: + typedef T type, * p_type; + typedef refcounter_t * rc_type; + +private: + rc_type m_rc; + p_type m_ptr; + +public: + ref_t() + : m_rc(nullptr) + , m_ptr(nullptr) + { + } + + ref_t(rc_type rc) + : m_rc(rc) + , m_ptr(rc->ref_inc()) + { + } + + ~ref_t() + { + if (m_ptr && m_rc->ref_dec()) + { + delete m_ptr; + } + } + + ref_t(const ref_t& right) = delete; + + ref_t(ref_t&& right_rv) + : m_rc(right_rv.m_rc) + , m_ptr(right_rv.m_ptr) + { + right_rv.m_rc = nullptr; + right_rv.m_ptr = nullptr; + } + + ref_t& operator =(const ref_t& right) = delete; + ref_t& operator =(ref_t&& right_rv) = delete; + +public: + T& operator *() const + { + return *m_ptr; + } + + T* operator ->() const + { + return m_ptr; + } + + explicit operator bool() const + { + return m_ptr; + } +}; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 6ef0770f1e..c3d5e90d0e 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -411,6 +411,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index ebc7d5d359..0d0905572a 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1540,5 +1540,8 @@ Emu\Memory + + Emu\Memory + \ No newline at end of file diff --git a/rpcs3/stdafx.h b/rpcs3/stdafx.h index a9c6e88dc4..bd4a76abc4 100644 --- a/rpcs3/stdafx.h +++ b/rpcs3/stdafx.h @@ -63,6 +63,7 @@ template __forceinline T align(const T addr, int align) #include "Utilities/StrFmt.h" #include "Emu/Memory/atomic.h" +#include "Emu/Memory/refcnt.h" #define _PRGNAME_ "RPCS3" #define _PRGVER_ "0.0.0.5"