// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Provide place to put profiling methods for the // Lock class. // The Lock class is used everywhere, and hence any changes // to lock.h tend to require a complete rebuild. To facilitate // profiler development, all the profiling methods are listed // here. Note that they are only instantiated in a debug // build, and the header provides all the trivial implementations // in a production build. #include "base/lock.h" #include "base/logging.h" Lock::Lock() : lock_() , recursion_count_shadow_(0) { #ifndef NDEBUG recursion_used_ = false; acquisition_count_ = 0; contention_count_ = 0; #endif } Lock::~Lock() { #ifndef NDEBUG // There should be no one to contend for the lock, // ...but we need the memory barrier to get a good value. lock_.Lock(); int final_recursion_count = recursion_count_shadow_; lock_.Unlock(); #endif // Allow unit test exception only at end of method. #ifndef NDEBUG DCHECK(0 == final_recursion_count); #endif } void Lock::Acquire() { #ifdef NDEBUG lock_.Lock(); recursion_count_shadow_++; #else // NDEBUG if (!lock_.Try()) { // We have contention. lock_.Lock(); contention_count_++; } // ONLY access data after locking. recursion_count_shadow_++; if (1 == recursion_count_shadow_) acquisition_count_++; else if (2 == recursion_count_shadow_ && !recursion_used_) // Usage Note: Set a break point to debug. recursion_used_ = true; #endif // NDEBUG } void Lock::Release() { --recursion_count_shadow_; // ONLY access while lock is still held. #ifndef NDEBUG DCHECK(0 <= recursion_count_shadow_); #endif lock_.Unlock(); } bool Lock::Try() { if (lock_.Try()) { recursion_count_shadow_++; #ifndef NDEBUG if (1 == recursion_count_shadow_) acquisition_count_++; else if (2 == recursion_count_shadow_ && !recursion_used_) // Usage Note: Set a break point to debug. recursion_used_ = true; #endif return true; } else { return false; } } // GetCurrentThreadRecursionCount returns the number of nested Acquire() calls // that have been made by the current thread holding this lock. The calling // thread is ***REQUIRED*** to be *currently* holding the lock. If that // calling requirement is violated, the return value is not well defined. // Return results are guaranteed correct if the caller has acquired this lock. // The return results might be incorrect otherwise. // This method is designed to be fast in non-debug mode by co-opting // synchronization using lock_ (no additional synchronization is used), but in // debug mode it slowly and carefully validates the requirement (and fires a // a DCHECK if it was called incorrectly). int32 Lock::GetCurrentThreadRecursionCount() { #ifndef NDEBUG // If this DCHECK fails, then the most probable cause is: // This method was called by class AutoUnlock during processing of a // Wait() call made into the ConditonVariable class. That call to // Wait() was made (incorrectly) without first Aquiring this Lock // instance. lock_.Lock(); int temp = recursion_count_shadow_; lock_.Unlock(); // Unit tests catch an exception, so we need to be careful to test // outside the critical section, since the Leave would be skipped!?! DCHECK(temp >= 1); // Allow unit test exception only at end of method. #endif // DEBUG // We hold lock, so this *is* correct value. return recursion_count_shadow_; } AutoUnlock::AutoUnlock(Lock& lock) : lock_(&lock), release_count_(0) { // We require our caller have the lock, so we can call for recursion count. // CRITICALLY: Fetch value before we release the lock. int32 count = lock_->GetCurrentThreadRecursionCount(); DCHECK(count > 0); // Make sure we owned the lock. while (count-- > 0) { release_count_++; lock_->Release(); } } AutoUnlock::~AutoUnlock() { DCHECK(release_count_ >= 0); while (release_count_-- > 0) lock_->Acquire(); }