mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-03 15:46:20 +00:00
kernel: Fix a bunch of bugs, kernel thread heap
This commit is contained in:
parent
00b84b2c7f
commit
d0d8b5eee5
35 changed files with 924 additions and 242 deletions
|
@ -821,6 +821,9 @@ else()
|
||||||
src/core/libraries/kernel/threads/thr_clean.cpp
|
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/thread_state.h src/core/libraries/kernel/threads/thread_state.cpp
|
||||||
src/core/libraries/kernel/threads/thr_ctrdtr.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()
|
endif()
|
||||||
|
|
||||||
|
|
163
src/common/slab_heap.h
Normal file
163
src/common/slab_heap.h
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#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<Node*>(obj);
|
||||||
|
node->next = m_head;
|
||||||
|
m_head = node;
|
||||||
|
|
||||||
|
m_lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<Node*> 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<uintptr_t>(memory);
|
||||||
|
m_end = m_start + num_obj * obj_size;
|
||||||
|
m_peak = m_start;
|
||||||
|
|
||||||
|
// Free the objects.
|
||||||
|
u8* cur = reinterpret_cast<u8*>(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<uintptr_t>(obj));
|
||||||
|
ASSERT(contained);
|
||||||
|
SlabHeapImpl::Free(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetObjectIndex(const void* obj) const {
|
||||||
|
return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetPeakIndex() const {
|
||||||
|
return this->GetObjectIndex(reinterpret_cast<const void*>(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 <typename T>
|
||||||
|
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<T*>(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
|
|
@ -3,10 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <bit>
|
|
||||||
#include <compare>
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
|
53
src/common/spin_lock.cpp
Executable file
53
src/common/spin_lock.cpp
Executable file
|
@ -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 <intrin.h>
|
||||||
|
#if _M_AMD64
|
||||||
|
#define __x86_64__ 1
|
||||||
|
#endif
|
||||||
|
#if _M_ARM64
|
||||||
|
#define __aarch64__ 1
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#if __x86_64__
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#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
|
33
src/common/spin_lock.h
Executable file
33
src/common/spin_lock.h
Executable file
|
@ -0,0 +1,33 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
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
|
|
@ -305,7 +305,7 @@ void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("XC9wM+xULz8", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerJumpToTime);
|
LIB_FUNCTION("XC9wM+xULz8", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerJumpToTime);
|
||||||
LIB_FUNCTION("9y5v+fGN4Wk", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPause);
|
LIB_FUNCTION("9y5v+fGN4Wk", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPause);
|
||||||
LIB_FUNCTION("HD1YKVU26-M", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPostInit);
|
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("w5moABNwnRY", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerResume);
|
||||||
LIB_FUNCTION("k-q+xOxdc3E", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0,
|
LIB_FUNCTION("k-q+xOxdc3E", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0,
|
||||||
sceAvPlayerSetAvSyncMode);
|
sceAvPlayerSetAvSyncMode);
|
||||||
|
|
|
@ -252,11 +252,9 @@ bool AvPlayerSource::Start() {
|
||||||
LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context.");
|
LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_demuxer_thread = std::jthread([this](std::stop_token stop) { this->DemuxerThread(stop); });
|
m_demuxer_thread.Run([this](std::stop_token stop) { this->DemuxerThread(stop); });
|
||||||
m_video_decoder_thread =
|
m_video_decoder_thread.Run([this](std::stop_token stop) { this->VideoDecoderThread(stop); });
|
||||||
std::jthread([this](std::stop_token stop) { this->VideoDecoderThread(stop); });
|
m_audio_decoder_thread.Run([this](std::stop_token stop) { this->AudioDecoderThread(stop); });
|
||||||
m_audio_decoder_thread =
|
|
||||||
std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); });
|
|
||||||
m_start_time = std::chrono::high_resolution_clock::now();
|
m_start_time = std::chrono::high_resolution_clock::now();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -269,18 +267,10 @@ bool AvPlayerSource::Stop() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_video_decoder_thread.request_stop();
|
m_video_decoder_thread.Stop();
|
||||||
m_audio_decoder_thread.request_stop();
|
m_audio_decoder_thread.Stop();
|
||||||
m_demuxer_thread.request_stop();
|
m_demuxer_thread.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();
|
|
||||||
}
|
|
||||||
if (m_current_audio_frame.has_value()) {
|
if (m_current_audio_frame.has_value()) {
|
||||||
m_audio_buffers.Push(std::move(m_current_audio_frame.value()));
|
m_audio_buffers.Push(std::move(m_current_audio_frame.value()));
|
||||||
m_current_audio_frame.reset();
|
m_current_audio_frame.reset();
|
||||||
|
@ -504,12 +494,8 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) {
|
||||||
m_video_frames_cv.Notify();
|
m_video_frames_cv.Notify();
|
||||||
m_audio_frames_cv.Notify();
|
m_audio_frames_cv.Notify();
|
||||||
|
|
||||||
if (m_video_decoder_thread.joinable()) {
|
m_video_decoder_thread.Join();
|
||||||
m_video_decoder_thread.join();
|
m_audio_decoder_thread.Join();
|
||||||
}
|
|
||||||
if (m_audio_decoder_thread.joinable()) {
|
|
||||||
m_audio_decoder_thread.join();
|
|
||||||
}
|
|
||||||
m_state.OnEOF();
|
m_state.OnEOF();
|
||||||
|
|
||||||
LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normally");
|
LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normally");
|
||||||
|
@ -802,8 +788,8 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvPlayerSource::HasRunningThreads() const {
|
bool AvPlayerSource::HasRunningThreads() const {
|
||||||
return m_demuxer_thread.joinable() || m_video_decoder_thread.joinable() ||
|
return m_demuxer_thread.Joinable() || m_video_decoder_thread.Joinable() ||
|
||||||
m_audio_decoder_thread.joinable();
|
m_audio_decoder_thread.Joinable();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libraries::AvPlayer
|
} // namespace Libraries::AvPlayer
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/polyfill_thread.h"
|
|
||||||
#include "core/libraries/avplayer/avplayer.h"
|
#include "core/libraries/avplayer/avplayer.h"
|
||||||
#include "core/libraries/avplayer/avplayer_common.h"
|
#include "core/libraries/avplayer/avplayer_common.h"
|
||||||
#include "core/libraries/avplayer/avplayer_data_streamer.h"
|
#include "core/libraries/avplayer/avplayer_data_streamer.h"
|
||||||
|
#include "core/libraries/kernel/thread_management.h"
|
||||||
|
|
||||||
struct AVCodecContext;
|
struct AVCodecContext;
|
||||||
struct AVFormatContext;
|
struct AVFormatContext;
|
||||||
|
@ -200,9 +200,9 @@ private:
|
||||||
EventCV m_stop_cv{};
|
EventCV m_stop_cv{};
|
||||||
|
|
||||||
std::mutex m_state_mutex{};
|
std::mutex m_state_mutex{};
|
||||||
std::jthread m_demuxer_thread{};
|
Kernel::Thread m_demuxer_thread{};
|
||||||
std::jthread m_video_decoder_thread{};
|
Kernel::Thread m_video_decoder_thread{};
|
||||||
std::jthread m_audio_decoder_thread{};
|
Kernel::Thread m_audio_decoder_thread{};
|
||||||
|
|
||||||
AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext};
|
AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext};
|
||||||
AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext};
|
AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext};
|
||||||
|
|
|
@ -117,10 +117,7 @@ AvPlayerState::~AvPlayerState() {
|
||||||
std::unique_lock lock(m_source_mutex);
|
std::unique_lock lock(m_source_mutex);
|
||||||
m_up_source.reset();
|
m_up_source.reset();
|
||||||
}
|
}
|
||||||
if (m_controller_thread.joinable()) {
|
m_controller_thread.Stop();
|
||||||
m_controller_thread.request_stop();
|
|
||||||
m_controller_thread.join();
|
|
||||||
}
|
|
||||||
m_event_queue.Clear();
|
m_event_queue.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,8 +218,7 @@ void AvPlayerState::WarningEvent(s32 id) {
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
void AvPlayerState::StartControllerThread() {
|
void AvPlayerState::StartControllerThread() {
|
||||||
m_controller_thread =
|
m_controller_thread.Run([this](std::stop_token stop) { this->AvControllerThread(stop); });
|
||||||
std::jthread([this](std::stop_token stop) { this->AvControllerThread(stop); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called inside GAME thread
|
// Called inside GAME thread
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
|
||||||
#include "common/polyfill_thread.h"
|
|
||||||
#include "core/libraries/avplayer/avplayer.h"
|
#include "core/libraries/avplayer/avplayer.h"
|
||||||
#include "core/libraries/avplayer/avplayer_source.h"
|
#include "core/libraries/avplayer/avplayer_source.h"
|
||||||
|
#include "core/libraries/kernel/thread_management.h"
|
||||||
|
|
||||||
namespace Libraries::AvPlayer {
|
namespace Libraries::AvPlayer {
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ private:
|
||||||
std::shared_mutex m_source_mutex{};
|
std::shared_mutex m_source_mutex{};
|
||||||
std::mutex m_state_machine_mutex{};
|
std::mutex m_state_machine_mutex{};
|
||||||
std::mutex m_event_handler_mutex{};
|
std::mutex m_event_handler_mutex{};
|
||||||
std::jthread m_controller_thread{};
|
Kernel::Thread m_controller_thread{};
|
||||||
AvPlayerQueue<AvPlayerEvent> m_event_queue{};
|
AvPlayerQueue<AvPlayerEvent> m_event_queue{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma clang optimize off
|
||||||
#include "gnm_error.h"
|
#include "gnm_error.h"
|
||||||
#include "gnmdriver.h"
|
#include "gnmdriver.h"
|
||||||
|
|
||||||
|
|
|
@ -85,8 +85,6 @@ struct iovec {
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) {
|
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;
|
size_t total_written = 0;
|
||||||
for (int i = 0; i < iovcn; i++) {
|
for (int i = 0; i < iovcn; i++) {
|
||||||
total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout);
|
total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout);
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <algorithm>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include "common/logging/log.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "core/libraries/error_codes.h"
|
#include "core/libraries/error_codes.h"
|
||||||
|
|
||||||
|
@ -17,11 +19,21 @@ void ErrSceToPosix(int result);
|
||||||
int ErrnoToSceKernelError(int e);
|
int ErrnoToSceKernelError(int e);
|
||||||
void SetPosixErrno(int e);
|
void SetPosixErrno(int e);
|
||||||
|
|
||||||
template <class F, F f>
|
template <size_t N>
|
||||||
|
struct StringLiteral {
|
||||||
|
constexpr StringLiteral(const char (&str)[N]) {
|
||||||
|
std::copy_n(str, N, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
char value[N];
|
||||||
|
};
|
||||||
|
|
||||||
|
template <StringLiteral name, class F, F f>
|
||||||
struct WrapperImpl;
|
struct WrapperImpl;
|
||||||
|
|
||||||
template <class R, class... Args, PS4_SYSV_ABI R (*f)(Args...)>
|
template <StringLiteral name, class R, class... Args, PS4_SYSV_ABI R (*f)(Args...)>
|
||||||
struct WrapperImpl<PS4_SYSV_ABI R (*)(Args...), f> {
|
struct WrapperImpl<name, PS4_SYSV_ABI R (*)(Args...), f> {
|
||||||
|
static constexpr StringLiteral Name{name};
|
||||||
static R PS4_SYSV_ABI wrap(Args... args) {
|
static R PS4_SYSV_ABI wrap(Args... args) {
|
||||||
u32 ret = f(args...);
|
u32 ret = f(args...);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
|
@ -31,10 +43,10 @@ struct WrapperImpl<PS4_SYSV_ABI R (*)(Args...), f> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class F, F f>
|
template <StringLiteral name, class F, F f>
|
||||||
constexpr auto OrbisWrapper = WrapperImpl<F, f>::wrap;
|
constexpr auto OrbisWrapper = WrapperImpl<name, F, f>::wrap;
|
||||||
|
|
||||||
#define ORBIS(func) OrbisWrapper<decltype(&func), func>
|
#define ORBIS(func) WrapperImpl<#func, decltype(&func), func>::wrap
|
||||||
|
|
||||||
int* PS4_SYSV_ABI __Error();
|
int* PS4_SYSV_ABI __Error();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
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);
|
void RegisterMemory(Core::Loader::SymbolsResolver* sym);
|
||||||
|
|
||||||
} // namespace Libraries::Kernel
|
} // namespace Libraries::Kernel
|
||||||
|
|
|
@ -7,20 +7,6 @@
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
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) {
|
void RegisterThreads(Core::Loader::SymbolsResolver* sym) {
|
||||||
RegisterMutex(sym);
|
RegisterMutex(sym);
|
||||||
RegisterCond(sym);
|
RegisterCond(sym);
|
||||||
|
|
|
@ -11,8 +11,60 @@ class SymbolsResolver;
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
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);
|
void RegisterThreads(Core::Loader::SymbolsResolver* sym);
|
||||||
|
|
||||||
|
class Thread {
|
||||||
|
public:
|
||||||
|
explicit Thread() = default;
|
||||||
|
~Thread() {
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Run(std::function<void(std::stop_token)>&& 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<void(std::stop_token)> func;
|
||||||
|
std::stop_source stop;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Libraries::Kernel
|
} // namespace Libraries::Kernel
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma clang optimize off
|
||||||
#include "core/libraries/error_codes.h"
|
#include "core/libraries/error_codes.h"
|
||||||
#include "core/libraries/kernel/libkernel.h"
|
#include "core/libraries/kernel/libkernel.h"
|
||||||
#include "core/libraries/kernel/threads/thread_state.h"
|
#include "core/libraries/kernel/threads/thread_state.h"
|
||||||
|
@ -18,9 +18,9 @@ struct PthreadPrio {
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::array<PthreadPrio, 3> ThrPriorities = {{
|
static constexpr std::array<PthreadPrio, 3> ThrPriorities = {{
|
||||||
{0x2BC, 0x300, 0x3BF}, // Fifo
|
{0x100, 0x2FF, 0x2BC}, // Fifo
|
||||||
{0x384, 0x100, 0x2FF}, // Other
|
{0x300, 0x3BF, 0x384}, // Other
|
||||||
{0x2BC, 0, 1}, // Round-Robin
|
{0x100, 0x2FF, 0x2BC}, // Round-Robin
|
||||||
}};
|
}};
|
||||||
|
|
||||||
PthreadAttr PthreadAttrDefault = {
|
PthreadAttr PthreadAttrDefault = {
|
||||||
|
@ -33,13 +33,14 @@ PthreadAttr PthreadAttrDefault = {
|
||||||
.stacksize_attr = ThrStackDefault,
|
.stacksize_attr = ThrStackDefault,
|
||||||
.guardsize_attr = 0,
|
.guardsize_attr = 0,
|
||||||
.cpusetsize = 0,
|
.cpusetsize = 0,
|
||||||
|
.cpuset = nullptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr) {
|
int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr) {
|
||||||
if (attr == nullptr || *attr == nullptr) {
|
if (attr == nullptr || *attr == nullptr) {
|
||||||
return POSIX_EINVAL;
|
return POSIX_EINVAL;
|
||||||
}
|
}
|
||||||
free(*attr);
|
delete *attr;
|
||||||
*attr = nullptr;
|
*attr = nullptr;
|
||||||
return 0;
|
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) {
|
int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr) {
|
||||||
PthreadAttrT pattr = (PthreadAttrT)malloc(sizeof(PthreadAttr));
|
PthreadAttrT pattr = new PthreadAttr{};
|
||||||
if (pattr == nullptr) {
|
if (pattr == nullptr) {
|
||||||
return POSIX_ENOMEM;
|
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;
|
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 ||
|
if (param->sched_priority < ThrPriorities[u32(policy) - 1].pri_min ||
|
||||||
param->sched_priority > ThrPriorities[u32(policy) - 1].pri_max) {
|
param->sched_priority > ThrPriorities[u32(policy) - 1].pri_max) {
|
||||||
return POSIX_ENOTSUP;
|
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)) {
|
if (True(pthread->flags & ThreadFlags::Detached)) {
|
||||||
attr.flags |= PthreadAttrFlags::Detached;
|
attr.flags |= PthreadAttrFlags::Detached;
|
||||||
}
|
}
|
||||||
pthread->lock->unlock();
|
pthread->lock.unlock();
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
memcpy(dst, &attr, sizeof(PthreadAttr));
|
memcpy(dst, &attr, sizeof(PthreadAttr));
|
||||||
}
|
}
|
||||||
return ret;
|
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) {
|
void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
|
||||||
// Posix
|
// Posix
|
||||||
LIB_FUNCTION("wtkt-teR1so", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_init);
|
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));
|
ORBIS(posix_pthread_attr_setstackaddr));
|
||||||
LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1,
|
LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1,
|
||||||
ORBIS(posix_pthread_attr_setguardsize));
|
ORBIS(posix_pthread_attr_setguardsize));
|
||||||
|
LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1,
|
||||||
|
ORBIS(scePthreadAttrGetaffinity));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libraries::Kernel
|
} // namespace Libraries::Kernel
|
||||||
|
|
|
@ -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) {
|
void PS4_SYSV_ABI posix_pthread_cleanup_push(PthreadCleanupFunc routine, void* arg) {
|
||||||
Pthread* curthread = g_curthread;
|
Pthread* curthread = g_curthread;
|
||||||
PthreadCleanup* newbuf = (PthreadCleanup*)malloc(sizeof(PthreadCleanup));
|
PthreadCleanup* newbuf = new PthreadCleanup{};
|
||||||
if (newbuf == nullptr) {
|
if (newbuf == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ void PS4_SYSV_ABI posix_pthread_cleanup_pop(int execute) {
|
||||||
old->routine(old->routine_arg);
|
old->routine(old->routine_arg);
|
||||||
}
|
}
|
||||||
if (old->onheap) {
|
if (old->onheap) {
|
||||||
free(old);
|
delete old;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma clang optimize off
|
#pragma clang optimize off
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include "common/assert.h"
|
||||||
#include "core/libraries/error_codes.h"
|
#include "core/libraries/error_codes.h"
|
||||||
#include "core/libraries/kernel/libkernel.h"
|
#include "core/libraries/kernel/libkernel.h"
|
||||||
#include "core/libraries/kernel/threads/threads.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_INITIALIZER ((PthreadCond*)NULL)
|
||||||
#define THR_COND_DESTROYED ((PthreadCond*)1)
|
#define THR_COND_DESTROYED ((PthreadCond*)1)
|
||||||
|
|
||||||
enum class ClockId : u32 {
|
static constexpr PthreadCondAttr PhreadCondattrDefault = {
|
||||||
Realtime = 0,
|
.c_pshared = 0,
|
||||||
Virtual = 1,
|
.c_clockid = ClockId::Realtime,
|
||||||
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 = PTHREAD_PROCESS_PRIVATE,
|
static int CondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr, const char* name) {
|
||||||
.c_clockid = CLOCK_REALTIME};
|
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) {
|
if (cvp == nullptr) {
|
||||||
return POSIX_ENOMEM;
|
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) {
|
if (cond_attr == nullptr || *cond_attr == nullptr) {
|
||||||
cvp->clock_id = CLOCK_REALTIME;
|
cvp->clock_id = ClockId::Realtime;
|
||||||
} else {
|
} else {
|
||||||
// if ((*cond_attr)->c_pshared) {
|
// if ((*cond_attr)->c_pshared) {
|
||||||
// cvp->flags |= USYNC_PROCESS_SHARED;
|
// 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) {
|
static int InitStatic(Pthread* thread, PthreadCondT* cond) {
|
||||||
std::scoped_lock lk{CondStaticLock};
|
std::scoped_lock lk{CondStaticLock};
|
||||||
if (*cond == NULL)
|
if (*cond == nullptr) {
|
||||||
return CondInit(cond, NULL);
|
return CondInit(cond, nullptr, nullptr);
|
||||||
|
}
|
||||||
return 0;
|
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) {
|
int PS4_SYSV_ABI posix_pthread_cond_init(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) {
|
||||||
*cond = nullptr;
|
*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) {
|
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;
|
cvp = *cond;
|
||||||
*cond = THR_COND_DESTROYED;
|
*cond = THR_COND_DESTROYED;
|
||||||
std::destroy_at(cvp);
|
|
||||||
free(cvp);
|
free(cvp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PthreadCond::Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime) {
|
static std::mutex sc_lock;
|
||||||
Pthread* curthread = g_curthread;
|
static std::unordered_map<void*, SleepQueue*> sc_table;
|
||||||
PthreadMutex* mp = *mutex;
|
|
||||||
|
|
||||||
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;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
//_thr_testcancel(curthread);
|
//_thr_testcancel(curthread);
|
||||||
//_thr_cancel_enter2(curthread, 0);
|
//_thr_cancel_enter2(curthread, 0);
|
||||||
if (abstime) {
|
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;
|
return status == std::cv_status::timeout ? POSIX_ETIMEDOUT : 0;
|
||||||
} else {
|
} else {
|
||||||
cond.wait(mp->m_lock);
|
cond.wait(*mp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
//_thr_cancel_leave(curthread, 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) {
|
int PS4_SYSV_ABI posix_pthread_cond_wait(PthreadCondT* cond, PthreadMutexT* mutex) {
|
||||||
PthreadCond* cvp;
|
PthreadCond* cvp{};
|
||||||
CHECK_AND_INIT_COND
|
CHECK_AND_INIT_COND
|
||||||
return cvp->Wait(mutex, nullptr);
|
return cvp->Wait(mutex, nullptr);
|
||||||
}
|
}
|
||||||
|
@ -124,27 +263,34 @@ int PS4_SYSV_ABI posix_pthread_cond_timedwait(PthreadCondT* cond, PthreadMutexT*
|
||||||
return POSIX_EINVAL;
|
return POSIX_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PthreadCond* cvp;
|
PthreadCond* cvp{};
|
||||||
CHECK_AND_INIT_COND
|
CHECK_AND_INIT_COND
|
||||||
return cvp->Wait(mutex, abstime);
|
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) {
|
int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) {
|
||||||
PthreadCond* cvp;
|
PthreadCond* cvp{};
|
||||||
CHECK_AND_INIT_COND
|
CHECK_AND_INIT_COND
|
||||||
cvp->cond.notify_one();
|
cvp->cond.notify_one();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_cond_broadcast(PthreadCondT* cond) {
|
int PS4_SYSV_ABI posix_pthread_cond_broadcast(PthreadCondT* cond) {
|
||||||
PthreadCond* cvp;
|
PthreadCond* cvp{};
|
||||||
CHECK_AND_INIT_COND
|
CHECK_AND_INIT_COND
|
||||||
cvp->cond.notify_all();
|
cvp->cond.notify_all();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_condattr_init(PthreadCondAttrT* attr) {
|
int PS4_SYSV_ABI posix_pthread_condattr_init(PthreadCondAttrT* attr) {
|
||||||
PthreadCondAttr* pattr = (PthreadCondAttr*)malloc(sizeof(PthreadCondAttr));
|
PthreadCondAttr* pattr = new PthreadCondAttr{};
|
||||||
if (pattr == nullptr) {
|
if (pattr == nullptr) {
|
||||||
return POSIX_ENOMEM;
|
return POSIX_ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -157,7 +303,7 @@ int PS4_SYSV_ABI posix_pthread_condattr_destroy(PthreadCondAttrT* attr) {
|
||||||
if (attr == nullptr || *attr == nullptr) {
|
if (attr == nullptr || *attr == nullptr) {
|
||||||
return POSIX_EINVAL;
|
return POSIX_EINVAL;
|
||||||
}
|
}
|
||||||
free(*attr);
|
delete *attr;
|
||||||
*attr = nullptr;
|
*attr = nullptr;
|
||||||
return 0;
|
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) {
|
clock_id != ClockId::Prof && clock_id != ClockId::Monotonic) {
|
||||||
return POSIX_EINVAL;
|
return POSIX_EINVAL;
|
||||||
}
|
}
|
||||||
(*attr)->c_clockid = static_cast<int>(clock_id);
|
(*attr)->c_clockid = clock_id;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +360,7 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast);
|
LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast);
|
||||||
|
|
||||||
// Orbis
|
// 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,
|
LIB_FUNCTION("m5-2bsNfv7s", "libkernel", 1, "libkernel", 1, 1,
|
||||||
ORBIS(posix_pthread_condattr_init));
|
ORBIS(posix_pthread_condattr_init));
|
||||||
LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1,
|
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,
|
LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1,
|
||||||
ORBIS(posix_pthread_cond_signal));
|
ORBIS(posix_pthread_cond_signal));
|
||||||
LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1,
|
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,
|
LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1,
|
||||||
ORBIS(posix_pthread_cond_destroy));
|
ORBIS(posix_pthread_cond_destroy));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma clang optimize off
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/debug_state.h"
|
#include "core/debug_state.h"
|
||||||
|
@ -25,6 +25,13 @@ extern PthreadAttr PthreadAttrDefault;
|
||||||
|
|
||||||
void _thread_cleanupspecific();
|
void _thread_cleanupspecific();
|
||||||
|
|
||||||
|
using ThreadDtor = void (*)();
|
||||||
|
static ThreadDtor* ThreadDtors{};
|
||||||
|
|
||||||
|
void PS4_SYSV_ABI _sceKernelSetThreadDtors(ThreadDtor* dtor) {
|
||||||
|
ThreadDtors = dtor;
|
||||||
|
}
|
||||||
|
|
||||||
static void ExitThread() {
|
static void ExitThread() {
|
||||||
Pthread* curthread = g_curthread;
|
Pthread* curthread = g_curthread;
|
||||||
|
|
||||||
|
@ -37,7 +44,7 @@ static void ExitThread() {
|
||||||
auto* thread_state = ThrState::Instance();
|
auto* thread_state = ThrState::Instance();
|
||||||
ASSERT(thread_state->active_threads.fetch_sub(1) != 1);
|
ASSERT(thread_state->active_threads.fetch_sub(1) != 1);
|
||||||
|
|
||||||
curthread->lock->lock();
|
curthread->lock.lock();
|
||||||
curthread->state = PthreadState::Dead;
|
curthread->state = PthreadState::Dead;
|
||||||
ASSERT(False(curthread->flags & ThreadFlags::NeedSuspend));
|
ASSERT(False(curthread->flags & ThreadFlags::NeedSuspend));
|
||||||
|
|
||||||
|
@ -46,7 +53,7 @@ static void ExitThread() {
|
||||||
* reference count to allow it to be garbage collected.
|
* reference count to allow it to be garbage collected.
|
||||||
*/
|
*/
|
||||||
curthread->refcount--;
|
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
|
* 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();
|
curthread->cleanup.pop_front();
|
||||||
old->routine(old->routine_arg);
|
old->routine(old->routine_arg);
|
||||||
if (old->onheap) {
|
if (old->onheap) {
|
||||||
free(old);
|
delete old;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*if (ThreadDtors && *ThreadDtors) {
|
||||||
|
(*ThreadDtors)();
|
||||||
|
}*/
|
||||||
ExitThread();
|
ExitThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,16 +120,16 @@ static int JoinThread(PthreadT pthread, void** thread_return, const OrbisKernelT
|
||||||
ret = POSIX_ENOTSUP;
|
ret = POSIX_ENOTSUP;
|
||||||
}
|
}
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pthread->lock->unlock();
|
pthread->lock.unlock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
/* Set the running thread to be the joiner: */
|
/* Set the running thread to be the joiner: */
|
||||||
pthread->joiner = curthread;
|
pthread->joiner = curthread;
|
||||||
pthread->lock->unlock();
|
pthread->lock.unlock();
|
||||||
|
|
||||||
const auto backout_join = [](void* arg) {
|
const auto backout_join = [](void* arg) {
|
||||||
Pthread* pthread = (Pthread*)arg;
|
Pthread* pthread = (Pthread*)arg;
|
||||||
std::scoped_lock lk{*pthread->lock};
|
std::scoped_lock lk{pthread->lock};
|
||||||
pthread->joiner = nullptr;
|
pthread->joiner = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -145,10 +154,10 @@ static int JoinThread(PthreadT pthread, void** thread_return, const OrbisKernelT
|
||||||
}
|
}
|
||||||
|
|
||||||
void* tmp = pthread->ret;
|
void* tmp = pthread->ret;
|
||||||
pthread->lock->lock();
|
pthread->lock.lock();
|
||||||
pthread->flags |= ThreadFlags::Detached;
|
pthread->flags |= ThreadFlags::Detached;
|
||||||
pthread->joiner = nullptr;
|
pthread->joiner = nullptr;
|
||||||
thread_state->TryCollect(curthread, pthread); /* thread lock released */
|
thread_state->TryCollect(pthread); /* thread lock released */
|
||||||
if (thread_return != nullptr) {
|
if (thread_return != nullptr) {
|
||||||
*thread_return = tmp;
|
*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. */
|
/* Check if the thread is already detached or has a joiner. */
|
||||||
if (True(pthread->flags & ThreadFlags::Detached) || (pthread->joiner != NULL)) {
|
if (True(pthread->flags & ThreadFlags::Detached) || (pthread->joiner != NULL)) {
|
||||||
pthread->lock->unlock();
|
pthread->lock.unlock();
|
||||||
return POSIX_EINVAL;
|
return POSIX_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flag the thread as detached. */
|
/* Flag the thread as detached. */
|
||||||
pthread->flags |= ThreadFlags::Detached;
|
pthread->flags |= ThreadFlags::Detached;
|
||||||
thread_state->TryCollect(g_curthread, pthread); /* thread lock released */
|
thread_state->TryCollect(pthread); /* thread lock released */
|
||||||
return 0;
|
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->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) {
|
if (thread_state->CreateStack(&new_thread->attr) != 0) {
|
||||||
/* Insufficient memory to create a stack: */
|
/* 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->arg = arg;
|
||||||
new_thread->cancel_enable = 1;
|
new_thread->cancel_enable = 1;
|
||||||
new_thread->cancel_async = 0;
|
new_thread->cancel_async = 0;
|
||||||
|
static std::atomic<int> counter = 0;
|
||||||
|
new_thread->name = fmt::format("NoName{}", counter++);
|
||||||
|
|
||||||
auto* memory = Core::Memory::Instance();
|
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;
|
new_thread->name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,10 +281,11 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
|
||||||
(*thread) = new_thread;
|
(*thread) = new_thread;
|
||||||
|
|
||||||
/* Create thread */
|
/* Create thread */
|
||||||
|
pthread_t pthr;
|
||||||
pthread_attr_t pattr;
|
pthread_attr_t pattr;
|
||||||
pthread_attr_init(&pattr);
|
pthread_attr_init(&pattr);
|
||||||
pthread_attr_setstack(&pattr, new_thread->attr.stackaddr_attr, new_thread->attr.stacksize_attr);
|
// pthread_attr_setstack(&pattr, new_thread->attr.stackaddr_attr,
|
||||||
pthread_t pthr;
|
// new_thread->attr.stacksize_attr);
|
||||||
int ret = pthread_create(&pthr, &pattr, (PthreadEntryFunc)RunThread, new_thread);
|
int ret = pthread_create(&pthr, &pattr, (PthreadEntryFunc)RunThread, new_thread);
|
||||||
ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret);
|
ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -281,6 +294,11 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
|
||||||
return ret;
|
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() {
|
int PS4_SYSV_ABI posix_pthread_getthreadid_np() {
|
||||||
return g_curthread->tid;
|
return g_curthread->tid;
|
||||||
}
|
}
|
||||||
|
@ -361,7 +379,51 @@ int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) {
|
||||||
return SCE_OK;
|
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) {
|
void RegisterThread(Core::Loader::SymbolsResolver* sym) {
|
||||||
|
LIB_FUNCTION("asz3TtIqGF8", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, sceNpWebApiTerminate);
|
||||||
|
|
||||||
// Posix
|
// Posix
|
||||||
LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once);
|
LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once);
|
||||||
LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal);
|
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("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self);
|
||||||
LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield);
|
LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield);
|
||||||
LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach);
|
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
|
// Posix-Kernel
|
||||||
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
|
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
|
||||||
|
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create);
|
||||||
|
|
||||||
// Orbis
|
// Orbis
|
||||||
LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once));
|
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,
|
LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1,
|
||||||
ORBIS(posix_pthread_create_name_np));
|
ORBIS(posix_pthread_create_name_np));
|
||||||
LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_detach));
|
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("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
|
||||||
LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_equal);
|
LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_equal);
|
||||||
LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, posix_pthread_yield);
|
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("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
|
} // namespace Libraries::Kernel
|
||||||
|
|
|
@ -25,7 +25,7 @@ Core::Tcb* TcbCtor(Pthread* thread, int initial) {
|
||||||
// Initialize allocated memory and allocate DTV table.
|
// Initialize allocated memory and allocate DTV table.
|
||||||
const u32 num_dtvs = linker->MaxTlsIndex();
|
const u32 num_dtvs = linker->MaxTlsIndex();
|
||||||
const auto static_tls_size = linker->StaticTlsSize();
|
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
|
// Initialize thread control block
|
||||||
u8* addr = reinterpret_cast<u8*>(addr_out);
|
u8* addr = reinterpret_cast<u8*>(addr_out);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma clang optimize off
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
@ -17,6 +17,7 @@ static std::mutex MutxStaticLock;
|
||||||
#define THR_MUTEX_INITIALIZER ((PthreadMutex*)NULL)
|
#define THR_MUTEX_INITIALIZER ((PthreadMutex*)NULL)
|
||||||
#define THR_ADAPTIVE_MUTEX_INITIALIZER ((PthreadMutex*)1)
|
#define THR_ADAPTIVE_MUTEX_INITIALIZER ((PthreadMutex*)1)
|
||||||
#define THR_MUTEX_DESTROYED ((PthreadMutex*)2)
|
#define THR_MUTEX_DESTROYED ((PthreadMutex*)2)
|
||||||
|
#define THR_MUTEX_RELTIME (const OrbisKernelTimespec*)-1
|
||||||
|
|
||||||
#define CPU_SPINWAIT __asm__ volatile("pause")
|
#define CPU_SPINWAIT __asm__ volatile("pause")
|
||||||
|
|
||||||
|
@ -39,8 +40,7 @@ static constexpr PthreadMutexAttr PthreadMutexattrAdaptiveDefault = {
|
||||||
|
|
||||||
using CallocFun = void* (*)(size_t, size_t);
|
using CallocFun = void* (*)(size_t, size_t);
|
||||||
|
|
||||||
static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr,
|
static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, const char* name) {
|
||||||
CallocFun calloc_cb) {
|
|
||||||
const PthreadMutexAttr* attr;
|
const PthreadMutexAttr* attr;
|
||||||
if (mutex_attr == NULL) {
|
if (mutex_attr == NULL) {
|
||||||
attr = &PthreadMutexattrDefault;
|
attr = &PthreadMutexattrDefault;
|
||||||
|
@ -53,14 +53,22 @@ static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr,
|
||||||
return POSIX_EINVAL;
|
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) {
|
if (pmutex == nullptr) {
|
||||||
return POSIX_ENOMEM;
|
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_flags = PthreadMutexFlags(attr->m_type);
|
||||||
pmutex->m_owner = NULL;
|
pmutex->m_owner = nullptr;
|
||||||
pmutex->m_count = 0;
|
pmutex->m_count = 0;
|
||||||
pmutex->m_spinloops = 0;
|
pmutex->m_spinloops = 0;
|
||||||
pmutex->m_yieldloops = 0;
|
pmutex->m_yieldloops = 0;
|
||||||
|
@ -78,16 +86,21 @@ static int InitStatic(Pthread* thread, PthreadMutexT* mutex) {
|
||||||
std::scoped_lock lk{MutxStaticLock};
|
std::scoped_lock lk{MutxStaticLock};
|
||||||
|
|
||||||
if (*mutex == THR_MUTEX_INITIALIZER) {
|
if (*mutex == THR_MUTEX_INITIALIZER) {
|
||||||
return MutexInit(mutex, &PthreadMutexattrDefault, calloc);
|
return MutexInit(mutex, &PthreadMutexattrDefault, nullptr);
|
||||||
} else if (*mutex == THR_ADAPTIVE_MUTEX_INITIALIZER) {
|
} else if (*mutex == THR_ADAPTIVE_MUTEX_INITIALIZER) {
|
||||||
return MutexInit(mutex, &PthreadMutexattrAdaptiveDefault, calloc);
|
return MutexInit(mutex, &PthreadMutexattrAdaptiveDefault, nullptr);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_mutex_init(PthreadMutexT* mutex,
|
int PS4_SYSV_ABI posix_pthread_mutex_init(PthreadMutexT* mutex,
|
||||||
const PthreadMutexAttrT* mutex_attr) {
|
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) {
|
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;
|
return POSIX_EBUSY;
|
||||||
}
|
}
|
||||||
*mutex = THR_MUTEX_DESTROYED;
|
*mutex = THR_MUTEX_DESTROYED;
|
||||||
std::destroy_at(m);
|
|
||||||
free(m);
|
free(m);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -125,11 +137,12 @@ int PthreadMutex::SelfTryLock() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) {
|
int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||||
switch (Type()) {
|
const auto DoSleep = [&] {
|
||||||
case PthreadMutexType::ErrorCheck:
|
if (abstime == THR_MUTEX_RELTIME) {
|
||||||
case PthreadMutexType::AdaptiveNp: {
|
std::this_thread::sleep_for(std::chrono::microseconds(usec));
|
||||||
if (abstime) {
|
return POSIX_ETIMEDOUT;
|
||||||
|
} else {
|
||||||
if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
|
if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
|
||||||
return POSIX_EINVAL;
|
return POSIX_EINVAL;
|
||||||
} else {
|
} else {
|
||||||
|
@ -137,10 +150,18 @@ int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) {
|
||||||
return POSIX_ETIMEDOUT;
|
return POSIX_ETIMEDOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
switch (Type()) {
|
||||||
|
case PthreadMutexType::ErrorCheck:
|
||||||
|
case PthreadMutexType::AdaptiveNp: {
|
||||||
|
if (abstime) {
|
||||||
|
return DoSleep();
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* POSIX specifies that mutexes should return
|
* POSIX specifies that mutexes should return
|
||||||
* EDEADLK if a recursive lock is detected.
|
* EDEADLK if a recursive lock is detected.
|
||||||
*/
|
*/
|
||||||
|
UNREACHABLE_MSG("Mutex deadlock occured");
|
||||||
return POSIX_EDEADLK;
|
return POSIX_EDEADLK;
|
||||||
}
|
}
|
||||||
case PthreadMutexType::Normal: {
|
case PthreadMutexType::Normal: {
|
||||||
|
@ -149,12 +170,7 @@ int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) {
|
||||||
* deadlock on attempts to get a lock you already own.
|
* deadlock on attempts to get a lock you already own.
|
||||||
*/
|
*/
|
||||||
if (abstime) {
|
if (abstime) {
|
||||||
if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
|
return DoSleep();
|
||||||
return POSIX_EINVAL;
|
|
||||||
} else {
|
|
||||||
std::this_thread::sleep_until(abstime->TimePoint());
|
|
||||||
return POSIX_ETIMEDOUT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
UNREACHABLE_MSG("Mutex deadlock occured");
|
UNREACHABLE_MSG("Mutex deadlock occured");
|
||||||
return 0;
|
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;
|
Pthread* curthread = g_curthread;
|
||||||
if (m_owner == curthread) {
|
if (m_owner == curthread) {
|
||||||
return SelfLock(abstime);
|
return SelfLock(abstime, usec);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -211,10 +227,15 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime) {
|
||||||
|
|
||||||
if (abstime == nullptr) {
|
if (abstime == nullptr) {
|
||||||
m_lock.lock();
|
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;
|
ret = POSIX_EINVAL;
|
||||||
} else {
|
} 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;
|
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,
|
int PS4_SYSV_ABI posix_pthread_mutex_timedlock(PthreadMutexT* mutex,
|
||||||
const OrbisKernelTimespec* abstime) {
|
const OrbisKernelTimespec* abstime) {
|
||||||
CHECK_AND_INIT_MUTEX
|
CHECK_AND_INIT_MUTEX
|
||||||
|
UNREACHABLE();
|
||||||
return (*mutex)->Lock(abstime);
|
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() {
|
int PthreadMutex::Unlock() {
|
||||||
Pthread* curthread = g_curthread;
|
Pthread* curthread = g_curthread;
|
||||||
/*
|
/*
|
||||||
|
@ -320,7 +347,7 @@ bool PthreadMutex::IsOwned(Pthread* curthread) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) {
|
int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) {
|
||||||
PthreadMutexAttrT pattr = (PthreadMutexAttrT)malloc(sizeof(PthreadMutexAttr));
|
PthreadMutexAttrT pattr = new PthreadMutexAttr{};
|
||||||
if (pattr == nullptr) {
|
if (pattr == nullptr) {
|
||||||
return POSIX_ENOMEM;
|
return POSIX_ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -367,7 +394,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) {
|
||||||
if (attr == nullptr || *attr == nullptr) {
|
if (attr == nullptr || *attr == nullptr) {
|
||||||
return POSIX_EINVAL;
|
return POSIX_EINVAL;
|
||||||
}
|
}
|
||||||
free(*attr);
|
delete *attr;
|
||||||
*attr = nullptr;
|
*attr = nullptr;
|
||||||
return 0;
|
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);
|
LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock);
|
||||||
|
|
||||||
// Orbis
|
// 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,
|
LIB_FUNCTION("2Of0f+3mhhE", "libkernel", 1, "libkernel", 1, 1,
|
||||||
ORBIS(posix_pthread_mutex_destroy));
|
ORBIS(posix_pthread_mutex_destroy));
|
||||||
LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1,
|
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,
|
LIB_FUNCTION("upoVrzMHFeE", "libkernel", 1, "libkernel", 1, 1,
|
||||||
ORBIS(posix_pthread_mutex_trylock));
|
ORBIS(posix_pthread_mutex_trylock));
|
||||||
LIB_FUNCTION("IafI2PxcPnQ", "libkernel", 1, "libkernel", 1, 1,
|
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("qH1gXoq71RY", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_init));
|
||||||
LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1,
|
LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1,
|
||||||
ORBIS(posix_pthread_mutexattr_init));
|
ORBIS(posix_pthread_mutexattr_init));
|
||||||
|
|
|
@ -27,11 +27,10 @@ static std::mutex RwlockStaticLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int RwlockInit(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) {
|
static int RwlockInit(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) {
|
||||||
PthreadRwlock* prwlock = (PthreadRwlock*)calloc(1, sizeof(PthreadRwlock));
|
PthreadRwlock* prwlock = new PthreadRwlock{};
|
||||||
if (prwlock == nullptr) {
|
if (prwlock == nullptr) {
|
||||||
return POSIX_ENOMEM;
|
return POSIX_ENOMEM;
|
||||||
}
|
}
|
||||||
std::construct_at(prwlock);
|
|
||||||
*rwlock = prwlock;
|
*rwlock = prwlock;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -45,8 +44,7 @@ int PS4_SYSV_ABI posix_pthread_rwlock_destroy(PthreadRwlockT* rwlock) {
|
||||||
return POSIX_EINVAL;
|
return POSIX_EINVAL;
|
||||||
}
|
}
|
||||||
*rwlock = THR_RWLOCK_DESTROYED;
|
*rwlock = THR_RWLOCK_DESTROYED;
|
||||||
std::destroy_at(prwlock);
|
delete prwlock;
|
||||||
free(prwlock);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,21 +119,21 @@ int PthreadRwlock::Wrlock(const OrbisKernelTimespec* abstime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(PthreadRwlockT* rwlock) {
|
int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(PthreadRwlockT* rwlock) {
|
||||||
PthreadRwlockT prwlock;
|
PthreadRwlockT prwlock{};
|
||||||
CHECK_AND_INIT_RWLOCK
|
CHECK_AND_INIT_RWLOCK
|
||||||
return prwlock->Rdlock(nullptr);
|
return prwlock->Rdlock(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock(PthreadRwlockT* rwlock,
|
int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock(PthreadRwlockT* rwlock,
|
||||||
const OrbisKernelTimespec* abstime) {
|
const OrbisKernelTimespec* abstime) {
|
||||||
PthreadRwlockT prwlock;
|
PthreadRwlockT prwlock{};
|
||||||
CHECK_AND_INIT_RWLOCK
|
CHECK_AND_INIT_RWLOCK
|
||||||
return prwlock->Rdlock(abstime);
|
return prwlock->Rdlock(abstime);
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(PthreadRwlockT* rwlock) {
|
int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(PthreadRwlockT* rwlock) {
|
||||||
Pthread* curthread = g_curthread;
|
Pthread* curthread = g_curthread;
|
||||||
PthreadRwlockT prwlock;
|
PthreadRwlockT prwlock{};
|
||||||
CHECK_AND_INIT_RWLOCK
|
CHECK_AND_INIT_RWLOCK
|
||||||
|
|
||||||
if (!prwlock->lock.try_lock_shared()) {
|
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) {
|
int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(PthreadRwlockT* rwlock) {
|
||||||
Pthread* curthread = g_curthread;
|
Pthread* curthread = g_curthread;
|
||||||
PthreadRwlockT prwlock;
|
PthreadRwlockT prwlock{};
|
||||||
CHECK_AND_INIT_RWLOCK
|
CHECK_AND_INIT_RWLOCK
|
||||||
|
|
||||||
if (!prwlock->lock.try_lock()) {
|
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) {
|
int PS4_SYSV_ABI posix_pthread_rwlock_wrlock(PthreadRwlockT* rwlock) {
|
||||||
PthreadRwlockT prwlock;
|
PthreadRwlockT prwlock{};
|
||||||
CHECK_AND_INIT_RWLOCK
|
CHECK_AND_INIT_RWLOCK
|
||||||
return prwlock->Wrlock(nullptr);
|
return prwlock->Wrlock(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock(PthreadRwlockT* rwlock,
|
int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock(PthreadRwlockT* rwlock,
|
||||||
const OrbisKernelTimespec* abstime) {
|
const OrbisKernelTimespec* abstime) {
|
||||||
PthreadRwlockT prwlock;
|
PthreadRwlockT prwlock{};
|
||||||
CHECK_AND_INIT_RWLOCK
|
CHECK_AND_INIT_RWLOCK
|
||||||
return prwlock->Wrlock(abstime);
|
return prwlock->Wrlock(abstime);
|
||||||
}
|
}
|
||||||
|
@ -213,7 +211,7 @@ int PS4_SYSV_ABI posix_pthread_rwlockattr_destroy(PthreadRwlockAttrT* rwlockattr
|
||||||
return POSIX_EINVAL;
|
return POSIX_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(prwlockattr);
|
delete prwlockattr;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +226,7 @@ int PS4_SYSV_ABI posix_pthread_rwlockattr_init(PthreadRwlockAttrT* rwlockattr) {
|
||||||
return POSIX_EINVAL;
|
return POSIX_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PthreadRwlockAttrT prwlockattr = (PthreadRwlockAttrT)malloc(sizeof(PthreadRwlockAttr));
|
PthreadRwlockAttrT prwlockattr = new PthreadRwlockAttr{};
|
||||||
if (prwlockattr == nullptr) {
|
if (prwlockattr == nullptr) {
|
||||||
return POSIX_ENOMEM;
|
return POSIX_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,7 @@ public:
|
||||||
OrbisSem(s32 init_count, s32 max_count, std::string_view name, bool is_fifo)
|
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},
|
: name{name}, token_count{init_count}, max_count{max_count}, init_count{init_count},
|
||||||
is_fifo{is_fifo} {}
|
is_fifo{is_fifo} {}
|
||||||
~OrbisSem() {
|
~OrbisSem() = default;
|
||||||
ASSERT(wait_list.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
int Wait(bool can_block, s32 need_count, u32* timeout) {
|
int Wait(bool can_block, s32 need_count, u32* timeout) {
|
||||||
std::unique_lock lk{mutex};
|
std::unique_lock lk{mutex};
|
||||||
|
@ -93,6 +91,15 @@ public:
|
||||||
return ORBIS_OK;
|
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:
|
public:
|
||||||
struct WaitingThread {
|
struct WaitingThread {
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
|
@ -219,7 +226,7 @@ int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) {
|
||||||
if (!sem) {
|
if (!sem) {
|
||||||
return SCE_KERNEL_ERROR_ESRCH;
|
return SCE_KERNEL_ERROR_ESRCH;
|
||||||
}
|
}
|
||||||
delete sem;
|
sem->Delete();
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ void _thread_cleanupspecific() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(curthread->specific);
|
delete[] curthread->specific;
|
||||||
curthread->specific = nullptr;
|
curthread->specific = nullptr;
|
||||||
ASSERT(curthread->specific_data_count == 0);
|
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;
|
Pthread* pthread = g_curthread;
|
||||||
|
|
||||||
if (!pthread->specific) {
|
if (!pthread->specific) {
|
||||||
pthread->specific =
|
pthread->specific = new PthreadSpecificElem[PthreadKeysMax];
|
||||||
(PthreadSpecificElem*)calloc(1, sizeof(PthreadSpecificElem) * PthreadKeysMax);
|
|
||||||
if (!pthread->specific) {
|
if (!pthread->specific) {
|
||||||
return POSIX_ENOMEM;
|
return POSIX_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,33 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
#include "common/alignment.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "core/libraries/error_codes.h"
|
#include "core/libraries/error_codes.h"
|
||||||
#include "core/libraries/kernel/threads/thread_state.h"
|
#include "core/libraries/kernel/threads/thread_state.h"
|
||||||
#include "core/libraries/kernel/threads/threads.h"
|
#include "core/libraries/kernel/threads/threads.h"
|
||||||
|
#include "core/memory.h"
|
||||||
#include "core/tls.h"
|
#include "core/tls.h"
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
namespace Libraries::Kernel {
|
||||||
|
|
||||||
Pthread* g_curthread{};
|
thread_local Pthread* g_curthread{};
|
||||||
|
|
||||||
Core::Tcb* TcbCtor(Pthread* thread, int initial);
|
Core::Tcb* TcbCtor(Pthread* thread, int initial);
|
||||||
void TcbDtor(Core::Tcb* oldtls);
|
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) {
|
void ThreadState::Collect(Pthread* curthread) {
|
||||||
boost::container::small_vector<Pthread*, 8> work_list;
|
boost::container::small_vector<Pthread*, 8> 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 {
|
SCOPE_EXIT {
|
||||||
thread->lock->unlock();
|
thread->lock.unlock();
|
||||||
};
|
};
|
||||||
if (!thread->ShouldCollect()) {
|
if (!thread->ShouldCollect()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread->refcount++;
|
thread->refcount++;
|
||||||
thread->lock->unlock();
|
thread->lock.unlock();
|
||||||
std::scoped_lock lk{thread_list_lock};
|
std::scoped_lock lk{thread_list_lock};
|
||||||
thread->lock->lock();
|
thread->lock.lock();
|
||||||
thread->refcount--;
|
thread->refcount--;
|
||||||
if (thread->ShouldCollect()) {
|
if (thread->ShouldCollect()) {
|
||||||
threads.erase(thread);
|
threads.erase(thread);
|
||||||
|
@ -71,9 +85,7 @@ Pthread* ThreadState::Alloc(Pthread* curthread) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
total_threads.fetch_add(1);
|
total_threads.fetch_add(1);
|
||||||
thread = (Pthread*)malloc(sizeof(Pthread));
|
thread = thread_heap.Allocate();
|
||||||
std::construct_at(thread);
|
|
||||||
thread->lock = std::make_unique<std::mutex>();
|
|
||||||
if (thread == nullptr) {
|
if (thread == nullptr) {
|
||||||
total_threads.fetch_sub(1);
|
total_threads.fetch_sub(1);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -87,12 +99,13 @@ Pthread* ThreadState::Alloc(Pthread* curthread) {
|
||||||
tcb = TcbCtor(thread, 1 /* initial tls */);
|
tcb = TcbCtor(thread, 1 /* initial tls */);
|
||||||
}
|
}
|
||||||
if (tcb != nullptr) {
|
if (tcb != nullptr) {
|
||||||
|
memset(thread, 0, sizeof(Pthread));
|
||||||
|
std::construct_at(thread);
|
||||||
thread->tcb = tcb;
|
thread->tcb = tcb;
|
||||||
// thread->sleepqueue = _sleepq_alloc();
|
// thread->sleepqueue = _sleepq_alloc();
|
||||||
// thread->wake_addr = _thr_alloc_wake_addr();
|
// thread->wake_addr = _thr_alloc_wake_addr();
|
||||||
} else {
|
} else {
|
||||||
std::destroy_at(thread);
|
thread_heap.Free(thread);
|
||||||
free(thread);
|
|
||||||
total_threads.fetch_sub(1);
|
total_threads.fetch_sub(1);
|
||||||
thread = nullptr;
|
thread = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -106,12 +119,12 @@ void ThreadState::Free(Pthread* curthread, Pthread* thread) {
|
||||||
} else {
|
} else {
|
||||||
TcbDtor(thread->tcb);
|
TcbDtor(thread->tcb);
|
||||||
}
|
}
|
||||||
thread->tcb = NULL;
|
thread->tcb = nullptr;
|
||||||
|
std::destroy_at(thread);
|
||||||
if (free_threads.size() >= MaxCachedThreads) {
|
if (free_threads.size() >= MaxCachedThreads) {
|
||||||
//_sleepq_free(thread->sleepqueue);
|
//_sleepq_free(thread->sleepqueue);
|
||||||
//_thr_release_wake_addr(thread->wake_addr);
|
//_thr_release_wake_addr(thread->wake_addr);
|
||||||
std::destroy_at(thread);
|
thread_heap.Free(thread);
|
||||||
free(thread);
|
|
||||||
total_threads.fetch_sub(1);
|
total_threads.fetch_sub(1);
|
||||||
} else {
|
} else {
|
||||||
std::scoped_lock lk{free_thread_lock};
|
std::scoped_lock lk{free_thread_lock};
|
||||||
|
@ -128,12 +141,33 @@ int ThreadState::FindThread(Pthread* thread, bool include_dead) {
|
||||||
if (it == threads.end()) {
|
if (it == threads.end()) {
|
||||||
return POSIX_ESRCH;
|
return POSIX_ESRCH;
|
||||||
}
|
}
|
||||||
thread->lock->lock();
|
thread->lock.lock();
|
||||||
if (!include_dead && thread->state == PthreadState::Dead) {
|
if (!include_dead && thread->state == PthreadState::Dead) {
|
||||||
thread->lock->unlock();
|
thread->lock.unlock();
|
||||||
return POSIX_ESRCH;
|
return POSIX_ESRCH;
|
||||||
}
|
}
|
||||||
return 0;
|
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
|
} // namespace Libraries::Kernel
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
|
#include "common/slab_heap.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
namespace Libraries::Kernel {
|
||||||
|
@ -27,13 +28,15 @@ struct ThreadState {
|
||||||
static constexpr size_t MaxThreads = 100000;
|
static constexpr size_t MaxThreads = 100000;
|
||||||
static constexpr size_t MaxCachedThreads = 100;
|
static constexpr size_t MaxCachedThreads = 100;
|
||||||
|
|
||||||
|
explicit ThreadState();
|
||||||
|
|
||||||
bool GcNeeded() const noexcept {
|
bool GcNeeded() const noexcept {
|
||||||
return gc_list.size() >= GcThreshold;
|
return gc_list.size() >= GcThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Collect(Pthread* curthread);
|
void Collect(Pthread* curthread);
|
||||||
|
|
||||||
void TryCollect(Pthread* curthread, Pthread* thread);
|
void TryCollect(Pthread* thread);
|
||||||
|
|
||||||
Pthread* Alloc(Pthread* curthread);
|
Pthread* Alloc(Pthread* curthread);
|
||||||
|
|
||||||
|
@ -41,6 +44,10 @@ struct ThreadState {
|
||||||
|
|
||||||
int FindThread(Pthread* thread, bool include_dead);
|
int FindThread(Pthread* thread, bool include_dead);
|
||||||
|
|
||||||
|
int RefAdd(Pthread* thread, bool include_dead);
|
||||||
|
|
||||||
|
void RefDelete(Pthread* thread);
|
||||||
|
|
||||||
int CreateStack(PthreadAttr* attr);
|
int CreateStack(PthreadAttr* attr);
|
||||||
|
|
||||||
void FreeStack(PthreadAttr* attr);
|
void FreeStack(PthreadAttr* attr);
|
||||||
|
@ -61,6 +68,7 @@ struct ThreadState {
|
||||||
active_threads.fetch_sub(1);
|
active_threads.fetch_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Common::SlabHeap<Pthread> thread_heap;
|
||||||
std::set<Pthread*> threads;
|
std::set<Pthread*> threads;
|
||||||
std::list<Pthread*> free_threads;
|
std::list<Pthread*> free_threads;
|
||||||
std::list<Pthread*> gc_list;
|
std::list<Pthread*> gc_list;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <deque>
|
||||||
#include <forward_list>
|
#include <forward_list>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
@ -53,16 +54,25 @@ struct PthreadMutex : public ListBaseHook {
|
||||||
int m_spinloops;
|
int m_spinloops;
|
||||||
int m_yieldloops;
|
int m_yieldloops;
|
||||||
PthreadMutexProt m_protocol;
|
PthreadMutexProt m_protocol;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
PthreadMutexType Type() const noexcept {
|
PthreadMutexType Type() const noexcept {
|
||||||
return static_cast<PthreadMutexType>(m_flags & PthreadMutexFlags::TypeMask);
|
return static_cast<PthreadMutexType>(m_flags & PthreadMutexFlags::TypeMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lock() {
|
||||||
|
Lock(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() {
|
||||||
|
Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
int SelfTryLock();
|
int SelfTryLock();
|
||||||
int SelfLock(const OrbisKernelTimespec* abstime);
|
int SelfLock(const OrbisKernelTimespec* abstime, u64 usec);
|
||||||
|
|
||||||
int TryLock();
|
int TryLock();
|
||||||
int Lock(const OrbisKernelTimespec* abstime);
|
int Lock(const OrbisKernelTimespec* abstime, u64 usec = 0);
|
||||||
|
|
||||||
int Unlock();
|
int Unlock();
|
||||||
|
|
||||||
|
@ -83,20 +93,38 @@ enum class PthreadCondFlags : u32 {
|
||||||
Busy = 4,
|
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 {
|
struct PthreadCond {
|
||||||
std::condition_variable_any cond;
|
std::condition_variable_any cond;
|
||||||
u32 has_user_waiters;
|
u32 has_user_waiters;
|
||||||
u32 has_kern_waiters;
|
u32 has_kern_waiters;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
u32 clock_id;
|
ClockId clock_id;
|
||||||
|
std::string name;
|
||||||
|
|
||||||
int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime);
|
int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime);
|
||||||
|
int Wait(PthreadMutexT* mutex, u64 usec);
|
||||||
};
|
};
|
||||||
using PthreadCondT = PthreadCond*;
|
using PthreadCondT = PthreadCond*;
|
||||||
|
|
||||||
struct PthreadCondAttr {
|
struct PthreadCondAttr {
|
||||||
int c_pshared;
|
int c_pshared;
|
||||||
int c_clockid;
|
ClockId c_clockid;
|
||||||
};
|
};
|
||||||
using PthreadCondAttrT = PthreadCondAttr*;
|
using PthreadCondAttrT = PthreadCondAttr*;
|
||||||
|
|
||||||
|
@ -122,6 +150,10 @@ enum class SchedPolicy : u32 {
|
||||||
RoundRobin = 3,
|
RoundRobin = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Cpuset {
|
||||||
|
u64 bits;
|
||||||
|
};
|
||||||
|
|
||||||
struct PthreadAttr {
|
struct PthreadAttr {
|
||||||
SchedPolicy sched_policy;
|
SchedPolicy sched_policy;
|
||||||
int sched_inherit;
|
int sched_inherit;
|
||||||
|
@ -132,6 +164,7 @@ struct PthreadAttr {
|
||||||
size_t stacksize_attr;
|
size_t stacksize_attr;
|
||||||
size_t guardsize_attr;
|
size_t guardsize_attr;
|
||||||
size_t cpusetsize;
|
size_t cpusetsize;
|
||||||
|
Cpuset* cpuset;
|
||||||
};
|
};
|
||||||
using PthreadAttrT = PthreadAttr*;
|
using PthreadAttrT = PthreadAttr*;
|
||||||
|
|
||||||
|
@ -198,17 +231,36 @@ using PthreadEntryFunc = void* (*)(void*);
|
||||||
|
|
||||||
constexpr u32 TidTerminated = 1;
|
constexpr u32 TidTerminated = 1;
|
||||||
|
|
||||||
|
struct WakeAddr {
|
||||||
|
WakeAddr* link;
|
||||||
|
u32 value;
|
||||||
|
char pad[12];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SleepQueue {
|
||||||
|
std::list<Pthread*> sq_blocked;
|
||||||
|
std::forward_list<SleepQueue*> sq_freeq;
|
||||||
|
std::list<SleepQueue*> sq_hash;
|
||||||
|
std::forward_list<SleepQueue*> sq_flink;
|
||||||
|
void* sq_wchan;
|
||||||
|
int sq_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SchedParam {
|
||||||
|
int sched_priority;
|
||||||
|
};
|
||||||
|
|
||||||
struct Pthread {
|
struct Pthread {
|
||||||
static constexpr u32 ThrMagic = 0xd09ba115U;
|
static constexpr u32 ThrMagic = 0xd09ba115U;
|
||||||
|
|
||||||
std::atomic<long> tid;
|
std::atomic<long> tid;
|
||||||
std::unique_ptr<std::mutex> lock;
|
std::mutex lock;
|
||||||
u32 cycle;
|
u32 cycle;
|
||||||
int locklevel;
|
int locklevel;
|
||||||
int critical_count;
|
int critical_count;
|
||||||
int sigblock;
|
int sigblock;
|
||||||
int refcount;
|
int refcount;
|
||||||
void PS4_SYSV_ABI* (*start_routine)(void*);
|
void* PS4_SYSV_ABI (*start_routine)(void*);
|
||||||
void* arg;
|
void* arg;
|
||||||
PthreadAttr attr;
|
PthreadAttr attr;
|
||||||
bool cancel_enable;
|
bool cancel_enable;
|
||||||
|
@ -226,8 +278,8 @@ struct Pthread {
|
||||||
Pthread* joiner;
|
Pthread* joiner;
|
||||||
ThreadFlags flags;
|
ThreadFlags flags;
|
||||||
ThreadListFlags tlflags;
|
ThreadListFlags tlflags;
|
||||||
boost::intrusive::list<PthreadMutex> mutexq;
|
std::list<PthreadMutex> mutexq;
|
||||||
boost::intrusive::list<PthreadMutex> pp_mutexq;
|
std::list<PthreadMutex> pp_mutexq;
|
||||||
void* ret;
|
void* ret;
|
||||||
PthreadSpecificElem* specific;
|
PthreadSpecificElem* specific;
|
||||||
int specific_data_count;
|
int specific_data_count;
|
||||||
|
@ -240,6 +292,12 @@ struct Pthread {
|
||||||
int report_events;
|
int report_events;
|
||||||
int event_mask;
|
int event_mask;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
WakeAddr* wake_addr;
|
||||||
|
SleepQueue* sleepqueue;
|
||||||
|
void* wchan;
|
||||||
|
PthreadMutex* mutex_obj;
|
||||||
|
bool will_sleep;
|
||||||
|
bool has_user_waiters;
|
||||||
|
|
||||||
bool InCritical() const noexcept {
|
bool InCritical() const noexcept {
|
||||||
return locklevel > 0 || critical_count > 0;
|
return locklevel > 0 || critical_count > 0;
|
||||||
|
@ -255,17 +313,17 @@ struct Pthread {
|
||||||
|
|
||||||
void Enqueue(PthreadMutex* mutex) {
|
void Enqueue(PthreadMutex* mutex) {
|
||||||
mutex->m_owner = this;
|
mutex->m_owner = this;
|
||||||
mutexq.push_back(*mutex);
|
// mutexq.push_back(*mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dequeue(PthreadMutex* mutex) {
|
void Dequeue(PthreadMutex* mutex) {
|
||||||
mutex->m_owner = nullptr;
|
mutex->m_owner = nullptr;
|
||||||
mutexq.erase(decltype(mutexq)::s_iterator_to(*mutex));
|
// mutexq.erase(decltype(mutexq)::s_iterator_to(*mutex));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
using PthreadT = Pthread*;
|
using PthreadT = Pthread*;
|
||||||
|
|
||||||
extern Pthread* g_curthread;
|
extern thread_local Pthread* g_curthread;
|
||||||
|
|
||||||
void RegisterMutex(Core::Loader::SymbolsResolver* sym);
|
void RegisterMutex(Core::Loader::SymbolsResolver* sym);
|
||||||
void RegisterCond(Core::Loader::SymbolsResolver* sym);
|
void RegisterCond(Core::Loader::SymbolsResolver* sym);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma clang optimize off
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma clang optimize off
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/logging/log.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");
|
LOG_ERROR(Lib_VideoOut, "Addresses are null");
|
||||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
||||||
}
|
}
|
||||||
|
VAddr ret_addr = (VAddr)__builtin_return_address(0);
|
||||||
auto* port = driver->GetPort(handle);
|
auto* port = driver->GetPort(handle);
|
||||||
if (!port || !port->is_open) {
|
if (!port || !port->is_open) {
|
||||||
LOG_ERROR(Lib_VideoOut, "Invalid handle = {}", handle);
|
LOG_ERROR(Lib_VideoOut, "Invalid handle = {}", handle);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/path_util.h"
|
#include "common/path_util.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
#include "common/thread.h"
|
||||||
#include "core/aerolib/aerolib.h"
|
#include "core/aerolib/aerolib.h"
|
||||||
#include "core/aerolib/stubs.h"
|
#include "core/aerolib/stubs.h"
|
||||||
#include "core/libraries/kernel/memory_management.h"
|
#include "core/libraries/kernel/memory_management.h"
|
||||||
|
@ -24,10 +25,8 @@ static PS4_SYSV_ABI void ProgramExitFunc() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARCH_X86_64
|
#ifdef ARCH_X86_64
|
||||||
static PS4_SYSV_ABI void* RunMainEntry [[noreturn]] (void* arg) {
|
static PS4_SYSV_ABI void* RunMainEntry [[noreturn]] (EntryParams* params) {
|
||||||
EntryParams* params = (EntryParams*)arg;
|
|
||||||
// Start shared library modules
|
// Start shared library modules
|
||||||
params->linker->LoadSharedLibraries();
|
|
||||||
asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes
|
asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes
|
||||||
"subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned
|
"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.
|
"jmp *%0\n" // can't use call here, as that would mangle the prepared stack.
|
||||||
// there's no coming back
|
// 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");
|
: "rax", "rsi", "rdi");
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
@ -82,14 +81,17 @@ void Linker::Execute() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start main module.
|
main_thread.Run([this, module](std::stop_token) {
|
||||||
EntryParams* params = new EntryParams;
|
Common::SetCurrentThreadName("GAME_MainThread");
|
||||||
params->argc = 1;
|
LoadSharedLibraries();
|
||||||
params->argv[0] = "eboot.bin";
|
|
||||||
params->entry_addr = module->GetEntryAddress();
|
// Start main module.
|
||||||
params->exit_func = ProgramExitFunc;
|
EntryParams params{};
|
||||||
params->linker = this;
|
params.argc = 1;
|
||||||
Libraries::Kernel::LaunchThread(RunMainEntry, params, "GAME_MainThread");
|
params.argv[0] = "eboot.bin";
|
||||||
|
params.entry_addr = module->GetEntryAddress();
|
||||||
|
RunMainEntry(¶ms);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 Linker::LoadModule(const std::filesystem::path& elf_name, bool is_dynamic) {
|
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) {
|
void Linker::Relocate(Module* module) {
|
||||||
module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool isJmpRel) {
|
module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool is_jmp_rel) {
|
||||||
const u32 bit_idx =
|
const u32 num_relocs = module->dynamic_info.relocation_table_size / sizeof(elf_relocation);
|
||||||
(isJmpRel ? module->dynamic_info.relocation_table_size / sizeof(elf_relocation) : 0) +
|
const u32 bit_idx = (is_jmp_rel ? num_relocs : 0) + i;
|
||||||
i;
|
|
||||||
if (module->TestRelaBit(bit_idx)) {
|
if (module->TestRelaBit(bit_idx)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -133,7 +134,7 @@ void Linker::Relocate(Module* module) {
|
||||||
auto symbol = rel->GetSymbol();
|
auto symbol = rel->GetSymbol();
|
||||||
auto addend = rel->rel_addend;
|
auto addend = rel->rel_addend;
|
||||||
auto* symbol_table = module->dynamic_info.symbol_table;
|
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_base_virtual_addr = module->GetBaseAddress();
|
||||||
const VAddr rel_virtual_addr = rel_base_virtual_addr + rel->rel_offset;
|
const VAddr rel_virtual_addr = rel_base_virtual_addr + rel->rel_offset;
|
||||||
|
@ -189,7 +190,7 @@ void Linker::Relocate(Module* module) {
|
||||||
break;
|
break;
|
||||||
case STB_GLOBAL:
|
case STB_GLOBAL:
|
||||||
case STB_WEAK: {
|
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)) {
|
if (Resolve(rel_name, rel_sym_type, module, &symrec)) {
|
||||||
// Only set the rela bit if the symbol was actually resolved and not stubbed.
|
// Only set the rela bit if the symbol was actually resolved and not stubbed.
|
||||||
module->SetRelaBit(bit_idx);
|
module->SetRelaBit(bit_idx);
|
||||||
|
@ -198,7 +199,7 @@ void Linker::Relocate(Module* module) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
ASSERT_MSG(0, "unknown bind type {}", sym_bind);
|
UNREACHABLE_MSG("Unknown bind type {}", sym_bind);
|
||||||
}
|
}
|
||||||
rel_is_resolved = (symbol_virtual_addr != 0);
|
rel_is_resolved = (symbol_virtual_addr != 0);
|
||||||
rel_value = (rel_is_resolved ? symbol_virtual_addr + addend : 0);
|
rel_value = (rel_is_resolved ? symbol_virtual_addr + addend : 0);
|
||||||
|
@ -212,7 +213,7 @@ void Linker::Relocate(Module* module) {
|
||||||
if (rel_is_resolved) {
|
if (rel_is_resolved) {
|
||||||
VirtualMemory::memory_patch(rel_virtual_addr, rel_value);
|
VirtualMemory::memory_patch(rel_virtual_addr, rel_value);
|
||||||
} else {
|
} 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;
|
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) {
|
if (!addr) {
|
||||||
// Module was just loaded by above code. Allocate TLS block for it.
|
// 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;
|
const u32 init_image_size = module->tls.init_image_size;
|
||||||
u8* dest =
|
u8* dest = reinterpret_cast<u8*>(heap_api->heap_malloc(module->tls.image_size));
|
||||||
reinterpret_cast<u8*>(ExecuteGuest(heap_api->heap_malloc, module->tls.image_size));
|
|
||||||
const u8* src = reinterpret_cast<const u8*>(module->tls.image_virtual_addr);
|
const u8* src = reinterpret_cast<const u8*>(module->tls.image_virtual_addr);
|
||||||
std::memcpy(dest, src, init_image_size);
|
std::memcpy(dest, src, init_image_size);
|
||||||
std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size);
|
std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "core/libraries/kernel/thread_management.h"
|
||||||
#include "core/module.h"
|
#include "core/module.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -49,8 +50,6 @@ struct EntryParams {
|
||||||
u32 padding;
|
u32 padding;
|
||||||
const char* argv[3];
|
const char* argv[3];
|
||||||
VAddr entry_addr;
|
VAddr entry_addr;
|
||||||
ExitFunc exit_func;
|
|
||||||
Linker* linker;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HeapAPI {
|
struct HeapAPI {
|
||||||
|
@ -141,6 +140,7 @@ private:
|
||||||
const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l);
|
const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l);
|
||||||
|
|
||||||
MemoryManager* memory;
|
MemoryManager* memory;
|
||||||
|
Libraries::Kernel::Thread main_thread;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
u32 dtv_generation_counter{1};
|
u32 dtv_generation_counter{1};
|
||||||
size_t static_tls_size{};
|
size_t static_tls_size{};
|
||||||
|
|
|
@ -166,9 +166,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
|
||||||
tls.align = elf_pheader[i].p_align;
|
tls.align = elf_pheader[i].p_align;
|
||||||
tls.image_virtual_addr = elf_pheader[i].p_vaddr + base_virtual_addr;
|
tls.image_virtual_addr = elf_pheader[i].p_vaddr + base_virtual_addr;
|
||||||
tls.image_size = GetAlignedSize(elf_pheader[i]);
|
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 virtual address = {:#x}", tls.image_virtual_addr);
|
||||||
LOG_INFO(Core_Linker, "TLS image size = {}", tls.image_size);
|
LOG_INFO(Core_Linker, "TLS image size = {}", tls.image_size);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -68,10 +68,9 @@ void Translator::V_READFIRSTLANE_B32(const GcnInst& inst) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Translator::V_READLANE_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 value{GetSrc(inst.src[0])};
|
||||||
const IR::U32 lane{GetSrc(inst.src[1])};
|
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) {
|
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 offset0 = inst.control.ds.offset0;
|
||||||
const u8 offset1 = inst.control.ds.offset1;
|
const u8 offset1 = inst.control.ds.offset1;
|
||||||
const IR::U32 src{GetSrc(inst.src[1])};
|
const IR::U32 src{GetSrc(inst.src[1])};
|
||||||
ASSERT(offset1 & 0x80);
|
// ASSERT(offset1 & 0x80);
|
||||||
const IR::U32 lane_id = ir.LaneId();
|
const IR::U32 lane_id = ir.LaneId();
|
||||||
const IR::U32 id_in_group = ir.BitwiseAnd(lane_id, ir.Imm32(0b11));
|
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));
|
const IR::U32 base = ir.ShiftLeftLogical(id_in_group, ir.Imm32(1));
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma clang optimize off
|
||||||
// Include the vulkan platform specific header
|
// Include the vulkan platform specific header
|
||||||
#if defined(ANDROID)
|
#if defined(ANDROID)
|
||||||
#define VK_USE_PLATFORM_ANDROID_KHR
|
#define VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue