blob: a18ff31df206859f6de300e8e6d9489f4189474b [file] [log] [blame]
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Utility functions for spending a fixed amount of time trying to acquire
// a base::Lock (or any lock with the same API).
#ifndef SYZYGY_AGENT_ASAN_TIMED_TRY_IMPL_H_
#define SYZYGY_AGENT_ASAN_TIMED_TRY_IMPL_H_
#ifndef SYZYGY_AGENT_ASAN_TIMED_TRY_H_
#error Meant to be included from timed_try.h only.
#endif
#include "base/logging.h"
#include "base/time/time.h"
#include "syzygy/agent/asan/heap.h"
namespace agent {
namespace asan {
namespace detail {
// A simple adapter for base::Lock-like locks (Try and Release).
template<typename LockType>
struct BaseLockAdapter {
bool Try(LockType* lock) {
DCHECK_NE(static_cast<LockType*>(NULL), lock);
return lock->Try();
}
void Release(LockType* lock) {
DCHECK_NE(static_cast<LockType*>(NULL), lock);
lock->Release();
}
};
// A lock adapter for HeapInterface objects.
struct HeapInterfaceAdapter {
bool Try(HeapInterface* heap) {
DCHECK_NE(static_cast<HeapInterface*>(NULL), heap);
return heap->TryLock();
}
void Release(HeapInterface* heap) {
DCHECK_NE(static_cast<HeapInterface*>(NULL), heap);
heap->Unlock();
}
};
// A lock adapter selector. By default selects BaseLockAdapter, and expects
// the lock to implement 'Try' and 'Release'.
template<typename LockType>
struct SelectLockAdapter {
typedef BaseLockAdapter<LockType> type;
};
// A specialization that selects HeapInterfaceAdapter for HeapInterface objects.
template<>
struct SelectLockAdapter<HeapInterface> {
typedef HeapInterfaceAdapter type;
};
} // namespace detail
template<typename LockType>
bool TimedTry(base::TimeDelta delta, LockType* lock) {
DCHECK_NE(static_cast<LockType*>(NULL), lock);
// Try at least once, even if |delta| is zero or negative.
detail::SelectLockAdapter<LockType>::type adapter;
if (adapter.Try(lock))
return true;
// Try repeatedly, until timeout.
base::Time end = base::Time::Now() + delta;
while (base::Time::Now() < end) {
// Spin a bunch of times.
for (size_t i = 0; i < 100; ++i)
if (adapter.Try(lock))
return true;
// Cede the processor to another thread, hoping the lock will become
// available at some point.
::SwitchToThread();
}
return false;
}
template<typename LockType>
AutoTimedTry<LockType>::AutoTimedTry(
base::TimeDelta delta, LockType* lock)
: lock_(lock) {
DCHECK_NE(static_cast<LockType*>(NULL), lock);
is_acquired_ = TimedTry<LockType>(delta, lock);
}
template<typename LockType>
AutoTimedTry<LockType>::~AutoTimedTry() {
detail::SelectLockAdapter<LockType>::type adapter;
if (is_acquired_)
adapter.Release(lock_);
}
template<typename LockType>
bool AutoTimedTry<LockType>::is_acquired() const {
return is_acquired_;
}
} // namespace asan
} // namespace agent
#endif // SYZYGY_AGENT_ASAN_TIMED_TRY_IMPL_H_