diff --git a/src/kernel/pthread.cpp b/src/kernel/pthread.cpp index ad22ca864..5f5b071ce 100644 --- a/src/kernel/pthread.cpp +++ b/src/kernel/pthread.cpp @@ -17,14 +17,18 @@ #include "loader/timer.h" #include +#include #include #include #include #include #include #include +#include #include +#include #include +#include #include #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 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 generation {0}; + pthread_key_destructor_func_t destructor = nullptr; +}; + +static std::array g_pthread_keys; +static std::mutex g_pthread_keys_mutex; +static thread_local std::unique_ptr 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 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 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::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(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(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) {