Add dex2oat watch dog thread for host builds

Change-Id: I90b4b6b3a1aebb82955b4aa84d3f2d599af1d13b
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index bbc07a6..40218a8 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -473,7 +473,108 @@
   return failure_count;
 }
 
+// The primary goal of the watchdog is to prevent stuck build servers
+// during development when fatal aborts lead to a cascade of failures
+// that result in a deadlock.
+class WatchDog {
+
+// WatchDog defines its own CHECK_PTHREAD_CALL to avoid using Log which uses locks
+#undef CHECK_PTHREAD_CALL
+#define CHECK_WATCH_DOG_PTHREAD_CALL(call, args, what) \
+  do { \
+    int rc = call args; \
+    if (rc != 0) { \
+      errno = rc; \
+      std::string message(# call); \
+      message += " failed for "; \
+      message += reason; \
+      Die(message); \
+    } \
+  } while (false)
+
+ public:
+  WatchDog() {
+    if (!kIsWatchDogEnabled) {
+      return;
+    }
+    shutting_down_ = false;
+    const char* reason = "dex2oat watch dog thread startup";
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_init, (&mutex_, NULL), reason);
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_cond_init, (&cond_, NULL), reason);
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_attr_init, (&attr_), reason);
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_create, (&pthread_, &attr_, &CallBack, this), reason);
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_attr_destroy, (&attr_), reason);
+  }
+  ~WatchDog() {
+    if (!kIsWatchDogEnabled) {
+      return;
+    }
+    const char* reason = "dex2oat watch dog thread shutdown";
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason);
+    shutting_down_ = true;
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_cond_signal, (&cond_), reason);
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_unlock, (&mutex_), reason);
+
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_join, (pthread_, NULL), reason);
+
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_cond_destroy, (&cond_), reason);
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_destroy, (&mutex_), reason);
+  }
+
+ private:
+  static void* CallBack(void* arg) {
+    WatchDog* self = reinterpret_cast<WatchDog*>(arg);
+    self->Wait();
+    return NULL;
+  }
+
+  static void Die(const std::string& message) {
+    // TODO: Switch to LOG(FATAL) when we can guarantee it won't prevent shutdown in error cases
+    fprintf(stderr, "%s\n", message.c_str());
+    exit(1);
+  }
+
+  void Wait() {
+    int64_t ms = kWatchDogTimeoutSeconds * 1000;
+    int32_t ns = 0;
+    timespec ts;
+    InitTimeSpec(true, CLOCK_REALTIME, ms, ns, &ts);
+    const char* reason = "dex2oat watch dog thread waiting";
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason);
+    while (!shutting_down_) {
+      int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &mutex_, &ts));
+      if (rc == ETIMEDOUT) {
+        std::string message(StringPrintf("dex2oat did not finish after %d seconds",
+                                         kWatchDogTimeoutSeconds));
+        Die(message.c_str());
+      }
+      if (rc != 0) {
+        std::string message(StringPrintf("pthread_cond_timedwait failed: %s",
+                                         strerror(errno)));
+        Die(message.c_str());
+      }
+    }
+    CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_unlock, (&mutex_), reason);
+  }
+
+  static const bool kIsWatchDogEnabled = !kIsTargetBuild;
+#ifdef ART_USE_LLVM_COMPILER
+  static const unsigned int kWatchDogTimeoutSeconds = 20 * 60; // 15 minutes + buffer
+#else
+  static const unsigned int kWatchDogTimeoutSeconds = 2 * 60;  // 1 minute + buffer
+#endif
+
+  bool shutting_down_;
+  // TODO: Switch to Mutex when we can guarantee it won't prevent shutdown in error cases
+  pthread_mutex_t mutex_;
+  pthread_cond_t cond_;
+  pthread_attr_t attr_;
+  pthread_t pthread_;
+};
+
 static int dex2oat(int argc, char** argv) {
+  WatchDog watch_dog;
+
   InitLogging(argv);
 
   // Skip over argv[0].
diff --git a/src/globals.h b/src/globals.h
index 8577b43..dc9341a 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -66,6 +66,13 @@
 const bool kIsDebugBuild = true;
 #endif
 
+// Whether or not this is a target (vs host) build. Useful in conditionals where ART_TARGET isn't.
+#if defined(ART_TARGET)
+const bool kIsTargetBuild = true;
+#else
+const bool kIsTargetBuild = false;
+#endif
+
 }  // namespace art
 
 #endif  // ART_SRC_GLOBALS_H_
diff --git a/src/mutex.cc b/src/mutex.cc
index 0e33f3c..e2044d6 100644
--- a/src/mutex.cc
+++ b/src/mutex.cc
@@ -27,12 +27,6 @@
 #include "thread.h"
 #include "utils.h"
 
-#if defined(__APPLE__)
-#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
-// No clocks to specify on OS/X, fake value to pass to routines that require a clock.
-#define CLOCK_REALTIME 0xebadf00d
-#endif
-
 #define CHECK_MUTEX_CALL(call, args) CHECK_PTHREAD_CALL(call, args, name_)
 
 extern int pthread_mutex_lock(pthread_mutex_t* mutex) EXCLUSIVE_LOCK_FUNCTION(mutex);
@@ -100,43 +94,6 @@
   }
 }
 
-// Initialize a timespec to either an absolute or relative time.
-static void InitTimeSpec(Thread* self, bool absolute, int clock, int64_t ms, int32_t ns,
-                         timespec* ts) {
-  int64_t endSec;
-
-  if (absolute) {
-#if !defined(__APPLE__)
-    clock_gettime(clock, ts);
-#else
-    UNUSED(clock);
-    timeval tv;
-    gettimeofday(&tv, NULL);
-    ts->tv_sec = tv.tv_sec;
-    ts->tv_nsec = tv.tv_usec * 1000;
-#endif
-  } else {
-    ts->tv_sec = 0;
-    ts->tv_nsec = 0;
-  }
-  endSec = ts->tv_sec + ms / 1000;
-  if (UNLIKELY(endSec >= 0x7fffffff)) {
-    std::ostringstream ss;
-    ScopedObjectAccess soa(self);
-    self->Dump(ss);
-    LOG(INFO) << "Note: end time exceeds epoch: " << ss.str();
-    endSec = 0x7ffffffe;
-  }
-  ts->tv_sec = endSec;
-  ts->tv_nsec = (ts->tv_nsec + (ms % 1000) * 1000000) + ns;
-
-  // Catch rollover.
-  if (ts->tv_nsec >= 1000000000L) {
-    ts->tv_sec++;
-    ts->tv_nsec -= 1000000000L;
-  }
-}
-
 #if ART_USE_FUTEXES
 static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, const timespec& rhs) {
   const long int one_sec = 1000 * 1000 * 1000;  // one second in nanoseconds.
@@ -563,7 +520,7 @@
   exclusive_owner_ = SafeGetTid(self);
 #else
   timespec ts;
-  InitTimeSpec(self, true, CLOCK_REALTIME, ms, ns, &ts);
+  InitTimeSpec(true, CLOCK_REALTIME, ms, ns, &ts);
   int result = pthread_rwlock_timedwrlock(&rwlock_, &ts);
   if (result == ETIMEDOUT) {
     return false;
@@ -891,8 +848,8 @@
 #endif
   guard_.recursion_count_ = 0;
   timespec ts;
-  InitTimeSpec(self, true, clock, ms, ns, &ts);
-  int rc = TIMEDWAIT(&cond_, &guard_.mutex_, &ts);
+  InitTimeSpec(true, clock, ms, ns, &ts);
+  int rc = TEMP_FAILURE_RETRY(TIMEDWAIT(&cond_, &guard_.mutex_, &ts));
   if (rc != 0 && rc != ETIMEDOUT) {
     errno = rc;
     PLOG(FATAL) << "TimedWait failed for " << name_;
diff --git a/src/thread_list.cc b/src/thread_list.cc
index 43a0cee..1a2fd47 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -207,7 +207,7 @@
       // Shouldn't need to wait for longer than 1 millisecond.
       const uint64_t threshold = 1;
       if (NsToMs(end - start) > threshold) {
-        LOG(INFO) << "Warning: waited longer than " << threshold << " ms for thrad suspend"
+        LOG(INFO) << "Warning: waited longer than " << threshold << " ms for thread suspend"
                   << std::endl;
       }
     }
diff --git a/src/thread_pool.cc b/src/thread_pool.cc
index f3319e2..ba53113 100644
--- a/src/thread_pool.cc
+++ b/src/thread_pool.cc
@@ -11,6 +11,7 @@
       name_(name),
       stack_size_(stack_size) {
   const char* reason = "new thread pool worker thread";
+  pthread_attr_t attr;
   CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), reason);
   CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), reason);
   CHECK_PTHREAD_CALL(pthread_create, (&pthread_, &attr, &Callback, this), reason);
diff --git a/src/thread_pool.h b/src/thread_pool.h
index 22e30b7..1d0f85d 100644
--- a/src/thread_pool.h
+++ b/src/thread_pool.h
@@ -47,7 +47,6 @@
   const std::string name_;
   const size_t stack_size_;
   pthread_t pthread_;
-  pthread_attr_t attr;
 
   friend class ThreadPool;
   DISALLOW_COPY_AND_ASSIGN(ThreadPoolWorker);
diff --git a/src/utils.cc b/src/utils.cc
index cbe07a2..3cf6914 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -180,6 +180,39 @@
 #endif
 }
 
+void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts) {
+  int64_t endSec;
+
+  if (absolute) {
+#if !defined(__APPLE__)
+    clock_gettime(clock, ts);
+#else
+    UNUSED(clock);
+    timeval tv;
+    gettimeofday(&tv, NULL);
+    ts->tv_sec = tv.tv_sec;
+    ts->tv_nsec = tv.tv_usec * 1000;
+#endif
+  } else {
+    ts->tv_sec = 0;
+    ts->tv_nsec = 0;
+  }
+  endSec = ts->tv_sec + ms / 1000;
+  if (UNLIKELY(endSec >= 0x7fffffff)) {
+    std::ostringstream ss;
+    LOG(INFO) << "Note: end time exceeds epoch: " << ss.str();
+    endSec = 0x7ffffffe;
+  }
+  ts->tv_sec = endSec;
+  ts->tv_nsec = (ts->tv_nsec + (ms % 1000) * 1000000) + ns;
+
+  // Catch rollover.
+  if (ts->tv_nsec >= 1000000000L) {
+    ts->tv_sec++;
+    ts->tv_nsec -= 1000000000L;
+  }
+}
+
 std::string PrettyDescriptor(const String* java_descriptor) {
   if (java_descriptor == NULL) {
     return "null";
diff --git a/src/utils.h b/src/utils.h
index 719ce57..d33cc4b 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -285,6 +285,14 @@
   return ns * 1000 * 1000;
 }
 
+#if defined(__APPLE__)
+// No clocks to specify on OS/X, fake value to pass to routines that require a clock.
+#define CLOCK_REALTIME 0xebadf00d
+#endif
+
+// Initialize a timespec to either an absolute or relative time.
+void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts);
+
 // Splits a string using the given separator character into a vector of
 // strings. Empty strings will be omitted.
 void Split(const std::string& s, char separator, std::vector<std::string>& result);