diff --git a/CMakeLists.txt b/CMakeLists.txt index 573532d..790c32a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -553,6 +553,7 @@ set(LOGGING_INCLUDES src/base/logging.h src/base/commandlineflags.h src/base/basictypes.h + src/base/threading.h src/base/dynamic_annotations.h) set(liblogging_la_SOURCES src/base/logging.cc src/base/generic_writer.cc @@ -565,6 +566,7 @@ set(SYSINFO_INCLUDES src/getenv_safe.h src/base/logging.h src/base/commandlineflags.h + src/base/threading.h src/base/basictypes.h) set(libsysinfo_la_SOURCES src/base/sysinfo.cc ${SYSINFO_INCLUDES}) @@ -634,6 +636,7 @@ endif() if(BUILD_TESTING) set(LOW_LEVEL_ALLOC_UNITTEST_INCLUDES src/base/low_level_alloc.h + src/base/threading.h src/base/basictypes.h src/gperftools/malloc_hook.h src/gperftools/malloc_hook_c.h @@ -735,6 +738,7 @@ set(S_TCMALLOC_MINIMAL_INCLUDES src/common.h ${SPINLOCK_INCLUDES} src/tcmalloc_guard.h src/base/commandlineflags.h + src/base/threading.h src/base/basictypes.h src/safe_strerror.h src/pagemap.h @@ -1322,6 +1326,7 @@ if(GPERFTOOLS_BUILD_CPU_PROFILER) set(S_CPU_PROFILER_INCLUDES src/profiledata.h src/profile-handler.h src/getpc.h + src/base/threading.h src/base/basictypes.h src/base/commandlineflags.h src/base/googleinit.h @@ -1369,6 +1374,7 @@ if(GPERFTOOLS_BUILD_CPU_PROFILER) src/profiledata.h src/base/commandlineflags.h src/base/logging.h + src/base/threading.h src/base/basictypes.h) target_link_libraries(profiledata_unittest ${LIBPROFILER}) add_test(profiledata_unittest profiledata_unittest) diff --git a/Makefile.am b/Makefile.am index 9653d8e..6f3e20e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -154,6 +154,7 @@ dist_doc_DATA += docs/index.html docs/designstyle.css # This is a 'convenience library' -- it's not actually installed or anything LOGGING_INCLUDES = src/base/logging.h \ src/base/commandlineflags.h \ + src/base/threading.h \ src/base/basictypes.h \ src/base/generic_writer.h \ src/base/dynamic_annotations.h @@ -166,6 +167,7 @@ SYSINFO_INCLUDES = src/base/sysinfo.h \ src/getenv_safe.h \ src/base/logging.h \ src/base/commandlineflags.h \ + src/base/threading.h \ src/base/basictypes.h noinst_LTLIBRARIES += libsysinfo.la libsysinfo_la_SOURCES = src/base/sysinfo.cc \ @@ -262,6 +264,7 @@ WINDOWS_PROJECTS += vsprojects/low_level_alloc_unittest/low_level_alloc_unittest TESTS += low_level_alloc_unittest LOW_LEVEL_ALLOC_UNITTEST_INCLUDES = src/base/low_level_alloc.h \ src/base/basictypes.h \ + src/base/threading.h \ src/gperftools/malloc_hook.h \ src/gperftools/malloc_hook_c.h \ src/malloc_hook-inl.h \ @@ -386,6 +389,7 @@ S_TCMALLOC_MINIMAL_INCLUDES = src/common.h \ $(SPINLOCK_INCLUDES) \ src/tcmalloc_guard.h \ src/base/commandlineflags.h \ + src/base/threading.h \ src/base/basictypes.h \ src/safe_strerror.h \ src/pagemap.h \ @@ -1246,6 +1250,7 @@ S_CPU_PROFILER_INCLUDES = src/profiledata.h \ src/getpc.h \ src/getpc-inl.h \ src/base/basictypes.h \ + src/base/threading.h \ src/base/commandlineflags.h \ src/base/googleinit.h \ src/base/logging.h \ @@ -1283,6 +1288,7 @@ profiledata_unittest_SOURCES = src/tests/profiledata_unittest.cc \ src/profiledata.h \ src/base/commandlineflags.h \ src/base/logging.h \ + src/base/threading.h \ src/base/basictypes.h profiledata_unittest_LDADD = libprofiler.la diff --git a/cmake/config.h.in b/cmake/config.h.in index 6b61736..46f990d 100644 --- a/cmake/config.h.in +++ b/cmake/config.h.in @@ -112,10 +112,6 @@ /* Define if you have POSIX threads libraries and header files. */ #cmakedefine HAVE_PTHREAD -/* defined to 1 if pthread symbols are exposed even without include pthread.h - */ -#cmakedefine HAVE_PTHREAD_DESPITE_ASKING_FOR - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PWD_H diff --git a/src/base/basictypes.h b/src/base/basictypes.h index ac2d9d6..32a25eb 100644 --- a/src/base/basictypes.h +++ b/src/base/basictypes.h @@ -101,17 +101,6 @@ const int64 kint64min = ( (((uint64) kint32min) << 32) | 0 ); #define PRIxPTR "lx" #endif -// Also allow for printing of a pthread_t. -#define GPRIuPTHREAD "lu" -#define GPRIxPTHREAD "lx" -#if defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) -#define PRINTABLE_PTHREAD(pthreadt) reinterpret_cast(pthreadt) -#elif defined(__QNXNTO__) -#define PRINTABLE_PTHREAD(pthreadt) static_cast(pthreadt) -#else -#define PRINTABLE_PTHREAD(pthreadt) pthreadt -#endif - #if defined(__GNUC__) #define PREDICT_TRUE(x) __builtin_expect(!!(x), 1) #define PREDICT_FALSE(x) __builtin_expect(!!(x), 0) diff --git a/src/base/threading.h b/src/base/threading.h new file mode 100644 index 0000000..cec3330 --- /dev/null +++ b/src/base/threading.h @@ -0,0 +1,127 @@ +// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- +// Copyright (c) 2024, gperftools Contributors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef THREADING_H_ +#define THREADING_H_ + +#include +#include "base/basictypes.h" + +// Also allow for printing of a PerftoolsThreadID. +#define GPRIuPTHREAD "lu" +#define GPRIxPTHREAD "lx" + +#if defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) +# define PRINTABLE_PTHREAD(pthreadt) reinterpret_cast(pthreadt) +#elif defined(__QNXNTO__) +# define PRINTABLE_PTHREAD(pthreadt) static_cast(pthreadt) +#else +# define PRINTABLE_PTHREAD(pthreadt) pthreadt +#endif + +#ifdef _WIN32 // Should cover both toolchains on Windows - MSVC & MINGW + +using PerftoolsThreadID = DWORD; +using PerftoolsTlsKey = DWORD; + +inline PerftoolsThreadID PerftoolsGetThreadID() { + return GetCurrentThreadId(); +} +inline int PerftoolsThreadIDEquals(PerftoolsThreadID left, PerftoolsThreadID right) { + return left == right; +} + +extern "C" PerftoolsTlsKey PthreadKeyCreate(void (*destr_fn)(void*)); /* port.cc */ + +inline int PerftoolsCreateTlsKey(PerftoolsTlsKey *pkey, void (*destructor)(void*)) { + PerftoolsTlsKey key = PthreadKeyCreate(destructor); + + if (key != TLS_OUT_OF_INDEXES) { + *(pkey) = key; + return 0; + } + else { + return GetLastError(); + } +} +inline void* PerftoolsGetTlsValue(PerftoolsTlsKey key) { + DWORD err = GetLastError(); + void* rv = TlsGetValue(key); + + if (err) SetLastError(err); + return rv; +} +inline int PerftoolsSetTlsValue(PerftoolsTlsKey key, const void* value) { + if (TlsSetValue(key, (LPVOID)value)) { + return 0; + } + else { + return GetLastError(); + } +} + +inline void PerftoolsYield() { + Sleep(0); +} + +#elif defined(HAVE_PTHREAD) + +# include +# include + +using PerftoolsThreadID = pthread_t; +using PerftoolsTlsKey = pthread_key_t; + +inline PerftoolsThreadID PerftoolsGetThreadID() { + return pthread_self(); +} +inline int PerftoolsThreadIDEquals(PerftoolsThreadID left, PerftoolsThreadID right) { + return pthread_equal(left, right); +} + +inline int PerftoolsCreateTlsKey(PerftoolsTlsKey *pkey, void (*destructor)(void*)) { + return pthread_key_create(pkey, destructor); +} +inline void* PerftoolsGetTlsValue(PerftoolsTlsKey key) { + return pthread_getspecific(key); +} +inline int PerftoolsSetTlsValue(PerftoolsTlsKey key, const void* value) { + return pthread_setspecific(key, value); +} + +inline ATTRIBUTE_ALWAYS_INLINE void PerftoolsYield() { + sched_yield(); +} + +#else +# error "Threading support is now mandatory" +#endif + +#endif // THREADING_H_ \ No newline at end of file diff --git a/src/debugallocation.cc b/src/debugallocation.cc index aa4c472..a903c6c 100644 --- a/src/debugallocation.cc +++ b/src/debugallocation.cc @@ -48,9 +48,6 @@ # include # endif #endif -#ifdef HAVE_PTHREAD -#include -#endif #include #include #include @@ -69,6 +66,7 @@ #include "addressmap-inl.h" #include "base/commandlineflags.h" +#include "base/threading.h" #include "base/googleinit.h" #include "base/logging.h" #include "base/spinlock.h" @@ -206,7 +204,7 @@ struct MallocBlockQueueEntry { deleter_pcs, sizeof(deleter_pcs) / sizeof(deleter_pcs[0]), 4); - deleter_threadid = pthread_self(); + deleter_threadid = PerftoolsGetThreadID(); } else { num_deleter_pcs = 0; // Zero is an illegal pthread id by my reading of the pthread @@ -224,7 +222,7 @@ struct MallocBlockQueueEntry { // overhead under the LP64 data model.) void* deleter_pcs[16]; int num_deleter_pcs; - pthread_t deleter_threadid; + PerftoolsThreadID deleter_threadid; }; class MallocBlock { @@ -1009,7 +1007,7 @@ static SpinLock malloc_trace_lock(SpinLock::LINKER_INITIALIZED); if (FLAGS_malloctrace) { \ SpinLockHolder l(&malloc_trace_lock); \ TracePrintf(TraceFd(), "%s\t%zu\t%p\t%" GPRIuPTHREAD, \ - name, size, addr, PRINTABLE_PTHREAD(pthread_self())); \ + name, size, addr, PRINTABLE_PTHREAD(PerftoolsGetThreadID())); \ TraceStack(); \ TracePrintf(TraceFd(), "\n"); \ } \ diff --git a/src/heap-checker.cc b/src/heap-checker.cc index 8b88c10..23c3b29 100644 --- a/src/heap-checker.cc +++ b/src/heap-checker.cc @@ -530,17 +530,17 @@ inline void set_thread_disable_counter(int value) { #else // #ifdef HAVE_TLS -static pthread_key_t thread_disable_counter_key; +static PerftoolsTlsKey thread_disable_counter_key; static int main_thread_counter; // storage for use before main() static bool use_main_thread_counter = true; // TODO(csilvers): this is called from NewHook, in the middle of malloc(). -// If perftools_pthread_getspecific calls malloc, that will lead to an +// If PerftoolsGetTlsValue calls malloc, that will lead to an // infinite loop. I don't know how to fix that, so I hope it never happens! inline int get_thread_disable_counter() { if (use_main_thread_counter) // means we're running really early return main_thread_counter; - void* p = perftools_pthread_getspecific(thread_disable_counter_key); + void* p = PerftoolsGetTlsValue(thread_disable_counter_key); return (intptr_t)p; // kinda evil: store the counter directly in the void* } @@ -553,10 +553,10 @@ inline void set_thread_disable_counter(int value) { // kinda evil: store the counter directly in the void* void* p = (void*)pointer_sized_value; // NOTE: this may call malloc, which will call NewHook which will call - // get_thread_disable_counter() which will call pthread_getspecific(). I + // get_thread_disable_counter() which will call PerftoolsGetTlsValue(). I // don't know if anything bad can happen if we call getspecific() in the // middle of a setspecific() call. It seems to work ok in practice... - perftools_pthread_setspecific(thread_disable_counter_key, p); + PerftoolsSetTlsValue(thread_disable_counter_key, p); } // The idea here is that this initializer will run pretty late: after @@ -565,10 +565,10 @@ inline void set_thread_disable_counter(int value) { class InitThreadDisableCounter { public: InitThreadDisableCounter() { - pthread_key_create(&thread_disable_counter_key, NULL); + PerftoolsCreateTlsKey(&thread_disable_counter_key, NULL); // Set up the main thread's value, which we have a special variable for. void* p = (void*)(intptr_t)main_thread_counter; // store the counter directly - perftools_pthread_setspecific(thread_disable_counter_key, p); + PerftoolsSetTlsValue(thread_disable_counter_key, p); use_main_thread_counter = false; } }; @@ -906,7 +906,7 @@ void HeapLeakChecker::DisableLibraryAllocsLocked(const char* library, if (IsLibraryNamed(library, "/libpthread") || // libpthread has a lot of small "system" leaks we don't care about. // In particular it allocates memory to store data supplied via - // pthread_setspecific (which can be the only pointer to a heap object). + // PerftoolsSetTlsValue (which can be the only pointer to a heap object). IsLibraryNamed(library, "/libdl") || // library loaders leak some "system" heap that we don't care about IsLibraryNamed(library, "/libcrypto") || diff --git a/src/memory_region_map.cc b/src/memory_region_map.cc index 5716d49..7e40990 100644 --- a/src/memory_region_map.cc +++ b/src/memory_region_map.cc @@ -108,9 +108,6 @@ #elif !defined(MAP_FAILED) #define MAP_FAILED -1 // the only thing we need from mman.h #endif -#ifdef HAVE_PTHREAD -#include // for pthread_t, pthread_self() -#endif #include #include @@ -121,6 +118,7 @@ #include "base/googleinit.h" #include "base/logging.h" #include "base/low_level_alloc.h" +#include "base/threading.h" #include "mmap_hook.h" #include @@ -138,7 +136,7 @@ SpinLock MemoryRegionMap::lock_(SpinLock::LINKER_INITIALIZED); SpinLock MemoryRegionMap::owner_lock_( // ACQUIRED_AFTER(lock_) SpinLock::LINKER_INITIALIZED); int MemoryRegionMap::recursion_count_ = 0; // GUARDED_BY(owner_lock_) -pthread_t MemoryRegionMap::lock_owner_tid_; // GUARDED_BY(owner_lock_) +PerftoolsThreadID MemoryRegionMap::lock_owner_tid_; // GUARDED_BY(owner_lock_) int64 MemoryRegionMap::map_size_ = 0; int64 MemoryRegionMap::unmap_size_ = 0; HeapProfileBucket** MemoryRegionMap::bucket_table_ = NULL; // GUARDED_BY(lock_) @@ -152,16 +150,16 @@ tcmalloc::MappingHookSpace MemoryRegionMap::mapping_hook_space_; // ========================================================================= // // Simple hook into execution of global object constructors, -// so that we do not call pthread_self() when it does not yet work. +// so that we do not call PerftoolsGetThreadID() when it does not yet work. static bool libpthread_initialized = false; REGISTER_MODULE_INITIALIZER(libpthread_initialized_setter, libpthread_initialized = true); -static inline bool current_thread_is(pthread_t should_be) { +static inline bool current_thread_is(PerftoolsThreadID should_be) { // Before main() runs, there's only one thread, so we're always that thread if (!libpthread_initialized) return true; // this starts working only sometime well into global constructor execution: - return pthread_equal(pthread_self(), should_be); + return PerftoolsThreadIDEquals(PerftoolsGetThreadID(), should_be); } // ========================================================================= // @@ -283,7 +281,7 @@ bool MemoryRegionMap::IsRecordingLocked() { // both lock_ and owner_lock_ are held. They may be read under // just owner_lock_. // * At entry and exit of Lock() and Unlock(), the current thread -// owns lock_ iff pthread_equal(lock_owner_tid_, pthread_self()) +// owns lock_ iff PerftoolsThreadIDEquals(lock_owner_tid_, PerftoolsGetThreadID()) // && recursion_count_ > 0. void MemoryRegionMap::Lock() NO_THREAD_SAFETY_ANALYSIS { { @@ -302,7 +300,7 @@ void MemoryRegionMap::Lock() NO_THREAD_SAFETY_ANALYSIS { RAW_CHECK(recursion_count_ == 0, "Last Unlock didn't reset recursion_count_"); if (libpthread_initialized) - lock_owner_tid_ = pthread_self(); + lock_owner_tid_ = PerftoolsGetThreadID(); recursion_count_ = 1; } } diff --git a/src/memory_region_map.h b/src/memory_region_map.h index f773105..dbb2219 100644 --- a/src/memory_region_map.h +++ b/src/memory_region_map.h @@ -37,13 +37,11 @@ #include -#ifdef HAVE_PTHREAD -#include -#endif #include #include #include "base/stl_allocator.h" #include "base/spinlock.h" +#include "base/threading.h" #include "base/thread_annotations.h" #include "base/low_level_alloc.h" #include "heap-profile-stats.h" @@ -310,7 +308,7 @@ class MemoryRegionMap { // Recursion count for the recursive lock. static int recursion_count_; // The thread id of the thread that's inside the recursive lock. - static pthread_t lock_owner_tid_; + static PerftoolsThreadID lock_owner_tid_; // Total size of all mapped pages so far static int64 map_size_; diff --git a/src/profile-handler.cc b/src/profile-handler.cc index 301fc84..0597f05 100644 --- a/src/profile-handler.cc +++ b/src/profile-handler.cc @@ -60,6 +60,7 @@ #include "base/googleinit.h" #include "base/logging.h" #include "base/spinlock.h" +#include "base/threading.h" // Some Linux systems don't have sigev_notify_thread_id defined in // signal.h (despite having SIGEV_THREAD_ID defined) and also lack @@ -181,7 +182,7 @@ class ProfileHandler { #if HAVE_LINUX_SIGEV_THREAD_ID // this is used to destroy per-thread profiling timers on thread // termination - pthread_key_t thread_timer_key; + PerftoolsTlsKey thread_timer_key; #endif // This lock serializes the registration of threads and protects the @@ -264,15 +265,15 @@ extern "C" { } } -static void CreateThreadTimerKey(pthread_key_t *pkey) { - int rv = pthread_key_create(pkey, ThreadTimerDestructor); +static void CreateThreadTimerKey(PerftoolsTlsKey *pkey) { + int rv = PerftoolsCreateTlsKey(pkey, ThreadTimerDestructor); if (rv) { - RAW_LOG(FATAL, "aborting due to pthread_key_create error: %s", strerror(rv)); + RAW_LOG(FATAL, "aborting due to PerftoolsCreateTlsKey error: %s", strerror(rv)); } } static void StartLinuxThreadTimer(int timer_type, int signal_number, - int32 frequency, pthread_key_t timer_key) { + int32 frequency, PerftoolsTlsKey timer_key) { int rv; struct sigevent sevp; timer_t timerid; @@ -291,9 +292,9 @@ static void StartLinuxThreadTimer(int timer_type, int signal_number, } timer_id_holder *holder = new timer_id_holder(timerid); - rv = pthread_setspecific(timer_key, holder); + rv = PerftoolsSetTlsValue(timer_key, holder); if (rv) { - RAW_LOG(FATAL, "aborting due to pthread_setspecific error: %s", strerror(rv)); + RAW_LOG(FATAL, "aborting due to PerftoolsSetTlsValue error: %s", strerror(rv)); } its.it_interval.tv_sec = 0; diff --git a/src/tcmalloc.cc b/src/tcmalloc.cc index dcb9460..e6d7c16 100644 --- a/src/tcmalloc.cc +++ b/src/tcmalloc.cc @@ -1101,7 +1101,7 @@ size_t TCMallocImplementation::GetEstimatedAllocatedSize(size_t size) { // runs before main(), and therefore we do not have a chance to become // multi-threaded before initialization. We also create the TSD key // here. Presumably by the time this constructor runs, glibc is in -// good enough shape to handle pthread_key_create(). +// good enough shape to handle PerftoolsCreateTlsKey(). // // The constructor also takes the opportunity to tell STL to use // tcmalloc. We want to do this early, before construct time, so diff --git a/src/tests/generic_writer_test.cc b/src/tests/generic_writer_test.cc index adbdb4f..f1af619 100644 --- a/src/tests/generic_writer_test.cc +++ b/src/tests/generic_writer_test.cc @@ -4,6 +4,7 @@ #include "base/generic_writer.h" #include +#define _USE_MATH_DEFINES #include #include diff --git a/src/tests/malloc_hook_test.cc b/src/tests/malloc_hook_test.cc index a128355..656ea26 100644 --- a/src/tests/malloc_hook_test.cc +++ b/src/tests/malloc_hook_test.cc @@ -48,6 +48,7 @@ #include "base/logging.h" #include "base/simple_mutex.h" #include "base/sysinfo.h" +#include "base/threading.h" #include "tests/testutil.h" // On systems (like freebsd) that don't define MAP_ANONYMOUS, use the old @@ -200,7 +201,7 @@ void MultithreadedTestThread(TestHookList* list, int shift, const auto value = reinterpret_cast((i << shift) + thread_num); EXPECT_TRUE(list->Add(value)); - sched_yield(); // Ensure some more interleaving. + PerftoolsYield(); // Ensure some more interleaving. MallocHook::NewHook values[kHookListMaxValues + 1]; int num_values = list->Traverse(values, kHookListMaxValues + 1); @@ -217,11 +218,11 @@ void MultithreadedTestThread(TestHookList* list, int shift, snprintf(buf, sizeof(buf), "[%d/%d; ", value_index, num_values); message += buf; - sched_yield(); + PerftoolsYield(); EXPECT_TRUE(list->Remove(value)); - sched_yield(); + PerftoolsYield(); num_values = list->Traverse(values, kHookListMaxValues); for (value_index = 0; @@ -234,7 +235,7 @@ void MultithreadedTestThread(TestHookList* list, int shift, snprintf(buf, sizeof(buf), "%d]", num_values); message += buf; - sched_yield(); + PerftoolsYield(); } fprintf(stderr, "thread %d: %s\n", thread_num, message.c_str()); } diff --git a/src/thread_cache.cc b/src/thread_cache.cc index 783d0a8..eb50748 100644 --- a/src/thread_cache.cc +++ b/src/thread_cache.cc @@ -72,9 +72,9 @@ __thread ThreadCache::ThreadLocalData ThreadCache::threadlocal_data_ ATTR_INITIAL_EXEC CACHELINE_ALIGNED; #endif bool ThreadCache::tsd_inited_ = false; -pthread_key_t ThreadCache::heap_key_; +PerftoolsTlsKey ThreadCache::heap_key_; -void ThreadCache::Init(pthread_t tid) { +void ThreadCache::Init(PerftoolsThreadID tid) { size_ = 0; max_size_ = 0; @@ -308,17 +308,17 @@ void ThreadCache::InitModule() { void ThreadCache::InitTSD() { ASSERT(!tsd_inited_); - perftools_pthread_key_create(&heap_key_, DestroyThreadCache); + PerftoolsCreateTlsKey(&heap_key_, DestroyThreadCache); tsd_inited_ = true; #ifdef PTHREADS_CRASHES_IF_RUN_TOO_EARLY - // We may have used a fake pthread_t for the main thread. Fix it. - pthread_t zero; + // We may have used a fake PerftoolsThreadID for the main thread. Fix it. + PerftoolsThreadID zero; memset(&zero, 0, sizeof(zero)); SpinLockHolder h(Static::pageheap_lock()); for (ThreadCache* h = thread_heaps_; h != NULL; h = h->next_) { if (h->tid_ == zero) { - h->tid_ = pthread_self(); + h->tid_ = PerftoolsGetThreadID(); } } #endif @@ -343,11 +343,11 @@ ThreadCache* ThreadCache::CreateCacheIfNecessary() { if (tsd_inited_) { // In most common case we're avoiding expensive linear search // through all heaps (see below). Working TLS enables faster - // protection from malloc recursion in pthread_setspecific + // protection from malloc recursion in PerftoolsSetTlsValue seach_condition = false; if (current_heap_ptr != NULL) { - // we're being recursively called by pthread_setspecific below. + // we're being recursively called by PerftoolsSetTlsValue below. return *current_heap_ptr; } current_heap_ptr = &heap; @@ -357,24 +357,24 @@ ThreadCache* ThreadCache::CreateCacheIfNecessary() { { SpinLockHolder h(Static::pageheap_lock()); // On some old glibc's, and on freebsd's libc (as of freebsd 8.1), - // calling pthread routines (even pthread_self) too early could + // calling pthread routines (even PerftoolsGetThreadID) too early could // cause a segfault. Since we can call pthreads quite early, we // have to protect against that in such situations by making a // 'fake' pthread. This is not ideal since it doesn't work well // when linking tcmalloc statically with apps that create threads // before main, so we only do it if we have to. #ifdef PTHREADS_CRASHES_IF_RUN_TOO_EARLY - pthread_t me; + PerftoolsThreadID me; if (!tsd_inited_) { memset(&me, 0, sizeof(me)); } else { - me = pthread_self(); + me = PerftoolsGetThreadID(); } #else - const pthread_t me = pthread_self(); + const PerftoolsThreadID me = PerftoolsGetThreadID(); #endif - // This may be a recursive malloc call from pthread_setspecific() + // This may be a recursive malloc call from PerftoolsSetTlsValue() // In that case, the heap for this thread has already been created // and added to the linked list. So we search for that first. if (seach_condition) { @@ -389,13 +389,13 @@ ThreadCache* ThreadCache::CreateCacheIfNecessary() { if (heap == NULL) heap = NewHeap(me); } - // We call pthread_setspecific() outside the lock because it may + // We call PerftoolsSetTlsValue() outside the lock because it may // call malloc() recursively. We check for the recursive call using // the "in_setspecific_" flag so that we can avoid calling - // pthread_setspecific() if we are already inside pthread_setspecific(). + // PerftoolsSetTlsValue() if we are already inside PerftoolsSetTlsValue(). if (!heap->in_setspecific_ && tsd_inited_) { heap->in_setspecific_ = true; - perftools_pthread_setspecific(heap_key_, heap); + PerftoolsSetTlsValue(heap_key_, heap); #ifdef HAVE_TLS // Also keep a copy in __thread for faster retrieval threadlocal_data_.heap = heap; @@ -409,7 +409,7 @@ ThreadCache* ThreadCache::CreateCacheIfNecessary() { return heap; } -ThreadCache* ThreadCache::NewHeap(pthread_t tid) { +ThreadCache* ThreadCache::NewHeap(PerftoolsThreadID tid) { // Create the heap and add it to the linked list ThreadCache *heap = threadcache_allocator.New(); heap->Init(tid); @@ -434,7 +434,7 @@ void ThreadCache::BecomeIdle() { if (heap->in_setspecific_) return; // Do not disturb the active caller heap->in_setspecific_ = true; - perftools_pthread_setspecific(heap_key_, NULL); + PerftoolsSetTlsValue(heap_key_, NULL); #ifdef HAVE_TLS // Also update the copy in __thread threadlocal_data_.heap = NULL; @@ -443,7 +443,7 @@ void ThreadCache::BecomeIdle() { heap->in_setspecific_ = false; if (GetThreadHeap() == heap) { // Somehow heap got reinstated by a recursive call to malloc - // from pthread_setspecific. We give up in this case. + // from PerftoolsSetTlsValue. We give up in this case. return; } diff --git a/src/thread_cache.h b/src/thread_cache.h index d1a8c6b..5b0fdbd 100644 --- a/src/thread_cache.h +++ b/src/thread_cache.h @@ -35,13 +35,11 @@ #define TCMALLOC_THREAD_CACHE_H_ #include -#ifdef HAVE_PTHREAD -#include // for pthread_t, pthread_key_t -#endif #include // for size_t, NULL #include // for uint32_t, uint64_t #include // for ssize_t #include "base/commandlineflags.h" +#include "base/threading.h" #include "common.h" #include "linked_list.h" #include "page_heap_allocator.h" @@ -57,12 +55,6 @@ DECLARE_int64(tcmalloc_sample_parameter); -#ifndef HAVE_PERFTOOLS_PTHREAD_KEYS -#define perftools_pthread_getspecific pthread_getspecific -#define perftools_pthread_setspecific pthread_setspecific -#define perftools_pthread_key_create pthread_key_create -#endif - namespace tcmalloc { //------------------------------------------------------------------- @@ -77,7 +69,7 @@ class ThreadCache { enum { have_tls = false }; #endif - void Init(pthread_t tid); + void Init(PerftoolsThreadID tid); void Cleanup(); // Accessors (mostly just for printing stats) @@ -268,7 +260,7 @@ class ThreadCache { // If TLS is available, we also store a copy of the per-thread object // in a __thread variable since __thread variables are faster to read - // than pthread_getspecific(). We still need pthread_setspecific() + // than PerftoolsGetTlsValue(). We still need PerftoolsSetTlsValue() // because __thread variables provide no way to run cleanup code when // a thread is destroyed. // We also give a hint to the compiler to use the "initial exec" TLS @@ -295,7 +287,7 @@ class ThreadCache { // Therefore, we use TSD keys only after tsd_inited is set to true. // Until then, we use a slow path to get the heap object. static ATTRIBUTE_HIDDEN bool tsd_inited_; - static pthread_key_t heap_key_; + static PerftoolsTlsKey heap_key_; // Linked list of heap objects. Protected by Static::pageheap_lock. static ThreadCache* thread_heaps_; @@ -331,11 +323,11 @@ class ThreadCache { // We sample allocations, biased by the size of the allocation Sampler sampler_; // A sampler - pthread_t tid_; // Which thread owns it - bool in_setspecific_; // In call to pthread_setspecific? + PerftoolsThreadID tid_; // Which thread owns it + bool in_setspecific_; // In call to PerftoolsSetTlsValue? // Allocate a new heap. REQUIRES: Static::pageheap_lock is held. - static ThreadCache* NewHeap(pthread_t tid); + static ThreadCache* NewHeap(PerftoolsThreadID tid); // Use only as pthread thread-specific destructor function. static void DestroyThreadCache(void* ptr); @@ -411,7 +403,7 @@ inline ThreadCache* ThreadCache::GetThreadHeap() { return threadlocal_data_.heap; #else return reinterpret_cast( - perftools_pthread_getspecific(heap_key_)); + PerftoolsGetTlsValue(heap_key_)); #endif } diff --git a/src/windows/config.h b/src/windows/config.h index 547e47c..4b65aa8 100644 --- a/src/windows/config.h +++ b/src/windows/config.h @@ -129,10 +129,6 @@ /* Define if you have POSIX threads libraries and header files. */ /* #undef HAVE_PTHREAD */ -/* defined to 1 if pthread symbols are exposed even without include pthread.h - */ -/* #undef HAVE_PTHREAD_DESPITE_ASKING_FOR */ - /* Define to 1 if you have the header file. */ /* #undef HAVE_PWD_H */ diff --git a/src/windows/mingw.h b/src/windows/mingw.h index 542f9ae..a3b4f8a 100644 --- a/src/windows/mingw.h +++ b/src/windows/mingw.h @@ -54,13 +54,6 @@ # define _WIN32_WINNT 0x0501 #endif -// Some mingw distributions have a pthreads wrapper, but it doesn't -// work as well as native windows spinlocks (at least for us). So -// pretend the pthreads wrapper doesn't exist, even when it does. -#ifndef HAVE_PTHREAD_DESPITE_ASKING_FOR -#undef HAVE_PTHREAD -#endif - #undef HAVE_FORK #define HAVE_PID_T diff --git a/src/windows/port.cc b/src/windows/port.cc index f779c8b..5ef6447 100644 --- a/src/windows/port.cc +++ b/src/windows/port.cc @@ -46,6 +46,7 @@ #include "port.h" #include "base/logging.h" #include "base/spinlock.h" +#include "base/threading.h" #include "internal_logging.h" // ----------------------------------------------------------------------- @@ -83,7 +84,7 @@ extern "C" PERFTOOLS_DLL_DECL void WriteToStderr(const char* buf, int len) { // ----------------------------------------------------------------------- // Threads code -// Windows doesn't support pthread_key_create's destr_function, and in +// Windows doesn't support PerftoolsCreateTlsKey's destr_function, and in // fact it's a bit tricky to get code to run when a thread exits. This // is cargo-cult magic from https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way // and http://lallouslab.net/2017/05/30/using-cc-tls-callbacks-in-visual-studio-with-your-32-or-64bits-programs/. @@ -111,7 +112,7 @@ extern "C" PERFTOOLS_DLL_DECL void WriteToStderr(const char* buf, int len) { #endif // When destr_fn eventually runs, it's supposed to take as its -// argument the tls-value associated with key that pthread_key_create +// argument the tls-value associated with key that PerftoolsCreateTlsKey // creates. (Yeah, it sounds confusing but it's really not.) We // store the destr_fn/key pair in this data structure. Because we // store this in a single var, this implies we can only have one @@ -120,7 +121,7 @@ extern "C" PERFTOOLS_DLL_DECL void WriteToStderr(const char* buf, int len) { // into an array. struct DestrFnClosure { void (*destr_fn)(void*); - pthread_key_t key_for_destr_fn_arg; + PerftoolsTlsKey key_for_destr_fn_arg; }; static DestrFnClosure destr_fn_info; // initted to all NULL/0. @@ -186,11 +187,11 @@ BOOL WINAPI DllMain(HINSTANCE h, DWORD dwReason, PVOID pv) { #endif // #ifdef _MSC_VER -extern "C" pthread_key_t PthreadKeyCreate(void (*destr_fn)(void*)) { +extern "C" PerftoolsTlsKey PthreadKeyCreate(void (*destr_fn)(void*)) { // Semantics are: we create a new key, and then promise to call // destr_fn with TlsGetValue(key) when the thread is destroyed // (as long as TlsGetValue(key) is not NULL). - pthread_key_t key = TlsAlloc(); + PerftoolsTlsKey key = TlsAlloc(); if (destr_fn) { // register it // If this assert fails, we'll need to support an array of destr_fn_infos assert(destr_fn_info.destr_fn == NULL); diff --git a/src/windows/port.h b/src/windows/port.h index 7ac423e..5bd575a 100644 --- a/src/windows/port.h +++ b/src/windows/port.h @@ -108,61 +108,6 @@ typedef intptr_t ssize_t; /* ----------------------------------- THREADS */ -#ifndef HAVE_PTHREAD /* not true for MSVC, but may be true for MSYS */ -typedef DWORD pthread_t; -typedef DWORD pthread_key_t; - -inline pthread_t pthread_self(void) { - return GetCurrentThreadId(); -} - -#ifdef __cplusplus -inline bool pthread_equal(pthread_t left, pthread_t right) { - return left == right; -} - -/* - * windows/port.h defines compatibility APIs for several .h files, which - * we therefore shouldn't be #including directly. This hack keeps us from - * doing so. TODO(csilvers): do something more principled. - */ -#define HAVE_PERFTOOLS_PTHREAD_KEYS - -EXTERN_C pthread_key_t PthreadKeyCreate(void (*destr_fn)(void*)); /* port.cc */ - -inline int perftools_pthread_key_create(pthread_key_t *pkey, - void (*destructor)(void*)) { - pthread_key_t key = PthreadKeyCreate(destructor); - if (key != TLS_OUT_OF_INDEXES) { - *(pkey) = key; - return 0; - } else { - return GetLastError(); - } -} - -inline void* perftools_pthread_getspecific(DWORD key) { - DWORD err = GetLastError(); - void* rv = TlsGetValue(key); - if (err) SetLastError(err); - return rv; -} - -inline int perftools_pthread_setspecific(pthread_key_t key, const void *value) { - if (TlsSetValue(key, (LPVOID)value)) - return 0; - else - return GetLastError(); -} - -#endif /* __cplusplus */ - -inline void sched_yield(void) { - Sleep(0); -} - -#endif /* HAVE_PTHREAD */ - /* * __declspec(thread) isn't usable in a dll opened via LoadLibrary(). * But it doesn't work to LoadLibrary() us anyway, because of all the