[jitzygote] Remap boot boot image methods in zygote when single-threaded.

This avoids concurrent updates made by other threads while copying the
data.

Bug: 143569713
Bug: 119800099
Test: boots
Change-Id: Ife0141a45b8d8671ab0c5868ccf90b4799a9f5db
diff --git a/libartbase/base/utils.cc b/libartbase/base/utils.cc
index a088434..7d4dd59 100644
--- a/libartbase/base/utils.cc
+++ b/libartbase/base/utils.cc
@@ -16,6 +16,7 @@
 
 #include "utils.h"
 
+#include <dirent.h>
 #include <inttypes.h>
 #include <pthread.h>
 #include <sys/stat.h>
@@ -355,4 +356,22 @@
   return (flags & (1LL << 61)) != 0;
 }
 
+int GetTaskCount() {
+  DIR* directory = opendir("/proc/self/task");
+  if (directory == nullptr) {
+    return -1;
+  }
+
+  uint32_t count = 0;
+  struct dirent* entry = nullptr;
+  while ((entry = readdir(directory)) != nullptr) {
+    if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
+      continue;
+    }
+    ++count;
+  }
+  closedir(directory);
+  return count;
+}
+
 }  // namespace art
diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h
index 6fc537a..4bcb915 100644
--- a/libartbase/base/utils.h
+++ b/libartbase/base/utils.h
@@ -159,6 +159,9 @@
 // following accesses repopulate the memory or return zero.
 bool IsAddressKnownBackedByFileOrShared(const void* addr);
 
+// Returns the number of threads running.
+int GetTaskCount();
+
 }  // namespace art
 
 #endif  // ART_LIBARTBASE_BASE_UTILS_H_
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 25b5be5..e64908d 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -710,6 +710,8 @@
     }
   }
 
+  LOG(INFO) << "Successfully notified child processes on sharing boot image methods";
+
   // Mark that compilation of boot classpath is done, and memory can now be
   // shared. Other processes will pick up this information.
   code_cache_->GetZygoteMap()->SetCompilationState(ZygoteCompilationState::kNotifiedOk);
@@ -821,9 +823,9 @@
     }
 
     if (Runtime::Current()->IsZygote()) {
-      // Copy the boot image methods data to the mappings we created to share
-      // with the children.
-      Runtime::Current()->GetJit()->NotifyZygoteCompilationDone();
+      // Record that we are done compiling the profile.
+      Runtime::Current()->GetJit()->GetCodeCache()->GetZygoteMap()->SetCompilationState(
+          ZygoteCompilationState::kDone);
     }
   }
 
@@ -1624,6 +1626,15 @@
   if (thread_pool_ == nullptr) {
     return;
   }
+  if (Runtime::Current()->IsZygote() &&
+      code_cache_->GetZygoteMap()->IsCompilationDoneButNotNotified()) {
+    // Copy the boot image methods data to the mappings we created to share
+    // with the children. We do this here as we are the only thread running and
+    // we don't risk other threads concurrently updating the ArtMethod's.
+    CHECK_EQ(GetTaskCount(), 1);
+    NotifyZygoteCompilationDone();
+    CHECK(code_cache_->GetZygoteMap()->IsCompilationNotified());
+  }
   thread_pool_->CreateThreads();
 }
 
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 637d5e3..58cf0e3 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -127,12 +127,17 @@
     region_->WriteData(compilation_state_, state);
   }
 
+  bool IsCompilationDoneButNotNotified() const {
+    return compilation_state_ != nullptr && *compilation_state_ == ZygoteCompilationState::kDone;
+  }
+
   bool IsCompilationNotified() const {
-    return *compilation_state_ > ZygoteCompilationState::kDone;
+    return compilation_state_ != nullptr && *compilation_state_ > ZygoteCompilationState::kDone;
   }
 
   bool CanMapBootImageMethods() const {
-    return *compilation_state_ == ZygoteCompilationState::kNotifiedOk;
+    return compilation_state_ != nullptr &&
+           *compilation_state_ == ZygoteCompilationState::kNotifiedOk;
   }
 
  private: