kernel: Fix a bunch of bugs, kernel thread heap

This commit is contained in:
IndecisiveTurtle 2024-10-23 22:47:33 +03:00
parent 00b84b2c7f
commit d0d8b5eee5
35 changed files with 924 additions and 242 deletions

View file

@ -821,6 +821,9 @@ else()
src/core/libraries/kernel/threads/thr_clean.cpp
src/core/libraries/kernel/threads/thread_state.h src/core/libraries/kernel/threads/thread_state.cpp
src/core/libraries/kernel/threads/thr_ctrdtr.cpp
src/common/slab_heap.h
src/common/spin_lock.cpp
src/common/spin_lock.h
)
endif()

163
src/common/slab_heap.h Normal file
View 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

View file

@ -3,10 +3,7 @@
#pragma once
#include <bit>
#include <compare>
#include <numeric>
#include <type_traits>
#include <utility>
#include <vector>
#include "common/assert.h"

53
src/common/spin_lock.cpp Executable file
View 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
View 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

View file

@ -305,7 +305,7 @@ void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("XC9wM+xULz8", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerJumpToTime);
LIB_FUNCTION("9y5v+fGN4Wk", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPause);
LIB_FUNCTION("HD1YKVU26-M", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPostInit);
LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf);
// LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf);
LIB_FUNCTION("w5moABNwnRY", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerResume);
LIB_FUNCTION("k-q+xOxdc3E", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0,
sceAvPlayerSetAvSyncMode);

View file

@ -252,11 +252,9 @@ bool AvPlayerSource::Start() {
LOG_ERROR(Lib_AvPlayer, "Could not start playback. NULL context.");
return false;
}
m_demuxer_thread = std::jthread([this](std::stop_token stop) { this->DemuxerThread(stop); });
m_video_decoder_thread =
std::jthread([this](std::stop_token stop) { this->VideoDecoderThread(stop); });
m_audio_decoder_thread =
std::jthread([this](std::stop_token stop) { this->AudioDecoderThread(stop); });
m_demuxer_thread.Run([this](std::stop_token stop) { this->DemuxerThread(stop); });
m_video_decoder_thread.Run([this](std::stop_token stop) { this->VideoDecoderThread(stop); });
m_audio_decoder_thread.Run([this](std::stop_token stop) { this->AudioDecoderThread(stop); });
m_start_time = std::chrono::high_resolution_clock::now();
return true;
}
@ -269,18 +267,10 @@ bool AvPlayerSource::Stop() {
return false;
}
m_video_decoder_thread.request_stop();
m_audio_decoder_thread.request_stop();
m_demuxer_thread.request_stop();
if (m_demuxer_thread.joinable()) {
m_demuxer_thread.join();
}
if (m_video_decoder_thread.joinable()) {
m_video_decoder_thread.join();
}
if (m_audio_decoder_thread.joinable()) {
m_audio_decoder_thread.join();
}
m_video_decoder_thread.Stop();
m_audio_decoder_thread.Stop();
m_demuxer_thread.Stop();
if (m_current_audio_frame.has_value()) {
m_audio_buffers.Push(std::move(m_current_audio_frame.value()));
m_current_audio_frame.reset();
@ -504,12 +494,8 @@ void AvPlayerSource::DemuxerThread(std::stop_token stop) {
m_video_frames_cv.Notify();
m_audio_frames_cv.Notify();
if (m_video_decoder_thread.joinable()) {
m_video_decoder_thread.join();
}
if (m_audio_decoder_thread.joinable()) {
m_audio_decoder_thread.join();
}
m_video_decoder_thread.Join();
m_audio_decoder_thread.Join();
m_state.OnEOF();
LOG_INFO(Lib_AvPlayer, "Demuxer Thread exited normally");
@ -802,8 +788,8 @@ void AvPlayerSource::AudioDecoderThread(std::stop_token stop) {
}
bool AvPlayerSource::HasRunningThreads() const {
return m_demuxer_thread.joinable() || m_video_decoder_thread.joinable() ||
m_audio_decoder_thread.joinable();
return m_demuxer_thread.Joinable() || m_video_decoder_thread.Joinable() ||
m_audio_decoder_thread.Joinable();
}
} // namespace Libraries::AvPlayer

View file

@ -11,10 +11,10 @@
#include <string_view>
#include "common/assert.h"
#include "common/polyfill_thread.h"
#include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/avplayer/avplayer_common.h"
#include "core/libraries/avplayer/avplayer_data_streamer.h"
#include "core/libraries/kernel/thread_management.h"
struct AVCodecContext;
struct AVFormatContext;
@ -200,9 +200,9 @@ private:
EventCV m_stop_cv{};
std::mutex m_state_mutex{};
std::jthread m_demuxer_thread{};
std::jthread m_video_decoder_thread{};
std::jthread m_audio_decoder_thread{};
Kernel::Thread m_demuxer_thread{};
Kernel::Thread m_video_decoder_thread{};
Kernel::Thread m_audio_decoder_thread{};
AVFormatContextPtr m_avformat_context{nullptr, &ReleaseAVFormatContext};
AVCodecContextPtr m_video_codec_context{nullptr, &ReleaseAVCodecContext};

View file

@ -117,10 +117,7 @@ AvPlayerState::~AvPlayerState() {
std::unique_lock lock(m_source_mutex);
m_up_source.reset();
}
if (m_controller_thread.joinable()) {
m_controller_thread.request_stop();
m_controller_thread.join();
}
m_controller_thread.Stop();
m_event_queue.Clear();
}
@ -221,8 +218,7 @@ void AvPlayerState::WarningEvent(s32 id) {
// Called inside GAME thread
void AvPlayerState::StartControllerThread() {
m_controller_thread =
std::jthread([this](std::stop_token stop) { this->AvControllerThread(stop); });
m_controller_thread.Run([this](std::stop_token stop) { this->AvControllerThread(stop); });
}
// Called inside GAME thread

View file

@ -7,9 +7,9 @@
#include <mutex>
#include <shared_mutex>
#include "common/polyfill_thread.h"
#include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/avplayer/avplayer_source.h"
#include "core/libraries/kernel/thread_management.h"
namespace Libraries::AvPlayer {
@ -80,7 +80,7 @@ private:
std::shared_mutex m_source_mutex{};
std::mutex m_state_machine_mutex{};
std::mutex m_event_handler_mutex{};
std::jthread m_controller_thread{};
Kernel::Thread m_controller_thread{};
AvPlayerQueue<AvPlayerEvent> m_event_queue{};
};

View file

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include "gnm_error.h"
#include "gnmdriver.h"

View file

@ -85,8 +85,6 @@ struct iovec {
};
size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) {
// weird it gives fd ==0 and writes to stdout , i am not sure if it that is valid (found in
// openorbis)
size_t total_written = 0;
for (int i = 0; i < iovcn; i++) {
total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout);

View file

@ -3,7 +3,9 @@
#pragma once
#include <sys/types.h>
#include <algorithm>
#include <fmt/core.h>
#include "common/logging/log.h"
#include "common/types.h"
#include "core/libraries/error_codes.h"
@ -17,11 +19,21 @@ void ErrSceToPosix(int result);
int ErrnoToSceKernelError(int e);
void SetPosixErrno(int e);
template <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;
template <class R, class... Args, PS4_SYSV_ABI R (*f)(Args...)>
struct WrapperImpl<PS4_SYSV_ABI R (*)(Args...), f> {
template <StringLiteral name, class R, class... Args, PS4_SYSV_ABI R (*f)(Args...)>
struct WrapperImpl<name, PS4_SYSV_ABI R (*)(Args...), f> {
static constexpr StringLiteral Name{name};
static R PS4_SYSV_ABI wrap(Args... args) {
u32 ret = f(args...);
if (ret != 0) {
@ -31,10 +43,10 @@ struct WrapperImpl<PS4_SYSV_ABI R (*)(Args...), f> {
}
};
template <class F, F f>
constexpr auto OrbisWrapper = WrapperImpl<F, f>::wrap;
template <StringLiteral name, class F, F f>
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();

View file

@ -129,6 +129,10 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags);
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len);
void* Malloc(size_t size);
void Free(void* ptr);
void RegisterMemory(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Kernel

View file

@ -7,20 +7,6 @@
namespace Libraries::Kernel {
int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr);
int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAttrT* attr,
PthreadEntryFunc start_routine, void* arg,
const char* name);
PthreadT LaunchThread(PthreadEntryFunc start_routine, void* arg, const char* name) {
PthreadT thread{};
PthreadAttrT attr{};
posix_pthread_attr_init(&attr);
posix_pthread_create_name_np(&thread, &attr, start_routine, arg, name);
return thread;
}
void RegisterThreads(Core::Loader::SymbolsResolver* sym) {
RegisterMutex(sym);
RegisterCond(sym);

View file

@ -11,8 +11,60 @@ class SymbolsResolver;
namespace Libraries::Kernel {
PthreadT LaunchThread(PthreadEntryFunc start_routine, void* arg, const char* name);
int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr);
int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr);
int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr,
PthreadEntryFunc start_routine, void* arg);
int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return);
void RegisterThreads(Core::Loader::SymbolsResolver* sym);
class Thread {
public:
explicit Thread() = default;
~Thread() {
Stop();
}
void Run(std::function<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

View file

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/libkernel.h"
#include "core/libraries/kernel/threads/thread_state.h"
@ -18,9 +18,9 @@ struct PthreadPrio {
};
static constexpr std::array<PthreadPrio, 3> ThrPriorities = {{
{0x2BC, 0x300, 0x3BF}, // Fifo
{0x384, 0x100, 0x2FF}, // Other
{0x2BC, 0, 1}, // Round-Robin
{0x100, 0x2FF, 0x2BC}, // Fifo
{0x300, 0x3BF, 0x384}, // Other
{0x100, 0x2FF, 0x2BC}, // Round-Robin
}};
PthreadAttr PthreadAttrDefault = {
@ -33,13 +33,14 @@ PthreadAttr PthreadAttrDefault = {
.stacksize_attr = ThrStackDefault,
.guardsize_attr = 0,
.cpusetsize = 0,
.cpuset = nullptr,
};
int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr) {
if (attr == nullptr || *attr == nullptr) {
return POSIX_EINVAL;
}
free(*attr);
delete *attr;
*attr = nullptr;
return 0;
}
@ -112,7 +113,7 @@ int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const PthreadAttrT* attr, size_
}
int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr) {
PthreadAttrT pattr = (PthreadAttrT)malloc(sizeof(PthreadAttr));
PthreadAttrT pattr = new PthreadAttr{};
if (pattr == nullptr) {
return POSIX_ENOMEM;
}
@ -180,7 +181,7 @@ int PS4_SYSV_ABI posix_pthread_attr_setschedparam(PthreadAttrT* attr, const sche
}
const auto policy = (*attr)->sched_policy;
if (policy == SchedPolicy::Fifo || policy == SchedPolicy::RoundRobin) {
if (policy == SchedPolicy::RoundRobin) {
if (param->sched_priority < ThrPriorities[u32(policy) - 1].pri_min ||
param->sched_priority > ThrPriorities[u32(policy) - 1].pri_max) {
return POSIX_ENOTSUP;
@ -224,13 +225,63 @@ int PS4_SYSV_ABI posix_pthread_attr_get_np(PthreadT pthread, PthreadAttrT* dstat
if (True(pthread->flags & ThreadFlags::Detached)) {
attr.flags |= PthreadAttrFlags::Detached;
}
pthread->lock->unlock();
pthread->lock.unlock();
if (ret == 0) {
memcpy(dst, &attr, sizeof(PthreadAttr));
}
return ret;
}
int PS4_SYSV_ABI posix_pthread_attr_getaffinity_np(const PthreadAttrT* pattr, size_t cpusetsize,
Cpuset* cpusetp) {
if (pattr == nullptr) {
return POSIX_EINVAL;
}
PthreadAttrT attr = *pattr;
if (attr == nullptr) {
return POSIX_EINVAL;
}
if (attr->cpuset != nullptr)
memcpy(cpusetp, attr->cpuset, std::min(cpusetsize, attr->cpusetsize));
else
memset(cpusetp, -1, sizeof(Cpuset));
return 0;
}
int PS4_SYSV_ABI scePthreadAttrGetaffinity(PthreadAttrT* param_1, Cpuset* mask) {
Cpuset cpuset;
const int ret = posix_pthread_attr_getaffinity_np(param_1, 0x10, &cpuset);
if (ret == 0) {
*mask = cpuset;
}
return ret;
}
int PS4_SYSV_ABI posix_pthread_attr_setaffinity_np(PthreadAttrT* pattr, size_t cpusetsize,
const Cpuset* cpusetp) {
if (pattr == nullptr) {
return POSIX_EINVAL;
}
PthreadAttrT attr = *pattr;
if (attr == nullptr) {
return POSIX_EINVAL;
}
if (cpusetsize == 0 || cpusetp == nullptr) {
if (attr->cpuset != nullptr) {
free(attr->cpuset);
attr->cpuset = NULL;
attr->cpusetsize = 0;
}
return 0;
}
if (attr->cpuset == nullptr) {
attr->cpuset = (Cpuset*)calloc(1, sizeof(Cpuset));
attr->cpusetsize = sizeof(Cpuset);
}
memcpy(attr->cpuset, cpusetp, sizeof(Cpuset));
return 0;
}
void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
// Posix
LIB_FUNCTION("wtkt-teR1so", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_init);
@ -280,6 +331,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
ORBIS(posix_pthread_attr_setstackaddr));
LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_attr_setguardsize));
LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1,
ORBIS(scePthreadAttrGetaffinity));
}
} // namespace Libraries::Kernel

View file

@ -15,7 +15,7 @@ void __pthread_cleanup_push_imp(PthreadCleanupFunc routine, void* arg, PthreadCl
void PS4_SYSV_ABI posix_pthread_cleanup_push(PthreadCleanupFunc routine, void* arg) {
Pthread* curthread = g_curthread;
PthreadCleanup* newbuf = (PthreadCleanup*)malloc(sizeof(PthreadCleanup));
PthreadCleanup* newbuf = new PthreadCleanup{};
if (newbuf == nullptr) {
return;
}
@ -35,7 +35,7 @@ void PS4_SYSV_ABI posix_pthread_cleanup_pop(int execute) {
old->routine(old->routine_arg);
}
if (old->onheap) {
free(old);
delete old;
}
}
}

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include <cstring>
#include "common/assert.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/libkernel.h"
#include "core/libraries/kernel/threads/threads.h"
@ -14,33 +15,29 @@ static std::mutex CondStaticLock;
#define THR_COND_INITIALIZER ((PthreadCond*)NULL)
#define THR_COND_DESTROYED ((PthreadCond*)1)
enum class ClockId : u32 {
Realtime = 0,
Virtual = 1,
Prof = 2,
Monotonic = 4,
Uptime = 5,
UptimePrecise = 7,
UptimeFast = 8,
RealtimePrecise = 9,
RealtimeFast = 10,
MonotonicPrecise = 11,
MonotonicFast = 12,
Second = 13,
ThreadCputimeID = 14,
static constexpr PthreadCondAttr PhreadCondattrDefault = {
.c_pshared = 0,
.c_clockid = ClockId::Realtime,
};
static constexpr PthreadCondAttr PhreadCondattrDefault = {.c_pshared = PTHREAD_PROCESS_PRIVATE,
.c_clockid = CLOCK_REALTIME};
static int CondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr, const char* name) {
auto* cvp = (PthreadCond*)malloc(sizeof(PthreadCond));
std::memset(cvp, 0, sizeof(PthreadCond));
std::construct_at(cvp);
static int CondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) {
PthreadCond* cvp = (PthreadCond*)calloc(1, sizeof(PthreadCond));
if (cvp == nullptr) {
return POSIX_ENOMEM;
}
std::construct_at(cvp);
if (name) {
cvp->name = name;
} else {
static int CondId = 0;
cvp->name = fmt::format("Cond{}", CondId++);
}
if (cond_attr == nullptr || *cond_attr == nullptr) {
cvp->clock_id = CLOCK_REALTIME;
cvp->clock_id = ClockId::Realtime;
} else {
// if ((*cond_attr)->c_pshared) {
// cvp->flags |= USYNC_PROCESS_SHARED;
@ -53,8 +50,9 @@ static int CondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) {
static int InitStatic(Pthread* thread, PthreadCondT* cond) {
std::scoped_lock lk{CondStaticLock};
if (*cond == NULL)
return CondInit(cond, NULL);
if (*cond == nullptr) {
return CondInit(cond, nullptr, nullptr);
}
return 0;
}
@ -73,7 +71,13 @@ static int InitStatic(Pthread* thread, PthreadCondT* cond) {
int PS4_SYSV_ABI posix_pthread_cond_init(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) {
*cond = nullptr;
return CondInit(cond, cond_attr);
return CondInit(cond, cond_attr, nullptr);
}
int PS4_SYSV_ABI scePthreadCondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr,
const char* name) {
*cond = nullptr;
return CondInit(cond, cond_attr, name);
}
int PS4_SYSV_ABI posix_pthread_cond_destroy(PthreadCondT* cond) {
@ -86,33 +90,168 @@ int PS4_SYSV_ABI posix_pthread_cond_destroy(PthreadCondT* cond) {
}
cvp = *cond;
*cond = THR_COND_DESTROYED;
std::destroy_at(cvp);
free(cvp);
return 0;
}
int PthreadCond::Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime) {
Pthread* curthread = g_curthread;
PthreadMutex* mp = *mutex;
static std::mutex sc_lock;
static std::unordered_map<void*, SleepQueue*> sc_table;
if (int error = mp->IsOwned(curthread); error != 0) {
void _sleepq_lock(void* wchan) {
sc_lock.lock();
}
void _sleepq_unlock(void* wchan) {
sc_lock.unlock();
}
SleepQueue* _sleepq_lookup(void* wchan) {
const auto it = sc_table.find(wchan);
if (it != sc_table.end()) {
return it->second;
}
return nullptr;
}
void _sleepq_add(void* wchan, Pthread* td) {
SleepQueue* sq = _sleepq_lookup(wchan);
if (sq != NULL) {
sq->sq_freeq.push_front(td->sleepqueue);
} else {
sc_table.emplace(wchan, td->sleepqueue);
td->sleepqueue->sq_wchan = wchan;
}
td->sleepqueue = nullptr;
td->wchan = wchan;
sq->sq_blocked.push_front(td);
}
bool _sleepq_remove(SleepQueue* sq, Pthread* td) {
std::erase(sq->sq_blocked, td);
if (sq->sq_blocked.empty()) {
sc_table.erase(td->wchan);
td->sleepqueue = sq;
td->wchan = nullptr;
return false;
} else {
td->sleepqueue = sq->sq_freeq.front();
sq->sq_freeq.pop_front();
td->wchan = nullptr;
return true;
}
}
void _sleepq_drop(SleepQueue* sq, void (*cb)(Pthread*, void* arg), void* arg) {
if (sq->sq_blocked.empty()) {
return;
}
Pthread* td = sq->sq_blocked.front();
sc_table.erase(td->wchan);
std::erase(sq->sq_blocked, td);
cb(td, arg);
td->sleepqueue = sq;
td->wchan = nullptr;
auto sq2 = sq->sq_freeq.begin();
for (Pthread* td : sq->sq_blocked) {
cb(td, arg);
td->sleepqueue = *sq2;
td->wchan = NULL;
sq2++;
}
sq->sq_blocked.clear();
sq->sq_freeq.clear();
}
/*static int cond_wait_user(PthreadCond *cvp, PthreadMutex* mp,
const OrbisKernelTimespec *abstime, int cancel) {
Pthread* curthread = g_curthread;
int recurse;
int error;
ASSERT_MSG(curthread->wchan == nullptr, "Thread was already on queue");
//if (cancel)
//_thr_testcancel(curthread);
_sleepq_lock(cvp);
cvp->has_user_waiters = 1;
curthread->will_sleep = 1;
_mutex_cv_unlock(mp, &recurse);
curthread->mutex_obj = mp;
_sleepq_add(cvp, curthread);
for(;;) {
_thr_clear_wake(curthread);
_sleepq_unlock(cvp);
if (cancel) {
//_thr_cancel_enter2(curthread, 0);
error = _thr_sleep(curthread, cvp->__clock_id, abstime);
//_thr_cancel_leave(curthread, 0);
} else {
error = _thr_sleep(curthread, cvp->__clock_id, abstime);
}
_sleepq_lock(cvp);
if (curthread->wchan == nullptr) {
error = 0;
break;
} else if (cancel && curthread->ShouldCancel()) {
SleepQueue* sq = _sleepq_lookup(cvp);
cvp->has_user_waiters = _sleepq_remove(sq, curthread);
_sleepq_unlock(cvp);
curthread->mutex_obj = NULL;
_mutex_cv_lock(mp, recurse);
if (!THR_IN_CRITICAL(curthread))
_pthread_exit(PTHREAD_CANCELED);
else
return (0);
} else if (error == POSIX_ETIMEDOUT) {
SleepQueue* sq = _sleepq_lookup(cvp);
cvp->has_user_waiters = _sleepq_remove(sq, curthread);
break;
}
}
_sleepq_unlock(cvp);
curthread->mutex_obj = NULL;
_mutex_cv_lock(mp, recurse);
return (error);
}*/
int PthreadCond::Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime) {
PthreadMutex* mp = *mutex;
if (int error = mp->IsOwned(g_curthread); error != 0) {
return error;
}
//_thr_testcancel(curthread);
//_thr_cancel_enter2(curthread, 0);
if (abstime) {
const auto status = cond.wait_until(mp->m_lock, abstime->TimePoint());
const auto status = cond.wait_until(*mp, abstime->TimePoint());
return status == std::cv_status::timeout ? POSIX_ETIMEDOUT : 0;
} else {
cond.wait(mp->m_lock);
cond.wait(*mp);
return 0;
}
//_thr_cancel_leave(curthread, 0);
}
int PthreadCond::Wait(PthreadMutexT* mutex, u64 usec) {
PthreadMutex* mp = *mutex;
if (int error = mp->IsOwned(g_curthread); error != 0) {
return error;
}
//_thr_testcancel(curthread);
//_thr_cancel_enter2(curthread, 0);
const auto status = cond.wait_for(*mp, std::chrono::microseconds(usec));
return status == std::cv_status::timeout ? POSIX_ETIMEDOUT : 0;
//_thr_cancel_leave(curthread, 0);
}
int PS4_SYSV_ABI posix_pthread_cond_wait(PthreadCondT* cond, PthreadMutexT* mutex) {
PthreadCond* cvp;
PthreadCond* cvp{};
CHECK_AND_INIT_COND
return cvp->Wait(mutex, nullptr);
}
@ -124,27 +263,34 @@ int PS4_SYSV_ABI posix_pthread_cond_timedwait(PthreadCondT* cond, PthreadMutexT*
return POSIX_EINVAL;
}
PthreadCond* cvp;
PthreadCond* cvp{};
CHECK_AND_INIT_COND
return cvp->Wait(mutex, abstime);
}
int PS4_SYSV_ABI posix_pthread_cond_reltimedwait_np(PthreadCondT* cond, PthreadMutexT* mutex,
u64 usec) {
PthreadCond* cvp{};
CHECK_AND_INIT_COND
return cvp->Wait(mutex, usec);
}
int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) {
PthreadCond* cvp;
PthreadCond* cvp{};
CHECK_AND_INIT_COND
cvp->cond.notify_one();
return 0;
}
int PS4_SYSV_ABI posix_pthread_cond_broadcast(PthreadCondT* cond) {
PthreadCond* cvp;
PthreadCond* cvp{};
CHECK_AND_INIT_COND
cvp->cond.notify_all();
return 0;
}
int PS4_SYSV_ABI posix_pthread_condattr_init(PthreadCondAttrT* attr) {
PthreadCondAttr* pattr = (PthreadCondAttr*)malloc(sizeof(PthreadCondAttr));
PthreadCondAttr* pattr = new PthreadCondAttr{};
if (pattr == nullptr) {
return POSIX_ENOMEM;
}
@ -157,7 +303,7 @@ int PS4_SYSV_ABI posix_pthread_condattr_destroy(PthreadCondAttrT* attr) {
if (attr == nullptr || *attr == nullptr) {
return POSIX_EINVAL;
}
free(*attr);
delete *attr;
*attr = nullptr;
return 0;
}
@ -178,7 +324,7 @@ int PS4_SYSV_ABI posix_pthread_condattr_setclock(PthreadCondAttrT* attr, ClockId
clock_id != ClockId::Prof && clock_id != ClockId::Monotonic) {
return POSIX_EINVAL;
}
(*attr)->c_clockid = static_cast<int>(clock_id);
(*attr)->c_clockid = clock_id;
return 0;
}
@ -214,7 +360,7 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast);
// Orbis
LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_cond_init));
LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadCondInit));
LIB_FUNCTION("m5-2bsNfv7s", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_condattr_init));
LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1,
@ -225,7 +371,7 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_cond_signal));
LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_cond_timedwait));
ORBIS(posix_pthread_cond_reltimedwait_np));
LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_cond_destroy));
}

View file

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include "common/assert.h"
#include "common/thread.h"
#include "core/debug_state.h"
@ -25,6 +25,13 @@ extern PthreadAttr PthreadAttrDefault;
void _thread_cleanupspecific();
using ThreadDtor = void (*)();
static ThreadDtor* ThreadDtors{};
void PS4_SYSV_ABI _sceKernelSetThreadDtors(ThreadDtor* dtor) {
ThreadDtors = dtor;
}
static void ExitThread() {
Pthread* curthread = g_curthread;
@ -37,7 +44,7 @@ static void ExitThread() {
auto* thread_state = ThrState::Instance();
ASSERT(thread_state->active_threads.fetch_sub(1) != 1);
curthread->lock->lock();
curthread->lock.lock();
curthread->state = PthreadState::Dead;
ASSERT(False(curthread->flags & ThreadFlags::NeedSuspend));
@ -46,7 +53,7 @@ static void ExitThread() {
* reference count to allow it to be garbage collected.
*/
curthread->refcount--;
thread_state->TryCollect(curthread, curthread); /* thread lock released */
thread_state->TryCollect(curthread); /* thread lock released */
/*
* Kernel will do wakeup at the address, so joiner thread
@ -80,10 +87,12 @@ void PS4_SYSV_ABI posix_pthread_exit(void* status) {
curthread->cleanup.pop_front();
old->routine(old->routine_arg);
if (old->onheap) {
free(old);
delete old;
}
}
/*if (ThreadDtors && *ThreadDtors) {
(*ThreadDtors)();
}*/
ExitThread();
}
@ -111,16 +120,16 @@ static int JoinThread(PthreadT pthread, void** thread_return, const OrbisKernelT
ret = POSIX_ENOTSUP;
}
if (ret) {
pthread->lock->unlock();
pthread->lock.unlock();
return ret;
}
/* Set the running thread to be the joiner: */
pthread->joiner = curthread;
pthread->lock->unlock();
pthread->lock.unlock();
const auto backout_join = [](void* arg) {
Pthread* pthread = (Pthread*)arg;
std::scoped_lock lk{*pthread->lock};
std::scoped_lock lk{pthread->lock};
pthread->joiner = nullptr;
};
@ -145,10 +154,10 @@ static int JoinThread(PthreadT pthread, void** thread_return, const OrbisKernelT
}
void* tmp = pthread->ret;
pthread->lock->lock();
pthread->lock.lock();
pthread->flags |= ThreadFlags::Detached;
pthread->joiner = nullptr;
thread_state->TryCollect(curthread, pthread); /* thread lock released */
thread_state->TryCollect(pthread); /* thread lock released */
if (thread_return != nullptr) {
*thread_return = tmp;
}
@ -182,13 +191,13 @@ int PS4_SYSV_ABI posix_pthread_detach(PthreadT pthread) {
/* Check if the thread is already detached or has a joiner. */
if (True(pthread->flags & ThreadFlags::Detached) || (pthread->joiner != NULL)) {
pthread->lock->unlock();
pthread->lock.unlock();
return POSIX_EINVAL;
}
/* Flag the thread as detached. */
pthread->flags |= ThreadFlags::Detached;
thread_state->TryCollect(g_curthread, pthread); /* thread lock released */
thread_state->TryCollect(pthread); /* thread lock released */
return 0;
}
@ -231,7 +240,8 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
new_thread->attr.sched_policy = curthread->attr.sched_policy;
}
new_thread->tid = TidTerminated;
static int TidCounter = 1;
new_thread->tid = ++TidCounter;
if (thread_state->CreateStack(&new_thread->attr) != 0) {
/* Insufficient memory to create a stack: */
@ -248,9 +258,11 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
new_thread->arg = arg;
new_thread->cancel_enable = 1;
new_thread->cancel_async = 0;
static std::atomic<int> counter = 0;
new_thread->name = fmt::format("NoName{}", counter++);
auto* memory = Core::Memory::Instance();
if (name && (std::string_view{name} == "GAME_MainThread" || memory->IsValidAddress(name))) {
if (name && memory->IsValidAddress(name)) {
new_thread->name = name;
}
@ -269,10 +281,11 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
(*thread) = new_thread;
/* Create thread */
pthread_t pthr;
pthread_attr_t pattr;
pthread_attr_init(&pattr);
pthread_attr_setstack(&pattr, new_thread->attr.stackaddr_attr, new_thread->attr.stacksize_attr);
pthread_t pthr;
// pthread_attr_setstack(&pattr, new_thread->attr.stackaddr_attr,
// new_thread->attr.stacksize_attr);
int ret = pthread_create(&pthr, &pattr, (PthreadEntryFunc)RunThread, new_thread);
ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret);
if (ret) {
@ -281,6 +294,11 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
return ret;
}
int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr,
PthreadEntryFunc start_routine, void* arg) {
return posix_pthread_create_name_np(thread, attr, start_routine, arg, nullptr);
}
int PS4_SYSV_ABI posix_pthread_getthreadid_np() {
return g_curthread->tid;
}
@ -361,7 +379,51 @@ int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) {
return SCE_OK;
}
int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* policy,
SchedParam* param) {
if (policy == nullptr || param == nullptr) {
return POSIX_EINVAL;
}
if (pthread == g_curthread) {
/*
* Avoid searching the thread list when it is the current
* thread.
*/
std::scoped_lock lk{g_curthread->lock};
*policy = g_curthread->attr.sched_policy;
param->sched_priority = g_curthread->attr.prio;
return 0;
}
auto* thread_state = ThrState::Instance();
/* Find the thread in the list of active threads. */
if (int ret = thread_state->RefAdd(pthread, /*include dead*/ 0); ret != 0) {
return ret;
}
pthread->lock.lock();
*policy = pthread->attr.sched_policy;
param->sched_priority = pthread->attr.prio;
pthread->lock.unlock();
thread_state->RefDelete(pthread);
return 0;
}
int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) {
SchedParam param;
SchedPolicy policy;
posix_pthread_getschedparam(thread, &policy, &param);
*priority = param.sched_priority;
return 0;
}
int sceNpWebApiTerminate() {
return 0;
}
void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("asz3TtIqGF8", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, sceNpWebApiTerminate);
// Posix
LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once);
LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal);
@ -370,9 +432,13 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self);
LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield);
LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach);
LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join);
LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create);
LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np);
// Posix-Kernel
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create);
// Orbis
LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once));
@ -380,10 +446,14 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_create_name_np));
LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_detach));
LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join));
LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit);
LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_equal);
LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, posix_pthread_yield);
LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getthreadid_np);
LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio);
LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", 1, 1, _sceKernelSetThreadDtors);
}
} // namespace Libraries::Kernel

View file

@ -25,7 +25,7 @@ Core::Tcb* TcbCtor(Pthread* thread, int initial) {
// Initialize allocated memory and allocate DTV table.
const u32 num_dtvs = linker->MaxTlsIndex();
const auto static_tls_size = linker->StaticTlsSize();
auto* dtv_table = new Core::DtvEntry[num_dtvs + 2];
auto* dtv_table = new Core::DtvEntry[num_dtvs + 2]{};
// Initialize thread control block
u8* addr = reinterpret_cast<u8*>(addr_out);

View file

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include "common/assert.h"
#include "common/scope_exit.h"
#include "common/types.h"
@ -17,6 +17,7 @@ static std::mutex MutxStaticLock;
#define THR_MUTEX_INITIALIZER ((PthreadMutex*)NULL)
#define THR_ADAPTIVE_MUTEX_INITIALIZER ((PthreadMutex*)1)
#define THR_MUTEX_DESTROYED ((PthreadMutex*)2)
#define THR_MUTEX_RELTIME (const OrbisKernelTimespec*)-1
#define CPU_SPINWAIT __asm__ volatile("pause")
@ -39,8 +40,7 @@ static constexpr PthreadMutexAttr PthreadMutexattrAdaptiveDefault = {
using CallocFun = void* (*)(size_t, size_t);
static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr,
CallocFun calloc_cb) {
static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, const char* name) {
const PthreadMutexAttr* attr;
if (mutex_attr == NULL) {
attr = &PthreadMutexattrDefault;
@ -53,14 +53,22 @@ static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr,
return POSIX_EINVAL;
}
}
PthreadMutex* pmutex = (PthreadMutex*)calloc(1, sizeof(PthreadMutex));
auto* pmutex = (PthreadMutex*)malloc(sizeof(PthreadMutex));
std::memset(pmutex, 0, sizeof(PthreadMutex));
std::construct_at(pmutex);
if (pmutex == nullptr) {
return POSIX_ENOMEM;
}
std::construct_at(pmutex);
if (name) {
pmutex->name = name;
} else {
static int MutexId = 0;
pmutex->name = fmt::format("Mutex{}", MutexId++);
}
pmutex->m_flags = PthreadMutexFlags(attr->m_type);
pmutex->m_owner = NULL;
pmutex->m_owner = nullptr;
pmutex->m_count = 0;
pmutex->m_spinloops = 0;
pmutex->m_yieldloops = 0;
@ -78,16 +86,21 @@ static int InitStatic(Pthread* thread, PthreadMutexT* mutex) {
std::scoped_lock lk{MutxStaticLock};
if (*mutex == THR_MUTEX_INITIALIZER) {
return MutexInit(mutex, &PthreadMutexattrDefault, calloc);
return MutexInit(mutex, &PthreadMutexattrDefault, nullptr);
} else if (*mutex == THR_ADAPTIVE_MUTEX_INITIALIZER) {
return MutexInit(mutex, &PthreadMutexattrAdaptiveDefault, calloc);
return MutexInit(mutex, &PthreadMutexattrAdaptiveDefault, nullptr);
}
return 0;
}
int PS4_SYSV_ABI posix_pthread_mutex_init(PthreadMutexT* mutex,
const PthreadMutexAttrT* mutex_attr) {
return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, calloc);
return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, nullptr);
}
int PS4_SYSV_ABI scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr,
const char* name) {
return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, name);
}
int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) {
@ -102,7 +115,6 @@ int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) {
return POSIX_EBUSY;
}
*mutex = THR_MUTEX_DESTROYED;
std::destroy_at(m);
free(m);
return 0;
}
@ -125,11 +137,12 @@ int PthreadMutex::SelfTryLock() {
}
}
int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) {
switch (Type()) {
case PthreadMutexType::ErrorCheck:
case PthreadMutexType::AdaptiveNp: {
if (abstime) {
int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) {
const auto DoSleep = [&] {
if (abstime == THR_MUTEX_RELTIME) {
std::this_thread::sleep_for(std::chrono::microseconds(usec));
return POSIX_ETIMEDOUT;
} else {
if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
return POSIX_EINVAL;
} else {
@ -137,10 +150,18 @@ int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) {
return POSIX_ETIMEDOUT;
}
}
};
switch (Type()) {
case PthreadMutexType::ErrorCheck:
case PthreadMutexType::AdaptiveNp: {
if (abstime) {
return DoSleep();
}
/*
* POSIX specifies that mutexes should return
* EDEADLK if a recursive lock is detected.
*/
UNREACHABLE_MSG("Mutex deadlock occured");
return POSIX_EDEADLK;
}
case PthreadMutexType::Normal: {
@ -149,12 +170,7 @@ int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) {
* deadlock on attempts to get a lock you already own.
*/
if (abstime) {
if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) {
return POSIX_EINVAL;
} else {
std::this_thread::sleep_until(abstime->TimePoint());
return POSIX_ETIMEDOUT;
}
return DoSleep();
}
UNREACHABLE_MSG("Mutex deadlock occured");
return 0;
@ -172,10 +188,10 @@ int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) {
}
}
int PthreadMutex::Lock(const OrbisKernelTimespec* abstime) {
int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) {
Pthread* curthread = g_curthread;
if (m_owner == curthread) {
return SelfLock(abstime);
return SelfLock(abstime, usec);
}
int ret = 0;
@ -211,11 +227,16 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime) {
if (abstime == nullptr) {
m_lock.lock();
} else if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) [[unlikely]] {
} else if (abstime != THR_MUTEX_RELTIME &&
(abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)) [[unlikely]] {
ret = POSIX_EINVAL;
} else {
if (THR_MUTEX_RELTIME) {
ret = m_lock.try_lock_for(std::chrono::microseconds(usec)) ? 0 : POSIX_ETIMEDOUT;
} else {
ret = m_lock.try_lock_until(abstime->TimePoint()) ? 0 : POSIX_ETIMEDOUT;
}
}
return ret;
}
@ -244,9 +265,15 @@ int PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex) {
int PS4_SYSV_ABI posix_pthread_mutex_timedlock(PthreadMutexT* mutex,
const OrbisKernelTimespec* abstime) {
CHECK_AND_INIT_MUTEX
UNREACHABLE();
return (*mutex)->Lock(abstime);
}
int PS4_SYSV_ABI posix_pthread_mutex_reltimedlock_np(PthreadMutexT* mutex, u64 usec) {
CHECK_AND_INIT_MUTEX
return (*mutex)->Lock(THR_MUTEX_RELTIME, usec);
}
int PthreadMutex::Unlock() {
Pthread* curthread = g_curthread;
/*
@ -320,7 +347,7 @@ bool PthreadMutex::IsOwned(Pthread* curthread) const {
}
int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) {
PthreadMutexAttrT pattr = (PthreadMutexAttrT)malloc(sizeof(PthreadMutexAttr));
PthreadMutexAttrT pattr = new PthreadMutexAttr{};
if (pattr == nullptr) {
return POSIX_ENOMEM;
}
@ -367,7 +394,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) {
if (attr == nullptr || *attr == nullptr) {
return POSIX_EINVAL;
}
free(*attr);
delete *attr;
*attr = nullptr;
return 0;
}
@ -412,7 +439,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock);
// Orbis
LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_init));
LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, ORBIS(scePthreadMutexInit));
LIB_FUNCTION("2Of0f+3mhhE", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_mutex_destroy));
LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1,
@ -429,7 +456,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("upoVrzMHFeE", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_mutex_trylock));
LIB_FUNCTION("IafI2PxcPnQ", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_mutex_timedlock));
ORBIS(posix_pthread_mutex_reltimedlock_np));
LIB_FUNCTION("qH1gXoq71RY", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_init));
LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1,
ORBIS(posix_pthread_mutexattr_init));

View file

@ -27,11 +27,10 @@ static std::mutex RwlockStaticLock;
}
static int RwlockInit(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) {
PthreadRwlock* prwlock = (PthreadRwlock*)calloc(1, sizeof(PthreadRwlock));
PthreadRwlock* prwlock = new PthreadRwlock{};
if (prwlock == nullptr) {
return POSIX_ENOMEM;
}
std::construct_at(prwlock);
*rwlock = prwlock;
return 0;
}
@ -45,8 +44,7 @@ int PS4_SYSV_ABI posix_pthread_rwlock_destroy(PthreadRwlockT* rwlock) {
return POSIX_EINVAL;
}
*rwlock = THR_RWLOCK_DESTROYED;
std::destroy_at(prwlock);
free(prwlock);
delete prwlock;
return 0;
}
@ -121,21 +119,21 @@ int PthreadRwlock::Wrlock(const OrbisKernelTimespec* abstime) {
}
int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(PthreadRwlockT* rwlock) {
PthreadRwlockT prwlock;
PthreadRwlockT prwlock{};
CHECK_AND_INIT_RWLOCK
return prwlock->Rdlock(nullptr);
}
int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock(PthreadRwlockT* rwlock,
const OrbisKernelTimespec* abstime) {
PthreadRwlockT prwlock;
PthreadRwlockT prwlock{};
CHECK_AND_INIT_RWLOCK
return prwlock->Rdlock(abstime);
}
int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(PthreadRwlockT* rwlock) {
Pthread* curthread = g_curthread;
PthreadRwlockT prwlock;
PthreadRwlockT prwlock{};
CHECK_AND_INIT_RWLOCK
if (!prwlock->lock.try_lock_shared()) {
@ -148,7 +146,7 @@ int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(PthreadRwlockT* rwlock) {
int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(PthreadRwlockT* rwlock) {
Pthread* curthread = g_curthread;
PthreadRwlockT prwlock;
PthreadRwlockT prwlock{};
CHECK_AND_INIT_RWLOCK
if (!prwlock->lock.try_lock()) {
@ -159,14 +157,14 @@ int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(PthreadRwlockT* rwlock) {
}
int PS4_SYSV_ABI posix_pthread_rwlock_wrlock(PthreadRwlockT* rwlock) {
PthreadRwlockT prwlock;
PthreadRwlockT prwlock{};
CHECK_AND_INIT_RWLOCK
return prwlock->Wrlock(nullptr);
}
int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock(PthreadRwlockT* rwlock,
const OrbisKernelTimespec* abstime) {
PthreadRwlockT prwlock;
PthreadRwlockT prwlock{};
CHECK_AND_INIT_RWLOCK
return prwlock->Wrlock(abstime);
}
@ -213,7 +211,7 @@ int PS4_SYSV_ABI posix_pthread_rwlockattr_destroy(PthreadRwlockAttrT* rwlockattr
return POSIX_EINVAL;
}
free(prwlockattr);
delete prwlockattr;
return 0;
}
@ -228,7 +226,7 @@ int PS4_SYSV_ABI posix_pthread_rwlockattr_init(PthreadRwlockAttrT* rwlockattr) {
return POSIX_EINVAL;
}
PthreadRwlockAttrT prwlockattr = (PthreadRwlockAttrT)malloc(sizeof(PthreadRwlockAttr));
PthreadRwlockAttrT prwlockattr = new PthreadRwlockAttr{};
if (prwlockattr == nullptr) {
return POSIX_ENOMEM;
}

View file

@ -27,9 +27,7 @@ public:
OrbisSem(s32 init_count, s32 max_count, std::string_view name, bool is_fifo)
: name{name}, token_count{init_count}, max_count{max_count}, init_count{init_count},
is_fifo{is_fifo} {}
~OrbisSem() {
ASSERT(wait_list.empty());
}
~OrbisSem() = default;
int Wait(bool can_block, s32 need_count, u32* timeout) {
std::unique_lock lk{mutex};
@ -93,6 +91,15 @@ public:
return ORBIS_OK;
}
void Delete() {
std::scoped_lock lk{mutex};
for (auto* waiter : wait_list) {
waiter->was_deleted = true;
waiter->cv.notify_one();
}
wait_list.clear();
}
public:
struct WaitingThread {
std::condition_variable cv;
@ -219,7 +226,7 @@ int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) {
if (!sem) {
return SCE_KERNEL_ERROR_ESRCH;
}
delete sem;
sem->Delete();
return ORBIS_OK;
}

View file

@ -90,7 +90,7 @@ void _thread_cleanupspecific() {
}
}
}
free(curthread->specific);
delete[] curthread->specific;
curthread->specific = nullptr;
ASSERT(curthread->specific_data_count == 0);
}
@ -100,8 +100,7 @@ int PS4_SYSV_ABI posix_pthread_setspecific(PthreadKeyT key, const void* value) {
Pthread* pthread = g_curthread;
if (!pthread->specific) {
pthread->specific =
(PthreadSpecificElem*)calloc(1, sizeof(PthreadSpecificElem) * PthreadKeysMax);
pthread->specific = new PthreadSpecificElem[PthreadKeysMax];
if (!pthread->specific) {
return POSIX_ENOMEM;
}

View file

@ -2,19 +2,33 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <boost/container/small_vector.hpp>
#include "common/alignment.h"
#include "common/scope_exit.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/threads/thread_state.h"
#include "core/libraries/kernel/threads/threads.h"
#include "core/memory.h"
#include "core/tls.h"
namespace Libraries::Kernel {
Pthread* g_curthread{};
thread_local Pthread* g_curthread{};
Core::Tcb* TcbCtor(Pthread* thread, int initial);
void TcbDtor(Core::Tcb* oldtls);
ThreadState::ThreadState() {
// Reserve memory for maximum amount of threads allowed.
auto* memory = Core::Memory::Instance();
static constexpr u32 ThrHeapSize = Common::AlignUp(sizeof(Pthread) * MaxThreads, 16_KB);
void* heap_addr{};
const int ret = memory->MapMemory(&heap_addr, Core::SYSTEM_RESERVED_MIN, ThrHeapSize,
Core::MemoryProt::CpuReadWrite, Core::MemoryMapFlags::NoFlags,
Core::VMAType::File, "ThrHeap");
ASSERT_MSG(ret == 0, "Unable to allocate thread heap memory {}", ret);
thread_heap.Initialize(heap_addr, ThrHeapSize);
}
void ThreadState::Collect(Pthread* curthread) {
boost::container::small_vector<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 {
thread->lock->unlock();
thread->lock.unlock();
};
if (!thread->ShouldCollect()) {
return;
}
thread->refcount++;
thread->lock->unlock();
thread->lock.unlock();
std::scoped_lock lk{thread_list_lock};
thread->lock->lock();
thread->lock.lock();
thread->refcount--;
if (thread->ShouldCollect()) {
threads.erase(thread);
@ -71,9 +85,7 @@ Pthread* ThreadState::Alloc(Pthread* curthread) {
return nullptr;
}
total_threads.fetch_add(1);
thread = (Pthread*)malloc(sizeof(Pthread));
std::construct_at(thread);
thread->lock = std::make_unique<std::mutex>();
thread = thread_heap.Allocate();
if (thread == nullptr) {
total_threads.fetch_sub(1);
return nullptr;
@ -87,12 +99,13 @@ Pthread* ThreadState::Alloc(Pthread* curthread) {
tcb = TcbCtor(thread, 1 /* initial tls */);
}
if (tcb != nullptr) {
memset(thread, 0, sizeof(Pthread));
std::construct_at(thread);
thread->tcb = tcb;
// thread->sleepqueue = _sleepq_alloc();
// thread->wake_addr = _thr_alloc_wake_addr();
} else {
std::destroy_at(thread);
free(thread);
thread_heap.Free(thread);
total_threads.fetch_sub(1);
thread = nullptr;
}
@ -106,12 +119,12 @@ void ThreadState::Free(Pthread* curthread, Pthread* thread) {
} else {
TcbDtor(thread->tcb);
}
thread->tcb = NULL;
thread->tcb = nullptr;
std::destroy_at(thread);
if (free_threads.size() >= MaxCachedThreads) {
//_sleepq_free(thread->sleepqueue);
//_thr_release_wake_addr(thread->wake_addr);
std::destroy_at(thread);
free(thread);
thread_heap.Free(thread);
total_threads.fetch_sub(1);
} else {
std::scoped_lock lk{free_thread_lock};
@ -128,12 +141,33 @@ int ThreadState::FindThread(Pthread* thread, bool include_dead) {
if (it == threads.end()) {
return POSIX_ESRCH;
}
thread->lock->lock();
thread->lock.lock();
if (!include_dead && thread->state == PthreadState::Dead) {
thread->lock->unlock();
thread->lock.unlock();
return POSIX_ESRCH;
}
return 0;
}
int ThreadState::RefAdd(Pthread* thread, bool include_dead) {
if (thread == nullptr) {
/* Invalid thread: */
return POSIX_EINVAL;
}
if (int ret = FindThread(thread, include_dead); ret != 0) {
return ret;
}
thread->refcount++;
thread->lock.unlock();
return 0;
}
void ThreadState::RefDelete(Pthread* thread) {
thread->lock.lock();
thread->refcount--;
TryCollect(thread);
}
} // namespace Libraries::Kernel

View file

@ -9,6 +9,7 @@
#include <set>
#include <stack>
#include "common/singleton.h"
#include "common/slab_heap.h"
#include "common/types.h"
namespace Libraries::Kernel {
@ -27,13 +28,15 @@ struct ThreadState {
static constexpr size_t MaxThreads = 100000;
static constexpr size_t MaxCachedThreads = 100;
explicit ThreadState();
bool GcNeeded() const noexcept {
return gc_list.size() >= GcThreshold;
}
void Collect(Pthread* curthread);
void TryCollect(Pthread* curthread, Pthread* thread);
void TryCollect(Pthread* thread);
Pthread* Alloc(Pthread* curthread);
@ -41,6 +44,10 @@ struct ThreadState {
int FindThread(Pthread* thread, bool include_dead);
int RefAdd(Pthread* thread, bool include_dead);
void RefDelete(Pthread* thread);
int CreateStack(PthreadAttr* attr);
void FreeStack(PthreadAttr* attr);
@ -61,6 +68,7 @@ struct ThreadState {
active_threads.fetch_sub(1);
}
Common::SlabHeap<Pthread> thread_heap;
std::set<Pthread*> threads;
std::list<Pthread*> free_threads;
std::list<Pthread*> gc_list;

View file

@ -4,6 +4,7 @@
#pragma once
#include <condition_variable>
#include <deque>
#include <forward_list>
#include <mutex>
#include <shared_mutex>
@ -53,16 +54,25 @@ struct PthreadMutex : public ListBaseHook {
int m_spinloops;
int m_yieldloops;
PthreadMutexProt m_protocol;
std::string name;
PthreadMutexType Type() const noexcept {
return static_cast<PthreadMutexType>(m_flags & PthreadMutexFlags::TypeMask);
}
void lock() {
Lock(nullptr);
}
void unlock() {
Unlock();
}
int SelfTryLock();
int SelfLock(const OrbisKernelTimespec* abstime);
int SelfLock(const OrbisKernelTimespec* abstime, u64 usec);
int TryLock();
int Lock(const OrbisKernelTimespec* abstime);
int Lock(const OrbisKernelTimespec* abstime, u64 usec = 0);
int Unlock();
@ -83,20 +93,38 @@ enum class PthreadCondFlags : u32 {
Busy = 4,
};
enum class ClockId : u32 {
Realtime = 0,
Virtual = 1,
Prof = 2,
Monotonic = 4,
Uptime = 5,
UptimePrecise = 7,
UptimeFast = 8,
RealtimePrecise = 9,
RealtimeFast = 10,
MonotonicPrecise = 11,
MonotonicFast = 12,
Second = 13,
ThreadCputimeID = 14,
};
struct PthreadCond {
std::condition_variable_any cond;
u32 has_user_waiters;
u32 has_kern_waiters;
u32 flags;
u32 clock_id;
ClockId clock_id;
std::string name;
int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime);
int Wait(PthreadMutexT* mutex, u64 usec);
};
using PthreadCondT = PthreadCond*;
struct PthreadCondAttr {
int c_pshared;
int c_clockid;
ClockId c_clockid;
};
using PthreadCondAttrT = PthreadCondAttr*;
@ -122,6 +150,10 @@ enum class SchedPolicy : u32 {
RoundRobin = 3,
};
struct Cpuset {
u64 bits;
};
struct PthreadAttr {
SchedPolicy sched_policy;
int sched_inherit;
@ -132,6 +164,7 @@ struct PthreadAttr {
size_t stacksize_attr;
size_t guardsize_attr;
size_t cpusetsize;
Cpuset* cpuset;
};
using PthreadAttrT = PthreadAttr*;
@ -198,17 +231,36 @@ using PthreadEntryFunc = void* (*)(void*);
constexpr u32 TidTerminated = 1;
struct WakeAddr {
WakeAddr* link;
u32 value;
char pad[12];
};
struct SleepQueue {
std::list<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 {
static constexpr u32 ThrMagic = 0xd09ba115U;
std::atomic<long> tid;
std::unique_ptr<std::mutex> lock;
std::mutex lock;
u32 cycle;
int locklevel;
int critical_count;
int sigblock;
int refcount;
void PS4_SYSV_ABI* (*start_routine)(void*);
void* PS4_SYSV_ABI (*start_routine)(void*);
void* arg;
PthreadAttr attr;
bool cancel_enable;
@ -226,8 +278,8 @@ struct Pthread {
Pthread* joiner;
ThreadFlags flags;
ThreadListFlags tlflags;
boost::intrusive::list<PthreadMutex> mutexq;
boost::intrusive::list<PthreadMutex> pp_mutexq;
std::list<PthreadMutex> mutexq;
std::list<PthreadMutex> pp_mutexq;
void* ret;
PthreadSpecificElem* specific;
int specific_data_count;
@ -240,6 +292,12 @@ struct Pthread {
int report_events;
int event_mask;
std::string name;
WakeAddr* wake_addr;
SleepQueue* sleepqueue;
void* wchan;
PthreadMutex* mutex_obj;
bool will_sleep;
bool has_user_waiters;
bool InCritical() const noexcept {
return locklevel > 0 || critical_count > 0;
@ -255,17 +313,17 @@ struct Pthread {
void Enqueue(PthreadMutex* mutex) {
mutex->m_owner = this;
mutexq.push_back(*mutex);
// mutexq.push_back(*mutex);
}
void Dequeue(PthreadMutex* mutex) {
mutex->m_owner = nullptr;
mutexq.erase(decltype(mutexq)::s_iterator_to(*mutex));
// mutexq.erase(decltype(mutexq)::s_iterator_to(*mutex));
}
};
using PthreadT = Pthread*;
extern Pthread* g_curthread;
extern thread_local Pthread* g_curthread;
void RegisterMutex(Core::Loader::SymbolsResolver* sym);
void RegisterCond(Core::Loader::SymbolsResolver* sym);

View file

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include <thread>
#include "common/assert.h"

View file

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
#include "common/assert.h"
#include "common/config.h"
#include "common/logging/log.h"
@ -95,7 +95,7 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co
LOG_ERROR(Lib_VideoOut, "Addresses are null");
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
}
VAddr ret_addr = (VAddr)__builtin_return_address(0);
auto* port = driver->GetPort(handle);
if (!port || !port->is_open) {
LOG_ERROR(Lib_VideoOut, "Invalid handle = {}", handle);

View file

@ -8,6 +8,7 @@
#include "common/logging/log.h"
#include "common/path_util.h"
#include "common/string_util.h"
#include "common/thread.h"
#include "core/aerolib/aerolib.h"
#include "core/aerolib/stubs.h"
#include "core/libraries/kernel/memory_management.h"
@ -24,10 +25,8 @@ static PS4_SYSV_ABI void ProgramExitFunc() {
}
#ifdef ARCH_X86_64
static PS4_SYSV_ABI void* RunMainEntry [[noreturn]] (void* arg) {
EntryParams* params = (EntryParams*)arg;
static PS4_SYSV_ABI void* RunMainEntry [[noreturn]] (EntryParams* params) {
// Start shared library modules
params->linker->LoadSharedLibraries();
asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes
"subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned
@ -43,7 +42,7 @@ static PS4_SYSV_ABI void* RunMainEntry [[noreturn]] (void* arg) {
"jmp *%0\n" // can't use call here, as that would mangle the prepared stack.
// there's no coming back
:
: "r"(params->entry_addr), "r"(params), "r"(params->exit_func)
: "r"(params->entry_addr), "r"(params), "r"(ProgramExitFunc)
: "rax", "rsi", "rdi");
UNREACHABLE();
}
@ -82,14 +81,17 @@ void Linker::Execute() {
}
}
main_thread.Run([this, module](std::stop_token) {
Common::SetCurrentThreadName("GAME_MainThread");
LoadSharedLibraries();
// Start main module.
EntryParams* params = new EntryParams;
params->argc = 1;
params->argv[0] = "eboot.bin";
params->entry_addr = module->GetEntryAddress();
params->exit_func = ProgramExitFunc;
params->linker = this;
Libraries::Kernel::LaunchThread(RunMainEntry, params, "GAME_MainThread");
EntryParams params{};
params.argc = 1;
params.argv[0] = "eboot.bin";
params.entry_addr = module->GetEntryAddress();
RunMainEntry(&params);
});
}
s32 Linker::LoadModule(const std::filesystem::path& elf_name, bool is_dynamic) {
@ -122,10 +124,9 @@ Module* Linker::FindByAddress(VAddr address) {
}
void Linker::Relocate(Module* module) {
module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool isJmpRel) {
const u32 bit_idx =
(isJmpRel ? module->dynamic_info.relocation_table_size / sizeof(elf_relocation) : 0) +
i;
module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool is_jmp_rel) {
const u32 num_relocs = module->dynamic_info.relocation_table_size / sizeof(elf_relocation);
const u32 bit_idx = (is_jmp_rel ? num_relocs : 0) + i;
if (module->TestRelaBit(bit_idx)) {
return;
}
@ -133,7 +134,7 @@ void Linker::Relocate(Module* module) {
auto symbol = rel->GetSymbol();
auto addend = rel->rel_addend;
auto* symbol_table = module->dynamic_info.symbol_table;
auto* namesTlb = module->dynamic_info.str_table;
auto* names_tlb = module->dynamic_info.str_table;
const VAddr rel_base_virtual_addr = module->GetBaseAddress();
const VAddr rel_virtual_addr = rel_base_virtual_addr + rel->rel_offset;
@ -189,7 +190,7 @@ void Linker::Relocate(Module* module) {
break;
case STB_GLOBAL:
case STB_WEAK: {
rel_name = namesTlb + sym.st_name;
rel_name = names_tlb + sym.st_name;
if (Resolve(rel_name, rel_sym_type, module, &symrec)) {
// Only set the rela bit if the symbol was actually resolved and not stubbed.
module->SetRelaBit(bit_idx);
@ -198,7 +199,7 @@ void Linker::Relocate(Module* module) {
break;
}
default:
ASSERT_MSG(0, "unknown bind type {}", sym_bind);
UNREACHABLE_MSG("Unknown bind type {}", sym_bind);
}
rel_is_resolved = (symbol_virtual_addr != 0);
rel_value = (rel_is_resolved ? symbol_virtual_addr + addend : 0);
@ -212,7 +213,7 @@ void Linker::Relocate(Module* module) {
if (rel_is_resolved) {
VirtualMemory::memory_patch(rel_virtual_addr, rel_value);
} else {
LOG_INFO(Core_Linker, "function not patched! {}", rel_name);
LOG_INFO(Core_Linker, "Function not patched! {}", rel_name);
}
});
}
@ -295,12 +296,13 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) {
}
u8* addr = dtv_table[module_index + 1].pointer;
Module* module = m_modules[module_index - 1].get();
// LOG_INFO(Core_Linker, "Got DTV addr {} from module index {} with name {}",
// fmt::ptr(addr), module_index, module->file.filename().string());
if (!addr) {
// Module was just loaded by above code. Allocate TLS block for it.
Module* module = m_modules[module_index - 1].get();
const u32 init_image_size = module->tls.init_image_size;
u8* dest =
reinterpret_cast<u8*>(ExecuteGuest(heap_api->heap_malloc, module->tls.image_size));
u8* dest = reinterpret_cast<u8*>(heap_api->heap_malloc(module->tls.image_size));
const u8* src = reinterpret_cast<const u8*>(module->tls.image_virtual_addr);
std::memcpy(dest, src, init_image_size);
std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size);

View file

@ -6,6 +6,7 @@
#include <algorithm>
#include <mutex>
#include <vector>
#include "core/libraries/kernel/thread_management.h"
#include "core/module.h"
namespace Core {
@ -49,8 +50,6 @@ struct EntryParams {
u32 padding;
const char* argv[3];
VAddr entry_addr;
ExitFunc exit_func;
Linker* linker;
};
struct HeapAPI {
@ -141,6 +140,7 @@ private:
const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l);
MemoryManager* memory;
Libraries::Kernel::Thread main_thread;
std::mutex mutex;
u32 dtv_generation_counter{1};
size_t static_tls_size{};

View file

@ -166,9 +166,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
tls.align = elf_pheader[i].p_align;
tls.image_virtual_addr = elf_pheader[i].p_vaddr + base_virtual_addr;
tls.image_size = GetAlignedSize(elf_pheader[i]);
if (tls.image_size != 0) {
tls.modid = ++max_tls_index;
}
LOG_INFO(Core_Linker, "TLS virtual address = {:#x}", tls.image_virtual_addr);
LOG_INFO(Core_Linker, "TLS image size = {}", tls.image_size);
break;

View file

@ -68,10 +68,9 @@ void Translator::V_READFIRSTLANE_B32(const GcnInst& inst) {
}
void Translator::V_READLANE_B32(const GcnInst& inst) {
const IR::ScalarReg dst{inst.dst[0].code};
const IR::U32 value{GetSrc(inst.src[0])};
const IR::U32 lane{GetSrc(inst.src[1])};
ir.SetScalarReg(dst, ir.ReadLane(value, lane));
SetDst(inst.dst[0], ir.ReadLane(value, lane));
}
void Translator::V_WRITELANE_B32(const GcnInst& inst) {
@ -155,7 +154,7 @@ void Translator::DS_SWIZZLE_B32(const GcnInst& inst) {
const u8 offset0 = inst.control.ds.offset0;
const u8 offset1 = inst.control.ds.offset1;
const IR::U32 src{GetSrc(inst.src[1])};
ASSERT(offset1 & 0x80);
// ASSERT(offset1 & 0x80);
const IR::U32 lane_id = ir.LaneId();
const IR::U32 id_in_group = ir.BitwiseAnd(lane_id, ir.Imm32(0b11));
const IR::U32 base = ir.ShiftLeftLogical(id_in_group, ir.Imm32(1));

View file

@ -1,6 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma clang optimize off
// Include the vulkan platform specific header
#if defined(ANDROID)
#define VK_USE_PLATFORM_ANDROID_KHR