From 4ce371f93683de7070d2f7ea76196b842542095e Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Fri, 12 Jul 2019 16:45:04 +0100 Subject: [PATCH] Tidy TLS implementation. --- src/mem/threadalloc.h | 206 +++++++++--------------------------------- 1 file changed, 43 insertions(+), 163 deletions(-) diff --git a/src/mem/threadalloc.h b/src/mem/threadalloc.h index 28bfafc..6684e91 100644 --- a/src/mem/threadalloc.h +++ b/src/mem/threadalloc.h @@ -7,10 +7,6 @@ #error At most one out of SNMALLOC_USE_THREAD_CLEANUP and SNMALLOC_USE_THREAD_DESTRUCTOR may be defined. #endif -#if !defined(_WIN32) && !defined(FreeBSD_KERNEL) -# include "pthread.h" -#endif - namespace snmalloc { extern "C" void _malloc_thread_cleanup(void); @@ -95,6 +91,20 @@ namespace snmalloc } static void register_cleanup() {} }; + + /** + * Helper class to execute a specified function on destruction. + */ + template + class OnDestruct + { + public: + ~OnDestruct() + { + f(); + } + }; + /** * Version of the `ThreadAlloc` interface that uses C++ `thread_local` * destructors for cleanup. If a per-thread allocator is used during the @@ -109,29 +119,35 @@ namespace snmalloc */ class ThreadAllocThreadDestructor { - /** - * A pointer to the allocator owned by this thread. - */ - Alloc* alloc; + template + friend class OnDestruct; /** - * Constructor. Acquires a new allocator and associates it with this - * object. There should be only one instance of this class per thread. + * Releases the allocator owned by this thread. */ - ThreadAllocThreadDestructor() : alloc(&GlobalPlaceHolder) {} - - /** - * Destructor. Releases the allocator owned by this thread. - */ - ~ThreadAllocThreadDestructor() + static void inner_release() { - if (alloc != &GlobalPlaceHolder) + if (get() != &GlobalPlaceHolder) { - current_alloc_pool()->release(alloc); - alloc = &GlobalPlaceHolder; + current_alloc_pool()->release(get()); + get() = &GlobalPlaceHolder; } } +#ifdef USE_SNMALLOC_STATS + static void print_stats() + { + Stats s; + current_alloc_pool()->aggregate_stats(s); + s.print(std::cout); + } + + static int atexit_print_stats() + { + return atexit(print_stats); + } +#endif + public: /** * Public interface, returns the allocator for this thread, constructing @@ -139,156 +155,20 @@ namespace snmalloc */ static inline Alloc*& get() { - static thread_local ThreadAllocThreadDestructor per_thread; - return per_thread.alloc; - } - static void register_cleanup() {} - }; - // When targeting the FreeBSD kernel, the pthread header exists, but the - // pthread symbols do not, so don't compile this because it will fail to - // link. -#ifndef FreeBSD_KERNEL - /** - * Version of the `ThreadAlloc` interface that uses thread-specific (POSIX - * threads) or Fiber-local (Windows) storage with an explicit destructor. - * Neither of the underlying mechanisms guarantee ordering, so the cleanup - * may be called before other cleanup functions or thread-local destructors. - * - * This implementation is used when using snmalloc as a library - * implementation of malloc, but not embedding it in C standard library. - * Using this implementation removes the dependency on a C++ runtime library. - */ - class ThreadAllocExplicitTLSCleanup - { - /** - * Cleanup function. This is registered with the operating system's - * thread- or fibre-local storage subsystem to clean up the per-thread - * allocator. - */ - static inline void -# ifdef _WIN32 - NTAPI -# endif - thread_alloc_release(void* p) - { - // Keep pthreads happy - void** pp = reinterpret_cast(p); - *pp = nullptr; - // Actually destroy the allocator and reset TLS - Alloc*& alloc = ThreadAllocExplicitTLSCleanup::get(); - if (alloc != &GlobalPlaceHolder) - { - current_alloc_pool()->release(alloc); - alloc = &GlobalPlaceHolder; - } + static thread_local Alloc* alloc = &GlobalPlaceHolder; + return alloc; } -# ifdef _WIN32 - /** - * Key type used to identify fibre-local storage. - */ - using tls_key_t = DWORD; - - /** - * On Windows, construct a new fibre-local storage allocation. This - * function must not be called more than once. - */ - static inline tls_key_t tls_key_create() noexcept - { - return FlsAlloc(thread_alloc_release); - } - - /** - * On Windows, store a pointer to a `thread_local` pointer to an allocator - * into fibre-local storage. This function takes a pointer to the - * `thread_local` allocation, rather than to the pointee, so that the - * cleanup function can zero the pointer. - * - * This must not be called until after `tls_key_create` has returned. - */ - static inline void tls_set_value(tls_key_t key, Alloc* value) - { - FlsSetValue(key, value); - } -# else - /** - * Key type used for thread-specific storage. - */ - using tls_key_t = pthread_key_t; - - /** - * On POSIX systems, construct a new thread-specific storage allocation. - * This function must not be called more than once. - */ - static inline tls_key_t tls_key_create() noexcept - { - tls_key_t key; - pthread_key_create(&key, thread_alloc_release); - return key; - } - - /** - * On POSIX systems, store a pointer to a `thread_local` pointer to an - * allocator into fibre-local storage. This function takes a pointer to - * the `thread_local` allocation, rather than to the pointee, so that the - * cleanup function can zero the pointer. - * - * This must not be called until after `tls_key_create` has returned. - */ - static inline void tls_set_value(tls_key_t key, Alloc* value) - { - pthread_setspecific(key, value); - } -# endif - - /** - * Private accessor to the per thread allocator - * Provides no checking for initialization - */ - static SNMALLOC_FAST_PATH Alloc*& inner_get() - { - static thread_local Alloc* per_thread = &GlobalPlaceHolder; - return per_thread; - } - -# ifdef USE_SNMALLOC_STATS - static void print_stats() - { - Stats s; - current_alloc_pool()->aggregate_stats(s); - s.print(std::cout); - } -# endif - - public: - /** - * Public interface, returns the allocator for the current thread, - * constructing it if necessary. - */ - static SNMALLOC_FAST_PATH Alloc*& get() - { - return inner_get(); - } static void register_cleanup() { - // Register the allocator destructor. - bool first = false; - tls_key_t key = Singleton::get(&first); - // Associate the new allocator with the destructor. - tls_set_value(key, &GlobalPlaceHolder); + static thread_local OnDestruct + tidier; -# ifdef USE_SNMALLOC_STATS - // Allocator is up and running now, safe to call atexit. - if (first) - { - atexit(print_stats); - } -# else - UNUSED(first); -# endif +#ifdef USE_SNMALLOC_STATS + Singleton::get(); +#endif } }; -#endif #ifdef SNMALLOC_USE_THREAD_CLEANUP /** @@ -305,7 +185,7 @@ namespace snmalloc #elif defined(SNMALLOC_EXTERNAL_THREAD_ALLOC) using ThreadAlloc = ThreadAllocUntypedWrapper; #else - using ThreadAlloc = ThreadAllocExplicitTLSCleanup; + using ThreadAlloc = ThreadAllocThreadDestructor; #endif /**