| // 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(); | |
| thread->ExitSafepointUsingLock(); | |
| 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 != nullptr); | |
| if (!mutex_->TryLock()) { | |
| // We did not get the lock and could potentially block, so transition | |
| // accordingly. | |
| Thread* thread = Thread::Current(); | |
| if (thread != nullptr) { | |
| TransitionVMToBlocked transition(thread); | |
| mutex->Lock(); | |
| } else { | |
| mutex->Lock(); | |
| } | |
| } | |
| } | |
| template <typename M> | |
| void SafepointLocker<M>::AcquireLock() { | |
| ASSERT(monitor_ != nullptr); | |
| if (!monitor_->TryEnter()) { | |
| // We did not get the lock and could potentially block, so transition | |
| // accordingly. | |
| Thread* thread = Thread::Current(); | |
| if (thread != nullptr) { | |
| TransitionVMToBlocked transition(thread); | |
| monitor_->Enter(); | |
| } else { | |
| monitor_->Enter(); | |
| } | |
| } | |
| } | |
| template <typename M> | |
| typename M::WaitResult SafepointLocker<M>::Wait(int64_t millis) { | |
| Thread* thread = Thread::Current(); | |
| if (thread != nullptr) { | |
| typename M::WaitResult result; | |
| { | |
| TransitionVMToBlocked transition(thread); | |
| result = monitor_->Wait(millis); | |
| } | |
| return result; | |
| } else { | |
| return monitor_->Wait(millis); | |
| } | |
| } | |
| template class SafepointLocker<ReentrantMonitor>; | |
| template class SafepointLocker<Monitor>; | |
| #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). | |
| // | |
| // 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() || | |
| IsCurrentThreadReader()); | |
| 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(); | |
| // 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. | |
| TransitionVMToBlocked transition(thread); | |
| const bool ok = TryEnterWrite(/*can_block=*/true); | |
| RELEASE_ASSERT(ok); | |
| } | |
| } | |
| 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()) { | |
| 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 |