Fix 616-cha-unloading.

Consider cases of implicit arena reuse to prevent false positives.

Test: 616-cha-unloading

Change-Id: Ia1755fb66167279c08dd9ba59813402e798c0b79
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 3025818..ba90c17 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8917,6 +8917,15 @@
   }
 }
 
+void ClassLinker::VisitAllocators(AllocatorVisitor* visitor) const {
+  for (const ClassLoaderData& data : class_loaders_) {
+    LinearAlloc* alloc = data.allocator;
+    if (alloc != nullptr && !visitor->Visit(alloc)) {
+        break;
+    }
+  }
+}
+
 void ClassLinker::InsertDexFileInToClassLoader(ObjPtr<mirror::Object> dex_file,
                                                ObjPtr<mirror::ClassLoader> class_loader) {
   DCHECK(dex_file != nullptr);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 2f6b754..fa70f65 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -97,6 +97,14 @@
       REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0;
 };
 
+class AllocatorVisitor {
+ public:
+  virtual ~AllocatorVisitor() {}
+  // Return true to continue visiting.
+  virtual bool Visit(LinearAlloc* alloc)
+      REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0;
+};
+
 class ClassLinker {
  public:
   // Well known mirror::Class roots accessed via GetClassRoot.
@@ -664,6 +672,11 @@
       REQUIRES(!Locks::classlinker_classes_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Visit all of the allocators that belong to classloaders except boot classloader.
+  // This is used by 616-cha-unloading test to confirm memory reuse.
+  void VisitAllocators(AllocatorVisitor* visitor) const
+      REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+
   // Throw the class initialization failure recorded when first trying to initialize the given
   // class.
   void ThrowEarlierClassFailure(ObjPtr<mirror::Class> c, bool wrap_in_no_class_def = false)
diff --git a/test/616-cha-unloading/cha_unload.cc b/test/616-cha-unloading/cha_unload.cc
index 4be3456..b17be6b 100644
--- a/test/616-cha-unloading/cha_unload.cc
+++ b/test/616-cha-unloading/cha_unload.cc
@@ -19,6 +19,7 @@
 #include <iostream>
 
 #include "art_method.h"
+#include "class_linker.h"
 #include "jit/jit.h"
 #include "linear_alloc.h"
 #include "nativehelper/ScopedUtfChars.h"
@@ -29,6 +30,22 @@
 namespace art {
 namespace {
 
+class FindPointerAllocatorVisitor : public AllocatorVisitor {
+ public:
+  explicit FindPointerAllocatorVisitor(void* ptr) : is_found(false), ptr_(ptr) {}
+
+  bool Visit(LinearAlloc* alloc)
+      REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
+    is_found = alloc->Contains(ptr_);
+    return !is_found;
+  }
+
+  bool is_found;
+
+ private:
+  void* ptr_;
+};
+
 extern "C" JNIEXPORT jlong JNICALL Java_Main_getArtMethod(JNIEnv* env,
                                                           jclass,
                                                           jobject java_method) {
@@ -40,13 +57,30 @@
 extern "C" JNIEXPORT void JNICALL Java_Main_reuseArenaOfMethod(JNIEnv*,
                                                                jclass,
                                                                jlong art_method) {
-  // Create a new allocation and use it to request a specified amount of arenas.
-  // Hopefully one of them is a reused one, the one that covers the art_method pointer.
+  void* ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(art_method));
+
+  ReaderMutexLock mu(Thread::Current(), *Locks::mutator_lock_);
+  ReaderMutexLock mu2(Thread::Current(), *Locks::classlinker_classes_lock_);
+  // Check if the arena was already implicitly reused by boot classloader.
+  if (Runtime::Current()->GetLinearAlloc()->Contains(ptr)) {
+    return;
+  }
+
+  // Check if the arena was already implicitly reused by some other classloader.
+  FindPointerAllocatorVisitor visitor(ptr);
+  Runtime::Current()->GetClassLinker()->VisitAllocators(&visitor);
+  if (visitor.is_found) {
+    return;
+  }
+
+  // The arena was not reused yet. Do it explicitly.
+  // Create a new allocation and use it to request new arenas until one of them is
+  // a reused one that covers the art_method pointer.
   std::unique_ptr<LinearAlloc> alloc(Runtime::Current()->CreateLinearAlloc());
   do {
-    // Ask for a byte - it's sufficient to get an arena and not have issues with size.
+    // Ask for a byte - it's sufficient to get an arena.
     alloc->Alloc(Thread::Current(), 1);
-  } while (!alloc->Contains(reinterpret_cast<void*>(static_cast<uintptr_t>(art_method))));
+  } while (!alloc->Contains(ptr));
 }
 
 }  // namespace