| // 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(); |
| } |
| } |
| } |
| |
| void SafepointMonitorLocker::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(); |
| } |
| } |
| } |
| |
| void SafepointMonitorLocker::ReleaseLock() { |
| monitor_->Exit(); |
| } |
| |
| Monitor::WaitResult SafepointMonitorLocker::Wait(int64_t millis) { |
| Thread* thread = Thread::Current(); |
| if (thread != nullptr) { |
| 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). |
| // |
| // Though if the lock was already acquired by this thread before entering a |
| // safepoint, we do allow the nested acquire (which is a NOP). |
| 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). |
| 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); |
| } |
| } |
| |
| 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 |