[tls] Remove support for pthread tls (#31040)

* [tls] Remove support for pthread tls

* fix

* fix

* fix

* lalala

* fix

* fix

* Clean up deployment target changes

* additional clean up of deployment target

* regen podspec for updated file list

* remove destination change

* deployment target override for ios cpp test

* fix?

* fix run test script & update test destination

* [tls] Remove support for pthread tls

* fix

* fix

* fix

* lalala

* fix

* fix

* Clean up deployment target changes

* additional clean up of deployment target

* regen podspec for updated file list

* remove destination change

* deployment target override for ios cpp test

* fix?

* fix run test script & update test destination

* merge

* fix

* final script fix for proper destination & target

* more deployment target fix

* objc ios test script fix

Co-authored-by: Denny C. Dai <dennycd@me.com>
Co-authored-by: dennycd <dennycd@google.com>
diff --git a/BUILD b/BUILD
index e7f519b..4f00a9d 100644
--- a/BUILD
+++ b/BUILD
@@ -1122,7 +1122,6 @@
         "env",
         "examine_stack",
         "gpr_atm",
-        "gpr_tls",
         "no_destruct",
         "tchar",
         "useful",
@@ -1130,12 +1129,6 @@
 )
 
 grpc_cc_library(
-    name = "gpr_tls",
-    hdrs = ["src/core/lib/gpr/tls.h"],
-    deps = ["gpr_platform"],
-)
-
-grpc_cc_library(
     name = "chunked_vector",
     hdrs = ["src/core/lib/gprpp/chunked_vector.h"],
     deps = [
@@ -1394,10 +1387,7 @@
     public_hdrs = [
         "src/core/lib/promise/context.h",
     ],
-    deps = [
-        "gpr_platform",
-        "gpr_tls",
-    ],
+    deps = ["gpr_platform"],
 )
 
 grpc_cc_library(
@@ -1675,7 +1665,6 @@
         "construct_destruct",
         "context",
         "gpr",
-        "gpr_tls",
         "no_destruct",
         "orphanable",
         "poll",
@@ -2223,7 +2212,6 @@
     deps = [
         "event_engine_base_hdrs",
         "gpr",
-        "gpr_tls",
         "no_destruct",
         "useful",
     ],
@@ -2250,7 +2238,6 @@
         "gpr",
         "gpr_atm",
         "gpr_spinlock",
-        "gpr_tls",
         "grpc_codegen",
         "grpc_trace",
         "time",
@@ -2320,7 +2307,6 @@
         "gpr_manual_constructor",
         "gpr_platform",
         "gpr_spinlock",
-        "gpr_tls",
         "grpc_trace",
         "iomgr_port",
         "time",
@@ -2505,7 +2491,6 @@
     deps = [
         "forkable",
         "gpr",
-        "gpr_tls",
     ],
 )
 
@@ -2525,7 +2510,6 @@
         "event_engine_base_hdrs",
         "forkable",
         "gpr",
-        "gpr_tls",
         "grpc_trace",
         "posix_event_engine_timer",
         "time",
@@ -3395,7 +3379,6 @@
         "gpr_manual_constructor",
         "gpr_murmur_hash",
         "gpr_spinlock",
-        "gpr_tls",
         "grpc_public_hdrs",
         "grpc_sockaddr",
         "grpc_trace",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2067ced..292b41f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1217,7 +1217,6 @@
   add_dependencies(buildtests_cxx tls_certificate_verifier_test)
   add_dependencies(buildtests_cxx tls_key_export_test)
   add_dependencies(buildtests_cxx tls_security_connector_test)
-  add_dependencies(buildtests_cxx tls_test)
   add_dependencies(buildtests_cxx too_many_pings_test)
   if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
     add_dependencies(buildtests_cxx traced_buffer_list_test)
@@ -18843,41 +18842,6 @@
 endif()
 if(gRPC_BUILD_TESTS)
 
-add_executable(tls_test
-  test/core/gpr/tls_test.cc
-  third_party/googletest/googletest/src/gtest-all.cc
-  third_party/googletest/googlemock/src/gmock-all.cc
-)
-
-target_include_directories(tls_test
-  PRIVATE
-    ${CMAKE_CURRENT_SOURCE_DIR}
-    ${CMAKE_CURRENT_SOURCE_DIR}/include
-    ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
-    ${_gRPC_RE2_INCLUDE_DIR}
-    ${_gRPC_SSL_INCLUDE_DIR}
-    ${_gRPC_UPB_GENERATED_DIR}
-    ${_gRPC_UPB_GRPC_GENERATED_DIR}
-    ${_gRPC_UPB_INCLUDE_DIR}
-    ${_gRPC_XXHASH_INCLUDE_DIR}
-    ${_gRPC_ZLIB_INCLUDE_DIR}
-    third_party/googletest/googletest/include
-    third_party/googletest/googletest
-    third_party/googletest/googlemock/include
-    third_party/googletest/googlemock
-    ${_gRPC_PROTO_GENS_DIR}
-)
-
-target_link_libraries(tls_test
-  ${_gRPC_PROTOBUF_LIBRARIES}
-  ${_gRPC_ALLTARGETS_LIBRARIES}
-  grpc_test_util
-)
-
-
-endif()
-if(gRPC_BUILD_TESTS)
-
 add_executable(too_many_pings_test
   test/core/end2end/cq_verifier.cc
   test/core/transport/chttp2/too_many_pings_test.cc
diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index 61e2d4e..7bc9f86 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -201,7 +201,6 @@
   - src/core/lib/gpr/alloc.h
   - src/core/lib/gpr/string.h
   - src/core/lib/gpr/time_precise.h
-  - src/core/lib/gpr/tls.h
   - src/core/lib/gpr/tmpfile.h
   - src/core/lib/gpr/useful.h
   - src/core/lib/gprpp/construct_destruct.h
@@ -5285,7 +5284,6 @@
   build: test
   language: c++
   headers:
-  - src/core/lib/gpr/tls.h
   - src/core/lib/promise/context.h
   src:
   - test/core/promise/context_test.cc
@@ -10213,16 +10211,6 @@
   - test/core/util/tracer_util.cc
   deps:
   - grpc_test_util
-- name: tls_test
-  gtest: true
-  build: test
-  language: c++
-  headers: []
-  src:
-  - test/core/gpr/tls_test.cc
-  deps:
-  - grpc_test_util
-  uses_polling: false
 - name: too_many_pings_test
   gtest: true
   build: test
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index 38c9e90..f175ba1 100644
--- a/gRPC-C++.podspec
+++ b/gRPC-C++.podspec
@@ -708,7 +708,6 @@
                       'src/core/lib/gpr/spinlock.h',
                       'src/core/lib/gpr/string.h',
                       'src/core/lib/gpr/time_precise.h',
-                      'src/core/lib/gpr/tls.h',
                       'src/core/lib/gpr/tmpfile.h',
                       'src/core/lib/gpr/useful.h',
                       'src/core/lib/gprpp/atomic_utils.h',
@@ -1573,7 +1572,6 @@
                               'src/core/lib/gpr/spinlock.h',
                               'src/core/lib/gpr/string.h',
                               'src/core/lib/gpr/time_precise.h',
-                              'src/core/lib/gpr/tls.h',
                               'src/core/lib/gpr/tmpfile.h',
                               'src/core/lib/gpr/useful.h',
                               'src/core/lib/gprpp/atomic_utils.h',
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index 39e910b..324823e 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -1119,7 +1119,6 @@
                       'src/core/lib/gpr/time_precise.cc',
                       'src/core/lib/gpr/time_precise.h',
                       'src/core/lib/gpr/time_windows.cc',
-                      'src/core/lib/gpr/tls.h',
                       'src/core/lib/gpr/tmpfile.h',
                       'src/core/lib/gpr/tmpfile_msys.cc',
                       'src/core/lib/gpr/tmpfile_posix.cc',
@@ -2201,7 +2200,6 @@
                               'src/core/lib/gpr/spinlock.h',
                               'src/core/lib/gpr/string.h',
                               'src/core/lib/gpr/time_precise.h',
-                              'src/core/lib/gpr/tls.h',
                               'src/core/lib/gpr/tmpfile.h',
                               'src/core/lib/gpr/useful.h',
                               'src/core/lib/gprpp/atomic_utils.h',
diff --git a/grpc.gemspec b/grpc.gemspec
index 5b40c08..de40264 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -1031,7 +1031,6 @@
   s.files += %w( src/core/lib/gpr/time_precise.cc )
   s.files += %w( src/core/lib/gpr/time_precise.h )
   s.files += %w( src/core/lib/gpr/time_windows.cc )
-  s.files += %w( src/core/lib/gpr/tls.h )
   s.files += %w( src/core/lib/gpr/tmpfile.h )
   s.files += %w( src/core/lib/gpr/tmpfile_msys.cc )
   s.files += %w( src/core/lib/gpr/tmpfile_posix.cc )
diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index ec8217a..2e408f1 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -234,9 +234,6 @@
 #define GPR_CPU_POSIX 1
 #define GPR_POSIX_CRASH_HANDLER 1
 #endif
-#if !(defined(__has_feature) && __has_feature(cxx_thread_local))
-#define GPR_PTHREAD_TLS 1
-#endif
 #define GPR_APPLE 1
 #define GPR_GCC_ATOMIC 1
 #define GPR_POSIX_LOG 1
diff --git a/package.xml b/package.xml
index 56349da..d1f9640 100644
--- a/package.xml
+++ b/package.xml
@@ -1013,7 +1013,6 @@
     <file baseinstalldir="/" name="src/core/lib/gpr/time_precise.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/time_precise.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/time_windows.cc" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/gpr/tls.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/tmpfile.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/tmpfile_msys.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/gpr/tmpfile_posix.cc" role="src" />
diff --git a/src/core/ext/transport/binder/utils/ndk_binder.cc b/src/core/ext/transport/binder/utils/ndk_binder.cc
index f5aafc0..298f988 100644
--- a/src/core/ext/transport/binder/utils/ndk_binder.cc
+++ b/src/core/ext/transport/binder/utils/ndk_binder.cc
@@ -24,7 +24,6 @@
 
 #include <grpc/support/log.h>
 
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gprpp/sync.h"
 
 namespace {
@@ -46,7 +45,7 @@
 
 // Whether the thread has already attached to JVM (this is to prevent
 // repeated attachment in `AttachJvm()`)
-GPR_THREAD_LOCAL(bool) g_is_jvm_attached = false;
+thread_local bool g_is_jvm_attached = false;
 
 void SetJvm(JNIEnv* env) {
   // OK to lock here since this function will only be called once for each
diff --git a/src/core/lib/event_engine/posix_engine/timer_manager.cc b/src/core/lib/event_engine/posix_engine/timer_manager.cc
index 48fa17f..b5d6635 100644
--- a/src/core/lib/event_engine/posix_engine/timer_manager.cc
+++ b/src/core/lib/event_engine/posix_engine/timer_manager.cc
@@ -33,10 +33,9 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/debug/trace.h"
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gprpp/thd.h"
 
-static GPR_THREAD_LOCAL(bool) g_timer_thread;
+static thread_local bool g_timer_thread;
 
 namespace grpc_event_engine {
 namespace posix_engine {
diff --git a/src/core/lib/event_engine/thread_pool.cc b/src/core/lib/event_engine/thread_pool.cc
index b1d4257..908b085 100644
--- a/src/core/lib/event_engine/thread_pool.cc
+++ b/src/core/lib/event_engine/thread_pool.cc
@@ -28,7 +28,6 @@
 
 #include <grpc/support/log.h>
 
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gprpp/thd.h"
 
 namespace grpc_event_engine {
@@ -37,7 +36,7 @@
 namespace {
 // TODO(drfloob): Remove this, and replace it with the WorkQueue* for the
 // current thread (with nullptr indicating not a threadpool thread).
-GPR_THREAD_LOCAL(bool) g_threadpool_thread;
+thread_local bool g_threadpool_thread;
 }  // namespace
 
 void ThreadPool::StartThread(StatePtr state, bool throttled) {
diff --git a/src/core/lib/gpr/log_linux.cc b/src/core/lib/gpr/log_linux.cc
index 850ee13..6417cc5 100644
--- a/src/core/lib/gpr/log_linux.cc
+++ b/src/core/lib/gpr/log_linux.cc
@@ -44,7 +44,6 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gprpp/examine_stack.h"
 
 int gpr_should_log_stacktrace(gpr_log_severity severity);
@@ -77,7 +76,7 @@
   time_t timer;
   gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
   struct tm tm;
-  static GPR_THREAD_LOCAL(long) tid(0);
+  static thread_local long tid(0);
   if (tid == 0) tid = sys_gettid();
 
   timer = static_cast<time_t>(now.tv_sec);
diff --git a/src/core/lib/gpr/tls.h b/src/core/lib/gpr/tls.h
deleted file mode 100644
index 2a85565..0000000
--- a/src/core/lib/gpr/tls.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- *
- * 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 <grpc/support/port_platform.h>
-
-#include <type_traits>
-
-/** 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 <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 <algorithm>
-#include <array>
-#include <cstring>
-
-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) {
-            if (0 != pthread_key_create(&key, nullptr)) abort();
-          }
-          return keys;
-        }()) {}
-  PthreadTlsImpl(T t) : PthreadTlsImpl() { *this = t; }
-  ~PthreadTlsImpl() {
-    for (pthread_key_t key : keys_) {
-      if (0 != pthread_key_delete(key)) abort();
-    }
-  }
-
-  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->() const {
-    static_assert(std::is_pointer<T>::value,
-                  "operator-> only usable on pointers");
-    return this->operator 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);
-      if (0 != pthread_setspecific(key, reinterpret_cast<void*>(dst))) abort();
-      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
-
-#endif
-
-#endif /* GRPC_CORE_LIB_GPR_TLS_H */
diff --git a/src/core/lib/gprpp/thd_windows.cc b/src/core/lib/gprpp/thd_windows.cc
index af5046c..5b48ec3 100644
--- a/src/core/lib/gprpp/thd_windows.cc
+++ b/src/core/lib/gprpp/thd_windows.cc
@@ -30,7 +30,6 @@
 #include <grpc/support/thd_id.h>
 #include <grpc/support/time.h>
 
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gprpp/memory.h"
 #include "src/core/lib/gprpp/thd.h"
 
@@ -44,7 +43,7 @@
   bool joinable;           /* whether it is joinable */
 };
 
-GPR_THREAD_LOCAL(struct thd_info*) g_thd_info;
+thread_local struct thd_info* g_thd_info;
 
 class ThreadInternalsWindows
     : public grpc_core::internal::ThreadInternalsInterface {
diff --git a/src/core/lib/gprpp/time.cc b/src/core/lib/gprpp/time.cc
index e588b12..b6cd4b5 100644
--- a/src/core/lib/gprpp/time.cc
+++ b/src/core/lib/gprpp/time.cc
@@ -142,8 +142,7 @@
 
 }  // namespace
 
-GPR_THREAD_LOCAL(Timestamp::Source*)
-Timestamp::thread_local_time_source_{
+thread_local Timestamp::Source* Timestamp::thread_local_time_source_{
     NoDestructSingleton<GprNowTimeSource>::Get()};
 
 Timestamp ScopedTimeCache::Now() {
diff --git a/src/core/lib/gprpp/time.h b/src/core/lib/gprpp/time.h
index b695c4b..ac50ed1 100644
--- a/src/core/lib/gprpp/time.h
+++ b/src/core/lib/gprpp/time.h
@@ -30,7 +30,6 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/gpr/time_precise.h"
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gpr/useful.h"
 
 namespace grpc_core {
@@ -150,7 +149,7 @@
   explicit constexpr Timestamp(int64_t millis) : millis_(millis) {}
 
   int64_t millis_ = 0;
-  static GPR_THREAD_LOCAL(Timestamp::Source*) thread_local_time_source_;
+  static thread_local Timestamp::Source* thread_local_time_source_;
 };
 
 class ScopedTimeCache final : public Timestamp::ScopedSource {
diff --git a/src/core/lib/iomgr/ev_epoll1_linux.cc b/src/core/lib/iomgr/ev_epoll1_linux.cc
index 2bb095a..b12aacd 100644
--- a/src/core/lib/iomgr/ev_epoll1_linux.cc
+++ b/src/core/lib/iomgr/ev_epoll1_linux.cc
@@ -48,7 +48,6 @@
 
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/gpr/string.h"
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
 #include "src/core/lib/iomgr/block_annotate.h"
@@ -462,8 +461,8 @@
  * Pollset Definitions
  */
 
-static GPR_THREAD_LOCAL(grpc_pollset*) g_current_thread_pollset;
-static GPR_THREAD_LOCAL(grpc_pollset_worker*) g_current_thread_worker;
+static thread_local grpc_pollset* g_current_thread_pollset;
+static thread_local grpc_pollset_worker* g_current_thread_worker;
 
 /* The designated poller */
 static gpr_atm g_active_poller;
diff --git a/src/core/lib/iomgr/ev_poll_posix.cc b/src/core/lib/iomgr/ev_poll_posix.cc
index 968428c..2a42265 100644
--- a/src/core/lib/iomgr/ev_poll_posix.cc
+++ b/src/core/lib/iomgr/ev_poll_posix.cc
@@ -41,7 +41,6 @@
 
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/gpr/murmur_hash.h"
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/gprpp/thd.h"
 #include "src/core/lib/iomgr/block_annotate.h"
@@ -714,8 +713,8 @@
  * pollset_posix.c
  */
 
-static GPR_THREAD_LOCAL(grpc_pollset*) g_current_thread_poller;
-static GPR_THREAD_LOCAL(grpc_pollset_worker*) g_current_thread_worker;
+static thread_local grpc_pollset* g_current_thread_poller;
+static thread_local grpc_pollset_worker* g_current_thread_worker;
 
 static void remove_worker(grpc_pollset* /*p*/, grpc_pollset_worker* worker) {
   worker->prev->next = worker->next;
diff --git a/src/core/lib/iomgr/exec_ctx.cc b/src/core/lib/iomgr/exec_ctx.cc
index 5a37133..a6710a7 100644
--- a/src/core/lib/iomgr/exec_ctx.cc
+++ b/src/core/lib/iomgr/exec_ctx.cc
@@ -53,9 +53,9 @@
 
 namespace grpc_core {
 
-GPR_THREAD_LOCAL(ExecCtx*) ExecCtx::exec_ctx_;
-GPR_THREAD_LOCAL(ApplicationCallbackExecCtx*)
-ApplicationCallbackExecCtx::callback_exec_ctx_;
+thread_local ExecCtx* ExecCtx::exec_ctx_;
+thread_local ApplicationCallbackExecCtx*
+    ApplicationCallbackExecCtx::callback_exec_ctx_;
 
 bool ExecCtx::Flush() {
   bool did_something = false;
diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h
index 08aeb23..0c17258 100644
--- a/src/core/lib/iomgr/exec_ctx.h
+++ b/src/core/lib/iomgr/exec_ctx.h
@@ -30,7 +30,6 @@
 #include <grpc/support/log.h>
 
 #include "src/core/lib/gpr/time_precise.h"
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gprpp/debug_location.h"
 #include "src/core/lib/gprpp/fork.h"
 #include "src/core/lib/gprpp/time.h"
@@ -211,7 +210,7 @@
   unsigned starting_cpu_ = std::numeric_limits<unsigned>::max();
 
   ScopedTimeCache time_cache_;
-  static GPR_THREAD_LOCAL(ExecCtx*) exec_ctx_;
+  static thread_local ExecCtx* exec_ctx_;
   ExecCtx* last_exec_ctx_ = Get();
 };
 
@@ -326,7 +325,7 @@
   uintptr_t flags_{0u};
   grpc_completion_queue_functor* head_{nullptr};
   grpc_completion_queue_functor* tail_{nullptr};
-  static GPR_THREAD_LOCAL(ApplicationCallbackExecCtx*) callback_exec_ctx_;
+  static thread_local ApplicationCallbackExecCtx* callback_exec_ctx_;
 };
 
 }  // namespace grpc_core
diff --git a/src/core/lib/iomgr/executor.cc b/src/core/lib/iomgr/executor.cc
index faed120..fd6ac70 100644
--- a/src/core/lib/iomgr/executor.cc
+++ b/src/core/lib/iomgr/executor.cc
@@ -27,7 +27,6 @@
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/gprpp/memory.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
@@ -52,7 +51,7 @@
 namespace grpc_core {
 namespace {
 
-GPR_THREAD_LOCAL(ThreadState*) g_this_thread_state;
+thread_local ThreadState* g_this_thread_state;
 
 Executor* executors[static_cast<size_t>(ExecutorType::NUM_EXECUTORS)];
 
diff --git a/src/core/lib/iomgr/timer_generic.cc b/src/core/lib/iomgr/timer_generic.cc
index 72525e4..f16c549 100644
--- a/src/core/lib/iomgr/timer_generic.cc
+++ b/src/core/lib/iomgr/timer_generic.cc
@@ -31,7 +31,6 @@
 
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/gpr/spinlock.h"
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gpr/useful.h"
 #include "src/core/lib/gprpp/manual_constructor.h"
 #include "src/core/lib/gprpp/time.h"
@@ -216,7 +215,7 @@
  * has last-seen. This is an optimization to prevent the thread from checking
  * shared_mutables.min_timer (which requires acquiring shared_mutables.mu lock,
  * an expensive operation) */
-static GPR_THREAD_LOCAL(int64_t) g_last_seen_min_timer;
+static thread_local int64_t g_last_seen_min_timer;
 
 struct shared_mutables {
   /* The deadline of the next timer due across all timer shards */
diff --git a/src/core/lib/promise/activity.cc b/src/core/lib/promise/activity.cc
index 3aa995e..7c2ed56 100644
--- a/src/core/lib/promise/activity.cc
+++ b/src/core/lib/promise/activity.cc
@@ -25,7 +25,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 // GLOBALS
 
-GPR_THREAD_LOCAL(Activity*) Activity::g_current_activity_{nullptr};
+thread_local Activity* Activity::g_current_activity_{nullptr};
 
 namespace promise_detail {
 
diff --git a/src/core/lib/promise/activity.h b/src/core/lib/promise/activity.h
index 9694d5d..9c4647f 100644
--- a/src/core/lib/promise/activity.h
+++ b/src/core/lib/promise/activity.h
@@ -31,7 +31,6 @@
 
 #include <grpc/support/log.h>
 
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gprpp/construct_destruct.h"
 #include "src/core/lib/gprpp/no_destruct.h"
 #include "src/core/lib/gprpp/orphanable.h"
@@ -203,7 +202,7 @@
  private:
   // Set during RunLoop to the Activity that's executing.
   // Being set implies that mu_ is held.
-  static GPR_THREAD_LOCAL(Activity*) g_current_activity_;
+  static thread_local Activity* g_current_activity_;
 };
 
 // Owned pointer to one Activity.
diff --git a/src/core/lib/promise/context.h b/src/core/lib/promise/context.h
index 29127bb..7b0f7c8 100644
--- a/src/core/lib/promise/context.h
+++ b/src/core/lib/promise/context.h
@@ -19,8 +19,6 @@
 
 #include <utility>
 
-#include "src/core/lib/gpr/tls.h"
-
 namespace grpc_core {
 
 // To avoid accidentally creating context types, we require an explicit
@@ -44,12 +42,11 @@
 
  private:
   T* const old_;
-  static GPR_THREAD_LOCAL(T*) current_;
+  static thread_local T* current_;
 };
 
 template <typename T>
-GPR_THREAD_LOCAL(T*)
-Context<T>::current_;
+thread_local T* Context<T>::current_;
 
 template <typename T, typename F>
 class WithContext {
diff --git a/src/core/lib/surface/completion_queue.cc b/src/core/lib/surface/completion_queue.cc
index 325c08b..1ddd97d 100644
--- a/src/core/lib/surface/completion_queue.cc
+++ b/src/core/lib/surface/completion_queue.cc
@@ -42,7 +42,6 @@
 
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/gpr/spinlock.h"
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/gprpp/atomic_utils.h"
 #include "src/core/lib/gprpp/debug_location.h"
 #include "src/core/lib/gprpp/ref_counted.h"
@@ -66,8 +65,8 @@
 // with a cq cache will go into that cache, and
 // will only be returned on the thread that initialized the cache.
 // NOTE: Only one event will ever be cached.
-GPR_THREAD_LOCAL(grpc_cq_completion*) g_cached_event;
-GPR_THREAD_LOCAL(grpc_completion_queue*) g_cached_cq;
+thread_local grpc_cq_completion* g_cached_event;
+thread_local grpc_completion_queue* g_cached_cq;
 
 struct plucker {
   grpc_pollset_worker** worker;
diff --git a/src/objective-c/tests/run_one_test.sh b/src/objective-c/tests/run_one_test.sh
index ed1ea56..f18c8a3 100755
--- a/src/objective-c/tests/run_one_test.sh
+++ b/src/objective-c/tests/run_one_test.sh
@@ -68,15 +68,19 @@
 set -o pipefail  # preserve xcodebuild exit code when piping output
 
 if [ -z $PLATFORM ]; then
-DESTINATION='name=iPhone 8'
+DESTINATION='platform=iOS Simulator,name=iPhone 11'
 elif [ $PLATFORM == ios ]; then
-DESTINATION='name=iPhone 8'
+DESTINATION='platform=iOS Simulator,name=iPhone 11'
 elif [ $PLATFORM == macos ]; then
 DESTINATION='platform=macOS'
 elif [ $PLATFORM == tvos ]; then
 DESTINATION='platform=tvOS Simulator,name=Apple TV'
 fi
 
+XCODEBUILD_FLAGS="
+  IPHONEOS_DEPLOYMENT_TARGET=10
+"
+
 XCODEBUILD_FILTER_OUTPUT_SCRIPT="./xcodebuild_filter_output.sh"
 
 TEST_DEFS="HOST_PORT_LOCAL=localhost:$PLAIN_PORT \
@@ -89,4 +93,6 @@
     -scheme $SCHEME \
     -destination "${DESTINATION}" \
     GCC_PREPROCESSOR_DEFINITIONS='$GCC_PREPROCESSOR_DEFINITIONS'" $TEST_DEFS" \
-    test | "${XCODEBUILD_FILTER_OUTPUT_SCRIPT}"
+    test \
+    "${XCODEBUILD_FLAGS}" \
+    | "${XCODEBUILD_FILTER_OUTPUT_SCRIPT}"
diff --git a/test/core/gpr/BUILD b/test/core/gpr/BUILD
index e788110..4d311d4 100644
--- a/test/core/gpr/BUILD
+++ b/test/core/gpr/BUILD
@@ -138,21 +138,6 @@
 )
 
 grpc_cc_test(
-    name = "tls_test",
-    srcs = ["tls_test.cc"],
-    external_deps = [
-        "gtest",
-    ],
-    language = "C++",
-    uses_event_engine = False,
-    uses_polling = False,
-    deps = [
-        "//:gpr",
-        "//test/core/util:grpc_test_util",
-    ],
-)
-
-grpc_cc_test(
     name = "useful_test",
     srcs = ["useful_test.cc"],
     external_deps = ["gtest"],
diff --git a/test/core/gpr/tls_test.cc b/test/core/gpr/tls_test.cc
deleted file mode 100644
index a99f308..0000000
--- a/test/core/gpr/tls_test.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- *
- * 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.
- *
- */
-
-/* Test of gpr thread local storage support. */
-
-#include "src/core/lib/gpr/tls.h"
-
-#include <array>
-
-#include <gtest/gtest.h>
-
-#include "src/core/lib/gprpp/thd.h"
-#include "test/core/util/test_config.h"
-
-struct BiggerThanMachineWord {
-  size_t a, b;
-  uint8_t c;
-};
-
-static GPR_THREAD_LOCAL(BiggerThanMachineWord) test_var;
-// Fails to compile: static GPR_THREAD_LOCAL(std::unique_ptr<char>) non_trivial;
-
-namespace {
-void thd_body(void*) {
-  for (size_t i = 0; i < 100000; i++) {
-    BiggerThanMachineWord next = {i, i, uint8_t(i)};
-    test_var = next;
-    BiggerThanMachineWord read = test_var;
-    ASSERT_EQ(read.a, i);
-    ASSERT_EQ(read.b, i);
-    ASSERT_EQ(read.c, uint8_t(i)) << i;
-  }
-}
-
-TEST(ThreadLocal, ReadWrite) {
-  std::array<grpc_core::Thread, 100> threads;
-  for (grpc_core::Thread& th : threads) {
-    th = grpc_core::Thread("grpc_tls_test", thd_body, nullptr);
-    th.Start();
-  }
-  for (grpc_core::Thread& th : threads) {
-    th.Join();
-  }
-}
-
-}  // namespace
-
-int main(int argc, char* argv[]) {
-  grpc::testing::TestEnvironment env(&argc, argv);
-  ::testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
diff --git a/test/core/iomgr/ios/CFStreamTests/build_and_run_tests.sh b/test/core/iomgr/ios/CFStreamTests/build_and_run_tests.sh
index 4d77091..c6428c4 100755
--- a/test/core/iomgr/ios/CFStreamTests/build_and_run_tests.sh
+++ b/test/core/iomgr/ios/CFStreamTests/build_and_run_tests.sh
@@ -23,28 +23,42 @@
 
 XCODEBUILD_FILTER_OUTPUT_SCRIPT="../../../../../src/objective-c/tests/xcodebuild_filter_output.sh"
 
+XCODEBUILD_FLAGS="
+  IPHONEOS_DEPLOYMENT_TARGET=10
+"
+
+XCODEBUILD_DESTINATION="platform=iOS Simulator,name=iPhone 11"
+
 time ./build_tests.sh
 
 time xcodebuild \
     -workspace CFStreamTests.xcworkspace \
     -scheme CFStreamTests \
-    -destination name="iPhone 8" \
-    test | "${XCODEBUILD_FILTER_OUTPUT_SCRIPT}"
+    -destination "${XCODEBUILD_DESTINATION}" \
+    test \
+    "${XCODEBUILD_FLAGS}" \
+    | "${XCODEBUILD_FILTER_OUTPUT_SCRIPT}"
 
 time xcodebuild \
     -workspace CFStreamTests.xcworkspace \
     -scheme CFStreamTests_Asan \
-    -destination name="iPhone 8" \
-    test | "${XCODEBUILD_FILTER_OUTPUT_SCRIPT}"
+    -destination "${XCODEBUILD_DESTINATION}" \
+    test \
+    "${XCODEBUILD_FLAGS}" \
+    | "${XCODEBUILD_FILTER_OUTPUT_SCRIPT}"
 
 time xcodebuild \
     -workspace CFStreamTests.xcworkspace \
     -scheme CFStreamTests_Tsan \
-    -destination name="iPhone 8" \
-    test | "${XCODEBUILD_FILTER_OUTPUT_SCRIPT}"
+    -destination "${XCODEBUILD_DESTINATION}" \
+    test \
+    "${XCODEBUILD_FLAGS}" \
+    | "${XCODEBUILD_FILTER_OUTPUT_SCRIPT}"
 
 time xcodebuild \
     -workspace CFStreamTests.xcworkspace \
     -scheme CFStreamTests_Msan \
-    -destination name="iPhone 8" \
-    test | "${XCODEBUILD_FILTER_OUTPUT_SCRIPT}"
+    -destination "${XCODEBUILD_DESTINATION}" \
+    test \
+    "${XCODEBUILD_FLAGS}" \
+    | "${XCODEBUILD_FILTER_OUTPUT_SCRIPT}"
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc
index 903e34a..3b2e47e 100644
--- a/test/cpp/end2end/async_end2end_test.cc
+++ b/test/cpp/end2end/async_end2end_test.cc
@@ -35,7 +35,6 @@
 #include <grpcpp/server_context.h>
 
 #include "src/core/ext/filters/client_channel/backup_poller.h"
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/iomgr/port.h"
 #include "src/proto/grpc/health/v1/health.grpc.pb.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
diff --git a/test/cpp/end2end/nonblocking_test.cc b/test/cpp/end2end/nonblocking_test.cc
index b5831a0..1005d61 100644
--- a/test/cpp/end2end/nonblocking_test.cc
+++ b/test/cpp/end2end/nonblocking_test.cc
@@ -27,7 +27,6 @@
 #include <grpcpp/server_builder.h>
 #include <grpcpp/server_context.h>
 
-#include "src/core/lib/gpr/tls.h"
 #include "src/core/lib/iomgr/port.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/util/port.h"
@@ -44,7 +43,7 @@
 // non-blocking (not polls from resolver, timer thread, etc), and only when the
 // thread is waiting on polls caused by CompletionQueue::AsyncNext (not for
 // picking a port or other reasons).
-static GPR_THREAD_LOCAL(bool) g_is_nonblocking_poll;
+static thread_local bool g_is_nonblocking_poll;
 
 namespace {
 
diff --git a/test/cpp/ios/build_and_run_tests.sh b/test/cpp/ios/build_and_run_tests.sh
index 53925f6..0c84d24 100755
--- a/test/cpp/ios/build_and_run_tests.sh
+++ b/test/cpp/ios/build_and_run_tests.sh
@@ -25,9 +25,16 @@
 
 XCODEBUILD_FILTER_OUTPUT_SCRIPT="../../../src/objective-c/tests/xcodebuild_filter_output.sh"
 
+XCODEBUILD_FLAGS="
+  IPHONEOS_DEPLOYMENT_TARGET=10
+"
+
+XCODEBUILD_DESTINATION="platform=iOS Simulator,name=iPhone 11"
+
 time xcodebuild \
     -workspace Tests.xcworkspace \
     -scheme CronetTests \
-    -destination name="iPhone 8" \
+    -destination "${XCODEBUILD_DESTINATION}" \
     test \
+    "${XCODEBUILD_FLAGS}" \
     | "${XCODEBUILD_FILTER_OUTPUT_SCRIPT}"
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 32063ab..81ae997 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -2016,7 +2016,6 @@
 src/core/lib/gpr/time_precise.cc \
 src/core/lib/gpr/time_precise.h \
 src/core/lib/gpr/time_windows.cc \
-src/core/lib/gpr/tls.h \
 src/core/lib/gpr/tmpfile.h \
 src/core/lib/gpr/tmpfile_msys.cc \
 src/core/lib/gpr/tmpfile_posix.cc \
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 185eb85..b613e5d 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -1805,7 +1805,6 @@
 src/core/lib/gpr/time_precise.cc \
 src/core/lib/gpr/time_precise.h \
 src/core/lib/gpr/time_windows.cc \
-src/core/lib/gpr/tls.h \
 src/core/lib/gpr/tmpfile.h \
 src/core/lib/gpr/tmpfile_msys.cc \
 src/core/lib/gpr/tmpfile_posix.cc \
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 9562f7f..0c76c08 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -7473,30 +7473,6 @@
     "ci_platforms": [
       "linux",
       "mac",
-      "posix",
-      "windows"
-    ],
-    "cpu_cost": 1.0,
-    "exclude_configs": [],
-    "exclude_iomgrs": [],
-    "flaky": false,
-    "gtest": true,
-    "language": "c++",
-    "name": "tls_test",
-    "platforms": [
-      "linux",
-      "mac",
-      "posix",
-      "windows"
-    ],
-    "uses_polling": false
-  },
-  {
-    "args": [],
-    "benchmark": false,
-    "ci_platforms": [
-      "linux",
-      "mac",
       "posix"
     ],
     "cpu_cost": 1.0,