[vm, gc] Don't hold PageSpace::tasks_lock_ while visiting remembered cards.

The scavenger may abort while visiting the remembered cards if we run out of memory. The longjmp implementing this abort would skip ~MonitorLocker, leaving the monitor locked and (in debug mode) Thread::no_safepoint_scope_ unbalanced.

TEST=fragmentation_test
Bug: https://github.com/dart-lang/sdk/issues/45059
Change-Id: I63d77709c8d8948b5827f7ecde5b0ecb3af9a245
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/186640
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Liam Appelbe <liama@google.com>
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index 8070a58..192c3df 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -804,9 +804,11 @@
          (Thread::Current()->task_kind() == Thread::kScavengerTask));
 
   // Wait for the sweeper to finish mutating the large page list.
-  MonitorLocker ml(tasks_lock());
-  while (phase() == kSweepingLarge) {
-    ml.Wait();  // No safepoint check.
+  {
+    MonitorLocker ml(tasks_lock());
+    while (phase() == kSweepingLarge) {
+      ml.Wait();  // No safepoint check.
+    }
   }
 
   // Large pages may be added concurrently due to promotion in another scavenge
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index 5a8c0d5..e4807d5 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -1710,6 +1710,7 @@
 
   // Reverse the partial forwarding from the aborted scavenge. This also
   // rebuilds the remembered set.
+  heap_->WaitForSweeperTasksAtSafepoint(thread);
   Become::FollowForwardingPointers(thread);
 
   // Don't scavenge again until the next old-space GC has occurred. Prevents