1 // Copyright 2022 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_ALLOCATOR_DISPATCHER_REENTRY_GUARD_H_ 6 #define BASE_ALLOCATOR_DISPATCHER_REENTRY_GUARD_H_ 7 8 #include "base/base_export.h" 9 #include "base/check.h" 10 #include "base/compiler_specific.h" 11 #include "build/build_config.h" 12 13 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) 14 #include <pthread.h> 15 #endif 16 17 namespace base::allocator::dispatcher { 18 19 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) 20 21 // The macOS implementation of libmalloc sometimes calls malloc recursively, 22 // delegating allocations between zones. That causes our hooks being called 23 // twice. The scoped guard allows us to detect that. 24 // 25 // Besides that the implementations of thread_local on macOS and Android 26 // seem to allocate memory lazily on the first access to thread_local variables 27 // (and on Android at least thread_local is implemented on top of pthread so is 28 // strictly worse for performance). Make use of pthread TLS instead of C++ 29 // thread_local there. 30 struct BASE_EXPORT ReentryGuard { ReentryGuardReentryGuard31 ALWAYS_INLINE ReentryGuard() : allowed_(!pthread_getspecific(entered_key_)) { 32 pthread_setspecific(entered_key_, reinterpret_cast<void*>(true)); 33 } 34 ~ReentryGuardReentryGuard35 ALWAYS_INLINE ~ReentryGuard() { 36 if (allowed_) [[likely]] { 37 pthread_setspecific(entered_key_, nullptr); 38 } 39 } 40 41 explicit operator bool() const noexcept { return allowed_; } 42 43 // This function must be called before installing any allocator hooks because 44 // some TLS implementations may allocate (eg. glibc will require a malloc call 45 // to allocate storage for a higher slot number (>= PTHREAD_KEY_2NDLEVEL_SIZE 46 // == 32). This touches the thread-local storage so that any malloc happens 47 // before installing the hooks. 48 static void InitTLSSlot(); 49 50 // InitTLSSlot() is called before crash keys are available. At some point 51 // after SetCrashKeyImplementation() is called, this function should be 52 // called to record `entered_key_` to a crash key for debugging. This may 53 // allocate so it must not be called from inside an allocator hook. 54 static void RecordTLSSlotToCrashKey(); 55 56 private: 57 static pthread_key_t entered_key_; 58 const bool allowed_; 59 }; 60 61 #else 62 63 // Use [[maybe_unused]] as this lightweight stand-in for the more heavyweight 64 // ReentryGuard above will otherwise trigger the "unused code" warnings. 65 struct [[maybe_unused]] BASE_EXPORT ReentryGuard { 66 constexpr explicit operator bool() const noexcept { return true; } 67 68 static void InitTLSSlot(); 69 static void RecordTLSSlotToCrashKey(); 70 }; 71 72 #endif 73 74 } // namespace base::allocator::dispatcher 75 76 #endif // BASE_ALLOCATOR_DISPATCHER_REENTRY_GUARD_H_ 77