Remove aos::ComplexThreadLocal

We no longer need it with the built in language thread_local.

Signed-off-by: Tyler Chatow <tchatow@gmail.com>
Change-Id: I17c87139ee0121da9b66a80003570cfb618d39f4
diff --git a/aos/BUILD b/aos/BUILD
index 8a8a19a..025e78c 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -159,35 +159,6 @@
 )
 
 cc_library(
-    name = "complex_thread_local",
-    srcs = [
-        "complex_thread_local.cc",
-    ],
-    hdrs = [
-        "complex_thread_local.h",
-    ],
-    target_compatible_with = ["@platforms//os:linux"],
-    visibility = ["//visibility:public"],
-    deps = [
-        "//aos:die",
-        "@com_google_absl//absl/base",
-    ],
-)
-
-cc_test(
-    name = "complex_thread_local_test",
-    srcs = [
-        "complex_thread_local_test.cc",
-    ],
-    target_compatible_with = ["@platforms//os:linux"],
-    deps = [
-        ":complex_thread_local",
-        "//aos/logging",
-        "//aos/testing:googletest",
-    ],
-)
-
-cc_library(
     name = "init",
     srcs = [
         "init.cc",
diff --git a/aos/complex_thread_local.cc b/aos/complex_thread_local.cc
deleted file mode 100644
index 70f3e82..0000000
--- a/aos/complex_thread_local.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-#include "aos/complex_thread_local.h"
-
-#include <pthread.h>
-
-#include "aos/die.h"
-#include "absl/base/call_once.h"
-
-#define SIMPLE_CHECK(call)              \
-  do {                                  \
-    const int value = call;             \
-    if (value != 0) {                   \
-      PRDie(value, "%s failed", #call); \
-    }                                   \
-  } while (false)
-
-namespace aos {
-namespace {
-
-void ExecuteDestructorList(void *v) {
-  for (const ComplexThreadLocalDestructor *c =
-           static_cast<ComplexThreadLocalDestructor *>(v);
-       c != nullptr; c = c->next) {
-    c->function(c->param);
-  }
-}
-
-void CreateKey(pthread_key_t **r) {
-  static pthread_key_t hr;
-  SIMPLE_CHECK(pthread_key_create(&hr, ExecuteDestructorList));
-  *r = &hr;
-}
-
-absl::once_flag key_once;
-
-pthread_key_t *GetKey() {
-  static pthread_key_t *key = nullptr;
-  absl::call_once(key_once, CreateKey, &key);
-  return key;
-}
-} // namespace
-
-void ComplexThreadLocalDestructor::Add() {
-  static_assert(
-      ::std::is_pod<ComplexThreadLocalDestructor>::value,
-      "ComplexThreadLocalDestructor might not be safe to pass through void*");
-  pthread_key_t *key = GetKey();
-
-  next = static_cast<ComplexThreadLocalDestructor *>(pthread_getspecific(*key));
-  SIMPLE_CHECK(pthread_setspecific(*key, this));
-}
-
-void ComplexThreadLocalDestructor::Remove() {
-  pthread_key_t *key = GetKey();
-
-  ComplexThreadLocalDestructor *previous = nullptr;
-  for (ComplexThreadLocalDestructor *c =
-           static_cast<ComplexThreadLocalDestructor *>(
-               pthread_getspecific(*key));
-       c != nullptr; c = c->next) {
-    if (c == this) {
-      // If it's the first one.
-      if (previous == nullptr) {
-        SIMPLE_CHECK(pthread_setspecific(*key, next));
-      } else {
-        previous->next = next;
-      }
-      return;
-    }
-    previous = c;
-  }
-  ::aos::Die("%p is not in the destructor list\n", this);
-}
-
-}  // namespace aos
diff --git a/aos/complex_thread_local.h b/aos/complex_thread_local.h
deleted file mode 100644
index 5397f2f..0000000
--- a/aos/complex_thread_local.h
+++ /dev/null
@@ -1,128 +0,0 @@
-#ifndef AOS_COMPLEX_THREAD_LOCAL_H_
-#define AOS_COMPLEX_THREAD_LOCAL_H_
-
-#include <cassert>
-#include <type_traits>
-#include <utility>
-
-namespace aos {
-
-// Instances form a (per-thread) list of destructor functions to call when the
-// thread exits.
-// Only ComplexThreadLocal should use this.
-struct ComplexThreadLocalDestructor {
-  // Adds this to the list of destructors in this thread.
-  void Add();
-  // Removes this from the list of destructors in this thread. ::aos::Dies if it
-  // is not there.
-  void Remove();
-
-  void (*function)(void *);
-  void *param;
-
-  ComplexThreadLocalDestructor *next;
-};
-
-// Handles creating a thread-local (per type) object with non-trivial
-// constructor and/or destructor. It will be correctly destroyed on thread exit.
-//
-// Each thread using an instantiation of this class has its own independent slot
-// for storing a T. An instance of T is not actually constructed until a thread
-// calls Create, after which a pointer to it will be returned from get() etc
-// until after Clear is called.
-//
-// Example usage:
-// class Something {
-//  private:
-//   class Data {
-//    public:
-//     Data(const ::std::string &value) : value_(value) {}
-//
-//     int DoSomething() {
-//       if (cached_result_ == 0) {
-//         // Do something expensive with value_ and store it in
-//         // cached_result_.
-//       }
-//       return cached_result_;
-//     }
-//
-//    private:
-//     const ::std::string value_;
-//     int cached_result_ = 0;
-//   };
-//   ComplexThreadLocal<Data> thread_local_;
-//   ::std::string a_string_;
-//
-//   int DoSomething() {
-//     thread_local_.Create(a_string_);
-//     return thread_local_->DoSomething();
-//   }
-// };
-//
-// The current implementation is based on
-// <http://stackoverflow.com/questions/12049684/gcc-4-7-on-linux-pthreads-nontrivial-thread-local-workaround-using-thread-n>.
-// TODO(brians): Change this to just simple standard C++ thread_local once all
-// of our compilers have support.
-template <typename T>
-class ComplexThreadLocal {
- public:
-  // Actually creates the object in this thread if there is not one there
-  // already.
-  // args are all perfectly forwarded to the constructor.
-  template <typename... Args>
-  void Create(Args &&...args) {
-    if (initialized) return;
-    new (&storage) T(::std::forward<Args>(args)...);
-    destructor.function = PlacementDelete;
-    destructor.param = &storage;
-    destructor.Add();
-    initialized = true;
-  }
-
-  // Removes the object in this thread (if any), including calling its
-  // destructor.
-  void Clear() {
-    if (!initialized) return;
-    destructor.Remove();
-    PlacementDelete(&storage);
-    initialized = false;
-  }
-
-  // Returns true if there is already an object in this thread.
-  bool created() const { return initialized; }
-
-  // Returns the object currently created in this thread or nullptr.
-  T *operator->() const { return get(); }
-  T *get() const {
-    if (initialized) {
-      return static_cast<T *>(static_cast<void *>(&storage));
-    } else {
-      return nullptr;
-    }
-  }
-
- private:
-  typedef typename ::std::aligned_storage<
-      sizeof(T), ::std::alignment_of<T>::value>::type Storage;
-
-  // Convenient helper for calling a destructor.
-  static void PlacementDelete(void *t) { static_cast<T *>(t)->~T(); }
-
-  // True iff this storage has been initialized.
-  static __thread bool initialized;
-  // Where we actually store the object for this thread (if any).
-  static __thread Storage storage;
-  // The linked list element representing this storage.
-  static __thread ComplexThreadLocalDestructor destructor;
-};
-
-template <typename T>
-__thread bool ComplexThreadLocal<T>::initialized;
-template <typename T>
-__thread typename ComplexThreadLocal<T>::Storage ComplexThreadLocal<T>::storage;
-template <typename T>
-__thread ComplexThreadLocalDestructor ComplexThreadLocal<T>::destructor;
-
-}  // namespace aos
-
-#endif  // AOS_COMPLEX_THREAD_LOCAL_H_
diff --git a/aos/complex_thread_local_test.cc b/aos/complex_thread_local_test.cc
deleted file mode 100644
index 5323dea..0000000
--- a/aos/complex_thread_local_test.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-#include "aos/complex_thread_local.h"
-
-#include <atomic>
-#include <thread>
-
-#include "gtest/gtest.h"
-
-namespace aos {
-namespace testing {
-
-class ComplexThreadLocalTest : public ::testing::Test {
- protected:
-  struct TraceableObject {
-    TraceableObject(int data = 0) : data(data) { ++constructions; }
-    ~TraceableObject() { ++destructions; }
-
-    static ::std::atomic<int> constructions, destructions;
-
-    int data;
-  };
-  ComplexThreadLocal<TraceableObject> local;
-
- private:
-  void SetUp() override {
-    local.Clear();
-    EXPECT_EQ(TraceableObject::constructions, TraceableObject::destructions)
-        << "There should be no way to create and destroy different numbers.";
-    TraceableObject::constructions = TraceableObject::destructions = 0;
-  }
-};
-::std::atomic<int> ComplexThreadLocalTest::TraceableObject::constructions;
-::std::atomic<int> ComplexThreadLocalTest::TraceableObject::destructions;
-
-TEST_F(ComplexThreadLocalTest, Basic) {
-  EXPECT_EQ(0, TraceableObject::constructions);
-  EXPECT_EQ(0, TraceableObject::destructions);
-  EXPECT_FALSE(local.created());
-  EXPECT_EQ(nullptr, local.get());
-
-  local.Create(971);
-  EXPECT_EQ(1, TraceableObject::constructions);
-  EXPECT_EQ(0, TraceableObject::destructions);
-  EXPECT_TRUE(local.created());
-  EXPECT_EQ(971, local->data);
-
-  local.Create(254);
-  EXPECT_EQ(1, TraceableObject::constructions);
-  EXPECT_EQ(0, TraceableObject::destructions);
-  EXPECT_TRUE(local.created());
-  EXPECT_EQ(971, local->data);
-
-  local.Clear();
-  EXPECT_EQ(1, TraceableObject::constructions);
-  EXPECT_EQ(1, TraceableObject::destructions);
-  EXPECT_FALSE(local.created());
-  EXPECT_EQ(nullptr, local.get());
-
-  local.Create(973);
-  EXPECT_EQ(2, TraceableObject::constructions);
-  EXPECT_EQ(1, TraceableObject::destructions);
-  EXPECT_TRUE(local.created());
-  EXPECT_EQ(973, local->data);
-}
-
-TEST_F(ComplexThreadLocalTest, AnotherThread) {
-  EXPECT_FALSE(local.created());
-  std::thread t1([this]() {
-    EXPECT_FALSE(local.created());
-    local.Create(971);
-    EXPECT_TRUE(local.created());
-    EXPECT_EQ(971, local->data);
-    EXPECT_EQ(1, TraceableObject::constructions);
-    EXPECT_EQ(0, TraceableObject::destructions);
-  });
-  t1.join();
-  EXPECT_EQ(1, TraceableObject::constructions);
-  EXPECT_EQ(1, TraceableObject::destructions);
-  EXPECT_FALSE(local.created());
-}
-
-TEST_F(ComplexThreadLocalTest, TwoThreads) {
-  std::thread thread([this]() {
-    local.Create(971);
-    EXPECT_EQ(971, local->data);
-    EXPECT_EQ(0, TraceableObject::destructions);
-  });
-  local.Create(973);
-  EXPECT_EQ(973, local->data);
-  thread.join();
-  EXPECT_TRUE(local.created());
-  EXPECT_EQ(2, TraceableObject::constructions);
-  EXPECT_EQ(1, TraceableObject::destructions);
-  local.Clear();
-  EXPECT_EQ(2, TraceableObject::constructions);
-  EXPECT_EQ(2, TraceableObject::destructions);
-  EXPECT_FALSE(local.created());
-}
-
-}  // namespace testing
-}  // namespace aos
diff --git a/aos/logging/BUILD b/aos/logging/BUILD
index 30fd0d1..10d41a3 100644
--- a/aos/logging/BUILD
+++ b/aos/logging/BUILD
@@ -20,7 +20,6 @@
     deps = [
         ":printf_formats",
         ":sizes",
-        "//aos:complex_thread_local",
         "//aos:die",
         "//aos:macros",
         "//aos:thread_local",
diff --git a/aos/logging/context.cc b/aos/logging/context.cc
index 30d275b..9eaac50 100644
--- a/aos/logging/context.cc
+++ b/aos/logging/context.cc
@@ -18,7 +18,6 @@
 extern char *program_invocation_name;
 extern char *program_invocation_short_name;
 
-#include "aos/complex_thread_local.h"
 #include "aos/die.h"
 #include "aos/logging/implementations.h"
 #include "aos/thread_local.h"
@@ -63,7 +62,7 @@
   return process_name + '.' + thread_name;
 }
 
-::aos::ComplexThreadLocal<Context> my_context;
+thread_local std::optional<Context> my_context;
 
 // True if we're going to delete the current Context object ASAP. The
 // reason for doing this instead of just deleting them is that tsan (at least)
@@ -77,7 +76,7 @@
 
 // Used in aos/linux_code/init.cc when a thread's name is changed.
 void ReloadThreadName() {
-  if (my_context.created()) {
+  if (my_context.has_value()) {
     my_context->ClearName();
   }
 }
@@ -99,21 +98,21 @@
 
 Context *Context::Get() {
   if (__builtin_expect(delete_current_context, false)) {
-    my_context.Clear();
+    my_context.reset();
     delete_current_context = false;
   }
-  if (__builtin_expect(!my_context.created(), false)) {
-    my_context.Create();
+  if (__builtin_expect(!my_context.has_value(), false)) {
+    my_context.emplace();
     my_context->ClearName();
     my_context->source = getpid();
   }
-  return my_context.get();
+  return &*my_context;
 }
 
 void Context::Delete() { delete_current_context = true; }
 
 void Context::DeleteNow() {
-  my_context.Clear();
+  my_context.reset();
   delete_current_context = false;
 }