diff --git a/CMakeLists.txt b/CMakeLists.txt index d50bd853e..46461c6ac 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -821,6 +821,9 @@ else() src/core/libraries/kernel/threads/thr_clean.cpp src/core/libraries/kernel/threads/thread_state.h src/core/libraries/kernel/threads/thread_state.cpp src/core/libraries/kernel/threads/thr_ctrdtr.cpp + src/common/slab_heap.h + src/common/spin_lock.cpp + src/common/spin_lock.h ) endif() diff --git a/src/common/slab_heap.h b/src/common/slab_heap.h new file mode 100644 index 000000000..7648ebea3 --- /dev/null +++ b/src/common/slab_heap.h @@ -0,0 +1,163 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/assert.h" +#include "common/spin_lock.h" + +namespace Common { + +class SlabHeapImpl { +public: + struct Node { + Node* next{}; + }; + +public: + constexpr SlabHeapImpl() = default; + + void Initialize() { + ASSERT(m_head == nullptr); + } + + Node* GetHead() const { + return m_head; + } + + void* Allocate() { + m_lock.lock(); + + Node* ret = m_head; + if (ret != nullptr) { + m_head = ret->next; + } + + m_lock.unlock(); + return ret; + } + + void Free(void* obj) { + m_lock.lock(); + + Node* node = static_cast(obj); + node->next = m_head; + m_head = node; + + m_lock.unlock(); + } + +private: + std::atomic m_head{}; + Common::SpinLock m_lock; +}; + +class SlabHeapBase : protected SlabHeapImpl { +private: + size_t m_obj_size{}; + uintptr_t m_peak{}; + uintptr_t m_start{}; + uintptr_t m_end{}; + +public: + constexpr SlabHeapBase() = default; + + bool Contains(uintptr_t address) const { + return m_start <= address && address < m_end; + } + + void Initialize(size_t obj_size, void* memory, size_t memory_size) { + // Ensure we don't initialize a slab using null memory. + ASSERT(memory != nullptr); + + // Set our object size. + m_obj_size = obj_size; + + // Initialize the base allocator. + SlabHeapImpl::Initialize(); + + // Set our tracking variables. + const size_t num_obj = (memory_size / obj_size); + m_start = reinterpret_cast(memory); + m_end = m_start + num_obj * obj_size; + m_peak = m_start; + + // Free the objects. + u8* cur = reinterpret_cast(m_end); + + for (size_t i = 0; i < num_obj; i++) { + cur -= obj_size; + SlabHeapImpl::Free(cur); + } + } + + size_t GetSlabHeapSize() const { + return (m_end - m_start) / this->GetObjectSize(); + } + + size_t GetObjectSize() const { + return m_obj_size; + } + + void* Allocate() { + void* obj = SlabHeapImpl::Allocate(); + return obj; + } + + void Free(void* obj) { + // Don't allow freeing an object that wasn't allocated from this heap. + const bool contained = this->Contains(reinterpret_cast(obj)); + ASSERT(contained); + SlabHeapImpl::Free(obj); + } + + size_t GetObjectIndex(const void* obj) const { + return (reinterpret_cast(obj) - m_start) / this->GetObjectSize(); + } + + size_t GetPeakIndex() const { + return this->GetObjectIndex(reinterpret_cast(m_peak)); + } + + uintptr_t GetSlabHeapAddress() const { + return m_start; + } + + size_t GetNumRemaining() const { + // Only calculate the number of remaining objects under debug configuration. + return 0; + } +}; + +template +class SlabHeap final : public SlabHeapBase { +private: + using BaseHeap = SlabHeapBase; + +public: + constexpr SlabHeap() = default; + + void Initialize(void* memory, size_t memory_size) { + BaseHeap::Initialize(sizeof(T), memory, memory_size); + } + + T* Allocate() { + T* obj = static_cast(BaseHeap::Allocate()); + + if (obj != nullptr) [[likely]] { + std::construct_at(obj); + } + return obj; + } + + void Free(T* obj) { + BaseHeap::Free(obj); + } + + size_t GetObjectIndex(const T* obj) const { + return BaseHeap::GetObjectIndex(obj); + } +}; + +} // namespace Common diff --git a/src/common/slot_vector.h b/src/common/slot_vector.h index 36e647971..d4ac51361 100644 --- a/src/common/slot_vector.h +++ b/src/common/slot_vector.h @@ -3,10 +3,7 @@ #pragma once -#include -#include #include -#include #include #include #include "common/assert.h" diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp new file mode 100755 index 000000000..9d4cfe36b --- /dev/null +++ b/src/common/spin_lock.cpp @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/spin_lock.h" + +#if _MSC_VER +#include +#if _M_AMD64 +#define __x86_64__ 1 +#endif +#if _M_ARM64 +#define __aarch64__ 1 +#endif +#else +#if __x86_64__ +#include +#endif +#endif + +namespace { + +void ThreadPause() { +#if __x86_64__ + _mm_pause(); +#elif __aarch64__ && _MSC_VER + __yield(); +#elif __aarch64__ + asm("yield"); +#endif +} + +} // Anonymous namespace + +namespace Common { + +void SpinLock::lock() { + while (lck.test_and_set(std::memory_order_acquire)) { + ThreadPause(); + } +} + +void SpinLock::unlock() { + lck.clear(std::memory_order_release); +} + +bool SpinLock::try_lock() { + if (lck.test_and_set(std::memory_order_acquire)) { + return false; + } + return true; +} + +} // namespace Common diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h new file mode 100755 index 000000000..3229a8c6a --- /dev/null +++ b/src/common/spin_lock.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace Common { + +/** + * SpinLock class + * a lock similar to mutex that forces a thread to spin wait instead calling the + * supervisor. Should be used on short sequences of code. + */ +class SpinLock { +public: + SpinLock() = default; + + SpinLock(const SpinLock&) = delete; + SpinLock& operator=(const SpinLock&) = delete; + + SpinLock(SpinLock&&) = delete; + SpinLock& operator=(SpinLock&&) = delete; + + void lock(); + void unlock(); + [[nodiscard]] bool try_lock(); + +private: + std::atomic_flag lck = ATOMIC_FLAG_INIT; +}; + +} // namespace Common diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 3f4b2d0d8..1257473e1 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -305,7 +305,7 @@ void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("XC9wM+xULz8", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerJumpToTime); LIB_FUNCTION("9y5v+fGN4Wk", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPause); LIB_FUNCTION("HD1YKVU26-M", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPostInit); - LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf); + // LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf); LIB_FUNCTION("w5moABNwnRY", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerResume); LIB_FUNCTION("k-q+xOxdc3E", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerSetAvSyncMode); diff --git a/src/core/libraries/avplayer/avplayer_source.cpp b/src/core/libraries/avplayer/avplayer_source.cpp index 76edc1dde..8a65377c2 100644 --- a/src/core/libraries/avplayer/avplayer_source.cpp +++ b/src/core/libraries/avplayer/avplayer_source.cpp @@ -252,11 +252,9 @@ bool AvPlayerSource::Start() { LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context."); return false; } - m_demuxer_thread = std::jthread([this](std::stop_token stop) { this->DemuxerThread(stop); }); - m_video_decoder_thread = - std::jthread([this](std::stop_token stop) { this->VideoDecoderThread(stop); }); - m_audio_decoder_thread = - std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); }); + m_demuxer_thread.Run([this](std::stop_token stop) { this->DemuxerThread(stop); }); + m_video_decoder_thread.Run([this](std::stop_token stop) { this->VideoDecoderThread(stop); }); + m_audio_decoder_thread.Run([this](std::stop_token stop) { this->AudioDecoderThread(stop); }); m_start_time = std::chrono::high_resolution_clock::now(); return true; } @@ -269,18 +267,10 @@ bool AvPlayerSource::Stop() { return false; } - m_video_decoder_thread.request_stop(); - m_audio_decoder_thread.request_stop(); - m_demuxer_thread.request_stop(); - if (m_demuxer_thread.joinable()) { - m_demuxer_thread.join(); - } - if (m_video_decoder_thread.joinable()) { - m_video_decoder_thread.join(); - } - if (m_audio_decoder_thread.joinable()) { - m_audio_decoder_thread.join(); - } + m_video_decoder_thread.Stop(); + m_audio_decoder_thread.Stop(); + m_demuxer_thread.Stop(); + if (m_current_audio_frame.has_value()) { m_audio_buffers.Push(std::move(m_current_audio_frame.value())); m_current_audio_frame.reset(); @@ -504,12 +494,8 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) { m_video_frames_cv.Notify(); m_audio_frames_cv.Notify(); - if (m_video_decoder_thread.joinable()) { - m_video_decoder_thread.join(); - } - if (m_audio_decoder_thread.joinable()) { - m_audio_decoder_thread.join(); - } + m_video_decoder_thread.Join(); + m_audio_decoder_thread.Join(); m_state.OnEOF(); LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normally"); @@ -802,8 +788,8 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) { } bool AvPlayerSource::HasRunningThreads() const { - return m_demuxer_thread.joinable() || m_video_decoder_thread.joinable() || - m_audio_decoder_thread.joinable(); + return m_demuxer_thread.Joinable() || m_video_decoder_thread.Joinable() || + m_audio_decoder_thread.Joinable(); } } // namespace Libraries::AvPlayer diff --git a/src/core/libraries/avplayer/avplayer_source.h b/src/core/libraries/avplayer/avplayer_source.h index b647600c4..c8ad75865 100644 --- a/src/core/libraries/avplayer/avplayer_source.h +++ b/src/core/libraries/avplayer/avplayer_source.h @@ -11,10 +11,10 @@ #include #include "common/assert.h" -#include "common/polyfill_thread.h" #include "core/libraries/avplayer/avplayer.h" #include "core/libraries/avplayer/avplayer_common.h" #include "core/libraries/avplayer/avplayer_data_streamer.h" +#include "core/libraries/kernel/thread_management.h" struct AVCodecContext; struct AVFormatContext; @@ -200,9 +200,9 @@ private: EventCV m_stop_cv{}; std::mutex m_state_mutex{}; - std::jthread m_demuxer_thread{}; - std::jthread m_video_decoder_thread{}; - std::jthread m_audio_decoder_thread{}; + Kernel::Thread m_demuxer_thread{}; + Kernel::Thread m_video_decoder_thread{}; + Kernel::Thread m_audio_decoder_thread{}; AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext}; AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext}; diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index ce9629c15..3d9840a1b 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -117,10 +117,7 @@ AvPlayerState::~AvPlayerState() { std::unique_lock lock(m_source_mutex); m_up_source.reset(); } - if (m_controller_thread.joinable()) { - m_controller_thread.request_stop(); - m_controller_thread.join(); - } + m_controller_thread.Stop(); m_event_queue.Clear(); } @@ -221,8 +218,7 @@ void AvPlayerState::WarningEvent(s32 id) { // Called inside GAME thread void AvPlayerState::StartControllerThread() { - m_controller_thread = - std::jthread([this](std::stop_token stop) { this->AvControllerThread(stop); }); + m_controller_thread.Run([this](std::stop_token stop) { this->AvControllerThread(stop); }); } // Called inside GAME thread diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index 8f8480ef8..4d24b1712 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -7,9 +7,9 @@ #include #include -#include "common/polyfill_thread.h" #include "core/libraries/avplayer/avplayer.h" #include "core/libraries/avplayer/avplayer_source.h" +#include "core/libraries/kernel/thread_management.h" namespace Libraries::AvPlayer { @@ -80,7 +80,7 @@ private: std::shared_mutex m_source_mutex{}; std::mutex m_state_machine_mutex{}; std::mutex m_event_handler_mutex{}; - std::jthread m_controller_thread{}; + Kernel::Thread m_controller_thread{}; AvPlayerQueue m_event_queue{}; }; diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 36337975c..c96fb4ec7 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -1,6 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - +#pragma clang optimize off #include "gnm_error.h" #include "gnmdriver.h" diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index 09bb16c11..9db42f326 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -85,8 +85,6 @@ struct iovec { }; size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) { - // weird it gives fd ==0 and writes to stdout , i am not sure if it that is valid (found in - // openorbis) size_t total_written = 0; for (int i = 0; i < iovcn; i++) { total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout); diff --git a/src/core/libraries/kernel/libkernel.h b/src/core/libraries/kernel/libkernel.h index 235fbc48b..44127b409 100644 --- a/src/core/libraries/kernel/libkernel.h +++ b/src/core/libraries/kernel/libkernel.h @@ -3,7 +3,9 @@ #pragma once -#include +#include +#include +#include "common/logging/log.h" #include "common/types.h" #include "core/libraries/error_codes.h" @@ -17,11 +19,21 @@ void ErrSceToPosix(int result); int ErrnoToSceKernelError(int e); void SetPosixErrno(int e); -template +template +struct StringLiteral { + constexpr StringLiteral(const char (&str)[N]) { + std::copy_n(str, N, value); + } + + char value[N]; +}; + +template struct WrapperImpl; -template -struct WrapperImpl { +template +struct WrapperImpl { + static constexpr StringLiteral Name{name}; static R PS4_SYSV_ABI wrap(Args... args) { u32 ret = f(args...); if (ret != 0) { @@ -31,10 +43,10 @@ struct WrapperImpl { } }; -template -constexpr auto OrbisWrapper = WrapperImpl::wrap; +template +constexpr auto OrbisWrapper = WrapperImpl::wrap; -#define ORBIS(func) OrbisWrapper +#define ORBIS(func) WrapperImpl<#func, decltype(&func), func>::wrap int* PS4_SYSV_ABI __Error(); diff --git a/src/core/libraries/kernel/memory_management.h b/src/core/libraries/kernel/memory_management.h index 0ff0d01c4..2d19ceb49 100644 --- a/src/core/libraries/kernel/memory_management.h +++ b/src/core/libraries/kernel/memory_management.h @@ -129,6 +129,10 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags); int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len); +void* Malloc(size_t size); + +void Free(void* ptr); + void RegisterMemory(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index 3ca10cf0c..c34ef45f6 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -7,20 +7,6 @@ namespace Libraries::Kernel { -int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr); - -int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAttrT* attr, - PthreadEntryFunc start_routine, void* arg, - const char* name); - -PthreadT LaunchThread(PthreadEntryFunc start_routine, void* arg, const char* name) { - PthreadT thread{}; - PthreadAttrT attr{}; - posix_pthread_attr_init(&attr); - posix_pthread_create_name_np(&thread, &attr, start_routine, arg, name); - return thread; -} - void RegisterThreads(Core::Loader::SymbolsResolver* sym) { RegisterMutex(sym); RegisterCond(sym); diff --git a/src/core/libraries/kernel/thread_management.h b/src/core/libraries/kernel/thread_management.h index feb60fc21..2a73d625f 100644 --- a/src/core/libraries/kernel/thread_management.h +++ b/src/core/libraries/kernel/thread_management.h @@ -11,8 +11,60 @@ class SymbolsResolver; namespace Libraries::Kernel { -PthreadT LaunchThread(PthreadEntryFunc start_routine, void* arg, const char* name); +int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr); + +int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr); + +int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr, + PthreadEntryFunc start_routine, void* arg); + +int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return); void RegisterThreads(Core::Loader::SymbolsResolver* sym); +class Thread { +public: + explicit Thread() = default; + ~Thread() { + Stop(); + } + + void Run(std::function&& func) { + this->func = std::move(func); + PthreadAttrT attr{}; + posix_pthread_attr_init(&attr); + posix_pthread_create(&thread, &attr, RunWrapper, this); + posix_pthread_attr_destroy(&attr); + } + + void Join() { + if (thread) { + posix_pthread_join(thread, nullptr); + thread = nullptr; + } + } + + bool Joinable() const { + return thread != nullptr; + } + + void Stop() { + if (Joinable()) { + stop.request_stop(); + Join(); + } + } + + static void* RunWrapper(void* arg) { + Thread* thr = (Thread*)arg; + thr->func(thr->stop.get_token()); + return nullptr; + } + +private: + PthreadT thread{}; + std::function func; + std::stop_source stop; +}; + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_attr.cpp b/src/core/libraries/kernel/threads/thr_attr.cpp index b06e84918..c34b8a595 100644 --- a/src/core/libraries/kernel/threads/thr_attr.cpp +++ b/src/core/libraries/kernel/threads/thr_attr.cpp @@ -1,6 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - +#pragma clang optimize off #include "core/libraries/error_codes.h" #include "core/libraries/kernel/libkernel.h" #include "core/libraries/kernel/threads/thread_state.h" @@ -18,9 +18,9 @@ struct PthreadPrio { }; static constexpr std::array ThrPriorities = {{ - {0x2BC, 0x300, 0x3BF}, // Fifo - {0x384, 0x100, 0x2FF}, // Other - {0x2BC, 0, 1}, // Round-Robin + {0x100, 0x2FF, 0x2BC}, // Fifo + {0x300, 0x3BF, 0x384}, // Other + {0x100, 0x2FF, 0x2BC}, // Round-Robin }}; PthreadAttr PthreadAttrDefault = { @@ -33,13 +33,14 @@ PthreadAttr PthreadAttrDefault = { .stacksize_attr = ThrStackDefault, .guardsize_attr = 0, .cpusetsize = 0, + .cpuset = nullptr, }; int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr) { if (attr == nullptr || *attr == nullptr) { return POSIX_EINVAL; } - free(*attr); + delete *attr; *attr = nullptr; return 0; } @@ -112,7 +113,7 @@ int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const PthreadAttrT* attr, size_ } int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr) { - PthreadAttrT pattr = (PthreadAttrT)malloc(sizeof(PthreadAttr)); + PthreadAttrT pattr = new PthreadAttr{}; if (pattr == nullptr) { return POSIX_ENOMEM; } @@ -180,7 +181,7 @@ int PS4_SYSV_ABI posix_pthread_attr_setschedparam(PthreadAttrT* attr, const sche } const auto policy = (*attr)->sched_policy; - if (policy == SchedPolicy::Fifo || policy == SchedPolicy::RoundRobin) { + if (policy == SchedPolicy::RoundRobin) { if (param->sched_priority < ThrPriorities[u32(policy) - 1].pri_min || param->sched_priority > ThrPriorities[u32(policy) - 1].pri_max) { return POSIX_ENOTSUP; @@ -224,13 +225,63 @@ int PS4_SYSV_ABI posix_pthread_attr_get_np(PthreadT pthread, PthreadAttrT* dstat if (True(pthread->flags & ThreadFlags::Detached)) { attr.flags |= PthreadAttrFlags::Detached; } - pthread->lock->unlock(); + pthread->lock.unlock(); if (ret == 0) { memcpy(dst, &attr, sizeof(PthreadAttr)); } return ret; } +int PS4_SYSV_ABI posix_pthread_attr_getaffinity_np(const PthreadAttrT* pattr, size_t cpusetsize, + Cpuset* cpusetp) { + if (pattr == nullptr) { + return POSIX_EINVAL; + } + PthreadAttrT attr = *pattr; + if (attr == nullptr) { + return POSIX_EINVAL; + } + if (attr->cpuset != nullptr) + memcpy(cpusetp, attr->cpuset, std::min(cpusetsize, attr->cpusetsize)); + else + memset(cpusetp, -1, sizeof(Cpuset)); + return 0; +} + +int PS4_SYSV_ABI scePthreadAttrGetaffinity(PthreadAttrT* param_1, Cpuset* mask) { + Cpuset cpuset; + const int ret = posix_pthread_attr_getaffinity_np(param_1, 0x10, &cpuset); + if (ret == 0) { + *mask = cpuset; + } + return ret; +} + +int PS4_SYSV_ABI posix_pthread_attr_setaffinity_np(PthreadAttrT* pattr, size_t cpusetsize, + const Cpuset* cpusetp) { + if (pattr == nullptr) { + return POSIX_EINVAL; + } + PthreadAttrT attr = *pattr; + if (attr == nullptr) { + return POSIX_EINVAL; + } + if (cpusetsize == 0 || cpusetp == nullptr) { + if (attr->cpuset != nullptr) { + free(attr->cpuset); + attr->cpuset = NULL; + attr->cpusetsize = 0; + } + return 0; + } + if (attr->cpuset == nullptr) { + attr->cpuset = (Cpuset*)calloc(1, sizeof(Cpuset)); + attr->cpusetsize = sizeof(Cpuset); + } + memcpy(attr->cpuset, cpusetp, sizeof(Cpuset)); + return 0; +} + void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) { // Posix LIB_FUNCTION("wtkt-teR1so", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_init); @@ -280,6 +331,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) { ORBIS(posix_pthread_attr_setstackaddr)); LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_attr_setguardsize)); + LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1, + ORBIS(scePthreadAttrGetaffinity)); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_clean.cpp b/src/core/libraries/kernel/threads/thr_clean.cpp index 26f984457..0f4150d27 100644 --- a/src/core/libraries/kernel/threads/thr_clean.cpp +++ b/src/core/libraries/kernel/threads/thr_clean.cpp @@ -15,7 +15,7 @@ void __pthread_cleanup_push_imp(PthreadCleanupFunc routine, void* arg, PthreadCl void PS4_SYSV_ABI posix_pthread_cleanup_push(PthreadCleanupFunc routine, void* arg) { Pthread* curthread = g_curthread; - PthreadCleanup* newbuf = (PthreadCleanup*)malloc(sizeof(PthreadCleanup)); + PthreadCleanup* newbuf = new PthreadCleanup{}; if (newbuf == nullptr) { return; } @@ -35,7 +35,7 @@ void PS4_SYSV_ABI posix_pthread_cleanup_pop(int execute) { old->routine(old->routine_arg); } if (old->onheap) { - free(old); + delete old; } } } diff --git a/src/core/libraries/kernel/threads/thr_cond.cpp b/src/core/libraries/kernel/threads/thr_cond.cpp index 5eae613f2..f346d2cda 100644 --- a/src/core/libraries/kernel/threads/thr_cond.cpp +++ b/src/core/libraries/kernel/threads/thr_cond.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma clang optimize off #include +#include "common/assert.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/libkernel.h" #include "core/libraries/kernel/threads/threads.h" @@ -14,33 +15,29 @@ static std::mutex CondStaticLock; #define THR_COND_INITIALIZER ((PthreadCond*)NULL) #define THR_COND_DESTROYED ((PthreadCond*)1) -enum class ClockId : u32 { - Realtime = 0, - Virtual = 1, - Prof = 2, - Monotonic = 4, - Uptime = 5, - UptimePrecise = 7, - UptimeFast = 8, - RealtimePrecise = 9, - RealtimeFast = 10, - MonotonicPrecise = 11, - MonotonicFast = 12, - Second = 13, - ThreadCputimeID = 14, +static constexpr PthreadCondAttr PhreadCondattrDefault = { + .c_pshared = 0, + .c_clockid = ClockId::Realtime, }; -static constexpr PthreadCondAttr PhreadCondattrDefault = {.c_pshared = PTHREAD_PROCESS_PRIVATE, - .c_clockid = CLOCK_REALTIME}; +static int CondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr, const char* name) { + auto* cvp = (PthreadCond*)malloc(sizeof(PthreadCond)); + std::memset(cvp, 0, sizeof(PthreadCond)); + std::construct_at(cvp); -static int CondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) { - PthreadCond* cvp = (PthreadCond*)calloc(1, sizeof(PthreadCond)); if (cvp == nullptr) { return POSIX_ENOMEM; } - std::construct_at(cvp); + + if (name) { + cvp->name = name; + } else { + static int CondId = 0; + cvp->name = fmt::format("Cond{}", CondId++); + } + if (cond_attr == nullptr || *cond_attr == nullptr) { - cvp->clock_id = CLOCK_REALTIME; + cvp->clock_id = ClockId::Realtime; } else { // if ((*cond_attr)->c_pshared) { // cvp->flags |= USYNC_PROCESS_SHARED; @@ -53,8 +50,9 @@ static int CondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) { static int InitStatic(Pthread* thread, PthreadCondT* cond) { std::scoped_lock lk{CondStaticLock}; - if (*cond == NULL) - return CondInit(cond, NULL); + if (*cond == nullptr) { + return CondInit(cond, nullptr, nullptr); + } return 0; } @@ -73,7 +71,13 @@ static int InitStatic(Pthread* thread, PthreadCondT* cond) { int PS4_SYSV_ABI posix_pthread_cond_init(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) { *cond = nullptr; - return CondInit(cond, cond_attr); + return CondInit(cond, cond_attr, nullptr); +} + +int PS4_SYSV_ABI scePthreadCondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr, + const char* name) { + *cond = nullptr; + return CondInit(cond, cond_attr, name); } int PS4_SYSV_ABI posix_pthread_cond_destroy(PthreadCondT* cond) { @@ -86,33 +90,168 @@ int PS4_SYSV_ABI posix_pthread_cond_destroy(PthreadCondT* cond) { } cvp = *cond; *cond = THR_COND_DESTROYED; - std::destroy_at(cvp); free(cvp); return 0; } -int PthreadCond::Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime) { - Pthread* curthread = g_curthread; - PthreadMutex* mp = *mutex; +static std::mutex sc_lock; +static std::unordered_map sc_table; - if (int error = mp->IsOwned(curthread); error != 0) { +void _sleepq_lock(void* wchan) { + sc_lock.lock(); +} + +void _sleepq_unlock(void* wchan) { + sc_lock.unlock(); +} + +SleepQueue* _sleepq_lookup(void* wchan) { + const auto it = sc_table.find(wchan); + if (it != sc_table.end()) { + return it->second; + } + return nullptr; +} + +void _sleepq_add(void* wchan, Pthread* td) { + SleepQueue* sq = _sleepq_lookup(wchan); + if (sq != NULL) { + sq->sq_freeq.push_front(td->sleepqueue); + } else { + sc_table.emplace(wchan, td->sleepqueue); + td->sleepqueue->sq_wchan = wchan; + } + td->sleepqueue = nullptr; + td->wchan = wchan; + sq->sq_blocked.push_front(td); +} + +bool _sleepq_remove(SleepQueue* sq, Pthread* td) { + std::erase(sq->sq_blocked, td); + if (sq->sq_blocked.empty()) { + sc_table.erase(td->wchan); + td->sleepqueue = sq; + td->wchan = nullptr; + return false; + } else { + td->sleepqueue = sq->sq_freeq.front(); + sq->sq_freeq.pop_front(); + td->wchan = nullptr; + return true; + } +} + +void _sleepq_drop(SleepQueue* sq, void (*cb)(Pthread*, void* arg), void* arg) { + if (sq->sq_blocked.empty()) { + return; + } + Pthread* td = sq->sq_blocked.front(); + sc_table.erase(td->wchan); + std::erase(sq->sq_blocked, td); + + cb(td, arg); + td->sleepqueue = sq; + td->wchan = nullptr; + + auto sq2 = sq->sq_freeq.begin(); + for (Pthread* td : sq->sq_blocked) { + cb(td, arg); + td->sleepqueue = *sq2; + td->wchan = NULL; + sq2++; + } + + sq->sq_blocked.clear(); + sq->sq_freeq.clear(); +} + +/*static int cond_wait_user(PthreadCond *cvp, PthreadMutex* mp, + const OrbisKernelTimespec *abstime, int cancel) { + Pthread* curthread = g_curthread; + int recurse; + int error; + + ASSERT_MSG(curthread->wchan == nullptr, "Thread was already on queue"); + //if (cancel) + //_thr_testcancel(curthread); + + _sleepq_lock(cvp); + cvp->has_user_waiters = 1; + curthread->will_sleep = 1; + _mutex_cv_unlock(mp, &recurse); + curthread->mutex_obj = mp; + _sleepq_add(cvp, curthread); + for(;;) { + _thr_clear_wake(curthread); + _sleepq_unlock(cvp); + + if (cancel) { + //_thr_cancel_enter2(curthread, 0); + error = _thr_sleep(curthread, cvp->__clock_id, abstime); + //_thr_cancel_leave(curthread, 0); + } else { + error = _thr_sleep(curthread, cvp->__clock_id, abstime); + } + + _sleepq_lock(cvp); + if (curthread->wchan == nullptr) { + error = 0; + break; + } else if (cancel && curthread->ShouldCancel()) { + SleepQueue* sq = _sleepq_lookup(cvp); + cvp->has_user_waiters = _sleepq_remove(sq, curthread); + _sleepq_unlock(cvp); + curthread->mutex_obj = NULL; + _mutex_cv_lock(mp, recurse); + if (!THR_IN_CRITICAL(curthread)) + _pthread_exit(PTHREAD_CANCELED); + else + return (0); + } else if (error == POSIX_ETIMEDOUT) { + SleepQueue* sq = _sleepq_lookup(cvp); + cvp->has_user_waiters = _sleepq_remove(sq, curthread); + break; + } + } + _sleepq_unlock(cvp); + curthread->mutex_obj = NULL; + _mutex_cv_lock(mp, recurse); + return (error); +}*/ + +int PthreadCond::Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime) { + PthreadMutex* mp = *mutex; + if (int error = mp->IsOwned(g_curthread); error != 0) { return error; } //_thr_testcancel(curthread); //_thr_cancel_enter2(curthread, 0); if (abstime) { - const auto status = cond.wait_until(mp->m_lock, abstime->TimePoint()); + const auto status = cond.wait_until(*mp, abstime->TimePoint()); return status == std::cv_status::timeout ? POSIX_ETIMEDOUT : 0; } else { - cond.wait(mp->m_lock); + cond.wait(*mp); return 0; } //_thr_cancel_leave(curthread, 0); } +int PthreadCond::Wait(PthreadMutexT* mutex, u64 usec) { + PthreadMutex* mp = *mutex; + if (int error = mp->IsOwned(g_curthread); error != 0) { + return error; + } + + //_thr_testcancel(curthread); + //_thr_cancel_enter2(curthread, 0); + const auto status = cond.wait_for(*mp, std::chrono::microseconds(usec)); + return status == std::cv_status::timeout ? POSIX_ETIMEDOUT : 0; + //_thr_cancel_leave(curthread, 0); +} + int PS4_SYSV_ABI posix_pthread_cond_wait(PthreadCondT* cond, PthreadMutexT* mutex) { - PthreadCond* cvp; + PthreadCond* cvp{}; CHECK_AND_INIT_COND return cvp->Wait(mutex, nullptr); } @@ -124,27 +263,34 @@ int PS4_SYSV_ABI posix_pthread_cond_timedwait(PthreadCondT* cond, PthreadMutexT* return POSIX_EINVAL; } - PthreadCond* cvp; + PthreadCond* cvp{}; CHECK_AND_INIT_COND return cvp->Wait(mutex, abstime); } +int PS4_SYSV_ABI posix_pthread_cond_reltimedwait_np(PthreadCondT* cond, PthreadMutexT* mutex, + u64 usec) { + PthreadCond* cvp{}; + CHECK_AND_INIT_COND + return cvp->Wait(mutex, usec); +} + int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) { - PthreadCond* cvp; + PthreadCond* cvp{}; CHECK_AND_INIT_COND cvp->cond.notify_one(); return 0; } int PS4_SYSV_ABI posix_pthread_cond_broadcast(PthreadCondT* cond) { - PthreadCond* cvp; + PthreadCond* cvp{}; CHECK_AND_INIT_COND cvp->cond.notify_all(); return 0; } int PS4_SYSV_ABI posix_pthread_condattr_init(PthreadCondAttrT* attr) { - PthreadCondAttr* pattr = (PthreadCondAttr*)malloc(sizeof(PthreadCondAttr)); + PthreadCondAttr* pattr = new PthreadCondAttr{}; if (pattr == nullptr) { return POSIX_ENOMEM; } @@ -157,7 +303,7 @@ int PS4_SYSV_ABI posix_pthread_condattr_destroy(PthreadCondAttrT* attr) { if (attr == nullptr || *attr == nullptr) { return POSIX_EINVAL; } - free(*attr); + delete *attr; *attr = nullptr; return 0; } @@ -178,7 +324,7 @@ int PS4_SYSV_ABI posix_pthread_condattr_setclock(PthreadCondAttrT* attr, ClockId clock_id != ClockId::Prof && clock_id != ClockId::Monotonic) { return POSIX_EINVAL; } - (*attr)->c_clockid = static_cast(clock_id); + (*attr)->c_clockid = clock_id; return 0; } @@ -214,7 +360,7 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); // Orbis - LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_cond_init)); + LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadCondInit)); LIB_FUNCTION("m5-2bsNfv7s", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_condattr_init)); LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1, @@ -225,7 +371,7 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_cond_signal)); LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1, - ORBIS(posix_pthread_cond_timedwait)); + ORBIS(posix_pthread_cond_reltimedwait_np)); LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_cond_destroy)); } diff --git a/src/core/libraries/kernel/threads/thr_create.cpp b/src/core/libraries/kernel/threads/thr_create.cpp index ef4a679c8..845a4b53b 100644 --- a/src/core/libraries/kernel/threads/thr_create.cpp +++ b/src/core/libraries/kernel/threads/thr_create.cpp @@ -1,6 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - +#pragma clang optimize off #include "common/assert.h" #include "common/thread.h" #include "core/debug_state.h" @@ -25,6 +25,13 @@ extern PthreadAttr PthreadAttrDefault; void _thread_cleanupspecific(); +using ThreadDtor = void (*)(); +static ThreadDtor* ThreadDtors{}; + +void PS4_SYSV_ABI _sceKernelSetThreadDtors(ThreadDtor* dtor) { + ThreadDtors = dtor; +} + static void ExitThread() { Pthread* curthread = g_curthread; @@ -37,7 +44,7 @@ static void ExitThread() { auto* thread_state = ThrState::Instance(); ASSERT(thread_state->active_threads.fetch_sub(1) != 1); - curthread->lock->lock(); + curthread->lock.lock(); curthread->state = PthreadState::Dead; ASSERT(False(curthread->flags & ThreadFlags::NeedSuspend)); @@ -46,7 +53,7 @@ static void ExitThread() { * reference count to allow it to be garbage collected. */ curthread->refcount--; - thread_state->TryCollect(curthread, curthread); /* thread lock released */ + thread_state->TryCollect(curthread); /* thread lock released */ /* * Kernel will do wakeup at the address, so joiner thread @@ -80,10 +87,12 @@ void PS4_SYSV_ABI posix_pthread_exit(void* status) { curthread->cleanup.pop_front(); old->routine(old->routine_arg); if (old->onheap) { - free(old); + delete old; } } - + /*if (ThreadDtors && *ThreadDtors) { + (*ThreadDtors)(); + }*/ ExitThread(); } @@ -111,16 +120,16 @@ static int JoinThread(PthreadT pthread, void** thread_return, const OrbisKernelT ret = POSIX_ENOTSUP; } if (ret) { - pthread->lock->unlock(); + pthread->lock.unlock(); return ret; } /* Set the running thread to be the joiner: */ pthread->joiner = curthread; - pthread->lock->unlock(); + pthread->lock.unlock(); const auto backout_join = [](void* arg) { Pthread* pthread = (Pthread*)arg; - std::scoped_lock lk{*pthread->lock}; + std::scoped_lock lk{pthread->lock}; pthread->joiner = nullptr; }; @@ -145,10 +154,10 @@ static int JoinThread(PthreadT pthread, void** thread_return, const OrbisKernelT } void* tmp = pthread->ret; - pthread->lock->lock(); + pthread->lock.lock(); pthread->flags |= ThreadFlags::Detached; pthread->joiner = nullptr; - thread_state->TryCollect(curthread, pthread); /* thread lock released */ + thread_state->TryCollect(pthread); /* thread lock released */ if (thread_return != nullptr) { *thread_return = tmp; } @@ -182,13 +191,13 @@ int PS4_SYSV_ABI posix_pthread_detach(PthreadT pthread) { /* Check if the thread is already detached or has a joiner. */ if (True(pthread->flags & ThreadFlags::Detached) || (pthread->joiner != NULL)) { - pthread->lock->unlock(); + pthread->lock.unlock(); return POSIX_EINVAL; } /* Flag the thread as detached. */ pthread->flags |= ThreadFlags::Detached; - thread_state->TryCollect(g_curthread, pthread); /* thread lock released */ + thread_state->TryCollect(pthread); /* thread lock released */ return 0; } @@ -231,7 +240,8 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt new_thread->attr.sched_policy = curthread->attr.sched_policy; } - new_thread->tid = TidTerminated; + static int TidCounter = 1; + new_thread->tid = ++TidCounter; if (thread_state->CreateStack(&new_thread->attr) != 0) { /* Insufficient memory to create a stack: */ @@ -248,9 +258,11 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt new_thread->arg = arg; new_thread->cancel_enable = 1; new_thread->cancel_async = 0; + static std::atomic counter = 0; + new_thread->name = fmt::format("NoName{}", counter++); auto* memory = Core::Memory::Instance(); - if (name && (std::string_view{name} == "GAME_MainThread" || memory->IsValidAddress(name))) { + if (name && memory->IsValidAddress(name)) { new_thread->name = name; } @@ -269,10 +281,11 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt (*thread) = new_thread; /* Create thread */ + pthread_t pthr; pthread_attr_t pattr; pthread_attr_init(&pattr); - pthread_attr_setstack(&pattr, new_thread->attr.stackaddr_attr, new_thread->attr.stacksize_attr); - pthread_t pthr; + // pthread_attr_setstack(&pattr, new_thread->attr.stackaddr_attr, + // new_thread->attr.stacksize_attr); int ret = pthread_create(&pthr, &pattr, (PthreadEntryFunc)RunThread, new_thread); ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret); if (ret) { @@ -281,6 +294,11 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt return ret; } +int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr, + PthreadEntryFunc start_routine, void* arg) { + return posix_pthread_create_name_np(thread, attr, start_routine, arg, nullptr); +} + int PS4_SYSV_ABI posix_pthread_getthreadid_np() { return g_curthread->tid; } @@ -361,7 +379,51 @@ int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) { return SCE_OK; } +int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* policy, + SchedParam* param) { + if (policy == nullptr || param == nullptr) { + return POSIX_EINVAL; + } + + if (pthread == g_curthread) { + /* + * Avoid searching the thread list when it is the current + * thread. + */ + std::scoped_lock lk{g_curthread->lock}; + *policy = g_curthread->attr.sched_policy; + param->sched_priority = g_curthread->attr.prio; + return 0; + } + auto* thread_state = ThrState::Instance(); + /* Find the thread in the list of active threads. */ + if (int ret = thread_state->RefAdd(pthread, /*include dead*/ 0); ret != 0) { + return ret; + } + pthread->lock.lock(); + *policy = pthread->attr.sched_policy; + param->sched_priority = pthread->attr.prio; + pthread->lock.unlock(); + thread_state->RefDelete(pthread); + return 0; +} + +int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) { + SchedParam param; + SchedPolicy policy; + + posix_pthread_getschedparam(thread, &policy, ¶m); + *priority = param.sched_priority; + return 0; +} + +int sceNpWebApiTerminate() { + return 0; +} + void RegisterThread(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("asz3TtIqGF8", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, sceNpWebApiTerminate); + // Posix LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal); @@ -370,9 +432,13 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self); LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield); LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach); + LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join); + LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); + LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np); // Posix-Kernel LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); // Orbis LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once)); @@ -380,10 +446,14 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_create_name_np)); LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_detach)); + LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join)); + LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit); LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_equal); LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, posix_pthread_yield); LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getthreadid_np); + LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio); + LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_ctrdtr.cpp b/src/core/libraries/kernel/threads/thr_ctrdtr.cpp index 34e0e0f5c..55504ec6d 100644 --- a/src/core/libraries/kernel/threads/thr_ctrdtr.cpp +++ b/src/core/libraries/kernel/threads/thr_ctrdtr.cpp @@ -25,7 +25,7 @@ Core::Tcb* TcbCtor(Pthread* thread, int initial) { // Initialize allocated memory and allocate DTV table. const u32 num_dtvs = linker->MaxTlsIndex(); const auto static_tls_size = linker->StaticTlsSize(); - auto* dtv_table = new Core::DtvEntry[num_dtvs + 2]; + auto* dtv_table = new Core::DtvEntry[num_dtvs + 2]{}; // Initialize thread control block u8* addr = reinterpret_cast(addr_out); diff --git a/src/core/libraries/kernel/threads/thr_mutex.cpp b/src/core/libraries/kernel/threads/thr_mutex.cpp index 66b38bd0a..e12e6ab08 100644 --- a/src/core/libraries/kernel/threads/thr_mutex.cpp +++ b/src/core/libraries/kernel/threads/thr_mutex.cpp @@ -1,6 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - +#pragma clang optimize off #include "common/assert.h" #include "common/scope_exit.h" #include "common/types.h" @@ -17,6 +17,7 @@ static std::mutex MutxStaticLock; #define THR_MUTEX_INITIALIZER ((PthreadMutex*)NULL) #define THR_ADAPTIVE_MUTEX_INITIALIZER ((PthreadMutex*)1) #define THR_MUTEX_DESTROYED ((PthreadMutex*)2) +#define THR_MUTEX_RELTIME (const OrbisKernelTimespec*)-1 #define CPU_SPINWAIT __asm__ volatile("pause") @@ -39,8 +40,7 @@ static constexpr PthreadMutexAttr PthreadMutexattrAdaptiveDefault = { using CallocFun = void* (*)(size_t, size_t); -static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, - CallocFun calloc_cb) { +static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, const char* name) { const PthreadMutexAttr* attr; if (mutex_attr == NULL) { attr = &PthreadMutexattrDefault; @@ -53,14 +53,22 @@ static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, return POSIX_EINVAL; } } - PthreadMutex* pmutex = (PthreadMutex*)calloc(1, sizeof(PthreadMutex)); + auto* pmutex = (PthreadMutex*)malloc(sizeof(PthreadMutex)); + std::memset(pmutex, 0, sizeof(PthreadMutex)); + std::construct_at(pmutex); if (pmutex == nullptr) { return POSIX_ENOMEM; } - std::construct_at(pmutex); + if (name) { + pmutex->name = name; + } else { + static int MutexId = 0; + pmutex->name = fmt::format("Mutex{}", MutexId++); + } + pmutex->m_flags = PthreadMutexFlags(attr->m_type); - pmutex->m_owner = NULL; + pmutex->m_owner = nullptr; pmutex->m_count = 0; pmutex->m_spinloops = 0; pmutex->m_yieldloops = 0; @@ -78,16 +86,21 @@ static int InitStatic(Pthread* thread, PthreadMutexT* mutex) { std::scoped_lock lk{MutxStaticLock}; if (*mutex == THR_MUTEX_INITIALIZER) { - return MutexInit(mutex, &PthreadMutexattrDefault, calloc); + return MutexInit(mutex, &PthreadMutexattrDefault, nullptr); } else if (*mutex == THR_ADAPTIVE_MUTEX_INITIALIZER) { - return MutexInit(mutex, &PthreadMutexattrAdaptiveDefault, calloc); + return MutexInit(mutex, &PthreadMutexattrAdaptiveDefault, nullptr); } return 0; } int PS4_SYSV_ABI posix_pthread_mutex_init(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr) { - return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, calloc); + return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, nullptr); +} + +int PS4_SYSV_ABI scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr, + const char* name) { + return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, name); } int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) { @@ -102,7 +115,6 @@ int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) { return POSIX_EBUSY; } *mutex = THR_MUTEX_DESTROYED; - std::destroy_at(m); free(m); return 0; } @@ -125,11 +137,12 @@ int PthreadMutex::SelfTryLock() { } } -int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) { - switch (Type()) { - case PthreadMutexType::ErrorCheck: - case PthreadMutexType::AdaptiveNp: { - if (abstime) { +int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) { + const auto DoSleep = [&] { + if (abstime == THR_MUTEX_RELTIME) { + std::this_thread::sleep_for(std::chrono::microseconds(usec)); + return POSIX_ETIMEDOUT; + } else { if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { return POSIX_EINVAL; } else { @@ -137,10 +150,18 @@ int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) { return POSIX_ETIMEDOUT; } } + }; + switch (Type()) { + case PthreadMutexType::ErrorCheck: + case PthreadMutexType::AdaptiveNp: { + if (abstime) { + return DoSleep(); + } /* * POSIX specifies that mutexes should return * EDEADLK if a recursive lock is detected. */ + UNREACHABLE_MSG("Mutex deadlock occured"); return POSIX_EDEADLK; } case PthreadMutexType::Normal: { @@ -149,12 +170,7 @@ int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) { * deadlock on attempts to get a lock you already own. */ if (abstime) { - if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { - return POSIX_EINVAL; - } else { - std::this_thread::sleep_until(abstime->TimePoint()); - return POSIX_ETIMEDOUT; - } + return DoSleep(); } UNREACHABLE_MSG("Mutex deadlock occured"); return 0; @@ -172,10 +188,10 @@ int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) { } } -int PthreadMutex::Lock(const OrbisKernelTimespec* abstime) { +int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) { Pthread* curthread = g_curthread; if (m_owner == curthread) { - return SelfLock(abstime); + return SelfLock(abstime, usec); } int ret = 0; @@ -211,10 +227,15 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime) { if (abstime == nullptr) { m_lock.lock(); - } else if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) [[unlikely]] { + } else if (abstime != THR_MUTEX_RELTIME && + (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)) [[unlikely]] { ret = POSIX_EINVAL; } else { - ret = m_lock.try_lock_until(abstime->TimePoint()) ? 0 : POSIX_ETIMEDOUT; + if (THR_MUTEX_RELTIME) { + ret = m_lock.try_lock_for(std::chrono::microseconds(usec)) ? 0 : POSIX_ETIMEDOUT; + } else { + ret = m_lock.try_lock_until(abstime->TimePoint()) ? 0 : POSIX_ETIMEDOUT; + } } return ret; } @@ -244,9 +265,15 @@ int PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex) { int PS4_SYSV_ABI posix_pthread_mutex_timedlock(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime) { CHECK_AND_INIT_MUTEX + UNREACHABLE(); return (*mutex)->Lock(abstime); } +int PS4_SYSV_ABI posix_pthread_mutex_reltimedlock_np(PthreadMutexT* mutex, u64 usec) { + CHECK_AND_INIT_MUTEX + return (*mutex)->Lock(THR_MUTEX_RELTIME, usec); +} + int PthreadMutex::Unlock() { Pthread* curthread = g_curthread; /* @@ -320,7 +347,7 @@ bool PthreadMutex::IsOwned(Pthread* curthread) const { } int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) { - PthreadMutexAttrT pattr = (PthreadMutexAttrT)malloc(sizeof(PthreadMutexAttr)); + PthreadMutexAttrT pattr = new PthreadMutexAttr{}; if (pattr == nullptr) { return POSIX_ENOMEM; } @@ -367,7 +394,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) { if (attr == nullptr || *attr == nullptr) { return POSIX_EINVAL; } - free(*attr); + delete *attr; *attr = nullptr; return 0; } @@ -412,7 +439,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); // Orbis - LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_init)); + LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadMutexInit)); LIB_FUNCTION("2Of0f+3mhhE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_destroy)); LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1, @@ -429,7 +456,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("upoVrzMHFeE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_trylock)); LIB_FUNCTION("IafI2PxcPnQ", "libkernel", 1, "libkernel", 1, 1, - ORBIS(posix_pthread_mutex_timedlock)); + ORBIS(posix_pthread_mutex_reltimedlock_np)); LIB_FUNCTION("qH1gXoq71RY", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_init)); LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutexattr_init)); diff --git a/src/core/libraries/kernel/threads/thr_rwlock.cpp b/src/core/libraries/kernel/threads/thr_rwlock.cpp index b06db6b32..5deda72b6 100644 --- a/src/core/libraries/kernel/threads/thr_rwlock.cpp +++ b/src/core/libraries/kernel/threads/thr_rwlock.cpp @@ -27,11 +27,10 @@ static std::mutex RwlockStaticLock; } static int RwlockInit(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) { - PthreadRwlock* prwlock = (PthreadRwlock*)calloc(1, sizeof(PthreadRwlock)); + PthreadRwlock* prwlock = new PthreadRwlock{}; if (prwlock == nullptr) { return POSIX_ENOMEM; } - std::construct_at(prwlock); *rwlock = prwlock; return 0; } @@ -45,8 +44,7 @@ int PS4_SYSV_ABI posix_pthread_rwlock_destroy(PthreadRwlockT* rwlock) { return POSIX_EINVAL; } *rwlock = THR_RWLOCK_DESTROYED; - std::destroy_at(prwlock); - free(prwlock); + delete prwlock; return 0; } @@ -121,21 +119,21 @@ int PthreadRwlock::Wrlock(const OrbisKernelTimespec* abstime) { } int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(PthreadRwlockT* rwlock) { - PthreadRwlockT prwlock; + PthreadRwlockT prwlock{}; CHECK_AND_INIT_RWLOCK return prwlock->Rdlock(nullptr); } int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock(PthreadRwlockT* rwlock, const OrbisKernelTimespec* abstime) { - PthreadRwlockT prwlock; + PthreadRwlockT prwlock{}; CHECK_AND_INIT_RWLOCK return prwlock->Rdlock(abstime); } int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(PthreadRwlockT* rwlock) { Pthread* curthread = g_curthread; - PthreadRwlockT prwlock; + PthreadRwlockT prwlock{}; CHECK_AND_INIT_RWLOCK if (!prwlock->lock.try_lock_shared()) { @@ -148,7 +146,7 @@ int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(PthreadRwlockT* rwlock) { int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(PthreadRwlockT* rwlock) { Pthread* curthread = g_curthread; - PthreadRwlockT prwlock; + PthreadRwlockT prwlock{}; CHECK_AND_INIT_RWLOCK if (!prwlock->lock.try_lock()) { @@ -159,14 +157,14 @@ int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(PthreadRwlockT* rwlock) { } int PS4_SYSV_ABI posix_pthread_rwlock_wrlock(PthreadRwlockT* rwlock) { - PthreadRwlockT prwlock; + PthreadRwlockT prwlock{}; CHECK_AND_INIT_RWLOCK return prwlock->Wrlock(nullptr); } int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock(PthreadRwlockT* rwlock, const OrbisKernelTimespec* abstime) { - PthreadRwlockT prwlock; + PthreadRwlockT prwlock{}; CHECK_AND_INIT_RWLOCK return prwlock->Wrlock(abstime); } @@ -213,7 +211,7 @@ int PS4_SYSV_ABI posix_pthread_rwlockattr_destroy(PthreadRwlockAttrT* rwlockattr return POSIX_EINVAL; } - free(prwlockattr); + delete prwlockattr; return 0; } @@ -228,7 +226,7 @@ int PS4_SYSV_ABI posix_pthread_rwlockattr_init(PthreadRwlockAttrT* rwlockattr) { return POSIX_EINVAL; } - PthreadRwlockAttrT prwlockattr = (PthreadRwlockAttrT)malloc(sizeof(PthreadRwlockAttr)); + PthreadRwlockAttrT prwlockattr = new PthreadRwlockAttr{}; if (prwlockattr == nullptr) { return POSIX_ENOMEM; } diff --git a/src/core/libraries/kernel/threads/thr_sem.cpp b/src/core/libraries/kernel/threads/thr_sem.cpp index 4eb59b188..6ff26db3e 100644 --- a/src/core/libraries/kernel/threads/thr_sem.cpp +++ b/src/core/libraries/kernel/threads/thr_sem.cpp @@ -27,9 +27,7 @@ public: OrbisSem(s32 init_count, s32 max_count, std::string_view name, bool is_fifo) : name{name}, token_count{init_count}, max_count{max_count}, init_count{init_count}, is_fifo{is_fifo} {} - ~OrbisSem() { - ASSERT(wait_list.empty()); - } + ~OrbisSem() = default; int Wait(bool can_block, s32 need_count, u32* timeout) { std::unique_lock lk{mutex}; @@ -93,6 +91,15 @@ public: return ORBIS_OK; } + void Delete() { + std::scoped_lock lk{mutex}; + for (auto* waiter : wait_list) { + waiter->was_deleted = true; + waiter->cv.notify_one(); + } + wait_list.clear(); + } + public: struct WaitingThread { std::condition_variable cv; @@ -219,7 +226,7 @@ int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) { if (!sem) { return SCE_KERNEL_ERROR_ESRCH; } - delete sem; + sem->Delete(); return ORBIS_OK; } diff --git a/src/core/libraries/kernel/threads/thr_spec.cpp b/src/core/libraries/kernel/threads/thr_spec.cpp index ccedf5b8c..ab000c1b6 100644 --- a/src/core/libraries/kernel/threads/thr_spec.cpp +++ b/src/core/libraries/kernel/threads/thr_spec.cpp @@ -90,7 +90,7 @@ void _thread_cleanupspecific() { } } } - free(curthread->specific); + delete[] curthread->specific; curthread->specific = nullptr; ASSERT(curthread->specific_data_count == 0); } @@ -100,8 +100,7 @@ int PS4_SYSV_ABI posix_pthread_setspecific(PthreadKeyT key, const void* value) { Pthread* pthread = g_curthread; if (!pthread->specific) { - pthread->specific = - (PthreadSpecificElem*)calloc(1, sizeof(PthreadSpecificElem) * PthreadKeysMax); + pthread->specific = new PthreadSpecificElem[PthreadKeysMax]; if (!pthread->specific) { return POSIX_ENOMEM; } diff --git a/src/core/libraries/kernel/threads/thread_state.cpp b/src/core/libraries/kernel/threads/thread_state.cpp index 671c8381a..9d91ab618 100644 --- a/src/core/libraries/kernel/threads/thread_state.cpp +++ b/src/core/libraries/kernel/threads/thread_state.cpp @@ -2,19 +2,33 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "common/alignment.h" #include "common/scope_exit.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/threads/thread_state.h" #include "core/libraries/kernel/threads/threads.h" +#include "core/memory.h" #include "core/tls.h" namespace Libraries::Kernel { -Pthread* g_curthread{}; +thread_local Pthread* g_curthread{}; Core::Tcb* TcbCtor(Pthread* thread, int initial); void TcbDtor(Core::Tcb* oldtls); +ThreadState::ThreadState() { + // Reserve memory for maximum amount of threads allowed. + auto* memory = Core::Memory::Instance(); + static constexpr u32 ThrHeapSize = Common::AlignUp(sizeof(Pthread) * MaxThreads, 16_KB); + void* heap_addr{}; + const int ret = memory->MapMemory(&heap_addr, Core::SYSTEM_RESERVED_MIN, ThrHeapSize, + Core::MemoryProt::CpuReadWrite, Core::MemoryMapFlags::NoFlags, + Core::VMAType::File, "ThrHeap"); + ASSERT_MSG(ret == 0, "Unable to allocate thread heap memory {}", ret); + thread_heap.Initialize(heap_addr, ThrHeapSize); +} + void ThreadState::Collect(Pthread* curthread) { boost::container::small_vector work_list; { @@ -35,18 +49,18 @@ void ThreadState::Collect(Pthread* curthread) { } } -void ThreadState::TryCollect(Pthread* curthread, Pthread* thread) { +void ThreadState::TryCollect(Pthread* thread) { SCOPE_EXIT { - thread->lock->unlock(); + thread->lock.unlock(); }; if (!thread->ShouldCollect()) { return; } thread->refcount++; - thread->lock->unlock(); + thread->lock.unlock(); std::scoped_lock lk{thread_list_lock}; - thread->lock->lock(); + thread->lock.lock(); thread->refcount--; if (thread->ShouldCollect()) { threads.erase(thread); @@ -71,9 +85,7 @@ Pthread* ThreadState::Alloc(Pthread* curthread) { return nullptr; } total_threads.fetch_add(1); - thread = (Pthread*)malloc(sizeof(Pthread)); - std::construct_at(thread); - thread->lock = std::make_unique(); + thread = thread_heap.Allocate(); if (thread == nullptr) { total_threads.fetch_sub(1); return nullptr; @@ -87,12 +99,13 @@ Pthread* ThreadState::Alloc(Pthread* curthread) { tcb = TcbCtor(thread, 1 /* initial tls */); } if (tcb != nullptr) { + memset(thread, 0, sizeof(Pthread)); + std::construct_at(thread); thread->tcb = tcb; // thread->sleepqueue = _sleepq_alloc(); // thread->wake_addr = _thr_alloc_wake_addr(); } else { - std::destroy_at(thread); - free(thread); + thread_heap.Free(thread); total_threads.fetch_sub(1); thread = nullptr; } @@ -106,12 +119,12 @@ void ThreadState::Free(Pthread* curthread, Pthread* thread) { } else { TcbDtor(thread->tcb); } - thread->tcb = NULL; + thread->tcb = nullptr; + std::destroy_at(thread); if (free_threads.size() >= MaxCachedThreads) { //_sleepq_free(thread->sleepqueue); //_thr_release_wake_addr(thread->wake_addr); - std::destroy_at(thread); - free(thread); + thread_heap.Free(thread); total_threads.fetch_sub(1); } else { std::scoped_lock lk{free_thread_lock}; @@ -128,12 +141,33 @@ int ThreadState::FindThread(Pthread* thread, bool include_dead) { if (it == threads.end()) { return POSIX_ESRCH; } - thread->lock->lock(); + thread->lock.lock(); if (!include_dead && thread->state == PthreadState::Dead) { - thread->lock->unlock(); + thread->lock.unlock(); return POSIX_ESRCH; } return 0; } +int ThreadState::RefAdd(Pthread* thread, bool include_dead) { + if (thread == nullptr) { + /* Invalid thread: */ + return POSIX_EINVAL; + } + + if (int ret = FindThread(thread, include_dead); ret != 0) { + return ret; + } + + thread->refcount++; + thread->lock.unlock(); + return 0; +} + +void ThreadState::RefDelete(Pthread* thread) { + thread->lock.lock(); + thread->refcount--; + TryCollect(thread); +} + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thread_state.h b/src/core/libraries/kernel/threads/thread_state.h index 6c65a5379..c98f2083e 100644 --- a/src/core/libraries/kernel/threads/thread_state.h +++ b/src/core/libraries/kernel/threads/thread_state.h @@ -9,6 +9,7 @@ #include #include #include "common/singleton.h" +#include "common/slab_heap.h" #include "common/types.h" namespace Libraries::Kernel { @@ -27,13 +28,15 @@ struct ThreadState { static constexpr size_t MaxThreads = 100000; static constexpr size_t MaxCachedThreads = 100; + explicit ThreadState(); + bool GcNeeded() const noexcept { return gc_list.size() >= GcThreshold; } void Collect(Pthread* curthread); - void TryCollect(Pthread* curthread, Pthread* thread); + void TryCollect(Pthread* thread); Pthread* Alloc(Pthread* curthread); @@ -41,6 +44,10 @@ struct ThreadState { int FindThread(Pthread* thread, bool include_dead); + int RefAdd(Pthread* thread, bool include_dead); + + void RefDelete(Pthread* thread); + int CreateStack(PthreadAttr* attr); void FreeStack(PthreadAttr* attr); @@ -61,6 +68,7 @@ struct ThreadState { active_threads.fetch_sub(1); } + Common::SlabHeap thread_heap; std::set threads; std::list free_threads; std::list gc_list; diff --git a/src/core/libraries/kernel/threads/threads.h b/src/core/libraries/kernel/threads/threads.h index 4a997ef41..15d89adaf 100644 --- a/src/core/libraries/kernel/threads/threads.h +++ b/src/core/libraries/kernel/threads/threads.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -53,16 +54,25 @@ struct PthreadMutex : public ListBaseHook { int m_spinloops; int m_yieldloops; PthreadMutexProt m_protocol; + std::string name; PthreadMutexType Type() const noexcept { return static_cast(m_flags & PthreadMutexFlags::TypeMask); } + void lock() { + Lock(nullptr); + } + + void unlock() { + Unlock(); + } + int SelfTryLock(); - int SelfLock(const OrbisKernelTimespec* abstime); + int SelfLock(const OrbisKernelTimespec* abstime, u64 usec); int TryLock(); - int Lock(const OrbisKernelTimespec* abstime); + int Lock(const OrbisKernelTimespec* abstime, u64 usec = 0); int Unlock(); @@ -83,20 +93,38 @@ enum class PthreadCondFlags : u32 { Busy = 4, }; +enum class ClockId : u32 { + Realtime = 0, + Virtual = 1, + Prof = 2, + Monotonic = 4, + Uptime = 5, + UptimePrecise = 7, + UptimeFast = 8, + RealtimePrecise = 9, + RealtimeFast = 10, + MonotonicPrecise = 11, + MonotonicFast = 12, + Second = 13, + ThreadCputimeID = 14, +}; + struct PthreadCond { std::condition_variable_any cond; u32 has_user_waiters; u32 has_kern_waiters; u32 flags; - u32 clock_id; + ClockId clock_id; + std::string name; int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime); + int Wait(PthreadMutexT* mutex, u64 usec); }; using PthreadCondT = PthreadCond*; struct PthreadCondAttr { int c_pshared; - int c_clockid; + ClockId c_clockid; }; using PthreadCondAttrT = PthreadCondAttr*; @@ -122,6 +150,10 @@ enum class SchedPolicy : u32 { RoundRobin = 3, }; +struct Cpuset { + u64 bits; +}; + struct PthreadAttr { SchedPolicy sched_policy; int sched_inherit; @@ -132,6 +164,7 @@ struct PthreadAttr { size_t stacksize_attr; size_t guardsize_attr; size_t cpusetsize; + Cpuset* cpuset; }; using PthreadAttrT = PthreadAttr*; @@ -198,17 +231,36 @@ using PthreadEntryFunc = void* (*)(void*); constexpr u32 TidTerminated = 1; +struct WakeAddr { + WakeAddr* link; + u32 value; + char pad[12]; +}; + +struct SleepQueue { + std::list sq_blocked; + std::forward_list sq_freeq; + std::list sq_hash; + std::forward_list sq_flink; + void* sq_wchan; + int sq_type; +}; + +struct SchedParam { + int sched_priority; +}; + struct Pthread { static constexpr u32 ThrMagic = 0xd09ba115U; std::atomic tid; - std::unique_ptr lock; + std::mutex lock; u32 cycle; int locklevel; int critical_count; int sigblock; int refcount; - void PS4_SYSV_ABI* (*start_routine)(void*); + void* PS4_SYSV_ABI (*start_routine)(void*); void* arg; PthreadAttr attr; bool cancel_enable; @@ -226,8 +278,8 @@ struct Pthread { Pthread* joiner; ThreadFlags flags; ThreadListFlags tlflags; - boost::intrusive::list mutexq; - boost::intrusive::list pp_mutexq; + std::list mutexq; + std::list pp_mutexq; void* ret; PthreadSpecificElem* specific; int specific_data_count; @@ -240,6 +292,12 @@ struct Pthread { int report_events; int event_mask; std::string name; + WakeAddr* wake_addr; + SleepQueue* sleepqueue; + void* wchan; + PthreadMutex* mutex_obj; + bool will_sleep; + bool has_user_waiters; bool InCritical() const noexcept { return locklevel > 0 || critical_count > 0; @@ -255,17 +313,17 @@ struct Pthread { void Enqueue(PthreadMutex* mutex) { mutex->m_owner = this; - mutexq.push_back(*mutex); + // mutexq.push_back(*mutex); } void Dequeue(PthreadMutex* mutex) { mutex->m_owner = nullptr; - mutexq.erase(decltype(mutexq)::s_iterator_to(*mutex)); + // mutexq.erase(decltype(mutexq)::s_iterator_to(*mutex)); } }; using PthreadT = Pthread*; -extern Pthread* g_curthread; +extern thread_local Pthread* g_curthread; void RegisterMutex(Core::Loader::SymbolsResolver* sym); void RegisterCond(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/kernel/time_management.cpp b/src/core/libraries/kernel/time_management.cpp index a7e46923a..7d5657b10 100644 --- a/src/core/libraries/kernel/time_management.cpp +++ b/src/core/libraries/kernel/time_management.cpp @@ -1,6 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - +#pragma clang optimize off #include #include "common/assert.h" diff --git a/src/core/libraries/videoout/video_out.cpp b/src/core/libraries/videoout/video_out.cpp index 31b8a21ca..72885888e 100644 --- a/src/core/libraries/videoout/video_out.cpp +++ b/src/core/libraries/videoout/video_out.cpp @@ -1,6 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - +#pragma clang optimize off #include "common/assert.h" #include "common/config.h" #include "common/logging/log.h" @@ -95,7 +95,7 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co LOG_ERROR(Lib_VideoOut, "Addresses are null"); return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS; } - + VAddr ret_addr = (VAddr)__builtin_return_address(0); auto* port = driver->GetPort(handle); if (!port || !port->is_open) { LOG_ERROR(Lib_VideoOut, "Invalid handle = {}", handle); diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 56b35b430..8781f7a03 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -8,6 +8,7 @@ #include "common/logging/log.h" #include "common/path_util.h" #include "common/string_util.h" +#include "common/thread.h" #include "core/aerolib/aerolib.h" #include "core/aerolib/stubs.h" #include "core/libraries/kernel/memory_management.h" @@ -24,10 +25,8 @@ static PS4_SYSV_ABI void ProgramExitFunc() { } #ifdef ARCH_X86_64 -static PS4_SYSV_ABI void* RunMainEntry [[noreturn]] (void* arg) { - EntryParams* params = (EntryParams*)arg; +static PS4_SYSV_ABI void* RunMainEntry [[noreturn]] (EntryParams* params) { // Start shared library modules - params->linker->LoadSharedLibraries(); asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes "subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned @@ -43,7 +42,7 @@ static PS4_SYSV_ABI void* RunMainEntry [[noreturn]] (void* arg) { "jmp *%0\n" // can't use call here, as that would mangle the prepared stack. // there's no coming back : - : "r"(params->entry_addr), "r"(params), "r"(params->exit_func) + : "r"(params->entry_addr), "r"(params), "r"(ProgramExitFunc) : "rax", "rsi", "rdi"); UNREACHABLE(); } @@ -82,14 +81,17 @@ void Linker::Execute() { } } - // Start main module. - EntryParams* params = new EntryParams; - params->argc = 1; - params->argv[0] = "eboot.bin"; - params->entry_addr = module->GetEntryAddress(); - params->exit_func = ProgramExitFunc; - params->linker = this; - Libraries::Kernel::LaunchThread(RunMainEntry, params, "GAME_MainThread"); + main_thread.Run([this, module](std::stop_token) { + Common::SetCurrentThreadName("GAME_MainThread"); + LoadSharedLibraries(); + + // Start main module. + EntryParams params{}; + params.argc = 1; + params.argv[0] = "eboot.bin"; + params.entry_addr = module->GetEntryAddress(); + RunMainEntry(¶ms); + }); } s32 Linker::LoadModule(const std::filesystem::path& elf_name, bool is_dynamic) { @@ -122,10 +124,9 @@ Module* Linker::FindByAddress(VAddr address) { } void Linker::Relocate(Module* module) { - module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool isJmpRel) { - const u32 bit_idx = - (isJmpRel ? module->dynamic_info.relocation_table_size / sizeof(elf_relocation) : 0) + - i; + module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool is_jmp_rel) { + const u32 num_relocs = module->dynamic_info.relocation_table_size / sizeof(elf_relocation); + const u32 bit_idx = (is_jmp_rel ? num_relocs : 0) + i; if (module->TestRelaBit(bit_idx)) { return; } @@ -133,7 +134,7 @@ void Linker::Relocate(Module* module) { auto symbol = rel->GetSymbol(); auto addend = rel->rel_addend; auto* symbol_table = module->dynamic_info.symbol_table; - auto* namesTlb = module->dynamic_info.str_table; + auto* names_tlb = module->dynamic_info.str_table; const VAddr rel_base_virtual_addr = module->GetBaseAddress(); const VAddr rel_virtual_addr = rel_base_virtual_addr + rel->rel_offset; @@ -189,7 +190,7 @@ void Linker::Relocate(Module* module) { break; case STB_GLOBAL: case STB_WEAK: { - rel_name = namesTlb + sym.st_name; + rel_name = names_tlb + sym.st_name; if (Resolve(rel_name, rel_sym_type, module, &symrec)) { // Only set the rela bit if the symbol was actually resolved and not stubbed. module->SetRelaBit(bit_idx); @@ -198,7 +199,7 @@ void Linker::Relocate(Module* module) { break; } default: - ASSERT_MSG(0, "unknown bind type {}", sym_bind); + UNREACHABLE_MSG("Unknown bind type {}", sym_bind); } rel_is_resolved = (symbol_virtual_addr != 0); rel_value = (rel_is_resolved ? symbol_virtual_addr + addend : 0); @@ -212,7 +213,7 @@ void Linker::Relocate(Module* module) { if (rel_is_resolved) { VirtualMemory::memory_patch(rel_virtual_addr, rel_value); } else { - LOG_INFO(Core_Linker, "function not patched! {}", rel_name); + LOG_INFO(Core_Linker, "Function not patched! {}", rel_name); } }); } @@ -295,12 +296,13 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { } u8* addr = dtv_table[module_index + 1].pointer; + Module* module = m_modules[module_index - 1].get(); + // LOG_INFO(Core_Linker, "Got DTV addr {} from module index {} with name {}", + // fmt::ptr(addr), module_index, module->file.filename().string()); if (!addr) { // Module was just loaded by above code. Allocate TLS block for it. - Module* module = m_modules[module_index - 1].get(); const u32 init_image_size = module->tls.init_image_size; - u8* dest = - reinterpret_cast(ExecuteGuest(heap_api->heap_malloc, module->tls.image_size)); + u8* dest = reinterpret_cast(heap_api->heap_malloc(module->tls.image_size)); const u8* src = reinterpret_cast(module->tls.image_virtual_addr); std::memcpy(dest, src, init_image_size); std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size); diff --git a/src/core/linker.h b/src/core/linker.h index 6709793a1..dfdc847a4 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -6,6 +6,7 @@ #include #include #include +#include "core/libraries/kernel/thread_management.h" #include "core/module.h" namespace Core { @@ -49,8 +50,6 @@ struct EntryParams { u32 padding; const char* argv[3]; VAddr entry_addr; - ExitFunc exit_func; - Linker* linker; }; struct HeapAPI { @@ -141,6 +140,7 @@ private: const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l); MemoryManager* memory; + Libraries::Kernel::Thread main_thread; std::mutex mutex; u32 dtv_generation_counter{1}; size_t static_tls_size{}; diff --git a/src/core/module.cpp b/src/core/module.cpp index cdd032e13..4414a24de 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -166,9 +166,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { tls.align = elf_pheader[i].p_align; tls.image_virtual_addr = elf_pheader[i].p_vaddr + base_virtual_addr; tls.image_size = GetAlignedSize(elf_pheader[i]); - if (tls.image_size != 0) { - tls.modid = ++max_tls_index; - } + tls.modid = ++max_tls_index; LOG_INFO(Core_Linker, "TLS virtual address = {:#x}", tls.image_virtual_addr); LOG_INFO(Core_Linker, "TLS image size = {}", tls.image_size); break; diff --git a/src/shader_recompiler/frontend/translate/data_share.cpp b/src/shader_recompiler/frontend/translate/data_share.cpp index a453023fc..adc2bbbc2 100644 --- a/src/shader_recompiler/frontend/translate/data_share.cpp +++ b/src/shader_recompiler/frontend/translate/data_share.cpp @@ -68,10 +68,9 @@ void Translator::V_READFIRSTLANE_B32(const GcnInst& inst) { } void Translator::V_READLANE_B32(const GcnInst& inst) { - const IR::ScalarReg dst{inst.dst[0].code}; const IR::U32 value{GetSrc(inst.src[0])}; const IR::U32 lane{GetSrc(inst.src[1])}; - ir.SetScalarReg(dst, ir.ReadLane(value, lane)); + SetDst(inst.dst[0], ir.ReadLane(value, lane)); } void Translator::V_WRITELANE_B32(const GcnInst& inst) { @@ -155,7 +154,7 @@ void Translator::DS_SWIZZLE_B32(const GcnInst& inst) { const u8 offset0 = inst.control.ds.offset0; const u8 offset1 = inst.control.ds.offset1; const IR::U32 src{GetSrc(inst.src[1])}; - ASSERT(offset1 & 0x80); + // ASSERT(offset1 & 0x80); const IR::U32 lane_id = ir.LaneId(); const IR::U32 id_in_group = ir.BitwiseAnd(lane_id, ir.Imm32(0b11)); const IR::U32 base = ir.ShiftLeftLogical(id_in_group, ir.Imm32(1)); diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp index 0eb7e0759..25b62039f 100644 --- a/src/video_core/renderer_vulkan/vk_platform.cpp +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -1,6 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - +#pragma clang optimize off // Include the vulkan platform specific header #if defined(ANDROID) #define VK_USE_PLATFORM_ANDROID_KHR