// SPDX-License-Identifier: MPL-2.0 // Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once #include namespace skyline { /** * @brief A thread-local RAII-bound wrapper class which unlike `thread_local` doesn't require the member to be static * @note Caller must ensure any arguments passed into the constructor remain valid throughout its lifetime * @note Caller must ensure the destructors of the object doesn't have any thread-local dependencies as they might be called from another thread * @note RAII-bound means that *all* thread-local instances of the object will be destroyed after this class is destroyed but can also be destroyed when a thread owning an instance dies */ template> class ThreadLocal; template class ThreadLocal { private: pthread_key_t key; std::function constructor; public: template ThreadLocal(Args &&... args) : constructor([args...]() { return new Type(args...); }) { if (int result = pthread_key_create(&key, nullptr)) throw exception("Cannot create pthread_key: {}", strerror(result)); } Type *operator->() { auto pointer{pthread_getspecific(key)}; if (pointer) return static_cast(pointer); Type *object{constructor(*this)}; if (int result = pthread_setspecific(key, object)) throw exception("Cannot set pthread_key to constructed type: {}", strerror(result)); return object; } Type &operator*() { return *operator->(); } ~ThreadLocal() { pthread_key_delete(key); } }; template class ThreadLocal { private: struct IntrustiveTypeNode { Type object; ThreadLocal &threadLocal; IntrustiveTypeNode *next{}; template IntrustiveTypeNode(ThreadLocal &threadLocal, Args &&... args) : object(std::forward(args)...), threadLocal(threadLocal) {} ~IntrustiveTypeNode() { auto current{threadLocal.list.load(std::memory_order_acquire)}; while (current == this) if (threadLocal.list.compare_exchange_strong(current, next, std::memory_order_release, std::memory_order_consume)) return; while (current) { if (current->next == this) { current->next = next; return; } current = current->next; } } }; pthread_key_t key; std::function constructor; std::atomic list{}; //!< An atomic instrusive linked list of all instances of the object to call non-trivial destructors for the objects public: template ThreadLocal(Args &&... args) : constructor([args...](ThreadLocal &threadLocal) { return new IntrustiveTypeNode(threadLocal, args...); }) { auto destructor{[](void *object) { static_cast(object)->~IntrustiveTypeNode(); }}; if (int result = pthread_key_create(&key, destructor)) throw exception("Cannot create pthread_key: {}", strerror(result)); } Type *operator->() { auto pointer{pthread_getspecific(key)}; if (pointer) return &static_cast(pointer)->object; IntrustiveTypeNode *node{constructor(*this)}; if (int result = pthread_setspecific(key, node)) throw exception("Cannot set pthread_key to constructed type: {}", strerror(result)); auto next{list.load(std::memory_order_acquire)}; do { node->next = next; } while (!list.compare_exchange_strong(next, node, std::memory_order_release, std::memory_order_consume)); return &node->object; } Type &operator*() { return *operator->(); } ~ThreadLocal() { auto current{list.exchange(nullptr, std::memory_order_acquire)}; while (current) { current->object.~Type(); current = current->next; } pthread_key_delete(key); } }; }