/* * * Copyright 2015 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifndef GRPC_CORE_LIB_GPR_TLS_H #define GRPC_CORE_LIB_GPR_TLS_H #include #include /** Thread local storage. 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. */ 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 class TlsTypeConstrainer { static_assert(std::is_trivial::value, "TLS support is limited to trivial types"); public: using Type = T; }; } // namespace grpc_core #if defined(GPR_PTHREAD_TLS) #include #include #include #include /* for GPR_ASSERT */ namespace grpc_core { template class PthreadTlsImpl : TlsTypeConstrainer { 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::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(&t); for (pthread_key_t key : keys_) { uintptr_t src = uintptr_t(pthread_getspecific(key)); size_t remaining = reinterpret_cast(&t + 1) - dst; size_t step = std::min(sizeof(src), remaining); memcpy(dst, &src, step); dst += step; } return t; } T operator->() const { static_assert(std::is_pointer::value, "operator-> only usable on pointers"); return this->operator T(); } T operator=(T t) { char* src = reinterpret_cast(&t); for (pthread_key_t key : keys_) { uintptr_t dst; size_t remaining = reinterpret_cast(&t + 1) - src; size_t step = std::min(sizeof(dst), remaining); memcpy(&dst, src, step); GPR_ASSERT(0 == pthread_setspecific(key, reinterpret_cast(dst))); src += step; } return t; } private: const std::array keys_; }; } // namespace grpc_core #define GPR_THREAD_LOCAL(type) grpc_core::PthreadTlsImpl #else #define GPR_THREAD_LOCAL(type) \ thread_local typename grpc_core::TlsTypeConstrainer::Type #endif #endif /* GRPC_CORE_LIB_GPR_TLS_H */