// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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(); }