[vm/gc] Account larger transferable to old space.

Currently external data is counted twice on the receiver size: once for
received transferable, second for newly created external typed data.

Ensure new space is collected when allocating external at the limit.

TEST=SendReceiveBytesTransferable with verbose-gc

Change-Id: I129d913f89e098b5d3066ce249ac0c4702e1394d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/199842
Commit-Queue: Alexander Aprelev <aam@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index a5d848f..e15ff53 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -1133,6 +1133,7 @@
     Exceptions::ThrowArgumentError(error);
     UNREACHABLE();
   }
+  tpeer->handle()->EnsureFreedExternal(IsolateGroup::Current());
   tpeer->ClearData();
 
   const ExternalTypedData& typed_data = ExternalTypedData::Handle(
diff --git a/runtime/tests/vm/dart/transferable_allocations_test.dart b/runtime/tests/vm/dart/transferable_allocations_test.dart
new file mode 100644
index 0000000..ec61786
--- /dev/null
+++ b/runtime/tests/vm/dart/transferable_allocations_test.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Verifies that Transferable (external old space) objects are promptly gc'ed.
+// The test will run out of ia32 3GB heap allocation if objects are not gc'ed.
+
+// VMOptions=--old_gen_heap_size=32
+
+import 'dart:isolate';
+import 'dart:typed_data';
+
+void main() {
+  final data = Uint8List.view(new Uint8List(5 * 1024 * 1024).buffer);
+  for (int i = 0; i < 1000; i++) {
+    TransferableTypedData.fromList(<Uint8List>[data]).materialize();
+  }
+}
diff --git a/runtime/tests/vm/dart_2/transferable_allocations_test.dart b/runtime/tests/vm/dart_2/transferable_allocations_test.dart
new file mode 100644
index 0000000..ec61786
--- /dev/null
+++ b/runtime/tests/vm/dart_2/transferable_allocations_test.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Verifies that Transferable (external old space) objects are promptly gc'ed.
+// The test will run out of ia32 3GB heap allocation if objects are not gc'ed.
+
+// VMOptions=--old_gen_heap_size=32
+
+import 'dart:isolate';
+import 'dart:typed_data';
+
+void main() {
+  final data = Uint8List.view(new Uint8List(5 * 1024 * 1024).buffer);
+  for (int i = 0; i < 1000; i++) {
+    TransferableTypedData.fromList(<Uint8List>[data]).materialize();
+  }
+}
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 411dbce..616c6bf 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -163,6 +163,9 @@
   }
 
   if (old_space_.ReachedHardThreshold()) {
+    if (last_gc_was_old_space_) {
+      CollectNewSpaceGarbage(Thread::Current(), kFull);
+    }
     CollectGarbage(kMarkSweep, kExternal);
   } else {
     CheckStartConcurrentMarking(Thread::Current(), kExternal);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index bdef06a..6f0bc6c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -24851,8 +24851,7 @@
 }
 
 TransferableTypedDataPtr TransferableTypedData::New(uint8_t* data,
-                                                    intptr_t length,
-                                                    Heap::Space space) {
+                                                    intptr_t length) {
   TransferableTypedDataPeer* peer = new TransferableTypedDataPeer(data, length);
 
   Thread* thread = Thread::Current();
@@ -24860,7 +24859,8 @@
   {
     ObjectPtr raw = Object::Allocate(
         TransferableTypedData::kClassId, TransferableTypedData::InstanceSize(),
-        space, TransferableTypedData::ContainsCompressedPointers());
+        thread->heap()->SpaceForExternal(length),
+        TransferableTypedData::ContainsCompressedPointers());
     NoSafepointScope no_safepoint;
     thread->heap()->SetPeer(raw, peer);
     result ^= raw;
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 8461552..c3361b3 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -11138,9 +11138,7 @@
 
 class TransferableTypedData : public Instance {
  public:
-  static TransferableTypedDataPtr New(uint8_t* data,
-                                      intptr_t len,
-                                      Heap::Space space = Heap::kNew);
+  static TransferableTypedDataPtr New(uint8_t* data, intptr_t len);
 
   static intptr_t InstanceSize() {
     return RoundedAllocationSize(sizeof(UntaggedTransferableTypedData));
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 6617eec..655857e 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -1689,7 +1689,7 @@
   ASSERT(reader != nullptr);
 
   ASSERT(!Snapshot::IsFull(kind));
-  const intptr_t length = reader->Read<int32_t>();
+  const intptr_t length = reader->Read<int64_t>();
 
   const FinalizableData finalizable_data =
       static_cast<MessageSnapshotReader*>(reader)->finalizable_data()->Take();
@@ -1726,7 +1726,7 @@
 
   writer->WriteIndexedObject(GetClassId());
   writer->WriteTags(writer->GetObjectTags(this));
-  writer->Write<int32_t>(length);
+  writer->Write<int64_t>(length);
 
   static_cast<MessageWriter*>(writer)->finalizable_data()->Put(
       length, data, tpeer,