blob: afa1a6f85999e36b97b6d0830f19e304938272e7 [file] [log] [blame]
// 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();
}