[vm] Check for safepoints while initializing or copying large arrays.
TEST=ci
Change-Id: I5ca25d3996e51210464b06492c0b8b6119c4242c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/246304
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/message_snapshot.cc b/runtime/vm/message_snapshot.cc
index 48f6aec..a24ad62 100644
--- a/runtime/vm/message_snapshot.cc
+++ b/runtime/vm/message_snapshot.cc
@@ -2848,7 +2848,7 @@
const intptr_t count = d->ReadUnsigned();
for (intptr_t i = 0; i < count; i++) {
intptr_t length = d->ReadUnsigned();
- d->AssignRef(Array::New(cid_, length));
+ d->AssignRef(Array::NewUninitialized(cid_, length));
}
}
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 46d3e1b..52de6b1 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2638,7 +2638,21 @@
initial_value |= initial_value << 32;
}
#endif
- needs_init = true;
+ if (class_id == kArrayCid) {
+ // If the size is greater than both kNewAllocatableSize and
+ // kAllocatablePageSize, the object must have been allocated to a new
+ // large page, which must already have been zero initialized by the OS.
+ // Zero is a GC-safe value. The caller will initialize the fields to
+ // null with safepoint checks to avoid blocking for the full duration of
+ // initializing this array.
+ needs_init = Heap::IsAllocatableInNewSpace(size) ||
+ Heap::IsAllocatableViaFreeLists(size);
+ if (!needs_init) {
+ initial_value = 0; // For ASSERT below.
+ }
+ } else {
+ needs_init = true;
+ }
}
if (needs_init) {
while (cur < end) {
@@ -24321,17 +24335,6 @@
return hash;
}
-ArrayPtr Array::New(intptr_t len, Heap::Space space) {
- ASSERT(IsolateGroup::Current()->object_store()->array_class() !=
- Class::null());
- ArrayPtr result = New(kClassId, len, space);
- if (UseCardMarkingForAllocation(len)) {
- ASSERT(result->IsOldObject());
- result->untag()->SetCardRememberedBitUnsynchronized();
- }
- return result;
-}
-
ArrayPtr Array::New(intptr_t len,
const AbstractType& element_type,
Heap::Space space) {
@@ -24345,7 +24348,9 @@
return result.ptr();
}
-ArrayPtr Array::New(intptr_t class_id, intptr_t len, Heap::Space space) {
+ArrayPtr Array::NewUninitialized(intptr_t class_id,
+ intptr_t len,
+ Heap::Space space) {
if (!IsValidLength(len)) {
// This should be caught before we reach here.
FATAL1("Fatal error in Array::New: invalid len %" Pd "\n", len);
@@ -24356,23 +24361,56 @@
Array::ContainsCompressedPointers()));
NoSafepointScope no_safepoint;
raw->untag()->set_length(Smi::New(len));
+ if (UseCardMarkingForAllocation(len)) {
+ ASSERT(raw->IsOldObject());
+ raw->untag()->SetCardRememberedBitUnsynchronized();
+ }
return raw;
}
}
+ArrayPtr Array::New(intptr_t class_id, intptr_t len, Heap::Space space) {
+ if (!UseCardMarkingForAllocation(len)) {
+ return NewUninitialized(class_id, len, space);
+ }
+
+ Thread* thread = Thread::Current();
+ Array& result =
+ Array::Handle(thread->zone(), NewUninitialized(class_id, len, space));
+ result.SetTypeArguments(Object::null_type_arguments());
+ for (intptr_t i = 0; i < len; i++) {
+ result.SetAt(i, Object::null_object(), thread);
+ if (((i + 1) % KB) == 0) {
+ thread->CheckForSafepoint();
+ }
+ }
+ return result.ptr();
+}
+
ArrayPtr Array::Slice(intptr_t start,
intptr_t count,
bool with_type_argument) const {
- // TODO(vegorov) introduce an array allocation method that fills newly
- // allocated array with values from the given source array instead of
- // null-initializing all elements.
- Array& dest = Array::Handle(Array::New(count));
- dest.StoreArrayPointers(dest.ObjectAddr(0), ObjectAddr(start), count);
-
+ Thread* thread = Thread::Current();
+ Zone* zone = thread->zone();
+ const Array& dest = Array::Handle(zone, Array::NewUninitialized(count));
if (with_type_argument) {
- dest.SetTypeArguments(TypeArguments::Handle(GetTypeArguments()));
+ dest.SetTypeArguments(TypeArguments::Handle(zone, GetTypeArguments()));
+ } else {
+ dest.SetTypeArguments(Object::null_type_arguments());
}
-
+ if (!UseCardMarkingForAllocation(count)) {
+ NoSafepointScope no_safepoint(thread);
+ for (int i = 0; i < count; i++) {
+ dest.untag()->set_element(i, untag()->element(i + start), thread);
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ dest.untag()->set_element(i, untag()->element(i + start), thread);
+ if (((i + 1) % KB) == 0) {
+ thread->CheckForSafepoint();
+ }
+ }
+ }
return dest.ptr();
}
@@ -24397,19 +24435,30 @@
Heap::Space space) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
- const Array& result = Array::Handle(zone, Array::New(new_length, space));
+ const Array& result =
+ Array::Handle(zone, Array::NewUninitialized(new_length, space));
intptr_t len = 0;
if (!source.IsNull()) {
len = source.Length();
result.SetTypeArguments(
TypeArguments::Handle(zone, source.GetTypeArguments()));
+ } else {
+ result.SetTypeArguments(Object::null_type_arguments());
}
ASSERT(new_length >= len); // Cannot copy 'source' into new array.
ASSERT(new_length != len); // Unnecessary copying of array.
- PassiveObject& obj = PassiveObject::Handle(zone);
- for (int i = 0; i < len; i++) {
- obj = source.At(i);
- result.SetAt(i, obj, thread);
+ if (!UseCardMarkingForAllocation(len)) {
+ NoSafepointScope no_safepoint(thread);
+ for (int i = 0; i < len; i++) {
+ result.untag()->set_element(i, source.untag()->element(i), thread);
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ result.untag()->set_element(i, source.untag()->element(i), thread);
+ if (((i + 1) % KB) == 0) {
+ thread->CheckForSafepoint();
+ }
+ }
}
return result.ptr();
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 6f226c3..17080ec 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -10419,7 +10419,15 @@
// to ImmutableArray.
void MakeImmutable() const;
- static ArrayPtr New(intptr_t len, Heap::Space space = Heap::kNew);
+ static ArrayPtr New(intptr_t len, Heap::Space space = Heap::kNew) {
+ return New(kArrayCid, len, space);
+ }
+ // The result's type arguments and elements are GC-safe but not initialized to
+ // null.
+ static ArrayPtr NewUninitialized(intptr_t len,
+ Heap::Space space = Heap::kNew) {
+ return NewUninitialized(kArrayCid, len, space);
+ }
static ArrayPtr New(intptr_t len,
const AbstractType& element_type,
Heap::Space space = Heap::kNew);
@@ -10457,6 +10465,9 @@
static ArrayPtr New(intptr_t class_id,
intptr_t len,
Heap::Space space = Heap::kNew);
+ static ArrayPtr NewUninitialized(intptr_t class_id,
+ intptr_t len,
+ Heap::Space space = Heap::kNew);
private:
CompressedObjectPtr const* ObjectAddr(intptr_t index) const {
@@ -10477,25 +10488,6 @@
ptr()->untag()->StoreArrayPointer<type, order, value_type>(addr, value);
}
- // Store a range of pointers [from, from + count) into [to, to + count).
- // TODO(koda): Use this to fix Object::Clone's broken store buffer logic.
- void StoreArrayPointers(CompressedObjectPtr const* to,
- CompressedObjectPtr const* from,
- intptr_t count) {
- ASSERT(Contains(reinterpret_cast<uword>(to)));
- if (ptr()->IsNewObject()) {
- memmove(const_cast<CompressedObjectPtr*>(to), from,
- count * kBytesPerElement);
- } else {
- Thread* thread = Thread::Current();
- const uword heap_base = ptr()->heap_base();
- for (intptr_t i = 0; i < count; ++i) {
- untag()->StoreArrayPointer(&to[i], from[i].Decompress(heap_base),
- thread);
- }
- }
- }
-
FINAL_HEAP_OBJECT_IMPLEMENTATION(Array, Instance);
friend class Class;
friend class ImmutableArray;