[vm] Fix deadlocks in shared field initialization.
TEST=ci Bug: https://github.com/dart-lang/sdk/issues/62392 Change-Id: Id34b7f47c8f03c4cd0fb6555912279ee03303f23 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/474565 Reviewed-by: Alexander Aprelev <aam@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
committed by
Commit Queue
parent
0eae14f76a
commit
b7a36e1c0f
@@ -376,6 +376,30 @@ class TransitionVMToBlocked : public TransitionSafepointState {
|
||||
DISALLOW_COPY_AND_ASSIGN(TransitionVMToBlocked);
|
||||
};
|
||||
|
||||
class TransitionVMToBlockedReloadableStealable
|
||||
: public TransitionSafepointState {
|
||||
public:
|
||||
explicit TransitionVMToBlockedReloadableStealable(Thread* T)
|
||||
: TransitionSafepointState(T) {
|
||||
ASSERT(T->CanAcquireSafepointLocks());
|
||||
// A thread blocked on a monitor is considered to be at a safepoint.
|
||||
ASSERT(T->execution_state() == Thread::kThreadInVM);
|
||||
T->set_execution_state(Thread::kThreadInReloadableBlockedState);
|
||||
T->EnterSafepointToNative();
|
||||
}
|
||||
|
||||
~TransitionVMToBlockedReloadableStealable() {
|
||||
// We are returning to vm code and so we are not at a safepoint anymore.
|
||||
ASSERT(thread()->execution_state() ==
|
||||
Thread::kThreadInReloadableBlockedState);
|
||||
thread()->ExitSafepointFromNative();
|
||||
thread()->set_execution_state(Thread::kThreadInVM);
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(TransitionVMToBlockedReloadableStealable);
|
||||
};
|
||||
|
||||
// TransitionVMToNative is used to transition the safepoint state of a
|
||||
// thread from "running vm code" to "running native code" and ensures
|
||||
// that the state is reverted back to "running vm code" when
|
||||
|
||||
@@ -198,6 +198,32 @@ void SafepointRwLock::EnterWrite() {
|
||||
}
|
||||
}
|
||||
|
||||
void SafepointRwLock::EnterWriteReloadableStealable() {
|
||||
// No need to safepoint if the current thread is not attached.
|
||||
auto thread = Thread::Current();
|
||||
// Attempt to acquire a lock while owning a safepoint could lead to a deadlock
|
||||
// (some other thread might be forced to a safepoint while holding this lock).
|
||||
//
|
||||
// Though if the lock was already acquired by this thread before entering a
|
||||
// safepoint, we do allow the nested acquire (which is a NOP).
|
||||
ASSERT(thread == nullptr || thread->execution_state() == Thread::kThreadInVM);
|
||||
DEBUG_ASSERT(thread == nullptr || thread->CanAcquireSafepointLocks() ||
|
||||
IsCurrentThreadWriter());
|
||||
|
||||
const bool can_block_without_safepoint = thread == nullptr;
|
||||
|
||||
RELEASE_ASSERT(can_block_without_safepoint ||
|
||||
thread->current_safepoint_level() >=
|
||||
expected_safepoint_level_);
|
||||
|
||||
if (!TryEnterWrite(can_block_without_safepoint)) {
|
||||
// Important: must never hold monitor_ when blocking for safepoint.
|
||||
TransitionVMToBlockedReloadableStealable transition(thread);
|
||||
const bool ok = TryEnterWrite(/*can_block=*/true);
|
||||
RELEASE_ASSERT(ok);
|
||||
}
|
||||
}
|
||||
|
||||
bool SafepointRwLock::TryEnterWrite(bool can_block) {
|
||||
MonitorLocker ml(&monitor_);
|
||||
if (IsCurrentThreadWriter()) {
|
||||
|
||||
@@ -379,6 +379,7 @@ class SafepointRwLock {
|
||||
private:
|
||||
friend class SafepointReadRwLocker;
|
||||
friend class SafepointWriteRwLocker;
|
||||
friend class ReloadableStealableWriteRwLocker;
|
||||
|
||||
// returns [true] if read lock was acquired,
|
||||
// returns [false] if the thread didn't have to acquire read lock due
|
||||
@@ -388,6 +389,7 @@ class SafepointRwLock {
|
||||
void LeaveRead();
|
||||
|
||||
void EnterWrite();
|
||||
void EnterWriteReloadableStealable();
|
||||
bool TryEnterWrite(bool can_block);
|
||||
void LeaveWrite();
|
||||
|
||||
@@ -500,6 +502,23 @@ class SafepointWriteRwLocker : public StackResource {
|
||||
SafepointRwLock* rw_lock_;
|
||||
};
|
||||
|
||||
/*
|
||||
* Allows reload safepoint and active mutator slot stealing while blocked.
|
||||
*/
|
||||
class ReloadableStealableWriteRwLocker : public StackResource {
|
||||
public:
|
||||
ReloadableStealableWriteRwLocker(ThreadState* thread_state,
|
||||
SafepointRwLock* rw_lock)
|
||||
: StackResource(thread_state), rw_lock_(rw_lock) {
|
||||
rw_lock_->EnterWriteReloadableStealable();
|
||||
}
|
||||
|
||||
~ReloadableStealableWriteRwLocker() { rw_lock_->LeaveWrite(); }
|
||||
|
||||
private:
|
||||
SafepointRwLock* rw_lock_;
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // RUNTIME_VM_LOCKERS_H_
|
||||
|
||||
@@ -5231,7 +5231,11 @@ DEFINE_LEAF_RUNTIME_ENTRY(PropagateError,
|
||||
DLRT_PropagateError);
|
||||
|
||||
DEFINE_RUNTIME_ENTRY(InitializeSharedField, 1) {
|
||||
SafepointWriteRwLocker locker(
|
||||
// Running the initializer means running arbitrary Dart code that might yield
|
||||
// to a reload safepoint or have its active mutator slot stolen. Make sure we
|
||||
// likewise yield while waiting for the lock to avoid the holder of the lock
|
||||
// being blocked on locker-waiters for reload or a mutator slot.
|
||||
ReloadableStealableWriteRwLocker locker(
|
||||
thread, thread->isolate_group()->shared_field_initializer_rwlock());
|
||||
const Field& field = Field::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
Object& result = Object::Handle(zone, field.StaticValue());
|
||||
|
||||
+4
-2
@@ -1108,7 +1108,8 @@ class Thread : public ThreadState, public IntrusiveDListEntry<Thread> {
|
||||
kThreadInVM = 0,
|
||||
kThreadInGenerated,
|
||||
kThreadInNative,
|
||||
kThreadInBlockedState
|
||||
kThreadInBlockedState,
|
||||
kThreadInReloadableBlockedState
|
||||
};
|
||||
|
||||
ExecutionState execution_state() const {
|
||||
@@ -1317,7 +1318,8 @@ class Thread : public ThreadState, public IntrusiveDListEntry<Thread> {
|
||||
if (no_reload_scope_depth_ > 0) {
|
||||
return SafepointLevel::kGCAndDeopt;
|
||||
}
|
||||
if (execution_state_ == kThreadInNative) {
|
||||
if (execution_state_ == kThreadInNative ||
|
||||
execution_state_ == kThreadInReloadableBlockedState) {
|
||||
return SafepointLevel::kGCAndDeoptAndReload;
|
||||
}
|
||||
if (allow_reload_scope_depth_ <= 0) {
|
||||
|
||||
Reference in New Issue
Block a user