add Thread::TryJoin

Change-Id: Idcd3aee2670a9c17fc38f60e9c07244478b69729
diff --git a/aos/common/util/thread.cc b/aos/common/util/thread.cc
index bfb1f51..54a8ef2 100644
--- a/aos/common/util/thread.cc
+++ b/aos/common/util/thread.cc
@@ -1,6 +1,7 @@
 #include "aos/common/util/thread.h"
 
 #include <pthread.h>
+#include <signal.h>
 
 #include "aos/common/logging/logging.h"
 
@@ -10,9 +11,7 @@
 Thread::Thread() : started_(false), joined_(false), should_terminate_(false) {}
 
 Thread::~Thread() {
-  if (started_ && !joined_) {
-    CHECK(false);
-  }
+  CHECK(!(started_ && !joined_));
 }
 
 void Thread::Start() {
@@ -28,6 +27,40 @@
   CHECK(pthread_join(thread_, NULL) == 0);
 }
 
+bool Thread::TryJoin() {
+  CHECK(!joined_ && started_);
+#ifdef AOS_SANITIZER_thread
+  // ThreadSanitizer misses the tryjoin and then complains about leaking the
+  // thread. Instead, we'll just check if the thread is still around and then
+  // do a regular Join() iff it isn't.
+  // TODO(brians): Remove this once tsan learns about pthread_tryjoin_np.
+  const int kill_ret = pthread_kill(thread_, 0);
+  // If it's still around.
+  if (kill_ret == 0) return false;
+  // If it died, we'll get ESRCH. Otherwise, something went wrong.
+  if (kill_ret != ESRCH) {
+    PELOG(FATAL, kill_ret, "pthread_kill(thread_, 0) failed");
+  }
+  Join();
+  return true;
+#else
+  const int ret = pthread_tryjoin_np(thread_, nullptr);
+  if (ret == 0) {
+    joined_ = true;
+    return true;
+  } else if (ret == EBUSY) {
+    return false;
+  } else {
+    PELOG(FATAL, ret, "pthread_tryjoin_np(thread_, nullptr) failed");
+  }
+#endif
+}
+
+void Thread::RequestStop() {
+  CHECK(!joined_ && started_);
+  should_terminate_.store(true);
+}
+
 void Thread::WaitUntilDone() {
   CHECK(!joined_ && started_);
   joined_ = true;