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].