• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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