tls.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef GRPC_CORE_LIB_GPR_TLS_H
20 #define GRPC_CORE_LIB_GPR_TLS_H
21 
23 
24 #include <type_traits>
25 
33 namespace grpc_core {
34 
35 // This class is never instantiated. It exists to statically ensure that all
36 // TLS usage is compatible with the most restrictive implementation, allowing
37 // developers to write correct code regardless of the platform they develop on.
38 template <typename T>
40  static_assert(std::is_trivial<T>::value,
41  "TLS support is limited to trivial types");
42 
43  public:
44  using Type = T;
45 };
46 
47 } // namespace grpc_core
48 
49 #if defined(GPR_PTHREAD_TLS)
50 
51 #include <pthread.h>
52 
53 #include <algorithm>
54 #include <array>
55 #include <cstring>
56 
57 namespace grpc_core {
58 
59 template <typename T>
60 class PthreadTlsImpl : TlsTypeConstrainer<T> {
61  public:
62  PthreadTlsImpl(const PthreadTlsImpl&) = delete;
63  PthreadTlsImpl& operator=(const PthreadTlsImpl&) = delete;
64 
65  // Achtung! This class emulates C++ `thread_local` using pthread keys. Each
66  // instance of this class is a stand in for a C++ `thread_local`. Think of
67  // each `thread_local` as a *global* pthread_key_t and a type tag. An
68  // important consequence of this is that the lifetime of a `pthread_key_t`
69  // is precisely the lifetime of an instance of this class. To understand why
70  // this is, consider the following scenario given a fictional implementation
71  // of this class which creates and destroys its `pthread_key_t` each time
72  // a given block of code runs (all actions take place on a single thread):
73  //
74  // - instance 1 (type tag = T*) is initialized, is assigned `pthread_key_t` 1
75  // - instance 2 (type tag = int) is initialized, is assigned `pthread_key_t` 2
76  // - instances 1 and 2 store and retrieve values; all is well
77  // - instances 1 and 2 are de-initialized; their keys are released to the pool
78  //
79  // - another run commences
80  // - instance 1 receives key 2
81  // - a value is read from instance 1, it observes a value of type int, but
82  // interprets it as T*; undefined behavior, kaboom
83  //
84  // To properly ensure these invariants are upheld the `pthread_key_t` must be
85  // `const`, which means it can only be released in the destructor. This is a
86  // a violation of the style guide, since these objects are always static (see
87  // footnote) but this code is used in sufficiently narrow circumstances to
88  // justify the deviation.
89  //
90  // https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
91  PthreadTlsImpl()
92  : keys_([]() {
93  typename std::remove_const<decltype(PthreadTlsImpl::keys_)>::type
94  keys;
95  for (pthread_key_t& key : keys) {
96  if (0 != pthread_key_create(&key, nullptr)) abort();
97  }
98  return keys;
99  }()) {}
100  PthreadTlsImpl(T t) : PthreadTlsImpl() { *this = t; }
101  ~PthreadTlsImpl() {
102  for (pthread_key_t key : keys_) {
103  if (0 != pthread_key_delete(key)) abort();
104  }
105  }
106 
107  operator T() const {
108  T t;
109  char* dst = reinterpret_cast<char*>(&t);
110  for (pthread_key_t key : keys_) {
111  uintptr_t src = uintptr_t(pthread_getspecific(key));
112  size_t remaining = reinterpret_cast<char*>(&t + 1) - dst;
113  size_t step = std::min(sizeof(src), remaining);
114  memcpy(dst, &src, step);
115  dst += step;
116  }
117  return t;
118  }
119 
120  T operator->() const {
121  static_assert(std::is_pointer<T>::value,
122  "operator-> only usable on pointers");
123  return this->operator T();
124  }
125 
126  T operator=(T t) {
127  char* src = reinterpret_cast<char*>(&t);
128  for (pthread_key_t key : keys_) {
129  uintptr_t dst;
130  size_t remaining = reinterpret_cast<char*>(&t + 1) - src;
131  size_t step = std::min(sizeof(dst), remaining);
132  memcpy(&dst, src, step);
133  if (0 != pthread_setspecific(key, reinterpret_cast<void*>(dst))) abort();
134  src += step;
135  }
136  return t;
137  }
138 
139  private:
140  const std::array<pthread_key_t,
141  (sizeof(T) + sizeof(void*) - 1) / sizeof(void*)>
142  keys_;
143 };
144 
145 } // namespace grpc_core
146 
147 #define GPR_THREAD_LOCAL(type) grpc_core::PthreadTlsImpl<type>
148 
149 #else
150 
151 #define GPR_THREAD_LOCAL(type) \
152  thread_local typename grpc_core::TlsTypeConstrainer<type>::Type
153 
154 #endif
155 
156 #endif /* GRPC_CORE_LIB_GPR_TLS_H */
dst
static const char dst[]
Definition: test-fs-copyfile.c:37
keys
const void * keys
Definition: abseil-cpp/absl/random/internal/randen.cc:49
grpc_core
Definition: call_metric_recorder.h:31
array
PHP_PROTO_OBJECT_FREE_END PHP_PROTO_OBJECT_DTOR_END intern array
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/array.c:111
T
#define T(upbtypeconst, upbtype, ctype, default_value)
memcpy
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
grpc_core::TlsTypeConstrainer
Definition: tls.h:39
uintptr_t
_W64 unsigned int uintptr_t
Definition: stdint-msvc2008.h:119
min
#define min(a, b)
Definition: qsort.h:83
value
const char * value
Definition: hpack_parser_table.cc:165
key
const char * key
Definition: hpack_parser_table.cc:164
absl::str_format_internal::LengthMod::t
@ t
step
static int step
Definition: test-mutexes.c:31
asyncio_get_stats.type
type
Definition: asyncio_get_stats.py:37
grpc_core::TlsTypeConstrainer::Type
T Type
Definition: tls.h:44
port_platform.h


grpc
Author(s):
autogenerated on Thu Mar 13 2025 03:01:39