mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-23 04:16:57 +02:00
kernel: index pthread key values per thread
This commit is contained in:
+84
-175
@@ -17,14 +17,18 @@
|
||||
#include "loader/timer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
@@ -612,35 +616,21 @@ private:
|
||||
Common::Mutex m_mutex;
|
||||
};
|
||||
|
||||
class PthreadKeys {
|
||||
public:
|
||||
PthreadKeys() { EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread()); }
|
||||
virtual ~PthreadKeys() { KYTY_NOT_IMPLEMENTED; }
|
||||
|
||||
KYTY_CLASS_NO_COPY(PthreadKeys);
|
||||
|
||||
bool Create(int* key, pthread_key_destructor_func_t destructor);
|
||||
bool Delete(int key);
|
||||
void Destruct(int thread_id);
|
||||
bool Set(int key, int thread_id, void* data);
|
||||
bool Get(int key, int thread_id, void** data);
|
||||
|
||||
private:
|
||||
struct Map {
|
||||
int thread_id = -1;
|
||||
void* data = nullptr;
|
||||
};
|
||||
|
||||
struct Key {
|
||||
bool used = false;
|
||||
pthread_key_destructor_func_t destructor = nullptr;
|
||||
std::vector<Map> specific_values;
|
||||
};
|
||||
|
||||
Common::Mutex m_mutex;
|
||||
Key m_keys[KEYS_MAX];
|
||||
struct PthreadSpecificValue {
|
||||
uint64_t generation = 0;
|
||||
void* data = nullptr;
|
||||
};
|
||||
|
||||
struct PthreadKeyState {
|
||||
// Odd generations are allocated; deletion advances to the next even generation.
|
||||
std::atomic<uint64_t> generation {0};
|
||||
pthread_key_destructor_func_t destructor = nullptr;
|
||||
};
|
||||
|
||||
static std::array<PthreadKeyState, KEYS_MAX> g_pthread_keys;
|
||||
static std::mutex g_pthread_keys_mutex;
|
||||
static thread_local std::unique_ptr<PthreadSpecificValue[]> g_pthread_specific;
|
||||
|
||||
class PthreadPool {
|
||||
public:
|
||||
PthreadPool() { EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread()); }
|
||||
@@ -676,8 +666,6 @@ public:
|
||||
void SetPthreadPool(PthreadPool* pool) { m_pthread_pool = pool; }
|
||||
PthreadStaticObjects* GetPthreadStaticObjects() { return m_pthread_static_objects; }
|
||||
void SetPthreadStaticObjects(PthreadStaticObjects* objs) { m_pthread_static_objects = objs; }
|
||||
PthreadKeys* GetPthreadKeys() { return m_pthread_keys; }
|
||||
void SetPthreadKeys(PthreadKeys* keys) { m_pthread_keys = keys; }
|
||||
|
||||
[[nodiscard]] thread_dtors_func_t GetThreadDtors() const { return m_thread_dtors; }
|
||||
void SetThreadDtors(thread_dtors_func_t dtors) { m_thread_dtors = dtors; }
|
||||
@@ -690,7 +678,6 @@ private:
|
||||
PthreadAttr m_default_attr = nullptr;
|
||||
PthreadPool* m_pthread_pool = nullptr;
|
||||
PthreadStaticObjects* m_pthread_static_objects = nullptr;
|
||||
PthreadKeys* m_pthread_keys = nullptr;
|
||||
|
||||
std::atomic<thread_dtors_func_t> m_thread_dtors = nullptr;
|
||||
};
|
||||
@@ -1140,7 +1127,6 @@ void Initialize() {
|
||||
|
||||
g_pthread_context->SetPthreadStaticObjects(new PthreadStaticObjects);
|
||||
g_pthread_context->SetPthreadPool(new PthreadPool);
|
||||
g_pthread_context->SetPthreadKeys(new PthreadKeys);
|
||||
Common::CondVar::SetWaitPollCallback(KernelDispatchPendingSignalForCurrentThread);
|
||||
|
||||
PthreadMutexattr default_mutexattr = nullptr;
|
||||
@@ -1600,119 +1586,37 @@ void PthreadPool::FreeDetachedThreads() {
|
||||
}
|
||||
}
|
||||
|
||||
bool PthreadKeys::Create(int* key, pthread_key_destructor_func_t destructor) {
|
||||
EXIT_IF(key == nullptr);
|
||||
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
for (int index = 0; index < KEYS_MAX; index++) {
|
||||
if (!m_keys[index].used) {
|
||||
*key = index;
|
||||
m_keys[index].used = true;
|
||||
m_keys[index].destructor = destructor;
|
||||
m_keys[index].specific_values.clear();
|
||||
return true;
|
||||
}
|
||||
static void DestructPthreadSpecific() {
|
||||
if (g_pthread_specific == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PthreadKeys::Delete(int key) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
if (key < 0 || key >= KEYS_MAX || !m_keys[key].used) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_keys[key].used = false;
|
||||
m_keys[key].destructor = nullptr;
|
||||
m_keys[key].specific_values.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PthreadKeys::Destruct(int thread_id) {
|
||||
struct CallInfo {
|
||||
pthread_key_destructor_func_t destructor;
|
||||
void* data;
|
||||
};
|
||||
|
||||
std::unique_lock lock(g_pthread_keys_mutex);
|
||||
for (int iter = 0; iter < DESTRUCTOR_ITERATIONS; iter++) {
|
||||
std::vector<CallInfo> delete_list;
|
||||
|
||||
{
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
for (auto& key: m_keys) {
|
||||
if (key.used && key.destructor != nullptr) {
|
||||
for (auto& v: key.specific_values) {
|
||||
if (v.thread_id == thread_id && v.data != nullptr) {
|
||||
delete_list.push_back(CallInfo {key.destructor, v.data});
|
||||
v.data = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool called = false;
|
||||
for (int index = 0; index < KEYS_MAX; index++) {
|
||||
const auto value = std::exchange(g_pthread_specific[index], {});
|
||||
if (value.data == nullptr) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_list.empty()) {
|
||||
return;
|
||||
const auto& entry = g_pthread_keys[index];
|
||||
const auto generation = entry.generation.load(std::memory_order_relaxed);
|
||||
if ((generation & 1u) == 0 || generation != value.generation ||
|
||||
entry.destructor == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const auto destructor = entry.destructor;
|
||||
lock.unlock();
|
||||
destructor(value.data);
|
||||
lock.lock();
|
||||
called = true;
|
||||
}
|
||||
|
||||
for (auto& d: delete_list) {
|
||||
d.destructor(d.data);
|
||||
if (!called) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
for (auto& key: m_keys) {
|
||||
auto& values = key.specific_values;
|
||||
values.erase(std::remove_if(values.begin(), values.end(),
|
||||
[thread_id](const Map& v) { return v.thread_id == thread_id; }),
|
||||
values.end());
|
||||
}
|
||||
}
|
||||
|
||||
bool PthreadKeys::Set(int key, int thread_id, void* data) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
if (key < 0 || key >= KEYS_MAX || !m_keys[key].used) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& v: m_keys[key].specific_values) {
|
||||
if (v.thread_id == thread_id) {
|
||||
v.data = data;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
m_keys[key].specific_values.push_back(Map({thread_id, data}));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PthreadKeys::Get(int key, int thread_id, void** data) {
|
||||
EXIT_IF(data == nullptr);
|
||||
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
if (key < 0 || key >= KEYS_MAX || !m_keys[key].used) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& v: m_keys[key].specific_values) {
|
||||
if (v.thread_id == thread_id) {
|
||||
*data = v.data;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*data = nullptr;
|
||||
|
||||
return true;
|
||||
g_pthread_specific.reset();
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadMutexattrInit(PthreadMutexattr* attr) {
|
||||
@@ -3380,7 +3284,7 @@ static void CleanupThread(void* arg) {
|
||||
thread_dtors();
|
||||
}
|
||||
|
||||
g_pthread_context->GetPthreadKeys()->Destruct(thread->unique_id);
|
||||
DestructPthreadSpecific();
|
||||
|
||||
auto* rt = Common::Singleton<Loader::RuntimeLinker>::Instance();
|
||||
rt->DeleteTlss(thread->unique_id);
|
||||
@@ -4096,62 +4000,71 @@ int KYTY_SYSV_ABI KernelNanosleep(const KernelTimespec* rqtp, KernelTimespec* rm
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadKeyCreate(PthreadKey* key, pthread_key_destructor_func_t destructor) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (key == nullptr) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
if (!g_pthread_context->GetPthreadKeys()->Create(key, destructor)) {
|
||||
return KERNEL_ERROR_EAGAIN;
|
||||
std::lock_guard lock(g_pthread_keys_mutex);
|
||||
for (int index = 0; index < KEYS_MAX; index++) {
|
||||
auto& entry = g_pthread_keys[index];
|
||||
const auto generation = entry.generation.load(std::memory_order_relaxed);
|
||||
if ((generation & 1u) == 0) {
|
||||
entry.destructor = destructor;
|
||||
entry.generation.store(generation + 1, std::memory_order_release);
|
||||
*key = index;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
LOGF("\t destructor = %016" PRIx64 "\n"
|
||||
"\t key = %d\n",
|
||||
reinterpret_cast<uint64_t>(destructor), *key);
|
||||
|
||||
return OK;
|
||||
return KERNEL_ERROR_EAGAIN;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadKeyDelete(PthreadKey key) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t key = %d\n", key);
|
||||
|
||||
if (!g_pthread_context->GetPthreadKeys()->Delete(key)) {
|
||||
if (key < 0 || key >= KEYS_MAX) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
std::lock_guard lock(g_pthread_keys_mutex);
|
||||
auto& entry = g_pthread_keys[key];
|
||||
const auto generation = entry.generation.load(std::memory_order_relaxed);
|
||||
if ((generation & 1u) == 0) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
entry.generation.store(generation + 1, std::memory_order_release);
|
||||
entry.destructor = nullptr;
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadSetspecific(PthreadKey key, void* value) {
|
||||
// PRINT_NAME();
|
||||
|
||||
int thread_id = Common::Thread::GetThreadIdUnique();
|
||||
|
||||
LOGF("\t key = %d\n"
|
||||
"\t thread_id = %d\n"
|
||||
"\t value = %016" PRIx64 "\n",
|
||||
key, thread_id, reinterpret_cast<uint64_t>(value));
|
||||
|
||||
if (!g_pthread_context->GetPthreadKeys()->Set(key, thread_id, value)) {
|
||||
if (key < 0 || key >= KEYS_MAX) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
const auto generation = g_pthread_keys[key].generation.load(std::memory_order_acquire);
|
||||
if ((generation & 1u) == 0) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (g_pthread_specific == nullptr) {
|
||||
if (value == nullptr) {
|
||||
return OK;
|
||||
}
|
||||
g_pthread_specific.reset(new (std::nothrow) PthreadSpecificValue[KEYS_MAX] {});
|
||||
if (g_pthread_specific == nullptr) {
|
||||
return KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
}
|
||||
g_pthread_specific[key] = {generation, value};
|
||||
return OK;
|
||||
}
|
||||
|
||||
void* KYTY_SYSV_ABI PthreadGetspecific(PthreadKey key) {
|
||||
int thread_id = Common::Thread::GetThreadIdUnique();
|
||||
|
||||
void* value = nullptr;
|
||||
|
||||
if (!g_pthread_context->GetPthreadKeys()->Get(key, thread_id, &value)) {
|
||||
if (key < 0 || key >= KEYS_MAX || g_pthread_specific == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return value;
|
||||
const auto generation = g_pthread_keys[key].generation.load(std::memory_order_acquire);
|
||||
const auto& value = g_pthread_specific[key];
|
||||
if ((generation & 1u) != 0 && generation == value.generation) {
|
||||
return value.data;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace LibKernel
|
||||
@@ -4581,15 +4494,11 @@ int KYTY_SYSV_ABI pthread_key_delete(LibKernel::PthreadKey key) {
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI pthread_setspecific(LibKernel::PthreadKey key, void* value) {
|
||||
PRINT_NAME();
|
||||
|
||||
return POSIX_PTHREAD_CALL(LibKernel::PthreadSetspecific(key, value));
|
||||
}
|
||||
|
||||
void* KYTY_SYSV_ABI pthread_getspecific(LibKernel::PthreadKey key) {
|
||||
PRINT_NAME();
|
||||
|
||||
return (LibKernel::PthreadGetspecific(key));
|
||||
return LibKernel::PthreadGetspecific(key);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI pthread_mutex_destroy(LibKernel::PthreadMutex* mutex) {
|
||||
|
||||
Reference in New Issue
Block a user