ART: Refactor Thread::Init

This refactor allows the parent thread to allocate the JNIEnvExt
for the child (with a fallback in place in Init). This allows to
throw an OOME in CreateNativeThread instead of aborting in the
child.

Bug: 21291279

(cherry picked from commit 520abbd0edcf333f07164539620ce65258c72383)

Change-Id: Iccc1a5c202999f5bfacec706d9833e53135ba2fa
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index d7efe1c..10ed0f4 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -104,11 +104,11 @@
             art::Thread::TopOfManagedStackOffset<__SIZEOF_POINTER__>().Int32Value())
 
 // Offset of field Thread::tlsPtr_.managed_stack.top_quick_frame_.
-#define THREAD_SELF_OFFSET (THREAD_CARD_TABLE_OFFSET + (8 * __SIZEOF_POINTER__))
+#define THREAD_SELF_OFFSET (THREAD_CARD_TABLE_OFFSET + (9 * __SIZEOF_POINTER__))
 ADD_TEST_EQ(THREAD_SELF_OFFSET,
             art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value())
 
-#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 146 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 147 * __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET,
             art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value())
 #define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__)
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 482f656..963dd02 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -88,7 +88,8 @@
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, stack_end, managed_stack, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, managed_stack, suspend_trigger, sizeof(ManagedStack));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, suspend_trigger, jni_env, sizeof(void*));
-    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jni_env, self, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jni_env, tmp_jni_env, sizeof(void*));
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, tmp_jni_env, self, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, self, opeer, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, opeer, jpeer, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, jpeer, stack_begin, sizeof(void*));
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 65999f7..67f611e 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -196,7 +196,12 @@
     // Check that if we got here we cannot be shutting down (as shutdown should never have started
     // while threads are being born).
     CHECK(!runtime->IsShuttingDownLocked());
-    CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM()));
+    // Note: given that the JNIEnv is created in the parent thread, the only failure point here is
+    //       a mess in InitStackHwm. We do not have a reasonable way to recover from that, so abort
+    //       the runtime in such a case. In case this ever changes, we need to make sure here to
+    //       delete the tmp_jni_env, as we own it at this point.
+    CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM(), self->tlsPtr_.tmp_jni_env));
+    self->tlsPtr_.tmp_jni_env = nullptr;
     Runtime::Current()->EndThreadBirth();
   }
   {
@@ -358,37 +363,59 @@
   env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer,
                     reinterpret_cast<jlong>(child_thread));
 
-  pthread_t new_pthread;
-  pthread_attr_t attr;
-  CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), "new thread");
-  CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_DETACHED), "PTHREAD_CREATE_DETACHED");
-  CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size);
-  int pthread_create_result = pthread_create(&new_pthread, &attr, Thread::CreateCallback, child_thread);
-  CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread");
+  // Try to allocate a JNIEnvExt for the thread. We do this here as we might be out of memory and
+  // do not have a good way to report this on the child's side.
+  std::unique_ptr<JNIEnvExt> child_jni_env_ext(
+      JNIEnvExt::Create(child_thread, Runtime::Current()->GetJavaVM()));
 
-  if (pthread_create_result != 0) {
-    // pthread_create(3) failed, so clean up.
-    {
-      MutexLock mu(self, *Locks::runtime_shutdown_lock_);
-      runtime->EndThreadBirth();
+  int pthread_create_result = 0;
+  if (child_jni_env_ext.get() != nullptr) {
+    pthread_t new_pthread;
+    pthread_attr_t attr;
+    child_thread->tlsPtr_.tmp_jni_env = child_jni_env_ext.get();
+    CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), "new thread");
+    CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_DETACHED),
+                       "PTHREAD_CREATE_DETACHED");
+    CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size);
+    pthread_create_result = pthread_create(&new_pthread,
+                                           &attr,
+                                           Thread::CreateCallback,
+                                           child_thread);
+    CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread");
+
+    if (pthread_create_result == 0) {
+      // pthread_create started the new thread. The child is now responsible for managing the
+      // JNIEnvExt we created.
+      // Note: we can't check for tmp_jni_env == nullptr, as that would require synchronization
+      //       between the threads.
+      child_jni_env_ext.release();
+      return;
     }
-    // Manually delete the global reference since Thread::Init will not have been run.
-    env->DeleteGlobalRef(child_thread->tlsPtr_.jpeer);
-    child_thread->tlsPtr_.jpeer = nullptr;
-    delete child_thread;
-    child_thread = nullptr;
-    // TODO: remove from thread group?
-    env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0);
-    {
-      std::string msg(StringPrintf("pthread_create (%s stack) failed: %s",
-                                   PrettySize(stack_size).c_str(), strerror(pthread_create_result)));
-      ScopedObjectAccess soa(env);
-      soa.Self()->ThrowOutOfMemoryError(msg.c_str());
-    }
+  }
+
+  // Either JNIEnvExt::Create or pthread_create(3) failed, so clean up.
+  {
+    MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+    runtime->EndThreadBirth();
+  }
+  // Manually delete the global reference since Thread::Init will not have been run.
+  env->DeleteGlobalRef(child_thread->tlsPtr_.jpeer);
+  child_thread->tlsPtr_.jpeer = nullptr;
+  delete child_thread;
+  child_thread = nullptr;
+  // TODO: remove from thread group?
+  env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0);
+  {
+    std::string msg(child_jni_env_ext.get() == nullptr ?
+        "Could not allocate JNI Env" :
+        StringPrintf("pthread_create (%s stack) failed: %s",
+                                 PrettySize(stack_size).c_str(), strerror(pthread_create_result)));
+    ScopedObjectAccess soa(env);
+    soa.Self()->ThrowOutOfMemoryError(msg.c_str());
   }
 }
 
-bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) {
+bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) {
   // This function does all the initialization that must be run by the native thread it applies to.
   // (When we create a new thread from managed code, we allocate the Thread* in Thread::Create so
   // we can handshake with the corresponding native thread when it's ready.) Check this native
@@ -415,9 +442,15 @@
 
   tls32_.thin_lock_thread_id = thread_list->AllocThreadId(this);
 
-  tlsPtr_.jni_env = JNIEnvExt::Create(this, java_vm);
-  if (tlsPtr_.jni_env == nullptr) {
-    return false;
+  if (jni_env_ext != nullptr) {
+    DCHECK_EQ(jni_env_ext->vm, java_vm);
+    DCHECK_EQ(jni_env_ext->self, this);
+    tlsPtr_.jni_env = jni_env_ext;
+  } else {
+    tlsPtr_.jni_env = JNIEnvExt::Create(this, java_vm);
+    if (tlsPtr_.jni_env == nullptr) {
+      return false;
+    }
   }
 
   thread_list->Register(this);
diff --git a/runtime/thread.h b/runtime/thread.h
index 8c2e215..3f0d0a5 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -975,7 +975,15 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void RemoveFromThreadGroup(ScopedObjectAccess& soa) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool Init(ThreadList*, JavaVMExt*) EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_);
+  // Initialize a thread.
+  //
+  // The third parameter is not mandatory. If given, the thread will use this JNIEnvExt. In case
+  // Init succeeds, this means the thread takes ownership of it. If Init fails, it is the caller's
+  // responsibility to destroy the given JNIEnvExt. If the parameter is null, Init will try to
+  // create a JNIEnvExt on its own (and potentially fail at that stage, indicated by a return value
+  // of false).
+  bool Init(ThreadList*, JavaVMExt*, JNIEnvExt* jni_env_ext = nullptr)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_);
   void InitCardTable();
   void InitCpu();
   void CleanupCpu();
@@ -1111,8 +1119,8 @@
 
   struct PACKED(4) tls_ptr_sized_values {
       tls_ptr_sized_values() : card_table(nullptr), exception(nullptr), stack_end(nullptr),
-      managed_stack(), suspend_trigger(nullptr), jni_env(nullptr), self(nullptr), opeer(nullptr),
-      jpeer(nullptr), stack_begin(nullptr), stack_size(0),
+      managed_stack(), suspend_trigger(nullptr), jni_env(nullptr), tmp_jni_env(nullptr),
+      self(nullptr), opeer(nullptr), jpeer(nullptr), stack_begin(nullptr), stack_size(0),
       stack_trace_sample(nullptr), wait_next(nullptr), monitor_enter_object(nullptr),
       top_handle_scope(nullptr), class_loader_override(nullptr), long_jump_context(nullptr),
       instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr),
@@ -1144,6 +1152,10 @@
     // Every thread may have an associated JNI environment
     JNIEnvExt* jni_env;
 
+    // Temporary storage to transfer a pre-allocated JNIEnvExt from the creating thread to the
+    // created thread.
+    JNIEnvExt* tmp_jni_env;
+
     // Initialized to "this". On certain architectures (such as x86) reading off of Thread::Current
     // is easy but getting the address of Thread::Current is hard. This field can be read off of
     // Thread::Current to give the address.