Avoid race on Thread::tlsPtr_::top_handle_scope.

Require mutator lock for that field and update tests to hold
the mutator lock when needed. This prevents GC thread that
executes a thread roots flip on behalf of suspended threads
from racing against construction or destruction of handle
scopes by those threads and possibly seeing invalid values.

(cherry picked from commit 1d326f94a3fdd6292ccdf0022cedfb2a2b8acfee)

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 189439174
Merged-In: I268a0ef6e5aa838347956febca0d3b6e02fe3ae5
Change-Id: If4cdcf4a488e77cb5aa7748de7c77c49f04388b0
diff --git a/compiler/optimizing/instruction_simplifier_test.cc b/compiler/optimizing/instruction_simplifier_test.cc
index ac0bdb9..2063eed 100644
--- a/compiler/optimizing/instruction_simplifier_test.cc
+++ b/compiler/optimizing/instruction_simplifier_test.cc
@@ -127,7 +127,8 @@
 // target_phi = PHI[param2, param3, obj2]
 // return PredFieldGet[val_phi, target_phi] => PredFieldGet[val_phi, target_phi]
 TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoMerge) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -219,7 +220,8 @@
 // target_phi = PHI[param2, param3, obj2]
 // return PredFieldGet[val_phi, target_phi] => PredFieldGet[3, target_phi]
 TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetMerge) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -308,7 +310,8 @@
 // target_phi = PHI[obj1, obj2]
 // return PredFieldGet[val_phi, target_phi] => FieldGet[target_phi]
 TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoNull) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -394,7 +397,8 @@
 // EXIT
 // return obj.field
 TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassInstanceOfOther) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   InitGraph(/*handles=*/&vshs);
 
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
@@ -483,7 +487,8 @@
 // EXIT
 // return obj
 TEST_P(InstanceOfInstructionSimplifierTestGroup, ExactClassCheckCastOther) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   InitGraph(/*handles=*/&vshs);
 
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", {{"entry", "exit"}}));
diff --git a/compiler/optimizing/load_store_analysis_test.cc b/compiler/optimizing/load_store_analysis_test.cc
index 67abc0f..cebc3f3 100644
--- a/compiler/optimizing/load_store_analysis_test.cc
+++ b/compiler/optimizing/load_store_analysis_test.cc
@@ -1265,7 +1265,8 @@
 // // EXIT
 // obj.field;
 TEST_F(LoadStoreAnalysisTest, PartialEscape5) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList(
       "entry",
diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc
index 1424652..812a32a 100644
--- a/compiler/optimizing/load_store_elimination_test.cc
+++ b/compiler/optimizing/load_store_elimination_test.cc
@@ -1233,7 +1233,8 @@
 //   return t;
 // }
 TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blocks(graph_,
                             GetAllocator(),
@@ -1361,7 +1362,8 @@
 //   return t;
 // }
 TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blocks(graph_,
                             GetAllocator(),
@@ -1500,7 +1502,8 @@
 }
 
 TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blocks(graph_,
                             GetAllocator(),
@@ -1594,7 +1597,8 @@
 }
 
 TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blocks(graph_,
                             GetAllocator(),
@@ -1685,7 +1689,8 @@
 // array location with index `idx + constant`. This could have led to
 // replacing the load with, for example, the default value 0.
 TEST_F(LoadStoreEliminationTest, ArrayLoopAliasing1) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blocks(graph_,
                             GetAllocator(),
@@ -1778,7 +1783,8 @@
 // loop unrolling. This gtest does not need to jump through those hoops
 // as we do not unnecessarily run those optimization passes.
 TEST_F(LoadStoreEliminationTest, ArrayLoopAliasing2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blocks(graph_,
                             GetAllocator(),
@@ -2043,7 +2049,8 @@
 // EXIT
 // return PHI(foo_l, foo_r)
 TEST_F(LoadStoreEliminationTest, PartialLoadElimination) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit_REAL",
@@ -2119,7 +2126,8 @@
 // return obj.field
 // This test runs with partial LSE disabled.
 TEST_F(LoadStoreEliminationTest, PartialLoadPreserved) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit_REAL",
@@ -2190,7 +2198,8 @@
 // return obj.field
 // NB This test is for non-partial LSE flow. Normally the obj.field writes will be removed
 TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit_REAL",
@@ -2278,7 +2287,8 @@
 // ELIMINATE
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PartialLoadElimination2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -2386,7 +2396,8 @@
 // // first = phi[out.foo, 13]
 // return first + new_inst.foo;
 TEST_F(LoadStoreEliminationTest, PartialPhiPropagation) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -2545,7 +2556,8 @@
 // return select(param3, obj1.foo, obj2.foo);
 // EXIT
 TEST_P(OrderDependentTestGroup, PredicatedUse) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -2712,7 +2724,8 @@
 // return obj1.foo + obj2.foo;
 // EXIT
 TEST_P(OrderDependentTestGroup, PredicatedEnvUse) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -2860,7 +2873,8 @@
 // predicated-ELIMINATE
 // return obj1.field + obj2.field
 TEST_P(OrderDependentTestGroup, FieldSetOrderEnv) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -3031,7 +3045,8 @@
 // // EXIT
 // return;
 TEST_P(OrderDependentTestGroup, MaterializationMovedUse) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -3141,7 +3156,8 @@
 // } else {}
 // EXIT
 TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -3226,7 +3242,8 @@
 // noescape();
 // return a + b + c
 TEST_F(LoadStoreEliminationTest, MutiPartialLoadStore) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -3385,7 +3402,8 @@
 // EXIT
 // return a + b + c + obj.foo
 TEST_F(LoadStoreEliminationTest, MutiPartialLoadStore2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   // Need to have an actual entry block since we check env-layout and the way we
   // add constants would screw this up otherwise.
@@ -3552,7 +3570,8 @@
 // }
 // EXIT
 TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -3663,7 +3682,8 @@
 // return obj.a;
 // EXIT
 TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc3) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -3781,7 +3801,8 @@
 // return obj.a;
 // EXIT
 TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc4) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   // Break the critical edge between entry and set_two with the
   // set_two_critical_break node. Graph simplification would do this for us if
@@ -3913,7 +3934,8 @@
 // return obj.a;
 // EXIT
 TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc5) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   // Break the critical edge between entry and set_two with the
   // set_two_critical_break node. Graph simplification would do this for us if
@@ -4044,7 +4066,8 @@
 // }
 // EXIT
 TEST_F(LoadStoreEliminationTest, PartialLoadElimination3) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList(
       "entry",
@@ -4121,7 +4144,8 @@
 // }
 // EXIT
 TEST_F(LoadStoreEliminationTest, PartialLoadElimination4) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -4223,7 +4247,8 @@
 // ELIMINATE
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PartialLoadElimination5) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -4306,7 +4331,8 @@
 // ELIMINATE
 // return obj.fid
 TEST_F(LoadStoreEliminationTest, PartialLoadElimination6) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -4398,7 +4424,8 @@
 // return obj.field;
 // EXIT
 TEST_F(LoadStoreEliminationTest, PartialLoadPreserved3) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -4506,7 +4533,8 @@
 // return obj.field;
 // EXIT
 TEST_F(LoadStoreEliminationTest, PartialLoadPreserved4) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -4611,7 +4639,8 @@
 // ELIMINATE
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PartialLoadPreserved5) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -4789,7 +4818,8 @@
 // PREDICATED GET
 // return obj.field
 TEST_P(PartialComparisonTestGroup, PartialComparisonBeforeCohort) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -4917,7 +4947,8 @@
 // PREDICATED GET
 // return obj.field
 TEST_P(PartialComparisonTestGroup, PartialComparisonInCohortBeforeEscape) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -5046,7 +5077,8 @@
 // PREDICATED GET
 // return obj.field
 TEST_P(PartialComparisonTestGroup, PartialComparisonAfterCohort) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -5176,7 +5208,8 @@
 // return obj.field
 TEST_P(PartialComparisonTestGroup, PartialComparisonInCohortAfterEscape) {
   PartialComparisonKind kind = GetParam();
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -5335,7 +5368,8 @@
 // predicated-ELIMINATE
 // obj.field = 3;
 TEST_F(LoadStoreEliminationTest, PredicatedStore1) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   InitGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -5423,7 +5457,8 @@
 // predicated-ELIMINATE
 // obj.field = 4;
 TEST_F(LoadStoreEliminationTest, PredicatedStore2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -5534,7 +5569,8 @@
 // predicated-ELIMINATE
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PredicatedLoad1) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -5635,7 +5671,8 @@
 // predicated-ELIMINATE
 // return obj1.field + obj2.field
 TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad1) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -5787,7 +5824,8 @@
 // predicated-ELIMINATE
 // return obj1.field + obj2.field
 TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -5951,7 +5989,8 @@
 // // allow us to entirely remove the allocation in this test.
 // return obj.foo;
 TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad3) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -6081,7 +6120,8 @@
 // predicated-ELIMINATE
 // return obj.field + abc
 TEST_F(LoadStoreEliminationTest, PredicatedLoad4) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -6221,7 +6261,8 @@
 // // won't be changed. The escape happens with .BAR set so this is in escaping cohort.
 // return read_bottom.foo;
 TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad4) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -6325,7 +6366,8 @@
 // predicated-ELIMINATE
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PredicatedLoad2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -6462,7 +6504,8 @@
 // predicated-ELIMINATE
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PredicatedLoad3) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -6589,7 +6632,8 @@
 // predicated-ELIMINATE
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PredicatedLoadDefaultValue) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -6685,7 +6729,8 @@
 // // predicated-ELIMINATE
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PartialLoopPhis1) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -6871,7 +6916,8 @@
 // // predicated-ELIMINATE
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PartialLoopPhis2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -7041,7 +7087,8 @@
 // EXIT
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PartialLoopPhis3) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -7188,7 +7235,8 @@
 // EXIT
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PartialLoopPhis4) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -7332,7 +7380,8 @@
 // EXIT
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PartialLoopPhis5) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -7490,7 +7539,8 @@
 // }
 // return obj.field
 TEST_F(LoadStoreEliminationTest, PartialLoopPhis6) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(/*handles=*/&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -7649,7 +7699,8 @@
 // }
 // return obj.field;
 TEST_F(LoadStoreEliminationTest, SimplifyTest) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -7740,7 +7791,8 @@
 // }
 // return obj.field;
 TEST_F(LoadStoreEliminationTest, SimplifyTest2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -7843,7 +7895,8 @@
 // }
 // return obj.field;
 TEST_F(LoadStoreEliminationTest, SimplifyTest3) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -7949,7 +8002,8 @@
 // }
 // return obj.field;
 TEST_F(LoadStoreEliminationTest, SimplifyTest4) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -8061,7 +8115,8 @@
 // }
 // return obj.foo;
 TEST_F(LoadStoreEliminationTest, PartialIrreducibleLoop) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("start",
                                                  "exit",
@@ -8229,7 +8284,8 @@
 // b = obj.foo;
 // return a + b;
 TEST_P(UsesOrderDependentTestGroup, RecordPredicatedReplacements1) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -8393,7 +8449,8 @@
 // b = obj.foo;
 // return a + b;
 TEST_P(UsesOrderDependentTestGroup, RecordPredicatedReplacements2) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
@@ -8566,7 +8623,8 @@
 // b = obj.foo;
 // return a + b + x;
 TEST_P(UsesOrderDependentTestGroupForThreeItems, RecordPredicatedReplacements3) {
-  VariableSizedHandleScope vshs(Thread::Current());
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope vshs(soa.Self());
   CreateGraph(&vshs);
   AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
                                                  "exit",
diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h
index cb0333f..3aa9e52 100644
--- a/runtime/handle_scope-inl.h
+++ b/runtime/handle_scope-inl.h
@@ -44,26 +44,22 @@
 }
 
 template<size_t kNumReferences>
-inline FixedSizeHandleScope<kNumReferences>::FixedSizeHandleScope(BaseHandleScope* link)
-    : HandleScope(link, kNumReferences) {
-  static_assert(kNumReferences >= 1, "FixedSizeHandleScope must contain at least 1 reference");
-  DCHECK_EQ(&storage_[0], GetReferences());  // TODO: Figure out how to use a compile assert.
-  for (size_t i = 0; i < kNumReferences; ++i) {
-    SetReferenceToNull(i);
-  }
-}
-
-template<size_t kNumReferences>
 inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self,
                                                           ObjPtr<mirror::Object> fill_value)
     : FixedSizeHandleScope<kNumReferences>(self->GetTopHandleScope(), fill_value),
       self_(self) {
   DCHECK_EQ(self, Thread::Current());
+  if (kDebugLocking) {
+    Locks::mutator_lock_->AssertSharedHeld(self_);
+  }
   self_->PushHandleScope(this);
 }
 
 template<size_t kNumReferences>
 inline StackHandleScope<kNumReferences>::~StackHandleScope() {
+  if (kDebugLocking) {
+    Locks::mutator_lock_->AssertSharedHeld(self_);
+  }
   BaseHandleScope* top_handle_scope = self_->PopHandleScope();
   DCHECK_EQ(top_handle_scope, this);
 }
@@ -161,12 +157,6 @@
   GetReferences()[i].Assign(object);
 }
 
-template<size_t kNumReferences>
-inline void FixedSizeHandleScope<kNumReferences>::SetReferenceToNull(size_t i) {
-  DCHECK_LT(i, kNumReferences);
-  GetReferences()[i].Assign(nullptr);
-}
-
 // Number of references contained within this handle scope.
 inline uint32_t BaseHandleScope::NumberOfReferences() const {
   return LIKELY(!IsVariableSized())
@@ -227,10 +217,17 @@
       self_(self),
       current_scope_(&first_scope_),
       first_scope_(/*link=*/ nullptr) {
+  DCHECK_EQ(self, Thread::Current());
+  if (kDebugLocking) {
+    Locks::mutator_lock_->AssertSharedHeld(self_);
+  }
   self_->PushHandleScope(this);
 }
 
 inline VariableSizedHandleScope::~VariableSizedHandleScope() {
+  if (kDebugLocking) {
+    Locks::mutator_lock_->AssertSharedHeld(self_);
+  }
   BaseHandleScope* top_handle_scope = self_->PopHandleScope();
   DCHECK_EQ(top_handle_scope, this);
   // Don't delete first_scope_ since it is not heap allocated.
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index ed77e19..58322d1 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -195,15 +195,10 @@
   }
 
  private:
-  explicit ALWAYS_INLINE FixedSizeHandleScope(BaseHandleScope* link);
   explicit ALWAYS_INLINE FixedSizeHandleScope(BaseHandleScope* link,
-                                              ObjPtr<mirror::Object> fill_value)
+                                              ObjPtr<mirror::Object> fill_value = nullptr)
       REQUIRES_SHARED(Locks::mutator_lock_);
-
-  ALWAYS_INLINE ~FixedSizeHandleScope() {}
-
-  // Helper to set references to null without any mutator-locks.
-  ALWAYS_INLINE void SetReferenceToNull(size_t i);
+  ALWAYS_INLINE ~FixedSizeHandleScope() REQUIRES_SHARED(Locks::mutator_lock_) {}
 
   template<class T>
   ALWAYS_INLINE MutableHandle<T> GetHandle(size_t i) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -226,8 +221,10 @@
 class PACKED(4) StackHandleScope final : public FixedSizeHandleScope<kNumReferences> {
  public:
   explicit ALWAYS_INLINE StackHandleScope(Thread* self,
-                                          ObjPtr<mirror::Object> fill_value = nullptr);
-  ALWAYS_INLINE ~StackHandleScope();
+                                          ObjPtr<mirror::Object> fill_value = nullptr)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  ALWAYS_INLINE ~StackHandleScope() REQUIRES_SHARED(Locks::mutator_lock_);
 
   Thread* Self() const {
     return self_;
@@ -246,8 +243,8 @@
 // list.
 class VariableSizedHandleScope : public BaseHandleScope {
  public:
-  explicit VariableSizedHandleScope(Thread* const self);
-  ~VariableSizedHandleScope();
+  explicit VariableSizedHandleScope(Thread* const self) REQUIRES_SHARED(Locks::mutator_lock_);
+  ~VariableSizedHandleScope() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<class T>
   MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/thread.h b/runtime/thread.h
index cd1227a..7a40802 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -908,16 +908,16 @@
   void HandleScopeVisitRoots(RootVisitor* visitor, uint32_t thread_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  BaseHandleScope* GetTopHandleScope() {
+  BaseHandleScope* GetTopHandleScope() REQUIRES_SHARED(Locks::mutator_lock_) {
     return tlsPtr_.top_handle_scope;
   }
 
-  void PushHandleScope(BaseHandleScope* handle_scope) {
+  void PushHandleScope(BaseHandleScope* handle_scope) REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK_EQ(handle_scope->GetLink(), tlsPtr_.top_handle_scope);
     tlsPtr_.top_handle_scope = handle_scope;
   }
 
-  BaseHandleScope* PopHandleScope() {
+  BaseHandleScope* PopHandleScope() REQUIRES_SHARED(Locks::mutator_lock_) {
     BaseHandleScope* handle_scope = tlsPtr_.top_handle_scope;
     DCHECK(handle_scope != nullptr);
     tlsPtr_.top_handle_scope = tlsPtr_.top_handle_scope->GetLink();