dart-sdk/runtime/vm/closure_functions_cache.cc
Alexander Aprelev 9302a7427b [vm/concurrency] Assert that no SafepointRwLocks are acquired while in SafepointOperationScope.
Attempts to acquire safepoint lock while in SafepointOperationScope could result
in deadlocks if somebody else was holding that lock when they were forced to a safepoint.
Clean up few places where locks were acquired in SafepointOperationScope.

Introduce StoppedMutatorsScope and GroupDebugger::RunUnderReadLockIfNeeded that acquires
a lock only if it runs outside of StoppedMutatorsScope - to prevent such deadlocks.

Also fix tsan warning about data race around message_notify_callback by making it atomic.

TEST=tsan runs of debugger CI tests

Fixes https://github.com/dart-lang/sdk/issues/45527
Fixes https://github.com/dart-lang/sdk/issues/45549

Issue https://github.com/dart-lang/sdk/issues/36097

Change-Id: Ibb53d9ce760d869e044e17075aeebf20fc0016a5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193582
Commit-Queue: Alexander Aprelev <aam@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
2021-04-03 03:36:32 +00:00

204 lines
6.7 KiB
C++

// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/closure_functions_cache.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/object.h"
#include "vm/object_store.h"
namespace dart {
FunctionPtr ClosureFunctionsCache::LookupClosureFunction(
const Class& owner,
TokenPosition token_pos) {
auto thread = Thread::Current();
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
return LookupClosureFunctionLocked(owner, token_pos);
}
FunctionPtr ClosureFunctionsCache::LookupClosureFunctionLocked(
const Class& owner,
TokenPosition token_pos) {
auto thread = Thread::Current();
auto zone = thread->zone();
auto object_store = thread->isolate_group()->object_store();
DEBUG_ASSERT(
thread->isolate_group()->program_lock()->IsCurrentThreadReader());
const auto& closures =
GrowableObjectArray::Handle(zone, object_store->closure_functions());
auto& closure = Function::Handle(zone);
intptr_t num_closures = closures.Length();
for (intptr_t i = 0; i < num_closures; i++) {
closure ^= closures.At(i);
if (closure.token_pos() == token_pos && closure.Owner() == owner.ptr()) {
return closure.ptr();
}
}
return Function::null();
}
FunctionPtr ClosureFunctionsCache::LookupClosureFunction(
const Function& parent,
TokenPosition token_pos) {
auto thread = Thread::Current();
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
return LookupClosureFunctionLocked(parent, token_pos);
}
FunctionPtr ClosureFunctionsCache::LookupClosureFunctionLocked(
const Function& parent,
TokenPosition token_pos) {
auto thread = Thread::Current();
auto zone = thread->zone();
auto object_store = thread->isolate_group()->object_store();
DEBUG_ASSERT(
thread->isolate_group()->program_lock()->IsCurrentThreadReader());
const auto& closures =
GrowableObjectArray::Handle(zone, object_store->closure_functions());
auto& closure = Function::Handle(zone);
intptr_t num_closures = closures.Length();
for (intptr_t i = 0; i < num_closures; i++) {
closure ^= closures.At(i);
if (closure.token_pos() == token_pos &&
closure.parent_function() == parent.ptr()) {
return closure.ptr();
}
}
return Function::null();
}
void ClosureFunctionsCache::AddClosureFunctionLocked(const Function& function) {
ASSERT(!Compiler::IsBackgroundCompilation());
auto thread = Thread::Current();
auto zone = thread->zone();
auto object_store = thread->isolate_group()->object_store();
DEBUG_ASSERT(
thread->isolate_group()->program_lock()->IsCurrentThreadWriter());
const auto& closures =
GrowableObjectArray::Handle(zone, object_store->closure_functions());
ASSERT(!closures.IsNull());
ASSERT(function.IsNonImplicitClosureFunction());
closures.Add(function, Heap::kOld);
}
intptr_t ClosureFunctionsCache::FindClosureIndex(const Function& needle) {
auto thread = Thread::Current();
auto zone = thread->zone();
auto object_store = thread->isolate_group()->object_store();
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
const auto& closures_array =
GrowableObjectArray::Handle(zone, object_store->closure_functions());
intptr_t num_closures = closures_array.Length();
for (intptr_t i = 0; i < num_closures; i++) {
if (closures_array.At(i) == needle.ptr()) {
return i;
}
}
return -1;
}
FunctionPtr ClosureFunctionsCache::ClosureFunctionFromIndex(intptr_t idx) {
auto thread = Thread::Current();
auto zone = thread->zone();
auto object_store = thread->isolate_group()->object_store();
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
const auto& closures_array =
GrowableObjectArray::Handle(zone, object_store->closure_functions());
if (idx < 0 || idx >= closures_array.Length()) {
return Function::null();
}
return Function::RawCast(closures_array.At(idx));
}
FunctionPtr ClosureFunctionsCache::GetUniqueInnerClosure(
const Function& outer) {
auto thread = Thread::Current();
auto zone = thread->zone();
auto object_store = thread->isolate_group()->object_store();
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
const auto& closures =
GrowableObjectArray::Handle(zone, object_store->closure_functions());
auto& entry = Function::Handle(zone);
for (intptr_t i = (closures.Length() - 1); i >= 0; i--) {
entry ^= closures.At(i);
if (entry.parent_function() == outer.ptr()) {
#if defined(DEBUG)
auto& other = Function::Handle(zone);
for (intptr_t j = i - 1; j >= 0; j--) {
other ^= closures.At(j);
ASSERT(other.parent_function() != outer.ptr());
}
#endif
return entry.ptr();
}
}
return Function::null();
}
void ClosureFunctionsCache::ForAllClosureFunctions(
std::function<bool(const Function&)> callback) {
auto thread = Thread::Current();
auto zone = thread->zone();
auto object_store = thread->isolate_group()->object_store();
auto& current_data = Array::Handle(zone);
auto& entry = Function::Handle(zone);
// NOTE: Inner functions may get added to the closures array while iterating -
// we guarantee that any closure functions added on this thread by a
// [callback] call will be visited as well.
//
// We avoid holding a lock while accessing the closures array, since often
// times [callback] will do very heavy things (e.g. compiling the function).
//
// This means we can possibly miss a concurrently added closure function -
// which the caller should be ok with (or it guarantees that this cannot
// happen).
const auto& closures =
GrowableObjectArray::Handle(zone, object_store->closure_functions());
if (!thread->IsInStoppedMutatorsScope()) {
// The empty read locker scope will implicitly issue an acquire memory
// fence, which means any closure functions added so far will be visible and
// iterated further down.
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
}
// We have an outer loop to ensure any new closure functions added by
// [callback] will be iterated as well.
intptr_t i = 0;
while (true) {
intptr_t current_length = closures.Length();
if (i == current_length) break;
current_data = closures.data();
if (current_data.Length() < current_length) {
current_length = current_data.Length();
}
for (; i < current_length; ++i) {
entry ^= current_data.At(i);
if (!callback(entry)) {
return; // Stop iteration.
}
}
}
}
} // namespace dart