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 (LIKELY(allowed_)) 37 pthread_setspecific(entered_key_, nullptr); 38 } 39 40 explicit operator bool() const noexcept { return allowed_; } 41 42 // This function must be called before installing any allocator hooks because 43 // some TLS implementations may allocate (eg. glibc will require a malloc call 44 // to allocate storage for a higher slot number (>= PTHREAD_KEY_2NDLEVEL_SIZE 45 // == 32). This touches the thread-local storage so that any malloc happens 46 // before installing the hooks. 47 static void InitTLSSlot(); 48 49 // InitTLSSlot() is called before crash keys are available. At some point 50 // after SetCrashKeyImplementation() is called, this function should be 51 // called to record `entered_key_` to a crash key for debugging. This may 52 // allocate so it must not be called from inside an allocator hook. 53 static void RecordTLSSlotToCrashKey(); 54 55 private: 56 static pthread_key_t entered_key_; 57 const bool allowed_; 58 }; 59 60 #else 61 62 // Use [[maybe_unused]] as this lightweight stand-in for the more heavyweight 63 // ReentryGuard above will otherwise trigger the "unused code" warnings. 64 struct [[maybe_unused]] BASE_EXPORT ReentryGuard { 65 constexpr explicit operator bool() const noexcept { return true; } 66 67 static void InitTLSSlot(); 68 static void RecordTLSSlotToCrashKey(); 69 }; 70 71 #endif 72 73 } // namespace base::allocator::dispatcher 74 75 #endif // BASE_ALLOCATOR_DISPATCHER_REENTRY_GUARD_H_ 76