Skip to content

Commit 38c6152

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
[vm/concurrency] Add IsolateGroup::RunWithStoppedMutators and use it in various places
When installing new code, we need to ensure the mutator is stopped for a variety of reasons. Right now we only need to distuinguish mutator and background compiler when installing code: The mutator can install code without any synchronization whereas the bg compiler needs to get the mutator to a safepoint. Yet once all isolates within one isolate group share a heap, a mutator might need to stop all other mutators before installing code (since they operate on pages on which we flip page protection bits) This CL adds IsolateGroup::RunWithStoppedMutators which will get other mutators to a safepoint (if there are multiple or we are on a bg compiler thread). Along with this we add a read-write lock and use it for the protection of the IsolateGroup::isolate_: While we make assumptions about the number of isolates in a group we force all pending additions of new isolates to wait. Later on this will also be used to allow iterating the list of isolates during GC and prevent new isolates from being added at the same time. Issue #36097 Change-Id: I6e761fa51d36b2f2b4b67995cac954898ce7fd69 Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-android-release-arm-try,vm-dartkb-linux-release-x64-abi-try,vm-kernel-precomp-bare-linux-release-x64-try,vm-kernel-precomp-mac-debug-simarm_x64-try,vm-kernel-precomp-win-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116767 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Alexander Aprelev <[email protected]> Reviewed-by: Ryan Macnak <[email protected]>
1 parent 97b561a commit 38c6152

10 files changed

+282
-86
lines changed

runtime/vm/code_patcher_ia32.cc

+23-12
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ class NativeCall : public UnoptimizedCall {
6464
}
6565

6666
void set_native_function(NativeFunction func) const {
67-
WritableInstructionsScope writable(start_ + 1, sizeof(func));
68-
*reinterpret_cast<NativeFunction*>(start_ + 1) = func;
67+
Thread::Current()->isolate_group()->RunWithStoppedMutators([&]() {
68+
WritableInstructionsScope writable(start_ + 1, sizeof(func));
69+
*reinterpret_cast<NativeFunction*>(start_ + 1) = func;
70+
});
6971
}
7072

7173
private:
@@ -179,11 +181,15 @@ RawCode* CodePatcher::GetStaticCallTargetAt(uword return_address,
179181
void CodePatcher::PatchStaticCallAt(uword return_address,
180182
const Code& code,
181183
const Code& new_target) {
182-
const Instructions& instrs = Instructions::Handle(code.instructions());
183-
WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
184-
ASSERT(code.ContainsInstructionAt(return_address));
185-
StaticCall call(return_address);
186-
call.set_target(new_target);
184+
auto thread = Thread::Current();
185+
auto zone = thread->zone();
186+
const Instructions& instrs = Instructions::Handle(zone, code.instructions());
187+
thread->isolate_group()->RunWithStoppedMutators([&]() {
188+
WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
189+
ASSERT(code.ContainsInstructionAt(return_address));
190+
StaticCall call(return_address);
191+
call.set_target(new_target);
192+
});
187193
}
188194

189195
void CodePatcher::InsertDeoptimizationCallAt(uword start) {
@@ -205,12 +211,17 @@ void CodePatcher::PatchInstanceCallAt(uword return_address,
205211
const Code& caller_code,
206212
const Object& data,
207213
const Code& target) {
214+
auto thread = Thread::Current();
215+
auto zone = thread->zone();
208216
ASSERT(caller_code.ContainsInstructionAt(return_address));
209-
const Instructions& instrs = Instructions::Handle(caller_code.instructions());
210-
WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
211-
InstanceCall call(return_address);
212-
call.set_data(data);
213-
call.set_target(target);
217+
const Instructions& instrs =
218+
Instructions::Handle(zone, caller_code.instructions());
219+
thread->isolate_group()->RunWithStoppedMutators([&]() {
220+
WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
221+
InstanceCall call(return_address);
222+
call.set_data(data);
223+
call.set_target(target);
224+
});
214225
}
215226

216227
RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(uword return_address,

runtime/vm/compiler/jit/compiler.cc

+24-15
Original file line numberDiff line numberDiff line change
@@ -646,25 +646,34 @@ RawCode* CompileParsedFunctionHelper::Compile(CompilationPipeline* pipeline) {
646646
}
647647
{
648648
TIMELINE_DURATION(thread(), CompilerVerbose, "FinalizeCompilation");
649-
if (thread()->IsMutatorThread()) {
649+
650+
auto mutator_fun = [&]() {
650651
*result =
651652
FinalizeCompilation(&assembler, &graph_compiler, flow_graph);
652-
} else {
653-
// This part of compilation must be at a safepoint.
654-
// Stop mutator thread before creating the instruction object and
655-
// installing code.
656-
// Mutator thread may not run code while we are creating the
657-
// instruction object, since the creation of instruction object
658-
// changes code page access permissions (makes them temporary not
659-
// executable).
660-
{
661-
CheckIfBackgroundCompilerIsBeingStopped(optimized());
662-
ForceGrowthSafepointOperationScope safepoint_scope(thread());
653+
};
654+
auto bg_compiler_fun = [&]() {
655+
if (Compiler::IsBackgroundCompilation()) {
663656
CheckIfBackgroundCompilerIsBeingStopped(optimized());
664-
*result =
665-
FinalizeCompilation(&assembler, &graph_compiler, flow_graph);
666657
}
667-
}
658+
*result =
659+
FinalizeCompilation(&assembler, &graph_compiler, flow_graph);
660+
};
661+
662+
// We have to ensure no mutators are running, because:
663+
//
664+
// a) We allocate an instructions object, which might cause us to
665+
// temporarily flip page protections (RX -> RW -> RX).
666+
//
667+
// b) We have to ensure the code generated does not violate
668+
// assumptions (e.g. CHA, field guards), the validation has to
669+
// happen while mutator is stopped.
670+
//
671+
// b) We update the [Function] object with a new [Code] which
672+
// requires updating several pointers: We have to ensure all of
673+
// those writes are observed atomically.
674+
//
675+
thread()->isolate_group()->RunWithStoppedMutators(
676+
mutator_fun, bg_compiler_fun, /*use_force_growth=*/true);
668677

669678
// We notify code observers after finalizing the code in order to be
670679
// outside a [SafepointOperationScope].

runtime/vm/debugger_dbc.cc

+12-8
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ static Instr* FastSmiInstructionFromReturnAddress(uword pc) {
2929

3030
void CodeBreakpoint::PatchCode() {
3131
ASSERT(!is_enabled_);
32-
const Code& code = Code::Handle(code_);
33-
const Instructions& instrs = Instructions::Handle(code.instructions());
34-
{
32+
auto thread = Thread::Current();
33+
auto zone = thread->zone();
34+
const Code& code = Code::Handle(zone, code_);
35+
const Instructions& instrs = Instructions::Handle(zone, code.instructions());
36+
thread->isolate_group()->RunWithStoppedMutators([&]() {
3537
WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
3638
saved_value_ = *CallInstructionFromReturnAddress(pc_);
3739
switch (breakpoint_kind_) {
@@ -67,15 +69,17 @@ void CodeBreakpoint::PatchCode() {
6769
} else {
6870
saved_value_fastsmi_ = SimulatorBytecode::kTrap;
6971
}
70-
}
72+
});
7173
is_enabled_ = true;
7274
}
7375

7476
void CodeBreakpoint::RestoreCode() {
7577
ASSERT(is_enabled_);
76-
const Code& code = Code::Handle(code_);
77-
const Instructions& instrs = Instructions::Handle(code.instructions());
78-
{
78+
auto thread = Thread::Current();
79+
auto zone = thread->zone();
80+
const Code& code = Code::Handle(zone, code_);
81+
const Instructions& instrs = Instructions::Handle(zone, code.instructions());
82+
thread->isolate_group()->RunWithStoppedMutators([&]() {
7983
WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
8084
switch (breakpoint_kind_) {
8185
case RawPcDescriptors::kIcCall:
@@ -94,7 +98,7 @@ void CodeBreakpoint::RestoreCode() {
9498
SimulatorBytecode::kNop);
9599
*FastSmiInstructionFromReturnAddress(pc_) = saved_value_fastsmi_;
96100
}
97-
}
101+
});
98102
is_enabled_ = false;
99103
}
100104

runtime/vm/debugger_ia32.cc

+13-9
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ RawCode* CodeBreakpoint::OrigStubAddress() const {
2525

2626
void CodeBreakpoint::PatchCode() {
2727
ASSERT(!is_enabled_);
28-
const Code& code = Code::Handle(code_);
29-
const Instructions& instrs = Instructions::Handle(code.instructions());
30-
Code& stub_target = Code::Handle();
31-
{
28+
auto thread = Thread::Current();
29+
auto zone = thread->zone();
30+
const Code& code = Code::Handle(zone, code_);
31+
const Instructions& instrs = Instructions::Handle(zone, code.instructions());
32+
Code& stub_target = Code::Handle(zone);
33+
thread->isolate_group()->RunWithStoppedMutators([&]() {
3234
WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
3335
switch (breakpoint_kind_) {
3436
case RawPcDescriptors::kIcCall: {
@@ -49,15 +51,17 @@ void CodeBreakpoint::PatchCode() {
4951
}
5052
saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
5153
CodePatcher::PatchStaticCallAt(pc_, code, stub_target);
52-
}
54+
});
5355
is_enabled_ = true;
5456
}
5557

5658
void CodeBreakpoint::RestoreCode() {
5759
ASSERT(is_enabled_);
58-
const Code& code = Code::Handle(code_);
59-
const Instructions& instrs = Instructions::Handle(code.instructions());
60-
{
60+
auto thread = Thread::Current();
61+
auto zone = thread->zone();
62+
const Code& code = Code::Handle(zone, code_);
63+
const Instructions& instrs = Instructions::Handle(zone, code.instructions());
64+
thread->isolate_group()->RunWithStoppedMutators([&]() {
6165
WritableInstructionsScope writable(instrs.PayloadStart(), instrs.Size());
6266
switch (breakpoint_kind_) {
6367
case RawPcDescriptors::kIcCall:
@@ -69,7 +73,7 @@ void CodeBreakpoint::RestoreCode() {
6973
default:
7074
UNREACHABLE();
7175
}
72-
}
76+
});
7377
is_enabled_ = false;
7478
}
7579

runtime/vm/isolate.cc

+31-7
Original file line numberDiff line numberDiff line change
@@ -132,25 +132,25 @@ static RawInstance* DeserializeMessage(Thread* thread, Message* message) {
132132

133133
IsolateGroup::IsolateGroup(std::unique_ptr<IsolateGroupSource> source,
134134
void* embedder_data)
135-
: source_(std::move(source)),
136-
embedder_data_(embedder_data),
135+
: embedder_data_(embedder_data),
136+
isolates_rwlock_(new RwLock()),
137+
isolates_(),
138+
source_(std::move(source)),
137139
thread_registry_(new ThreadRegistry()),
138-
safepoint_handler_(new SafepointHandler(this)),
139-
isolates_monitor_(new Monitor()),
140-
isolates_() {}
140+
safepoint_handler_(new SafepointHandler(this)) {}
141141

142142
IsolateGroup::~IsolateGroup() {}
143143

144144
void IsolateGroup::RegisterIsolate(Isolate* isolate) {
145-
MonitorLocker ml(isolates_monitor_.get());
145+
WriteRwLocker wl(ThreadState::Current(), isolates_rwlock_.get());
146146
isolates_.Append(isolate);
147147
isolate_count_++;
148148
}
149149

150150
void IsolateGroup::UnregisterIsolate(Isolate* isolate) {
151151
bool is_last_isolate = false;
152152
{
153-
MonitorLocker ml(isolates_monitor_.get());
153+
WriteRwLocker wl(ThreadState::Current(), isolates_rwlock_.get());
154154
isolates_.Remove(isolate);
155155
isolate_count_--;
156156
is_last_isolate = isolate_count_ == 0;
@@ -2226,6 +2226,30 @@ void Isolate::DisableIncrementalBarrier() {
22262226
ASSERT(!Thread::Current()->is_marking());
22272227
}
22282228

2229+
void IsolateGroup::RunWithStoppedMutators(
2230+
std::function<void()> single_current_mutator,
2231+
std::function<void()> otherwise,
2232+
bool use_force_growth_in_otherwise) {
2233+
auto thread = Thread::Current();
2234+
2235+
ReadRwLocker wl(thread, isolates_rwlock_.get());
2236+
const bool only_one_isolate = isolates_.First() == isolates_.Last();
2237+
if (thread->IsMutatorThread() && only_one_isolate) {
2238+
single_current_mutator();
2239+
} else {
2240+
// We use the more strict safepoint operation scope here (which ensures that
2241+
// all other threads, including auxiliary threads are at a safepoint), even
2242+
// though we only need to ensure that the mutator threads are stopped.
2243+
if (use_force_growth_in_otherwise) {
2244+
ForceGrowthSafepointOperationScope safepoint_scope(thread);
2245+
otherwise();
2246+
} else {
2247+
SafepointOperationScope safepoint_scope(thread);
2248+
otherwise();
2249+
}
2250+
}
2251+
}
2252+
22292253
RawClass* Isolate::GetClassForHeapWalkAt(intptr_t cid) {
22302254
RawClass* raw_class = nullptr;
22312255
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)

runtime/vm/isolate.h

+26-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#error "Should not include runtime"
1010
#endif
1111

12+
#include <functional>
1213
#include <memory>
1314
#include <utility>
1415

@@ -78,6 +79,7 @@ class RawFloat32x4;
7879
class RawInt32x4;
7980
class RawUserTag;
8081
class ReversePcLookupCache;
82+
class RwLock;
8183
class SafepointHandler;
8284
class SampleBuffer;
8385
class SendPort;
@@ -262,16 +264,36 @@ class IsolateGroup {
262264
library_tag_handler_ = handler;
263265
}
264266

267+
// Ensures mutators are stopped during execution of the provided function.
268+
//
269+
// If the current thread is the only mutator in the isolate group,
270+
// [single_current_mutator] will be called. Otherwise [otherwise] will be
271+
// called inside a [SafepointOperationsScope] (or
272+
// [ForceGrowthSafepointOperationScope] if [use_force_growth_in_otherwise]
273+
// is set).
274+
//
275+
// During the duration of this function, no new isolates can be added to the
276+
// isolate group.
277+
void RunWithStoppedMutators(std::function<void()> single_current_mutator,
278+
std::function<void()> otherwise,
279+
bool use_force_growth_in_otherwise = false);
280+
281+
void RunWithStoppedMutators(std::function<void()> function) {
282+
RunWithStoppedMutators(function, function);
283+
}
284+
265285
private:
266-
std::unique_ptr<IsolateGroupSource> source_;
267286
void* embedder_data_ = nullptr;
268-
std::unique_ptr<ThreadRegistry> thread_registry_;
269-
std::unique_ptr<SafepointHandler> safepoint_handler_;
270-
std::unique_ptr<Monitor> isolates_monitor_;
287+
288+
std::unique_ptr<RwLock> isolates_rwlock_;
271289
IntrusiveDList<Isolate> isolates_;
272290
intptr_t isolate_count_ = 0;
273291
bool initial_spawn_successful_ = false;
274292
Dart_LibraryTagHandler library_tag_handler_ = nullptr;
293+
294+
std::unique_ptr<IsolateGroupSource> source_;
295+
std::unique_ptr<ThreadRegistry> thread_registry_;
296+
std::unique_ptr<SafepointHandler> safepoint_handler_;
275297
};
276298

277299
class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {

runtime/vm/lockers.cc

+18
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,22 @@ Monitor::WaitResult SafepointMonitorLocker::Wait(int64_t millis) {
101101
}
102102
}
103103

104+
ReadRwLocker::ReadRwLocker(ThreadState* thread_state, RwLock* rw_lock)
105+
: StackResource(thread_state), rw_lock_(rw_lock) {
106+
rw_lock_->EnterRead();
107+
}
108+
109+
ReadRwLocker::~ReadRwLocker() {
110+
rw_lock_->LeaveRead();
111+
}
112+
113+
WriteRwLocker::WriteRwLocker(ThreadState* thread_state, RwLock* rw_lock)
114+
: StackResource(thread_state), rw_lock_(rw_lock) {
115+
rw_lock_->EnterWrite();
116+
}
117+
118+
WriteRwLocker::~WriteRwLocker() {
119+
rw_lock_->LeaveWrite();
120+
}
121+
104122
} // namespace dart

0 commit comments

Comments
 (0)