// Copyright (c) 2016, 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/lockers.h" #include "platform/assert.h" #include "vm/heap/safepoint.h" #include "vm/isolate.h" namespace dart { Monitor::WaitResult MonitorLocker::WaitWithSafepointCheck(Thread* thread, int64_t millis) { ASSERT(thread == Thread::Current()); ASSERT(thread->execution_state() == Thread::kThreadInVM); #if defined(DEBUG) if (no_safepoint_scope_) { thread->DecrementNoSafepointScopeDepth(); } #endif thread->set_execution_state(Thread::kThreadInBlockedState); thread->EnterSafepoint(); Monitor::WaitResult result = monitor_->Wait(millis); // First try a fast update of the thread state to indicate it is not at a // safepoint anymore. if (!thread->TryExitSafepoint()) { // Fast update failed which means we could potentially be in the middle // of a safepoint operation and need to block for it. monitor_->Exit(); SafepointHandler* handler = thread->isolate_group()->safepoint_handler(); handler->ExitSafepointUsingLock(thread); monitor_->Enter(); } thread->set_execution_state(Thread::kThreadInVM); #if defined(DEBUG) if (no_safepoint_scope_) { thread->IncrementNoSafepointScopeDepth(); } #endif return result; } SafepointMutexLocker::SafepointMutexLocker(ThreadState* thread, Mutex* mutex) : StackResource(thread), mutex_(mutex) { ASSERT(mutex != NULL); if (!mutex_->TryLock()) { // We did not get the lock and could potentially block, so transition // accordingly. Thread* thread = Thread::Current(); if (thread != NULL) { TransitionVMToBlocked transition(thread); mutex->Lock(); } else { mutex->Lock(); } } } void SafepointMonitorLocker::AcquireLock() { ASSERT(monitor_ != NULL); if (!monitor_->TryEnter()) { // We did not get the lock and could potentially block, so transition // accordingly. Thread* thread = Thread::Current(); if (thread != NULL) { TransitionVMToBlocked transition(thread); monitor_->Enter(); } else { monitor_->Enter(); } } } void SafepointMonitorLocker::ReleaseLock() { monitor_->Exit(); } Monitor::WaitResult SafepointMonitorLocker::Wait(int64_t millis) { Thread* thread = Thread::Current(); if (thread != NULL) { Monitor::WaitResult result; { TransitionVMToBlocked transition(thread); result = monitor_->Wait(millis); } return result; } else { return monitor_->Wait(millis); } } #if defined(DEBUG) bool SafepointRwLock::IsCurrentThreadReader() { ThreadId id = OSThread::GetCurrentThreadId(); if (IsCurrentThreadWriter()) { return true; } MonitorLocker ml(&monitor_); for (intptr_t i = readers_ids_.length() - 1; i >= 0; i--) { if (readers_ids_.At(i) == id) { return true; } } return false; } #endif // defined(DEBUG) bool SafepointRwLock::EnterRead() { // 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). ASSERT(thread == nullptr || !thread->isolate_group()->safepoint_handler()->IsOwnedByTheThread( thread)); const bool can_block_without_safepoint = thread == nullptr; bool acquired_read_lock = false; if (!TryEnterRead(can_block_without_safepoint, &acquired_read_lock)) { // Important: must never hold monitor_ when blocking for safepoint. TransitionVMToBlocked transition(thread); const bool ok = TryEnterRead(/*can_block=*/true, &acquired_read_lock); RELEASE_ASSERT(ok); RELEASE_ASSERT(acquired_read_lock); } return acquired_read_lock; } bool SafepointRwLock::TryEnterRead(bool can_block, bool* acquired_read_lock) { MonitorLocker ml(&monitor_); if (IsCurrentThreadWriter()) { *acquired_read_lock = false; return true; } if (can_block) { while (state_ < 0) { ml.Wait(); } } if (state_ >= 0) { ++state_; DEBUG_ONLY(readers_ids_.Add(OSThread::GetCurrentThreadId())); *acquired_read_lock = true; return true; } return false; } void SafepointRwLock::LeaveRead() { MonitorLocker ml(&monitor_); ASSERT(state_ > 0); #if defined(DEBUG) { intptr_t i = readers_ids_.length() - 1; ThreadId id = OSThread::GetCurrentThreadId(); while (i >= 0) { if (readers_ids_.At(i) == id) { readers_ids_.RemoveAt(i); break; } i--; } ASSERT(i >= 0); } #endif if (--state_ == 0) { ml.NotifyAll(); } } void SafepointRwLock::EnterWrite() { // No need to safepoint if the current thread is not attached. auto thread = Thread::Current(); const bool can_block_without_safepoint = thread == nullptr; if (!TryEnterWrite(can_block_without_safepoint)) { // Important: must never hold monitor_ when blocking for safepoint. TransitionVMToBlocked transition(thread); const bool ok = TryEnterWrite(/*can_block=*/true); RELEASE_ASSERT(ok); } } bool SafepointRwLock::TryEnterWrite(bool can_block) { MonitorLocker ml(&monitor_); if (IsCurrentThreadWriter()) { state_--; return true; } if (can_block) { while (state_ != 0) { ml.Wait(); } } if (state_ == 0) { writer_id_ = OSThread::GetCurrentThreadId(); state_ = -1; return true; } return false; } void SafepointRwLock::LeaveWrite() { MonitorLocker ml(&monitor_); ASSERT(state_ < 0); if (++state_ < 0) { return; } writer_id_ = OSThread::kInvalidThreadId; ml.NotifyAll(); } } // namespace dart