Fix-forward to avoid TSAN race on detached thread deletion
diff --git a/src/core/lib/gprpp/thd.h b/src/core/lib/gprpp/thd.h
index 0d94f2e..5631c5f 100644
--- a/src/core/lib/gprpp/thd.h
+++ b/src/core/lib/gprpp/thd.h
@@ -112,19 +112,22 @@
   }
 
   /// The destructor is strictly optional; either the thread never came to life
-  /// and the constructor itself killed it or it has already been joined and
-  /// the Join function kills it. The destructor shouldn't have to do anything.
-  ~Thread() { GPR_ASSERT(impl_ == nullptr); }
+  /// and the constructor itself killed it, or it has already been joined and
+  /// the Join function kills it, or it was detached (non-joinable) and it has
+  /// run to completion and is now killing itself. The destructor shouldn't have
+  /// to do anything.
+  ~Thread() { GPR_ASSERT(!options_.joinable() || impl_ == nullptr); }
 
   void Start() {
     if (impl_ != nullptr) {
       GPR_ASSERT(state_ == ALIVE);
       state_ = STARTED;
       impl_->Start();
-      if (!options_.joinable()) {
-        state_ = DONE;
-        impl_ = nullptr;
-      }
+      // If the Thread is not joinable, then the impl_ will cause the deletion
+      // of this Thread object when the thread function completes. Since no
+      // other operation is allowed to a detached thread after Start, there is
+      // no need to change the value of the impl_ or state_ . The next operation
+      // on this object will be the deletion, which will trigger the destructor.
     } else {
       GPR_ASSERT(state_ == FAILED);
     }