Imported Upstream version 1.41.0
[platform/upstream/grpc.git] / src / core / lib / gpr / tls.h
index 5eb6951..32add50 100644 (file)
 #ifndef GRPC_CORE_LIB_GPR_TLS_H
 #define GRPC_CORE_LIB_GPR_TLS_H
 
-#include <grpc/support/port_platform.h>
+#include <grpc/impl/codegen/port_platform.h>
 
-/** Thread local storage.
-
-   A minimal wrapper that should be implementable across many compilers,
-   and implementable efficiently across most modern compilers.
-
-   Thread locals have type intptr_t.
-
-   Declaring a thread local variable 'foo':
-     GPR_TLS_DECL(foo);
-   Thread locals always have static scope.
-
-   Declaring a thread local class variable 'foo':
-     GPR_TLS_CLASS_DECL(foo);
-
-   Defining the thread local class variable:
-     GPR_TLS_CLASS_DEF(foo);
-
-   Initializing a thread local (must be done at library initialization
-   time):
-     gpr_tls_init(&foo);
+#include <type_traits>
 
-   Destroying a thread local:
-     gpr_tls_destroy(&foo);
-
-   Setting a thread local (returns new_value):
-     gpr_tls_set(&foo, new_value);
+/** Thread local storage.
 
-   Accessing a thread local:
-     current_value = gpr_tls_get(&foo);
+   Usage is the same as C++ thread_local. Declaring a thread local:
+     static GPR_THREAD_LOCAL(uint32_t) foo;
 
    ALL functions here may be implemented as macros. */
 
-#ifdef GPR_STDCPP_TLS
-#include "src/core/lib/gpr/tls_stdcpp.h"
-#endif
-
-#ifdef GPR_GCC_TLS
-#include "src/core/lib/gpr/tls_gcc.h"
-#endif
-
-#ifdef GPR_MSVC_TLS
-#include "src/core/lib/gpr/tls_msvc.h"
-#endif
+namespace grpc_core {
+
+// This class is never instantiated. It exists to statically ensure that all
+// TLS usage is compatible with the most restrictive implementation, allowing
+// developers to write correct code regardless of the platform they develop on.
+template <typename T>
+class TlsTypeConstrainer {
+  static_assert(std::is_trivial<T>::value,
+                "TLS support is limited to trivial types");
+
+ public:
+  using Type = T;
+};
+
+}  // namespace grpc_core
+
+#if defined(GPR_PTHREAD_TLS)
+
+#include <pthread.h>
+
+#include <array>
+#include <cstring>
+
+#include <grpc/support/log.h> /* for GPR_ASSERT */
+
+namespace grpc_core {
+
+template <typename T>
+class PthreadTlsImpl : TlsTypeConstrainer<T> {
+ public:
+  PthreadTlsImpl(const PthreadTlsImpl&) = delete;
+  PthreadTlsImpl& operator=(const PthreadTlsImpl&) = delete;
+
+  // Achtung! This class emulates C++ `thread_local` using pthread keys. Each
+  // instance of this class is a stand in for a C++ `thread_local`. Think of
+  // each `thread_local` as a *global* pthread_key_t and a type tag. An
+  // important consequence of this is that the lifetime of a `pthread_key_t`
+  // is precisely the lifetime of an instance of this class. To understand why
+  // this is, consider the following scenario given a fictional implementation
+  // of this class which creates and destroys its `pthread_key_t` each time
+  // a given block of code runs (all actions take place on a single thread):
+  //
+  // - instance 1 (type tag = T*) is initialized, is assigned `pthread_key_t` 1
+  // - instance 2 (type tag = int) is initialized, is assigned `pthread_key_t` 2
+  // - instances 1 and 2 store and retrieve values; all is well
+  // - instances 1 and 2 are de-initialized; their keys are released to the pool
+  //
+  // - another run commences
+  // - instance 1 receives key 2
+  // - a value is read from instance 1, it observes a value of type int, but
+  //   interprets it as T*; undefined behavior, kaboom
+  //
+  // To properly ensure these invariants are upheld the `pthread_key_t` must be
+  // `const`, which means it can only be released in the destructor. This is a
+  // a violation of the style guide, since these objects are always static (see
+  // footnote) but this code is used in sufficiently narrow circumstances to
+  // justify the deviation.
+  //
+  // https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
+  PthreadTlsImpl()
+      : keys_([]() {
+          typename std::remove_const<decltype(PthreadTlsImpl::keys_)>::type
+              keys;
+          for (pthread_key_t& key : keys) {
+            GPR_ASSERT(0 == pthread_key_create(&key, nullptr));
+          }
+          return keys;
+        }()) {}
+  PthreadTlsImpl(T t) : PthreadTlsImpl() { *this = t; }
+  ~PthreadTlsImpl() {
+    for (pthread_key_t key : keys_) {
+      GPR_ASSERT(0 == pthread_key_delete(key));
+    }
+  }
+
+  operator T() const {
+    T t;
+    char* dst = reinterpret_cast<char*>(&t);
+    for (pthread_key_t key : keys_) {
+      uintptr_t src = uintptr_t(pthread_getspecific(key));
+      size_t remaining = reinterpret_cast<char*>(&t + 1) - dst;
+      size_t step = std::min(sizeof(src), remaining);
+      memcpy(dst, &src, step);
+      dst += step;
+    }
+    return t;
+  }
+
+  T operator=(T t) {
+    char* src = reinterpret_cast<char*>(&t);
+    for (pthread_key_t key : keys_) {
+      uintptr_t dst;
+      size_t remaining = reinterpret_cast<char*>(&t + 1) - src;
+      size_t step = std::min(sizeof(dst), remaining);
+      memcpy(&dst, src, step);
+      GPR_ASSERT(0 == pthread_setspecific(key, reinterpret_cast<void*>(dst)));
+      src += step;
+    }
+    return t;
+  }
+
+ private:
+  const std::array<pthread_key_t,
+                   (sizeof(T) + sizeof(void*) - 1) / sizeof(void*)>
+      keys_;
+};
+
+}  // namespace grpc_core
+
+#define GPR_THREAD_LOCAL(type) grpc_core::PthreadTlsImpl<type>
+
+#else
+
+#define GPR_THREAD_LOCAL(type) \
+  thread_local typename grpc_core::TlsTypeConstrainer<type>::Type
 
-#ifdef GPR_PTHREAD_TLS
-#include "src/core/lib/gpr/tls_pthread.h"
 #endif
 
 #endif /* GRPC_CORE_LIB_GPR_TLS_H */