xref: /aosp_15_r20/external/cronet/base/allocator/dispatcher/reentry_guard.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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