mirror of
https://github.com/dart-lang/sdk
synced 2024-10-15 00:28:05 +00:00
[vm/debugger] Use safepoint-safe RwLock for breakpoint locations locks.
Fixes https://github.com/dart-lang/sdk/issues/54650 TEST=DartAPI_BreakpointLockRace Change-Id: I83b5be80d66ad30c46ea99de08ce7a8d70182414 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/347420 Reviewed-by: Ben Konyi <bkonyi@google.com> Commit-Queue: Alexander Aprelev <aam@google.com>
This commit is contained in:
parent
eb5c77e7c2
commit
84b38aee39
|
@ -411,9 +411,9 @@ static bool IsImplicitFunction(const Function& func) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool GroupDebugger::HasCodeBreakpointInFunction(const Function& func) {
|
||||
auto thread = Thread::Current();
|
||||
ReadRwLocker sl(thread, code_breakpoints_lock());
|
||||
bool GroupDebugger::HasCodeBreakpointInFunctionUnsafe(const Function& func) {
|
||||
DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadReader() ||
|
||||
Thread::Current()->IsInStoppedMutatorsScope());
|
||||
CodeBreakpoint* cbpt = code_breakpoints_;
|
||||
while (cbpt != nullptr) {
|
||||
if (func.ptr() == cbpt->function()) {
|
||||
|
@ -424,9 +424,20 @@ bool GroupDebugger::HasCodeBreakpointInFunction(const Function& func) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool GroupDebugger::HasCodeBreakpointInFunction(const Function& func) {
|
||||
auto thread = Thread::Current();
|
||||
// Don't need to worry about the lock if mutators are stopped.
|
||||
if (thread->IsInStoppedMutatorsScope()) {
|
||||
return HasCodeBreakpointInFunctionUnsafe(func);
|
||||
} else {
|
||||
SafepointReadRwLocker sl(thread, code_breakpoints_lock());
|
||||
return HasCodeBreakpointInFunctionUnsafe(func);
|
||||
}
|
||||
}
|
||||
|
||||
bool GroupDebugger::HasBreakpointInCode(const Code& code) {
|
||||
auto thread = Thread::Current();
|
||||
ReadRwLocker sl(thread, code_breakpoints_lock());
|
||||
SafepointReadRwLocker sl(thread, code_breakpoints_lock());
|
||||
CodeBreakpoint* cbpt = code_breakpoints_;
|
||||
while (cbpt != nullptr) {
|
||||
if (code.ptr() == cbpt->code_) {
|
||||
|
@ -1376,9 +1387,9 @@ BreakpointLocation* CodeBreakpoint::FindBreakpointForDebugger(
|
|||
|
||||
GroupDebugger::GroupDebugger(IsolateGroup* isolate_group)
|
||||
: isolate_group_(isolate_group),
|
||||
code_breakpoints_lock_(new RwLock()),
|
||||
code_breakpoints_lock_(new SafepointRwLock()),
|
||||
code_breakpoints_(nullptr),
|
||||
breakpoint_locations_lock_(new RwLock()),
|
||||
breakpoint_locations_lock_(new SafepointRwLock()),
|
||||
single_stepping_set_lock_(new RwLock()),
|
||||
needs_breakpoint_cleanup_(false) {}
|
||||
|
||||
|
@ -1424,8 +1435,8 @@ void Debugger::Shutdown() {
|
|||
return;
|
||||
}
|
||||
{
|
||||
WriteRwLocker sl(Thread::Current(),
|
||||
group_debugger()->breakpoint_locations_lock());
|
||||
SafepointWriteRwLocker sl(Thread::Current(),
|
||||
group_debugger()->breakpoint_locations_lock());
|
||||
while (breakpoint_locations_ != nullptr) {
|
||||
BreakpointLocation* loc = breakpoint_locations_;
|
||||
group_debugger()->UnlinkCodeBreakpoints(loc);
|
||||
|
@ -2184,8 +2195,11 @@ bool BreakpointLocation::EnsureIsResolved(const Function& target_function,
|
|||
return true;
|
||||
}
|
||||
|
||||
void GroupDebugger::MakeCodeBreakpointAt(const Function& func,
|
||||
BreakpointLocation* loc) {
|
||||
void GroupDebugger::MakeCodeBreakpointAtUnsafe(const Function& func,
|
||||
BreakpointLocation* loc) {
|
||||
DEBUG_ASSERT(Thread::Current()->IsInStoppedMutatorsScope() ||
|
||||
code_breakpoints_lock()->IsCurrentThreadWriter());
|
||||
|
||||
ASSERT(loc->token_pos().IsReal());
|
||||
ASSERT((loc != nullptr) && loc->IsResolved());
|
||||
ASSERT(!func.HasOptimizedCode());
|
||||
|
@ -2211,7 +2225,6 @@ void GroupDebugger::MakeCodeBreakpointAt(const Function& func,
|
|||
}
|
||||
|
||||
uword lowest_pc = code.PayloadStart() + lowest_pc_offset;
|
||||
WriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc);
|
||||
if (code_bpt == nullptr) {
|
||||
// No code breakpoint for this code exists; create one.
|
||||
|
@ -2240,6 +2253,17 @@ void GroupDebugger::MakeCodeBreakpointAt(const Function& func,
|
|||
}
|
||||
}
|
||||
|
||||
void GroupDebugger::MakeCodeBreakpointAt(const Function& func,
|
||||
BreakpointLocation* loc) {
|
||||
auto thread = Thread::Current();
|
||||
if (thread->IsInStoppedMutatorsScope()) {
|
||||
MakeCodeBreakpointAtUnsafe(func, loc);
|
||||
} else {
|
||||
SafepointWriteRwLocker sl(thread, code_breakpoints_lock());
|
||||
MakeCodeBreakpointAtUnsafe(func, loc);
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::FindCompiledFunctions(
|
||||
const GrowableHandlePtrArray<const Script>& scripts,
|
||||
TokenPosition start_pos,
|
||||
|
@ -2645,7 +2669,7 @@ BreakpointLocation* Debugger::SetBreakpoint(
|
|||
// associated with the breakpoint location loc.
|
||||
void GroupDebugger::SyncBreakpointLocation(BreakpointLocation* loc) {
|
||||
bool any_enabled = loc->AnyEnabled();
|
||||
WriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
CodeBreakpoint* cbpt = code_breakpoints_;
|
||||
while (cbpt != nullptr) {
|
||||
if (cbpt->HasBreakpointLocation(loc)) {
|
||||
|
@ -3033,7 +3057,7 @@ void Debugger::Pause(ServiceEvent* event) {
|
|||
}
|
||||
|
||||
void GroupDebugger::Pause() {
|
||||
WriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
if (needs_breakpoint_cleanup_) {
|
||||
RemoveUnlinkedCodeBreakpoints();
|
||||
}
|
||||
|
@ -3384,20 +3408,35 @@ void GroupDebugger::UnregisterSingleSteppingDebugger(Thread* thread,
|
|||
single_stepping_set_.Remove(debugger);
|
||||
}
|
||||
|
||||
bool GroupDebugger::HasBreakpoint(Thread* thread, const Function& function) {
|
||||
{
|
||||
ReadRwLocker(thread, breakpoint_locations_lock());
|
||||
// Check if function has any breakpoints.
|
||||
String& url = String::Handle(thread->zone());
|
||||
for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
|
||||
BreakpointLocation* location = breakpoint_locations_.At(i);
|
||||
url = location->url();
|
||||
if (FunctionOverlaps(function, url, location->token_pos(),
|
||||
location->end_token_pos())) {
|
||||
return true;
|
||||
}
|
||||
bool GroupDebugger::HasBreakpointUnsafe(Thread* thread,
|
||||
const Function& function) {
|
||||
DEBUG_ASSERT(thread->IsInStoppedMutatorsScope() ||
|
||||
breakpoint_locations_lock()->IsCurrentThreadReader());
|
||||
// Check if function has any breakpoints.
|
||||
String& url = String::Handle(thread->zone());
|
||||
for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
|
||||
BreakpointLocation* location = breakpoint_locations_.At(i);
|
||||
url = location->url();
|
||||
if (FunctionOverlaps(function, url, location->token_pos(),
|
||||
location->end_token_pos())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GroupDebugger::HasBreakpoint(Thread* thread, const Function& function) {
|
||||
bool hasBreakpoint = false;
|
||||
// Don't need to worry about the lock if mutators are stopped.
|
||||
if (thread->IsInStoppedMutatorsScope()) {
|
||||
hasBreakpoint = HasBreakpointUnsafe(thread, function);
|
||||
} else {
|
||||
SafepointReadRwLocker sl(thread, breakpoint_locations_lock());
|
||||
hasBreakpoint = HasBreakpointUnsafe(thread, function);
|
||||
}
|
||||
if (hasBreakpoint) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(aam): do we have to iterate over both code breakpoints and
|
||||
// breakpoint locations? Wouldn't be sufficient to iterate over only
|
||||
|
@ -3573,8 +3612,8 @@ ErrorPtr Debugger::PauseBreakpoint() {
|
|||
BreakpointLocation* bpt_location = nullptr;
|
||||
const char* cbpt_tostring = nullptr;
|
||||
{
|
||||
ReadRwLocker cbl(Thread::Current(),
|
||||
group_debugger()->code_breakpoints_lock());
|
||||
SafepointReadRwLocker cbl(Thread::Current(),
|
||||
group_debugger()->code_breakpoints_lock());
|
||||
CodeBreakpoint* cbpt = nullptr;
|
||||
bpt_location = group_debugger()->GetBreakpointLocationFor(
|
||||
this, top_frame->pc(), &cbpt);
|
||||
|
@ -3822,7 +3861,7 @@ void Debugger::NotifyDoneLoading() {
|
|||
// TODO(hausner): Could potentially make this faster by checking
|
||||
// whether the call target at pc is a debugger stub.
|
||||
bool GroupDebugger::HasActiveBreakpoint(uword pc) {
|
||||
ReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
CodeBreakpoint* cbpt = GetCodeBreakpoint(pc);
|
||||
return (cbpt != nullptr) && (cbpt->IsEnabled());
|
||||
}
|
||||
|
@ -3843,7 +3882,7 @@ BreakpointLocation* GroupDebugger::GetBreakpointLocationFor(
|
|||
uword breakpoint_address,
|
||||
CodeBreakpoint** pcbpt) {
|
||||
ASSERT(pcbpt != nullptr);
|
||||
ReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
*pcbpt = code_breakpoints_;
|
||||
while (*pcbpt != nullptr) {
|
||||
if ((*pcbpt)->pc() == breakpoint_address) {
|
||||
|
@ -3863,7 +3902,7 @@ void GroupDebugger::RegisterCodeBreakpoint(CodeBreakpoint* cbpt) {
|
|||
}
|
||||
|
||||
CodePtr GroupDebugger::GetPatchedStubAddress(uword breakpoint_address) {
|
||||
ReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
CodeBreakpoint* cbpt = GetCodeBreakpoint(breakpoint_address);
|
||||
if (cbpt != nullptr) {
|
||||
return cbpt->OrigStubAddress();
|
||||
|
@ -3873,8 +3912,8 @@ CodePtr GroupDebugger::GetPatchedStubAddress(uword breakpoint_address) {
|
|||
}
|
||||
|
||||
bool Debugger::SetBreakpointState(Breakpoint* bpt, bool enable) {
|
||||
WriteRwLocker sl(Thread::Current(),
|
||||
group_debugger()->breakpoint_locations_lock());
|
||||
SafepointWriteRwLocker sl(Thread::Current(),
|
||||
group_debugger()->breakpoint_locations_lock());
|
||||
if (bpt->is_enabled() != enable) {
|
||||
if (FLAG_verbose_debug) {
|
||||
OS::PrintErr("Setting breakpoint %" Pd " to state: %s\n", bpt->id(),
|
||||
|
@ -3890,8 +3929,8 @@ bool Debugger::SetBreakpointState(Breakpoint* bpt, bool enable) {
|
|||
// Remove and delete the source breakpoint bpt and its associated
|
||||
// code breakpoints.
|
||||
void Debugger::RemoveBreakpoint(intptr_t bp_id) {
|
||||
WriteRwLocker sl(Thread::Current(),
|
||||
group_debugger()->breakpoint_locations_lock());
|
||||
SafepointWriteRwLocker sl(Thread::Current(),
|
||||
group_debugger()->breakpoint_locations_lock());
|
||||
if (RemoveBreakpointFromTheList(bp_id, &breakpoint_locations_)) {
|
||||
return;
|
||||
}
|
||||
|
@ -3966,7 +4005,8 @@ bool Debugger::RemoveBreakpointFromTheList(intptr_t bp_id,
|
|||
}
|
||||
|
||||
void GroupDebugger::RegisterBreakpointLocation(BreakpointLocation* location) {
|
||||
ASSERT(breakpoint_locations_lock()->IsCurrentThreadWriter());
|
||||
DEBUG_ASSERT(breakpoint_locations_lock()->IsCurrentThreadWriter() ||
|
||||
Thread::Current()->IsInStoppedMutatorsScope());
|
||||
breakpoint_locations_.Add(location);
|
||||
}
|
||||
|
||||
|
@ -3986,7 +4026,7 @@ void GroupDebugger::UnregisterBreakpointLocation(BreakpointLocation* location) {
|
|||
// should be hit before it gets deleted.
|
||||
void GroupDebugger::UnlinkCodeBreakpoints(BreakpointLocation* bpt_location) {
|
||||
ASSERT(bpt_location != nullptr);
|
||||
WriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
|
||||
CodeBreakpoint* curr_bpt = code_breakpoints_;
|
||||
while (curr_bpt != nullptr) {
|
||||
if (curr_bpt->FindAndDeleteBreakpointLocation(bpt_location)) {
|
||||
|
@ -4000,7 +4040,8 @@ void GroupDebugger::UnlinkCodeBreakpoints(BreakpointLocation* bpt_location) {
|
|||
// Remove and delete unlinked code breakpoints, i.e. breakpoints that
|
||||
// are not associated with a breakpoint location.
|
||||
void GroupDebugger::RemoveUnlinkedCodeBreakpoints() {
|
||||
ASSERT(code_breakpoints_lock()->IsCurrentThreadWriter());
|
||||
DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadWriter() ||
|
||||
Thread::Current()->IsInStoppedMutatorsScope());
|
||||
CodeBreakpoint* prev_bpt = nullptr;
|
||||
CodeBreakpoint* curr_bpt = code_breakpoints_;
|
||||
while (curr_bpt != nullptr) {
|
||||
|
@ -4125,15 +4166,27 @@ BreakpointLocation* Debugger::GetLatentBreakpoint(const String& url,
|
|||
return loc;
|
||||
}
|
||||
|
||||
void Debugger::RegisterBreakpointLocation(BreakpointLocation* loc) {
|
||||
WriteRwLocker sl(Thread::Current(),
|
||||
group_debugger()->breakpoint_locations_lock());
|
||||
void Debugger::RegisterBreakpointLocationUnsafe(BreakpointLocation* loc) {
|
||||
DEBUG_ASSERT(
|
||||
group_debugger()->breakpoint_locations_lock()->IsCurrentThreadWriter() ||
|
||||
Thread::Current()->IsInStoppedMutatorsScope());
|
||||
ASSERT(loc->next() == nullptr);
|
||||
loc->set_next(breakpoint_locations_);
|
||||
breakpoint_locations_ = loc;
|
||||
group_debugger()->RegisterBreakpointLocation(loc);
|
||||
}
|
||||
|
||||
void Debugger::RegisterBreakpointLocation(BreakpointLocation* loc) {
|
||||
auto thread = Thread::Current();
|
||||
if (thread->IsInStoppedMutatorsScope()) {
|
||||
RegisterBreakpointLocationUnsafe(loc);
|
||||
} else {
|
||||
SafepointWriteRwLocker sl(thread,
|
||||
group_debugger()->breakpoint_locations_lock());
|
||||
RegisterBreakpointLocationUnsafe(loc);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !PRODUCT
|
||||
|
||||
} // namespace dart
|
||||
|
|
|
@ -574,6 +574,8 @@ class GroupDebugger {
|
|||
explicit GroupDebugger(IsolateGroup* isolate_group);
|
||||
~GroupDebugger();
|
||||
|
||||
void MakeCodeBreakpointAtUnsafe(const Function& func,
|
||||
BreakpointLocation* bpt);
|
||||
void MakeCodeBreakpointAt(const Function& func, BreakpointLocation* bpt);
|
||||
|
||||
// Returns [nullptr] if no breakpoint exists for the given address.
|
||||
|
@ -592,6 +594,7 @@ class GroupDebugger {
|
|||
// Returns true if the call at address pc is patched to point to
|
||||
// a debugger stub.
|
||||
bool HasActiveBreakpoint(uword pc);
|
||||
bool HasCodeBreakpointInFunctionUnsafe(const Function& func);
|
||||
bool HasCodeBreakpointInFunction(const Function& func);
|
||||
bool HasCodeBreakpointInCode(const Code& code);
|
||||
|
||||
|
@ -609,9 +612,11 @@ class GroupDebugger {
|
|||
|
||||
void VisitObjectPointers(ObjectPointerVisitor* visitor);
|
||||
|
||||
RwLock* code_breakpoints_lock() { return code_breakpoints_lock_.get(); }
|
||||
SafepointRwLock* code_breakpoints_lock() {
|
||||
return code_breakpoints_lock_.get();
|
||||
}
|
||||
|
||||
RwLock* breakpoint_locations_lock() {
|
||||
SafepointRwLock* breakpoint_locations_lock() {
|
||||
return breakpoint_locations_lock_.get();
|
||||
}
|
||||
|
||||
|
@ -623,6 +628,7 @@ class GroupDebugger {
|
|||
|
||||
// Returns [true] if there is at least one breakpoint set in function or code.
|
||||
// Checks for both user-defined and internal temporary breakpoints.
|
||||
bool HasBreakpointUnsafe(Thread* thread, const Function& function);
|
||||
bool HasBreakpoint(Thread* thread, const Function& function);
|
||||
bool IsDebugging(Thread* thread, const Function& function);
|
||||
|
||||
|
@ -631,13 +637,13 @@ class GroupDebugger {
|
|||
private:
|
||||
IsolateGroup* isolate_group_;
|
||||
|
||||
std::unique_ptr<RwLock> code_breakpoints_lock_;
|
||||
std::unique_ptr<SafepointRwLock> code_breakpoints_lock_;
|
||||
CodeBreakpoint* code_breakpoints_;
|
||||
|
||||
// Secondary list of all breakpoint_locations_(primary is in Debugger class).
|
||||
// This list is kept in sync with all the lists in Isolate Debuggers and is
|
||||
// used to quickly scan BreakpointLocations when new Function is compiled.
|
||||
std::unique_ptr<RwLock> breakpoint_locations_lock_;
|
||||
std::unique_ptr<SafepointRwLock> breakpoint_locations_lock_;
|
||||
MallocGrowableArray<BreakpointLocation*> breakpoint_locations_;
|
||||
|
||||
std::unique_ptr<RwLock> single_stepping_set_lock_;
|
||||
|
@ -823,6 +829,7 @@ class Debugger {
|
|||
BreakpointLocation* GetLatentBreakpoint(const String& url,
|
||||
intptr_t line,
|
||||
intptr_t column);
|
||||
void RegisterBreakpointLocationUnsafe(BreakpointLocation* loc);
|
||||
void RegisterBreakpointLocation(BreakpointLocation* bpt);
|
||||
BreakpointLocation* GetResolvedBreakpointLocation(
|
||||
const String& script_url,
|
||||
|
|
|
@ -157,6 +157,15 @@ Dart_Handle Dart_SetBreakpoint(Dart_Handle script_url_in,
|
|||
return Dart_NewInteger(bpt->id());
|
||||
}
|
||||
|
||||
Dart_Handle Dart_RemoveBreakpoint(Dart_Handle breakpoint_id_in) {
|
||||
DARTSCOPE(Thread::Current());
|
||||
Isolate* I = T->isolate();
|
||||
CHECK_DEBUGGER(I);
|
||||
UNWRAP_AND_CHECK_PARAM(Integer, breakpoint_id, breakpoint_id_in);
|
||||
I->debugger()->RemoveBreakpoint(breakpoint_id.AsInt64Value());
|
||||
return Api::Success();
|
||||
}
|
||||
|
||||
Dart_Handle Dart_EvaluateStaticExpr(Dart_Handle lib_handle,
|
||||
Dart_Handle expr_in) {
|
||||
DARTSCOPE(Thread::Current());
|
||||
|
|
|
@ -82,6 +82,12 @@ Dart_Handle Dart_GetLibraryDebuggable(intptr_t library_id, bool* is_debuggable);
|
|||
*/
|
||||
Dart_Handle Dart_SetLibraryDebuggable(intptr_t library_id, bool is_debuggable);
|
||||
|
||||
/**
|
||||
* Remove breakpoint with provided id.
|
||||
*
|
||||
* Requires there to be a current isolate.
|
||||
*/
|
||||
Dart_Handle Dart_RemoveBreakpoint(Dart_Handle breakpoint_id);
|
||||
/**
|
||||
* Sets a breakpoint at line \line_number in \script_url, or the closest
|
||||
* following line (within the same function) where a breakpoint can be set.
|
||||
|
|
|
@ -5870,6 +5870,109 @@ TEST_CASE(DeoptimizeFramesWhenSettingBreakpoint) {
|
|||
Dart_EnterIsolate(parent);
|
||||
}
|
||||
|
||||
class ToggleBreakpointTask : public ThreadPool::Task {
|
||||
public:
|
||||
ToggleBreakpointTask(IsolateGroup* isolate_group,
|
||||
Dart_Isolate isolate,
|
||||
std::atomic<bool>* done)
|
||||
: isolate_group_(isolate_group), isolate_(isolate), done_(done) {}
|
||||
virtual void Run() {
|
||||
Dart_EnterIsolate(isolate_);
|
||||
Dart_EnterScope();
|
||||
const int kBreakpointLine = 5; // in the dart script below
|
||||
Thread* t = Thread::Current();
|
||||
for (intptr_t i = 0; i < 1000; i++) {
|
||||
Dart_Handle result =
|
||||
Dart_SetBreakpoint(NewString(TestCase::url()), kBreakpointLine);
|
||||
EXPECT_VALID(result);
|
||||
int64_t breakpoint_id;
|
||||
{
|
||||
TransitionNativeToVM transition(t);
|
||||
Integer& breakpoint_id_handle = Integer::Handle();
|
||||
breakpoint_id_handle ^= Api::UnwrapHandle(result);
|
||||
breakpoint_id = breakpoint_id_handle.AsInt64Value();
|
||||
}
|
||||
result = Dart_RemoveBreakpoint(Dart_NewInteger(breakpoint_id));
|
||||
EXPECT_VALID(result);
|
||||
}
|
||||
Dart_ExitScope();
|
||||
Dart_ExitIsolate();
|
||||
*done_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
IsolateGroup* isolate_group_;
|
||||
Dart_Isolate isolate_;
|
||||
std::atomic<bool>* done_;
|
||||
};
|
||||
|
||||
TEST_CASE(DartAPI_BreakpointLockRace) {
|
||||
const char* kScriptChars =
|
||||
"class A {\n"
|
||||
" a() {\n"
|
||||
" }\n"
|
||||
" b() {\n"
|
||||
" a();\n" // This is line 5.
|
||||
" }\n"
|
||||
"}\n"
|
||||
"test() {\n"
|
||||
" new A().b();\n"
|
||||
"}";
|
||||
// Create a test library and Load up a test script in it.
|
||||
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr);
|
||||
EXPECT_VALID(lib);
|
||||
|
||||
// Run function A.b one time.
|
||||
Dart_Handle result = Dart_Invoke(lib, NewString("test"), 0, nullptr);
|
||||
EXPECT_VALID(result);
|
||||
|
||||
// Launch second isolate so that running with stopped mutators during
|
||||
// deoptimizattion requests a safepoint.
|
||||
Dart_Isolate parent = Dart_CurrentIsolate();
|
||||
Dart_ExitIsolate();
|
||||
char* error = nullptr;
|
||||
Dart_Isolate child = Dart_CreateIsolateInGroup(parent, "child",
|
||||
/*shutdown_callback=*/nullptr,
|
||||
/*cleanup_callback=*/nullptr,
|
||||
/*peer=*/nullptr, &error);
|
||||
EXPECT_NE(nullptr, child);
|
||||
EXPECT_EQ(nullptr, error);
|
||||
Dart_ExitIsolate();
|
||||
Dart_EnterIsolate(parent);
|
||||
|
||||
// Run function A.b one time.
|
||||
std::atomic<bool> done = false;
|
||||
Dart::thread_pool()->Run<ToggleBreakpointTask>(IsolateGroup::Current(), child,
|
||||
&done);
|
||||
|
||||
while (!done) {
|
||||
ReloadParticipationScope allow_reload(thread);
|
||||
{
|
||||
TransitionNativeToVM transition(thread);
|
||||
const String& name = String::Handle(String::New(TestCase::url()));
|
||||
const Library& vmlib =
|
||||
Library::Handle(Library::LookupLibrary(thread, name));
|
||||
EXPECT(!vmlib.IsNull());
|
||||
const Class& class_a = Class::Handle(
|
||||
vmlib.LookupClass(String::Handle(Symbols::New(thread, "A"))));
|
||||
Function& func_b = Function::Handle(GetFunction(class_a, "b"));
|
||||
func_b.CanBeInlined();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure child isolate finishes.
|
||||
Dart_ExitIsolate();
|
||||
Dart_EnterIsolate(child);
|
||||
{
|
||||
bool result =
|
||||
Dart_RunLoopAsync(/*errors_are_fatal=*/true,
|
||||
/*on_error_port=*/0, /*on_exit_port=*/0, &error);
|
||||
EXPECT_EQ(true, result);
|
||||
}
|
||||
EXPECT_EQ(nullptr, error);
|
||||
Dart_EnterIsolate(parent);
|
||||
}
|
||||
|
||||
ISOLATE_UNIT_TEST_CASE(SpecialClassesHaveEmptyArrays) {
|
||||
ObjectStore* object_store = IsolateGroup::Current()->object_store();
|
||||
Class& cls = Class::Handle();
|
||||
|
|
Loading…
Reference in a new issue