Skip to content

Commit dc7e082

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
[vm/concurrency] Ensure rehashing of maps correctly resets [_deletedKeys]
When we rehash a linked hash map, we rebuild the index but keep the existing data array. The [_deletedKeys] specifies the number of entries in the [_index] which were marked as deleted. The Dart code that triggers rehashing and initializes a new [_index] didn't initialize [_deletedKeys]. => We'll fix this in the Dart code and as a precaution also in the transitive object copy code. Furthermore we add a missing optimization: In the example from the bug report we shouldn't even have attempted to re-hash, because the maps have only primitive keys (which do not rely on identity or user-defined hashcodes). => We'll change the existing optimization to ignore deleted entries when determining whether a rehash is needed. The rehashing code for Sets works differently and takes this already into account. Closes #47598 TEST=vm/dart{,_2}/isolates/fast_object_copy_test Change-Id: I3529ecbf500009ab0c72b98e6c427b8840a69371 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/219000 Reviewed-by: Alexander Aprelev <[email protected]> Commit-Queue: Martin Kustermann <[email protected]>
1 parent 12a8926 commit dc7e082

File tree

4 files changed

+51
-4
lines changed

4 files changed

+51
-4
lines changed

runtime/tests/vm/dart/isolates/fast_object_copy_test.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ class SendReceiveTest extends SendReceiveTestBase {
233233
await testMapRehash();
234234
await testMapRehash2();
235235
await testMapRehash3();
236+
await testMapRehash4();
236237

237238
await testSetRehash();
238239
await testSetRehash2();
@@ -532,6 +533,27 @@ class SendReceiveTest extends SendReceiveTestBase {
532533
Expect.equals(before + 1, after);
533534
}
534535

536+
Future testMapRehash4() async {
537+
// This is a regression test for http://dartbug.com/47598
538+
print('testMapRehash4');
539+
540+
// This map doesn't need rehashing
541+
final graph = {'a': 1, 'b': 2, 'c': 3}..remove('b');
542+
final graphCopy = (await sendReceive(graph) as Map);
543+
Expect.equals(2, graphCopy.length);
544+
Expect.equals(1, graphCopy['a']);
545+
Expect.equals(3, graphCopy['c']);
546+
547+
// This map will need re-hashing due to usage of a key that has a
548+
// user-defined get:hashCode.
549+
final graph2 = {'a': 1, 'b': 2, const HashIncrementer(): 3}..remove('b');
550+
final graph2Copy = (await sendReceive(graph2) as Map);
551+
Expect.equals(2, graph2Copy.length);
552+
Expect.equals(1, graph2Copy['a']);
553+
--HashIncrementer.counter;
554+
Expect.equals(3, graph2Copy[const HashIncrementer()]);
555+
}
556+
535557
Future testSetRehash() async {
536558
print('testSetRehash');
537559
final obj = Object();

runtime/tests/vm/dart_2/isolates/fast_object_copy_test.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ class SendReceiveTest extends SendReceiveTestBase {
235235
await testMapRehash();
236236
await testMapRehash2();
237237
await testMapRehash3();
238+
await testMapRehash4();
238239

239240
await testSetRehash();
240241
await testSetRehash2();
@@ -534,6 +535,27 @@ class SendReceiveTest extends SendReceiveTestBase {
534535
Expect.equals(before + 1, after);
535536
}
536537

538+
Future testMapRehash4() async {
539+
// This is a regression test for http://dartbug.com/47598
540+
print('testMapRehash4');
541+
542+
// This map doesn't need rehashing
543+
final graph = {'a': 1, 'b': 2, 'c': 3}..remove('b');
544+
final graphCopy = (await sendReceive(graph) as Map);
545+
Expect.equals(2, graphCopy.length);
546+
Expect.equals(1, graphCopy['a']);
547+
Expect.equals(3, graphCopy['c']);
548+
549+
// This map will need re-hashing due to usage of a key that has a
550+
// user-defined get:hashCode.
551+
final graph2 = {'a': 1, 'b': 2, const HashIncrementer(): 3}..remove('b');
552+
final graph2Copy = (await sendReceive(graph2) as Map);
553+
Expect.equals(2, graph2Copy.length);
554+
Expect.equals(1, graph2Copy['a']);
555+
--HashIncrementer.counter;
556+
Expect.equals(3, graph2Copy[const HashIncrementer()]);
557+
}
558+
537559
Future testSetRehash() async {
538560
print('testSetRehash');
539561
final obj = Object();

runtime/vm/object_graph_copy.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,8 +1148,9 @@ class ObjectCopy : public Base {
11481148
auto key_value_pairs = untagged_data->data();
11491149
for (intptr_t i = 0; i < length; i += one_for_set_two_for_map) {
11501150
ObjectPtr key = key_value_pairs[i].Decompress(Base::heap_base_);
1151+
const bool is_deleted_entry = key == data;
11511152
if (key->IsHeapObject()) {
1152-
if (MightNeedReHashing(key)) {
1153+
if (!is_deleted_entry && MightNeedReHashing(key)) {
11531154
needs_rehashing = true;
11541155
break;
11551156
}
@@ -1171,6 +1172,7 @@ class ObjectCopy : public Base {
11711172
if (needs_rehashing) {
11721173
to_untagged->hash_mask_ = Smi::New(0);
11731174
to_untagged->index_ = TypedData::RawCast(Object::null());
1175+
to_untagged->deleted_keys_ = Smi::New(0);
11741176
Base::EnqueueObjectToRehash(to);
11751177
}
11761178

@@ -1185,15 +1187,15 @@ class ObjectCopy : public Base {
11851187
Base::StoreCompressedPointersNoBarrier(
11861188
from, to, OFFSET_OF(UntaggedLinkedHashBase, hash_mask_),
11871189
OFFSET_OF(UntaggedLinkedHashBase, hash_mask_));
1190+
Base::StoreCompressedPointersNoBarrier(
1191+
from, to, OFFSET_OF(UntaggedLinkedHashMap, deleted_keys_),
1192+
OFFSET_OF(UntaggedLinkedHashMap, deleted_keys_));
11881193
}
11891194
Base::ForwardCompressedPointer(from, to,
11901195
OFFSET_OF(UntaggedLinkedHashBase, data_));
11911196
Base::StoreCompressedPointersNoBarrier(
11921197
from, to, OFFSET_OF(UntaggedLinkedHashBase, used_data_),
11931198
OFFSET_OF(UntaggedLinkedHashBase, used_data_));
1194-
Base::StoreCompressedPointersNoBarrier(
1195-
from, to, OFFSET_OF(UntaggedLinkedHashMap, deleted_keys_),
1196-
OFFSET_OF(UntaggedLinkedHashMap, deleted_keys_));
11971199
}
11981200

11991201
void CopyLinkedHashMap(typename Types::LinkedHashMap from,

sdk/lib/_internal/vm/lib/compact_hash.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ abstract class _LinkedHashMapMixin<K, V> implements _HashBase {
372372
_hashMask = _HashBase._indexSizeToHashMask(_index.length);
373373
final int tmpUsed = _usedData;
374374
_usedData = 0;
375+
_deletedKeys = 0;
375376
for (int i = 0; i < tmpUsed; i += 2) {
376377
final key = _data[i];
377378
if (!_HashBase._isDeleted(_data, key)) {

0 commit comments

Comments
 (0)