Ensure one can call DisposeEnvironment during event callbacks.

Previously calling DisposeEnvironment during an event callback would
cause a currently in-use iterator to be invalidated. This could cause
undefined behavior.

Bug: 37283252
Test: ./test/testrunner/testrunner.py --host --jvmti-stress -j40
Change-Id: I49c02b925f0da5d4f66965f0fca21bf8dc83132a
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 1ddbb86..233b45c 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -131,6 +131,9 @@
   unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
   ArtJvmTiEnv* last_env = nullptr;
   for (ArtJvmTiEnv* env : envs) {
+    if (env == nullptr) {
+      continue;
+    }
     if (ShouldDispatch<kEvent>(env, thread)) {
       jint new_len = 0;
       unsigned char* new_data = nullptr;
@@ -171,7 +174,9 @@
 template <ArtJvmtiEvent kEvent, typename ...Args>
 inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const {
   for (ArtJvmTiEnv* env : envs) {
-    DispatchEvent<kEvent, Args...>(env, thread, args...);
+    if (env != nullptr) {
+      DispatchEvent<kEvent, Args...>(env, thread, args...);
+    }
   }
 }
 
@@ -253,6 +258,9 @@
 inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) {
   bool union_value = false;
   for (const ArtJvmTiEnv* stored_env : envs) {
+    if (stored_env == nullptr) {
+      continue;
+    }
     union_value |= stored_env->event_masks.global_event_mask.Test(event);
     union_value |= stored_env->event_masks.unioned_thread_event_mask.Test(event);
     if (union_value) {
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 34492a9..521494a 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -141,13 +141,21 @@
 }
 
 void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) {
-  envs.push_back(env);
+  // Since we never shrink this array we might as well try to fill gaps.
+  auto it = std::find(envs.begin(), envs.end(), nullptr);
+  if (it != envs.end()) {
+    *it = env;
+  } else {
+    envs.push_back(env);
+  }
 }
 
 void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) {
+  // Since we might be currently iterating over the envs list we cannot actually erase elements.
+  // Instead we will simply replace them with 'nullptr' and skip them manually.
   auto it = std::find(envs.begin(), envs.end(), env);
   if (it != envs.end()) {
-    envs.erase(it);
+    *it = nullptr;
     for (size_t i = static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal);
          i <= static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal);
          ++i) {
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index ae8bf0f..b9e3cf0 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -202,6 +202,8 @@
   void HandleEventType(ArtJvmtiEvent event, bool enable);
 
   // List of all JvmTiEnv objects that have been created, in their creation order.
+  // NB Some elements might be null representing envs that have been deleted. They should be skipped
+  // anytime this list is used.
   std::vector<ArtJvmTiEnv*> envs;
 
   // A union of all enabled events, anywhere.
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 26b9ccb..54786bb 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -616,11 +616,5 @@
         ],
         "bug": "b/37240685 & b/37239009",
         "variant": "jvmti-stress"
-    },
-    {
-        "tests": ["901-hello-ti-agent"],
-        "description": ["Test fails to call VMInit event for unknown reasons."],
-        "bug": "b/37283252",
-        "variant": "jvmti-stress"
     }
 ]