xref: /aosp_15_r20/external/cronet/base/allocator/partition_allocator/src/partition_alloc/partition_tls.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2020 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 PARTITION_ALLOC_PARTITION_TLS_H_
6 #define PARTITION_ALLOC_PARTITION_TLS_H_
7 
8 #include "build/build_config.h"
9 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
10 #include "partition_alloc/partition_alloc_base/component_export.h"
11 #include "partition_alloc/partition_alloc_base/immediate_crash.h"
12 #include "partition_alloc/partition_alloc_check.h"
13 
14 #if BUILDFLAG(IS_POSIX)
15 #include <pthread.h>
16 #endif
17 
18 #if BUILDFLAG(IS_WIN)
19 #include "partition_alloc/partition_alloc_base/win/windows_types.h"
20 #endif
21 
22 // Barebones TLS implementation for use in PartitionAlloc. This doesn't use the
23 // general chromium TLS handling to avoid dependencies, but more importantly
24 // because it allocates memory.
25 namespace partition_alloc::internal {
26 
27 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
28 using PartitionTlsKey = pthread_key_t;
29 
30 // Only on x86_64, the implementation is not stable on ARM64. For instance, in
31 // macOS 11, the TPIDRRO_EL0 registers holds the CPU index in the low bits,
32 // which is not the case in macOS 12. See libsyscall/os/tsd.h in XNU
33 // (_os_tsd_get_direct() is used by pthread_getspecific() internally).
34 #if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64)
35 namespace {
36 
FastTlsGet(PartitionTlsKey index)37 PA_ALWAYS_INLINE void* FastTlsGet(PartitionTlsKey index) {
38   // On macOS, pthread_getspecific() is in libSystem, so a call to it has to go
39   // through PLT. However, and contrary to some other platforms, *all* TLS keys
40   // are in a static array in the thread structure. So they are *always* at a
41   // fixed offset from the segment register holding the thread structure
42   // address.
43   //
44   // We could use _pthread_getspecific_direct(), but it is not
45   // exported. However, on all macOS versions we support, the TLS array is at
46   // %gs. This is used in V8 to back up InternalGetExistingThreadLocal(), and
47   // can also be seen by looking at pthread_getspecific() disassembly:
48   //
49   // libsystem_pthread.dylib`pthread_getspecific:
50   // libsystem_pthread.dylib[0x7ff800316099] <+0>: movq   %gs:(,%rdi,8), %rax
51   // libsystem_pthread.dylib[0x7ff8003160a2] <+9>: retq
52   //
53   // This function is essentially inlining the content of pthread_getspecific()
54   // here.
55   intptr_t result;
56   static_assert(sizeof index <= sizeof(intptr_t));
57   asm("movq %%gs:(,%1,8), %0;"
58       : "=r"(result)
59       : "r"(static_cast<intptr_t>(index)));
60 
61   return reinterpret_cast<void*>(result);
62 }
63 
64 }  // namespace
65 #endif  // BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64)
66 
PartitionTlsCreate(PartitionTlsKey * key,void (* destructor)(void *))67 PA_ALWAYS_INLINE bool PartitionTlsCreate(PartitionTlsKey* key,
68                                          void (*destructor)(void*)) {
69   return !pthread_key_create(key, destructor);
70 }
71 
PartitionTlsGet(PartitionTlsKey key)72 PA_ALWAYS_INLINE void* PartitionTlsGet(PartitionTlsKey key) {
73 #if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64)
74   PA_DCHECK(pthread_getspecific(key) == FastTlsGet(key));
75   return FastTlsGet(key);
76 #else
77   return pthread_getspecific(key);
78 #endif
79 }
80 
PartitionTlsSet(PartitionTlsKey key,void * value)81 PA_ALWAYS_INLINE void PartitionTlsSet(PartitionTlsKey key, void* value) {
82   int ret = pthread_setspecific(key, value);
83   PA_DCHECK(!ret);
84 }
85 
86 #elif BUILDFLAG(IS_WIN)
87 // Note: supports only a single TLS key on Windows. Not a hard constraint, may
88 // be lifted.
89 using PartitionTlsKey = unsigned long;
90 
91 PA_COMPONENT_EXPORT(PARTITION_ALLOC)
92 bool PartitionTlsCreate(PartitionTlsKey* key, void (*destructor)(void*));
93 
94 PA_ALWAYS_INLINE void* PartitionTlsGet(PartitionTlsKey key) {
95   // Accessing TLS resets the last error, which then makes |GetLastError()|
96   // return something misleading. While this means that properly using
97   // |GetLastError()| is difficult, there is currently code in Chromium which
98   // expects malloc() to *not* reset it. Meaning that we either have to fix this
99   // code, or pay the cost of saving/restoring it.
100   //
101   // Source:
102   // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlsgetvalue
103   // "Functions that return indications of failure call SetLastError() when they
104   // fail. They generally do not call SetLastError() when they succeed. The
105   // TlsGetValue() function is an exception to this general rule. The
106   // TlsGetValue() function calls SetLastError() to clear a thread's last error
107   // when it succeeds."
108   DWORD saved_error = GetLastError();
109   void* ret = TlsGetValue(key);
110   // Only non-zero errors need to be restored.
111   if (PA_UNLIKELY(saved_error)) {
112     SetLastError(saved_error);
113   }
114   return ret;
115 }
116 
117 PA_ALWAYS_INLINE void PartitionTlsSet(PartitionTlsKey key, void* value) {
118   BOOL ret = TlsSetValue(key, value);
119   PA_DCHECK(ret);
120 }
121 
122 // Registers a callback for DLL_PROCESS_DETACH events.
123 void PartitionTlsSetOnDllProcessDetach(void (*callback)());
124 
125 #else
126 // Not supported.
127 using PartitionTlsKey = int;
128 
129 PA_ALWAYS_INLINE bool PartitionTlsCreate(PartitionTlsKey* key,
130                                          void (*destructor)(void*)) {
131   // NOTIMPLEMENTED() may allocate, crash instead.
132   PA_IMMEDIATE_CRASH();
133 }
134 
135 PA_ALWAYS_INLINE void* PartitionTlsGet(PartitionTlsKey key) {
136   PA_IMMEDIATE_CRASH();
137 }
138 
139 PA_ALWAYS_INLINE void PartitionTlsSet(PartitionTlsKey key, void* value) {
140   PA_IMMEDIATE_CRASH();
141 }
142 
143 #endif  // BUILDFLAG(IS_WIN)
144 
145 }  // namespace partition_alloc::internal
146 
147 #endif  // PARTITION_ALLOC_PARTITION_TLS_H_
148