[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>
This commit is contained in:
Ryan Macnak 2022-06-01 17:45:24 +00:00 committed by Commit Bot
parent e51af7cd0c
commit 188dfbd52b
3 changed files with 88 additions and 47 deletions

View file

@ -2848,7 +2848,7 @@ class ArrayMessageDeserializationCluster
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));
}
}

View file

@ -2638,7 +2638,21 @@ void Object::InitializeObject(uword address,
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 @@ uint32_t Array::CanonicalizeHash() const {
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 @@ ArrayPtr Array::New(intptr_t len,
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 @@ ArrayPtr Array::New(intptr_t class_id, intptr_t len, Heap::Space space) {
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 @@ ArrayPtr Array::Grow(const Array& source,
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();
}

View file

@ -10419,7 +10419,15 @@ class Array : public Instance {
// 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 @@ class Array : public Instance {
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 @@ class Array : public Instance {
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;