Skip to content

Commit e5f6611

Browse files
aamCommit Queue
authored andcommitted
[vm/shared] Ensure to iterate over mutators, rather than only isolates during deopt.
Iterating over only isolates ignores isolategroup-bound dart code. Fixes #61326 TEST=isolate_group_bound_lazy_deopt_test Change-Id: Ic27679ab7eb37aa2ca703937355dc83d751e0054 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/448254 Reviewed-by: Slava Egorov <[email protected]> Commit-Queue: Alexander Aprelev <[email protected]>
1 parent 9b3fd2b commit e5f6611

File tree

7 files changed

+3744
-3656
lines changed

7 files changed

+3744
-3656
lines changed

runtime/vm/compiler/runtime_offsets_extracted.h

Lines changed: 3627 additions & 3627 deletions
Large diffs are not rendered by default.

runtime/vm/heap/weak_code.cc

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "vm/object.h"
1212
#include "vm/runtime_entry.h"
1313
#include "vm/stack_frame.h"
14+
#include "vm/thread_registry.h"
1415

1516
namespace dart {
1617

@@ -55,28 +56,22 @@ void WeakCodeReferences::DisableCode(bool are_mutators_stopped) {
5556
auto isolate_group = IsolateGroup::Current();
5657
auto disable_code_fun = [&]() {
5758
Code& code = Code::Handle();
58-
isolate_group->ForEachIsolate(
59-
[&](Isolate* isolate) {
60-
auto mutator_thread = isolate->mutator_thread();
61-
if (mutator_thread == nullptr) {
62-
return;
59+
isolate_group->ForEachMutatorAtASafepoint([&](Thread* thread) {
60+
DartFrameIterator iterator(
61+
thread, StackFrameIterator::kAllowCrossThreadIteration);
62+
StackFrame* frame = iterator.NextFrame();
63+
while (frame != nullptr) {
64+
if (!frame->is_interpreted()) {
65+
code = frame->LookupDartCode();
66+
67+
if (set.ContainsKey(code)) {
68+
ReportDeoptimization(code);
69+
DeoptimizeAt(thread, code, frame);
6370
}
64-
DartFrameIterator iterator(
65-
mutator_thread, StackFrameIterator::kAllowCrossThreadIteration);
66-
StackFrame* frame = iterator.NextFrame();
67-
while (frame != nullptr) {
68-
if (!frame->is_interpreted()) {
69-
code = frame->LookupDartCode();
70-
71-
if (set.ContainsKey(code)) {
72-
ReportDeoptimization(code);
73-
DeoptimizeAt(mutator_thread, code, frame);
74-
}
75-
}
76-
frame = iterator.NextFrame();
77-
}
78-
},
79-
/*at_safepoint=*/true);
71+
}
72+
frame = iterator.NextFrame();
73+
}
74+
});
8075

8176
// Switch functions that use dependent code to unoptimized code.
8277
Object& owner = Object::Handle();

runtime/vm/isolate.cc

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ IsolateGroup::IsolateGroup(std::shared_ptr<IsolateGroupSource> source,
320320
thread_pool_(),
321321
isolates_lock_(new SafepointRwLock()),
322322
isolates_(),
323+
mutators_(),
323324
start_time_micros_(OS::GetCurrentMonotonicMicros()),
324325
is_system_isolate_group_(source->flags.is_system_isolate),
325326
random_(),
@@ -473,13 +474,15 @@ bool IsolateGroup::UnregisterIsolateDecrementCount() {
473474
return isolate_count_ == 0;
474475
}
475476

476-
void IsolateGroup::RegisterIsolateGroupMutator() {
477+
void IsolateGroup::RegisterIsolateGroupMutator(Thread* mutator) {
477478
SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
479+
mutators_.Append(mutator);
478480
group_mutator_count_++;
479481
}
480482

481-
void IsolateGroup::UnregisterIsolateGroupMutator() {
483+
void IsolateGroup::UnregisterIsolateGroupMutator(Thread* mutator) {
482484
SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
485+
mutators_.Remove(mutator);
483486
group_mutator_count_--;
484487
}
485488

@@ -2914,6 +2917,27 @@ Isolate* IsolateGroup::FirstIsolateLocked() const {
29142917
return isolates_.IsEmpty() ? nullptr : isolates_.First();
29152918
}
29162919

2920+
void IsolateGroup::ForEachMutatorAtASafepoint(
2921+
std::function<void(Thread* thread)> function) {
2922+
auto thread = Thread::Current();
2923+
ASSERT(thread->OwnsSafepoint() ||
2924+
(thread->task_kind() == Thread::kMutatorTask) ||
2925+
(thread->task_kind() == Thread::kMarkerTask) ||
2926+
(thread->task_kind() == Thread::kCompactorTask) ||
2927+
(thread->task_kind() == Thread::kScavengerTask) ||
2928+
(thread->task_kind() == Thread::kIncrementalCompactorTask));
2929+
for (Isolate* isolate : isolates_) {
2930+
auto thread = isolate->mutator_thread();
2931+
if (thread != nullptr) {
2932+
function(thread);
2933+
}
2934+
}
2935+
for (Thread* mutator : mutators_) {
2936+
ASSERT(mutator != nullptr);
2937+
function(mutator);
2938+
}
2939+
}
2940+
29172941
void IsolateGroup::RunWithStoppedMutatorsCallable(Callable* callable) {
29182942
auto thread = Thread::Current();
29192943
StoppedMutatorsScope stopped_mutators_scope(thread);

runtime/vm/isolate.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,8 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
374374
// Returns `true` if this was the last isolate and the caller is responsible
375375
// for deleting the isolate group.
376376
bool UnregisterIsolateDecrementCount();
377-
void RegisterIsolateGroupMutator();
378-
void UnregisterIsolateGroupMutator();
377+
void RegisterIsolateGroupMutator(Thread* mutator);
378+
void UnregisterIsolateGroupMutator(Thread* mutator);
379379

380380
bool ContainsOnlyOneIsolate();
381381

@@ -650,6 +650,8 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
650650
Isolate* FirstIsolate() const;
651651
Isolate* FirstIsolateLocked() const;
652652

653+
void ForEachMutatorAtASafepoint(std::function<void(Thread* thread)> function);
654+
653655
// Ensures mutators are stopped during execution of the provided function.
654656
//
655657
// If the current thread is the only mutator in the isolate group,
@@ -907,6 +909,7 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
907909
IntrusiveDList<Isolate> isolates_;
908910
RelaxedAtomic<Dart_Port> interrupt_port_ = ILLEGAL_PORT;
909911
intptr_t isolate_count_ = 0;
912+
IntrusiveDList<Thread> mutators_;
910913
intptr_t group_mutator_count_ = 0;
911914
bool initial_spawn_successful_ = false;
912915
Dart_LibraryTagHandler library_tag_handler_ = nullptr;

runtime/vm/thread.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -537,10 +537,10 @@ void Thread::EnterIsolateGroupAsMutator(IsolateGroup* isolate_group,
537537
isolate_group->IncreaseMutatorCount(/*thread=*/nullptr,
538538
/*is_nested_reenter=*/true,
539539
/*was_stolen=*/false);
540-
isolate_group->RegisterIsolateGroupMutator();
541-
542540
Thread* thread = AddActiveThread(isolate_group, /*isolate=*/nullptr,
543541
kMutatorTask, bypass_safepoint);
542+
isolate_group->RegisterIsolateGroupMutator(thread);
543+
544544
RELEASE_ASSERT(thread != nullptr);
545545
// Even if [bypass_safepoint] is true, a thread may need mutator state (e.g.
546546
// parallel scavenger threads write to the [Thread]s storebuffer)
@@ -584,7 +584,7 @@ void Thread::ExitIsolateGroupAsMutator(bool bypass_safepoint) {
584584
SuspendThreadInternal(thread, VMTag::kInvalidTagId);
585585
auto group = thread->isolate_group();
586586
FreeActiveThread(thread, /*isolate=*/nullptr, bypass_safepoint);
587-
group->UnregisterIsolateGroupMutator();
587+
group->UnregisterIsolateGroupMutator(thread);
588588
group->DecreaseMutatorCount(/*is_nested_exit=*/true);
589589
}
590590

runtime/vm/thread.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "vm/handles.h"
2424
#include "vm/heap/pointer_block.h"
2525
#include "vm/heap/sampler.h"
26+
#include "vm/intrusive_dlist.h"
2627
#include "vm/os_thread.h"
2728
#include "vm/pending_deopts.h"
2829
#include "vm/random.h"
@@ -364,7 +365,7 @@ class MutatorThreadVisitor {
364365
// a thread is allocated by ThreadRegistry::GetFromFreelistLocked either
365366
// before entering an isolate or entering an isolate group, and destroyed
366367
// automatically when the underlying OS thread exits.
367-
class Thread : public ThreadState {
368+
class Thread : public ThreadState, public IntrusiveDListEntry<Thread> {
368369
public:
369370
// The kind of task this thread is performing. Sampled by the profiler.
370371
enum TaskKind {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
//
5+
// Tests deoptimization in presence of isolategroup-bound code.
6+
//
7+
// VMOptions=--experimental-shared-data --optimization-counter-threshold=10 --no-background-compilation
8+
9+
// Test that lazy deoptimization works if the program returns to a function
10+
// that is scheduled for lazy deoptimization via an exception.
11+
12+
import 'package:dart_internal/isolate_group.dart' show IsolateGroup;
13+
import 'package:expect/expect.dart';
14+
15+
class C {
16+
dynamic x = 42;
17+
}
18+
19+
@pragma('vm:never-inline')
20+
AA(C c, bool b) {
21+
if (b) {
22+
c.x = 2.5;
23+
throw 123;
24+
}
25+
}
26+
27+
@pragma('vm:never-inline')
28+
T1(C c, bool b) {
29+
try {
30+
AA(c, b);
31+
} on dynamic {}
32+
return c.x + 1;
33+
}
34+
35+
@pragma('vm:never-inline')
36+
T2(C c, bool b) {
37+
try {
38+
AA(c, b);
39+
} on String {
40+
Expect.isTrue(false);
41+
} on int catch (e) {
42+
Expect.equals(e, 123);
43+
Expect.equals(b, true);
44+
Expect.equals(c.x, 2.5);
45+
}
46+
return c.x + 1;
47+
}
48+
49+
mainBody() {
50+
var c = new C();
51+
for (var i = 0; i < 10000; ++i) {
52+
T1(c, false);
53+
T2(c, false);
54+
}
55+
Expect.equals(43, T1(c, false));
56+
Expect.equals(43, T2(c, false));
57+
Expect.equals(3.5, T1(c, true));
58+
Expect.equals(3.5, T2(c, true));
59+
}
60+
61+
main() {
62+
IsolateGroup.runSync(() {
63+
mainBody();
64+
});
65+
}

0 commit comments

Comments
 (0)