Skip to content

Commit f5c0e2e

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
[vm/concurrency] Ensure instance size in class table is either 0 or the correct size
Until now it was possible to register classes with a default size (16 bytes on 64-bit) and later on change the size for the cid. This CL changes this to ensure the size information in the class table for a given cid is either 0 or the final instance size (and adds an ASSERT for it) Issue #36097 Change-Id: I94c61c6a1566c13dec7b9eb80c9ae0dadf0e6b6a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/115861 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Ryan Macnak <[email protected]>
1 parent eb60f05 commit f5c0e2e

File tree

6 files changed

+268
-321
lines changed

6 files changed

+268
-321
lines changed

runtime/vm/class_table.cc

+6-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,12 @@ void ClassTable::SetAt(intptr_t index, RawClass* raw_cls) {
253253
if (raw_cls == NULL) {
254254
table_[index] = ClassAndSize(raw_cls, 0);
255255
} else {
256-
table_[index] = ClassAndSize(raw_cls, Class::instance_size(raw_cls));
256+
// Ensure we never change size for a given cid from one non-zero size to
257+
// another non-zero size.
258+
const intptr_t old_size = table_[index].size_;
259+
const intptr_t new_size = Class::instance_size(raw_cls);
260+
ASSERT(old_size == 0 || old_size == new_size);
261+
table_[index] = ClassAndSize(raw_cls, new_size);
257262
}
258263
}
259264

runtime/vm/class_table.h

+41
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,47 @@ class ClassTable {
193193
explicit ClassTable(ClassTable* original);
194194
~ClassTable();
195195

196+
void CopyBeforeHotReload(ClassAndSize** copy, intptr_t* copy_num_cids) {
197+
// The [IsolateReloadContext] will need to maintain a copy of the old class
198+
// table until instances have been morphed.
199+
const intptr_t bytes = sizeof(ClassAndSize) * NumCids();
200+
*copy_num_cids = NumCids();
201+
*copy = static_cast<ClassAndSize*>(malloc(bytes));
202+
memmove(*copy, table_, bytes);
203+
}
204+
205+
void ResetBeforeHotReload() {
206+
// The [IsolateReloadContext] is now source-of-truth for GC.
207+
//
208+
// Though we cannot clear out the class pointers, because a hot-reload
209+
// contains only a diff: If e.g. a class included in the hot-reload has a
210+
// super class not included in the diff, it will look up in this class table
211+
// to find the super class (e.g. `cls.SuperClass` will cause us to come
212+
// here).
213+
for (intptr_t i = 0; i < top_; ++i) {
214+
table_[i].size_ = 0;
215+
}
216+
}
217+
218+
void ResetAfterHotReload(ClassAndSize* old_table,
219+
intptr_t num_old_cids,
220+
bool is_rollback) {
221+
// The [IsolateReloadContext] is no longer source-of-truth for GC after we
222+
// return, so we restore size information for all classes.
223+
if (is_rollback) {
224+
SetNumCids(num_old_cids);
225+
memmove(table_, old_table, num_old_cids * sizeof(ClassAndSize));
226+
} else {
227+
CopySizesFromClassObjects();
228+
}
229+
230+
// Can't free this table immediately as another thread (e.g., concurrent
231+
// marker or sweeper) may be between loading the table pointer and loading
232+
// the table element. The table will be freed at the next major GC or
233+
// isolate shutdown.
234+
AddOldTable(old_table);
235+
}
236+
196237
// Thread-safe.
197238
RawClass* At(intptr_t index) const {
198239
ASSERT(IsValidIndex(index));

runtime/vm/isolate_reload.cc

+21-153
Original file line numberDiff line numberDiff line change
@@ -972,28 +972,21 @@ void IsolateReloadContext::CheckpointClasses() {
972972
// Copy the size of the class table.
973973
saved_num_cids_ = I->class_table()->NumCids();
974974

975-
// Copy of the class table.
976-
ClassAndSize* local_saved_class_table = reinterpret_cast<ClassAndSize*>(
977-
malloc(sizeof(ClassAndSize) * saved_num_cids_));
975+
ClassAndSize* saved_class_table = nullptr;
976+
class_table->CopyBeforeHotReload(&saved_class_table, &saved_num_cids_);
978977

979978
// Copy classes into saved_class_table_ first. Make sure there are no
980979
// safepoints until saved_class_table_ is filled up and saved so class raw
981980
// pointers in saved_class_table_ are properly visited by GC.
982981
{
983982
NoSafepointScope no_safepoint_scope(Thread::Current());
984983

985-
for (intptr_t i = 0; i < saved_num_cids_; i++) {
986-
if (class_table->IsValidIndex(i) && class_table->HasValidClassAt(i)) {
987-
// Copy the class into the saved class table.
988-
local_saved_class_table[i] = class_table->PairAt(i);
989-
} else {
990-
// No class at this index, mark it as NULL.
991-
local_saved_class_table[i] = ClassAndSize(NULL);
992-
}
993-
}
984+
// The saved_class_table_ is now source of truth for GC.
985+
AtomicOperations::StoreRelease(&saved_class_table_, saved_class_table);
994986

995-
// Elements of saved_class_table_ are now visible to GC.
996-
saved_class_table_ = local_saved_class_table;
987+
// We can therefore wipe out all of the old entries (if that table is used
988+
// for GC during the hot-reload we have a bug).
989+
class_table->ResetBeforeHotReload();
997990
}
998991

999992
// Add classes to the set. Set is stored in the Array, so adding an element
@@ -1026,20 +1019,6 @@ bool IsolateReloadContext::ScriptModifiedSince(const Script& script,
10261019
return (*file_modified_callback_)(url_chars, since);
10271020
}
10281021

1029-
static void PropagateLibraryModified(
1030-
const ZoneGrowableArray<ZoneGrowableArray<intptr_t>*>* imported_by,
1031-
intptr_t lib_index,
1032-
BitVector* modified_libs) {
1033-
ZoneGrowableArray<intptr_t>* dep_libs = (*imported_by)[lib_index];
1034-
for (intptr_t i = 0; i < dep_libs->length(); i++) {
1035-
intptr_t dep_lib_index = (*dep_libs)[i];
1036-
if (!modified_libs->Contains(dep_lib_index)) {
1037-
modified_libs->Add(dep_lib_index);
1038-
PropagateLibraryModified(imported_by, dep_lib_index, modified_libs);
1039-
}
1040-
}
1041-
}
1042-
10431022
static bool ContainsScriptUri(const GrowableArray<const char*>& seen_uris,
10441023
const char* uri) {
10451024
for (intptr_t i = 0; i < seen_uris.length(); i++) {
@@ -1112,109 +1091,6 @@ void IsolateReloadContext::FindModifiedSources(
11121091
}
11131092
}
11141093

1115-
BitVector* IsolateReloadContext::FindModifiedLibraries(bool force_reload,
1116-
bool root_lib_modified) {
1117-
Thread* thread = Thread::Current();
1118-
int64_t last_reload = I->last_reload_timestamp();
1119-
1120-
const GrowableObjectArray& libs =
1121-
GrowableObjectArray::Handle(object_store()->libraries());
1122-
Library& lib = Library::Handle();
1123-
Array& scripts = Array::Handle();
1124-
Script& script = Script::Handle();
1125-
intptr_t num_libs = libs.Length();
1126-
1127-
// Construct the imported-by graph.
1128-
ZoneGrowableArray<ZoneGrowableArray<intptr_t>*>* imported_by = new (zone_)
1129-
ZoneGrowableArray<ZoneGrowableArray<intptr_t>*>(zone_, num_libs);
1130-
imported_by->SetLength(num_libs);
1131-
for (intptr_t i = 0; i < num_libs; i++) {
1132-
(*imported_by)[i] = new (zone_) ZoneGrowableArray<intptr_t>(zone_, 0);
1133-
}
1134-
Array& ports = Array::Handle();
1135-
Namespace& ns = Namespace::Handle();
1136-
Library& target = Library::Handle();
1137-
1138-
for (intptr_t lib_idx = 0; lib_idx < num_libs; lib_idx++) {
1139-
lib ^= libs.At(lib_idx);
1140-
ASSERT(lib_idx == lib.index());
1141-
if (lib.is_dart_scheme()) {
1142-
// We don't care about imports among dart scheme libraries.
1143-
continue;
1144-
}
1145-
1146-
// Add imports to the import-by graph.
1147-
ports = lib.imports();
1148-
for (intptr_t import_idx = 0; import_idx < ports.Length(); import_idx++) {
1149-
ns ^= ports.At(import_idx);
1150-
if (!ns.IsNull()) {
1151-
target = ns.library();
1152-
(*imported_by)[target.index()]->Add(lib.index());
1153-
}
1154-
}
1155-
1156-
// Add exports to the import-by graph.
1157-
ports = lib.exports();
1158-
for (intptr_t export_idx = 0; export_idx < ports.Length(); export_idx++) {
1159-
ns ^= ports.At(export_idx);
1160-
if (!ns.IsNull()) {
1161-
target = ns.library();
1162-
(*imported_by)[target.index()]->Add(lib.index());
1163-
}
1164-
}
1165-
1166-
// Add prefixed imports to the import-by graph.
1167-
DictionaryIterator entries(lib);
1168-
Object& entry = Object::Handle();
1169-
LibraryPrefix& prefix = LibraryPrefix::Handle();
1170-
while (entries.HasNext()) {
1171-
entry = entries.GetNext();
1172-
if (entry.IsLibraryPrefix()) {
1173-
prefix ^= entry.raw();
1174-
ports = prefix.imports();
1175-
for (intptr_t import_idx = 0; import_idx < ports.Length();
1176-
import_idx++) {
1177-
ns ^= ports.At(import_idx);
1178-
if (!ns.IsNull()) {
1179-
target = ns.library();
1180-
(*imported_by)[target.index()]->Add(lib.index());
1181-
}
1182-
}
1183-
}
1184-
}
1185-
}
1186-
1187-
BitVector* modified_libs = new (Z) BitVector(Z, num_libs);
1188-
1189-
if (root_lib_modified) {
1190-
// The root library was either moved or replaced. Mark it as modified to
1191-
// force a reload of the potential root library replacement.
1192-
lib = object_store()->root_library();
1193-
modified_libs->Add(lib.index());
1194-
}
1195-
1196-
for (intptr_t lib_idx = 0; lib_idx < num_libs; lib_idx++) {
1197-
lib ^= libs.At(lib_idx);
1198-
if (lib.is_dart_scheme() || modified_libs->Contains(lib_idx)) {
1199-
// We don't consider dart scheme libraries during reload. If
1200-
// the modified libs set already contains this library, then we
1201-
// have already visited it.
1202-
continue;
1203-
}
1204-
scripts = lib.LoadedScripts();
1205-
for (intptr_t script_idx = 0; script_idx < scripts.Length(); script_idx++) {
1206-
script ^= scripts.At(script_idx);
1207-
if (force_reload || ScriptModifiedSince(script, last_reload)) {
1208-
modified_libs->Add(lib_idx);
1209-
PropagateLibraryModified(imported_by, lib_idx, modified_libs);
1210-
break;
1211-
}
1212-
}
1213-
}
1214-
1215-
return modified_libs;
1216-
}
1217-
12181094
void IsolateReloadContext::CheckpointLibraries() {
12191095
TIMELINE_SCOPE(CheckpointLibraries);
12201096
TIR_Print("---- CHECKPOINTING LIBRARIES\n");
@@ -1272,16 +1148,8 @@ void IsolateReloadContext::RollbackClasses() {
12721148
TIR_Print("---- ROLLING BACK CLASS TABLE\n");
12731149
ASSERT(saved_num_cids_ > 0);
12741150
ASSERT(saved_class_table_ != NULL);
1275-
ClassTable* class_table = I->class_table();
1276-
class_table->SetNumCids(saved_num_cids_);
1277-
// Overwrite classes in class table with the saved classes.
1278-
for (intptr_t i = 0; i < saved_num_cids_; i++) {
1279-
if (class_table->IsValidIndex(i)) {
1280-
class_table->SetAt(i, saved_class_table_[i].get_raw_class());
1281-
}
1282-
}
12831151

1284-
DiscardSavedClassTable();
1152+
DiscardSavedClassTable(/*is_rollback=*/true);
12851153
}
12861154

12871155
void IsolateReloadContext::RollbackLibraries() {
@@ -1676,7 +1544,7 @@ void IsolateReloadContext::MorphInstancesAndApplyNewClassTable() {
16761544
TIMELINE_SCOPE(MorphInstances);
16771545
if (!HasInstanceMorphers()) {
16781546
// Fast path: no class had a shape change.
1679-
DiscardSavedClassTable();
1547+
DiscardSavedClassTable(/*is_rollback=*/false);
16801548
return;
16811549
}
16821550

@@ -1698,7 +1566,7 @@ void IsolateReloadContext::MorphInstancesAndApplyNewClassTable() {
16981566
intptr_t count = locator.count();
16991567
if (count == 0) {
17001568
// Fast path: classes with shape change have no instances.
1701-
DiscardSavedClassTable();
1569+
DiscardSavedClassTable(/*is_rollback=*/false);
17021570
return;
17031571
}
17041572

@@ -1742,8 +1610,10 @@ void IsolateReloadContext::MorphInstancesAndApplyNewClassTable() {
17421610
saved_class_table_[i] = ClassAndSize(nullptr, -1);
17431611
}
17441612
#endif
1745-
free(saved_class_table_);
1746-
saved_class_table_ = nullptr;
1613+
1614+
// We accepted the hot-reload and morphed instances. So now we can commit to
1615+
// the changed class table and deleted the saved one.
1616+
DiscardSavedClassTable(/*is_rollback=*/false);
17471617

17481618
Become::ElementsForwardIdentity(before, after);
17491619
// The heap now contains only instances with the new size. Ordinary GC is safe
@@ -1808,7 +1678,7 @@ RawClass* IsolateReloadContext::FindOriginalClass(const Class& cls) {
18081678

18091679
RawClass* IsolateReloadContext::GetClassForHeapWalkAt(intptr_t cid) {
18101680
ClassAndSize* class_table =
1811-
AtomicOperations::LoadRelaxed(&saved_class_table_);
1681+
AtomicOperations::LoadAcquire(&saved_class_table_);
18121682
if (class_table != NULL) {
18131683
ASSERT(cid > 0);
18141684
ASSERT(cid < saved_num_cids_);
@@ -1820,7 +1690,7 @@ RawClass* IsolateReloadContext::GetClassForHeapWalkAt(intptr_t cid) {
18201690

18211691
intptr_t IsolateReloadContext::GetClassSizeForHeapWalkAt(intptr_t cid) {
18221692
ClassAndSize* class_table =
1823-
AtomicOperations::LoadRelaxed(&saved_class_table_);
1693+
AtomicOperations::LoadAcquire(&saved_class_table_);
18241694
if (class_table != NULL) {
18251695
ASSERT(cid > 0);
18261696
ASSERT(cid < saved_num_cids_);
@@ -1830,14 +1700,12 @@ intptr_t IsolateReloadContext::GetClassSizeForHeapWalkAt(intptr_t cid) {
18301700
}
18311701
}
18321702

1833-
void IsolateReloadContext::DiscardSavedClassTable() {
1703+
void IsolateReloadContext::DiscardSavedClassTable(bool is_rollback) {
18341704
ClassAndSize* local_saved_class_table = saved_class_table_;
1835-
saved_class_table_ = nullptr;
1836-
// Can't free this table immediately as another thread (e.g., concurrent
1837-
// marker or sweeper) may be between loading the table pointer and loading the
1838-
// table element. The table will be freed at the next major GC or isolate
1839-
// shutdown.
1840-
I->class_table()->AddOldTable(local_saved_class_table);
1705+
I->class_table()->ResetAfterHotReload(local_saved_class_table,
1706+
saved_num_cids_, is_rollback);
1707+
AtomicOperations::StoreRelease(&saved_class_table_,
1708+
static_cast<ClassAndSize*>(nullptr));
18411709
}
18421710

18431711
RawLibrary* IsolateReloadContext::saved_root_library() const {

runtime/vm/isolate_reload.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ class IsolateReloadContext {
173173
// Prefers old classes when we are in the middle of a reload.
174174
RawClass* GetClassForHeapWalkAt(intptr_t cid);
175175
intptr_t GetClassSizeForHeapWalkAt(intptr_t cid);
176-
void DiscardSavedClassTable();
176+
void DiscardSavedClassTable(bool is_rollback);
177177

178178
void RegisterClass(const Class& new_cls);
179179

@@ -236,7 +236,6 @@ class IsolateReloadContext {
236236
void CheckpointClasses();
237237

238238
bool ScriptModifiedSince(const Script& script, int64_t since);
239-
BitVector* FindModifiedLibraries(bool force_reload, bool root_lib_modified);
240239
void FindModifiedSources(Thread* thread,
241240
bool force_reload,
242241
Dart_SourceFile** modified_sources,

0 commit comments

Comments
 (0)