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.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 189439174
Change-Id: I268a0ef6e5aa838347956febca0d3b6e02fe3ae5
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 bf8b00a..676bfd8 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();