blob: 49d14064df92b0945844ec34cc0833329c7d2b0f [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <zircon/assert.h>
#include <zircon/compiler.h>
#include <lockdep/common.h>
#include <lockdep/lock_class.h>
#include <lockdep/lock_policy.h>
#include <lockdep/lock_traits.h>
#include <type_traits>
#include <utility>
namespace lockdep {
namespace internal {
// Utility to deduce the LockType given any subclass of Lock<LockType>.
template <typename LockType>
LockType DeduceLockType(Lock<LockType>*);
// Determines the LockType of any subclass of Lock<LockType> or
// Lock<GlobalReference<LockType, Reference>>.
template <typename T>
using GetLockType = RemoveGlobalReference<
decltype(DeduceLockType(static_cast<T*>(nullptr)))>;
// Trait type that determines whether the given LockType is nestable.
template <typename T>
struct IsNestable {
using LockType = RemoveGlobalReference<T>;
static constexpr bool Value =
(LockTraits<LockType>::Flags & LockFlagsNestable) != 0;
};
// Trait type that determines whether the given policy type has a nested type
// tag named Shared.
template <typename LockPolicy, typename Enabled = void>
struct IsSharedLockPolicy {
static constexpr bool Value = false;
};
template <typename LockPolicy>
struct IsSharedLockPolicy<LockPolicy,
std::void_t<typename LockPolicy::Shared>> {
static constexpr bool Value = true;
};
// Enable if the given T is nestable and uses same type as LockType.
template <typename T, typename LockType>
using EnableIfNestable = typename std::enable_if<
std::is_same<GetLockType<T>, LockType>::value &&
IsNestable<GetLockType<T>>::Value>::type;
// Enable if the given T is not nestable and uses same type as LockType.
template <typename T, typename LockType>
using EnableIfNotNestable = typename std::enable_if<
std::is_same<GetLockType<T>, LockType>::value &&
!IsNestable<GetLockType<T>>::Value>::type;
template <typename LockType, typename Option>
using EnableIfShared = typename std::enable_if<
IsSharedLockPolicy<LockPolicy<LockType, Option>>::Value>::type;
template <typename LockType, typename Option>
using EnableIfNotShared = typename std::enable_if<
!IsSharedLockPolicy<LockPolicy<LockType, Option>>::Value>::type;
} // namespace internal
// Type tag to select the (private) ordered Guard constructor.
enum OrderedLockTag { OrderedLock };
// Type tag to select the adopting Guard constructor.
enum AdoptLockTag { AdoptLock };
// Base RAII type that automatically manages the duration of a lock acquisition.
// TODO(eieio): Specializations handle exclusive and shared lock acquisitions.
// These are largely identical except for the static lock analysis annotations.
// See if there is away to factor out the common logic for better
// maintainability.
template <typename LockType, typename Option = void, typename Enable = void>
class Guard;
// Specialization of Guard that acquires the given lock in exclusive mode.
template <typename LockType, typename Option>
class __TA_SCOPED_CAPABILITY Guard<LockType, Option,
internal::EnableIfNotShared<LockType, Option>> {
static_assert(
!std::is_same<LockPolicy<LockType, Option>, AmbiguousOption>::value,
"The Option argument of Guard<LockType, Option> must always "
"be specified when the policy for LockType is defined using "
"the macro LOCK_DEP_POLICY_OPTION(). See the macro docs for "
"details.");
public:
Guard(Guard&&) = delete;
Guard(const Guard&) = delete;
Guard& operator=(Guard&&) = delete;
Guard& operator=(const Guard&) = delete;
// Acquires the given lock. This constructor participates in overload
// resolution when the underlying lock type is not nestable.
template <typename Lockable, typename... Args,
typename = internal::EnableIfNotNestable<Lockable, LockType>>
Guard(Lockable* lock, Args&&... state_args)
__TA_ACQUIRE(lock) __TA_ACQUIRE(lock->capability())
: validator_{lock->id()}, lock_{&lock->lock()},
state_{std::forward<Args>(state_args)...} { ValidateAndAcquire(); }
// Acquires the given lock. This constructor participates in overload
// resolution when the underlying lock type is nestable.
template <typename Lockable, typename... Args,
typename = internal::EnableIfNestable<Lockable, LockType>>
Guard(Lockable* lock, uintptr_t order, Args&&... state_args)
__TA_ACQUIRE(lock) __TA_ACQUIRE(lock->capability())
: Guard{OrderedLock, lock, order, std::forward<Args>(state_args)...} {}
// Destructor that automatically releases the lock if not already released.
~Guard() __TA_RELEASE() {
Release();
}
// Releases the lock early before this guard instance goes out of scope.
template <typename... Args>
void Release(Args&&... args) __TA_RELEASE() {
if (lock_ != nullptr) {
LockPolicy<LockType, Option>::Release(lock_, &state_,
std::forward<Args>(args)...);
validator_.ValidateRelease();
lock_ = nullptr;
}
}
// Returns true if the guard has an actively acquired lock.
explicit operator bool() const { return lock_ != nullptr; }
// Returns true if this guard wraps |lock|.
bool wraps_lock(const LockType& lock) const { return &lock == lock_; }
// Releases this scoped capability without releasing the underlying lock or
// un-tracking the lock in the validator. Returns an rvalue reference to the
// lock state and validator state which may be adopted by another Guard.
// This is useful in the rare situation where a lock must be released by a
// function called in the current protected scope. This is primarily needed
// to keep the Clang static lock validator happy; static analysis complains
// when a scoped capability is passed by pointer/reference and released in
// another scope.
//
// Example:
// Guard<fbl::Mutex> guard{&lock};
//
// // Setup actions...
//
// DoTaskAndReleaseLock(guard.take());
//
Guard&& take() __TA_RELEASE() {
return std::move(*this);
}
// Adopts the lock state and validator state. This constructor uses a type
// tag argument to avoid automatic move constructor semantics.
//
// Example:
// Guard<fbl::Mutex> guard{AdoptLock, std::move(rvalue_arugment)};
//
Guard(AdoptLockTag, Guard&& other) __TA_ACQUIRE(other.lock_)
: validator_{std::move(other.validator_)}, lock_{other.lock_},
state_{std::move(other.state_)} { other.lock_ = nullptr; }
// Temporarily releases and un-tracks the guarded lock before executing the
// given callable Op and then re-acquires and tracks the lock. This permits
// the same Guard instance to protect a larger scope while performing an
// operation unlocked. This is especially useful in guarded loops:
//
// Guard<fbl::Mutex> guard{&lock_};
// for (auto* entry : objects_.next()) {
// if (Pred(entry)) {
// objects_.erase(entry);
// guard.CallUnlocked([entry]() {
// // Unlocked operation on entry ...
// });
// }
// }
//
template <typename Op, typename... ReleaseArgs>
void CallUnlocked(Op&& op, ReleaseArgs&&... release_args)
__TA_NO_THREAD_SAFETY_ANALYSIS {
ZX_DEBUG_ASSERT(lock_ != nullptr);
LockPolicy<LockType, Option>::Release(
lock_, &state_, std::forward<ReleaseArgs>(release_args)...);
validator_.ValidateRelease();
std::forward<Op>(op)();
ValidateAndAcquire();
}
private:
template <size_t, typename, typename>
friend class GuardMultiple;
// Validates and acquires the lock. If the lock is a try-lock that failed
// the release bookkeeping is performed and the guard is left in the empty
// state. This method factors out the common body of the main constructors;
// thread safety analysis is disabled to silence the unnecessary warning
// about the conditional path that would not be raised in the constructor
// body.
void ValidateAndAcquire() __TA_NO_THREAD_SAFETY_ANALYSIS {
validator_.ValidateAcquire();
if (!LockPolicy<LockType, Option>::Acquire(lock_, &state_)) {
lock_ = nullptr;
validator_.ValidateRelease();
}
}
// Ordered lock constructor used by the nestable lock constructor above and
// by GuardMultiple.
template <typename Lockable, typename... Args>
Guard(OrderedLockTag, Lockable* lock,
uintptr_t order, Args&&... state_args)
__TA_ACQUIRE(lock) __TA_ACQUIRE(lock->capability())
: validator_{lock->id(), order}, lock_{&lock->lock()},
state_{std::forward<Args>(state_args)...} { ValidateAndAcquire(); }
// Validator type used when lock validation is enabled. Provides the
// AcquiredLockEntry instance and bookkeeping calls required by
// ThreadLockState.
struct LockValidator {
LockValidator(LockClassId id, uintptr_t order = 0)
: lock_entry{id, order} {}
void ValidateAcquire() {
ThreadLockState::Get()->Acquire(&lock_entry);
}
void ValidateRelease() {
ThreadLockState::Get()->Release(&lock_entry);
}
AcquiredLockEntry lock_entry;
};
// Validator type used when lock validation is disabled.
struct DummyValidator {
DummyValidator(LockClassId, uintptr_t = 0) {}
void ValidateAcquire() {}
void ValidateRelease() {}
};
// Alias of the configured validator.
using Validator = IfLockValidationEnabled<LockValidator, DummyValidator>;
// The validator to use when acquiring and releasing the lock.
Validator validator_;
// Pointer to the acquired lock.
LockType* lock_;
// State to store in the guard as specified by the lock policy. For example,
// this may be used to save IRQ state for spinlocks.
typename LockPolicy<LockType, Option>::State state_;
};
// Specialization of Guard that acquires the given lock in shared mode.
template <typename LockType, typename Option>
class __TA_SCOPED_CAPABILITY Guard<LockType, Option,
internal::EnableIfShared<LockType, Option>> {
static_assert(
!std::is_same<LockPolicy<LockType, Option>, AmbiguousOption>::value,
"The Option argument of Guard<LockType, Option> must always "
"be specified when the policy for LockType is defined using "
"the macro LOCK_DEP_POLICY_OPTION(). See the macro docs for "
"details.");
public:
Guard(Guard&&) = delete;
Guard(const Guard&) = delete;
Guard& operator=(Guard&&) = delete;
Guard& operator=(const Guard&) = delete;
// Acquires the given lock. This constructor participates in overload
// resolution when the underlying lock type is not nestable.
template <typename Lockable, typename... Args,
typename = internal::EnableIfNotNestable<Lockable, LockType>>
Guard(Lockable* lock, Args&&... state_args)
__TA_ACQUIRE_SHARED(lock) __TA_ACQUIRE_SHARED(lock->capability())
: validator_{lock->id()}, lock_{&lock->lock()},
state_{std::forward<Args>(state_args)...} { ValidateAndAcquire(); }
// Acquires the given lock. This constructor participates in overload
// resolution when the underlying lock type is nestable.
template <typename Lockable, typename... Args,
typename = internal::EnableIfNestable<Lockable, LockType>>
Guard(Lockable* lock, uintptr_t order, Args&&... state_args)
__TA_ACQUIRE_SHARED(lock) __TA_ACQUIRE_SHARED(lock->capability())
: Guard{OrderedLock, lock, order, std::forward<Args>(state_args)...} {}
// Destructor that automatically releases the lock if not already released.
~Guard() __TA_RELEASE() {
Release();
}
// Releases the lock early before this guard instance goes out of scope.
template <typename... Args>
void Release(Args&&... args) __TA_RELEASE() {
if (lock_ != nullptr) {
LockPolicy<LockType, Option>::Release(lock_, &state_,
std::forward<Args>(args)...);
validator_.ValidateRelease();
lock_ = nullptr;
}
}
// Returns true if the guard has an actively acquired lock.
explicit operator bool() const { return lock_ != nullptr; }
// Releases this scoped capability without releasing the underlying lock or
// un-tracking the lock in the validator. Returns an rvalue reference to the
// lock state and validator state which may be adopted by another Guard.
// This is useful in the rare situation where a lock must be released by a
// function called in the current protected scope. This is primarily needed
// to keep the Clang static lock validator happy; static analysis complains
// when a scoped capability is passed by pointer/reference and released in
// another scope.
//
// Example:
// Guard<fbl::Mutex> guard{&lock};
//
// // Setup actions...
//
// DoTaskAndReleaseLock(guard.take());
//
Guard&& take() __TA_RELEASE() {
return std::move(*this);
}
// Adopts the lock state and validator state. This constructor uses a type
// tag argument to avoid automatic move constructor semantics.
//
// Example:
// Guard<fbl::Mutex> guard{AdoptLock, std::move(rvalue_arugment)};
//
Guard(AdoptLockTag, Guard&& other) __TA_ACQUIRE_SHARED(other.lock_)
: validator_{std::move(other.validator_)}, lock_{other.lock_},
state_{std::move(other.state_)} { other.lock_ = nullptr; }
// Temporarily releases and un-tracks the guarded lock before executing the
// given callable Op and then re-acquires and tracks the lock. This permits
// the same Guard instance to protect a larger scope while performing an
// operation unlocked. This is especially useful in guarded loops:
//
// Guard<fbl::Mutex> guard{&lock_};
// for (auto* entry : objects_.next()) {
// if (Pred(entry)) {
// objects_.erase(entry);
// guard.CallUnlocked([entry]() {
// // Unlocked operation on entry ...
// });
// }
// }
//
template <typename Op, typename... ReleaseArgs>
void CallUnlocked(Op&& op, ReleaseArgs&&... release_args)
__TA_NO_THREAD_SAFETY_ANALYSIS {
ZX_DEBUG_ASSERT(lock_ != nullptr);
LockPolicy<LockType, Option>::Release(
lock_, &state_, std::forward<ReleaseArgs>(release_args)...);
validator_.ValidateRelease();
std::forward<Op>(op)();
ValidateAndAcquire();
}
private:
template <size_t, typename, typename>
friend class GuardMultiple;
// Validates and acquires the lock. If the lock is a try-lock that failed
// the release bookkeeping is performed and the guard is left in the empty
// state. This method factors out the common body of the main constructors;
// thread safety analysis is disabled to silence the unnecessary warning
// about the conditional path that would not be raised in the constructor
// body.
void ValidateAndAcquire() __TA_NO_THREAD_SAFETY_ANALYSIS {
validator_.ValidateAcquire();
if (!LockPolicy<LockType, Option>::Acquire(lock_, &state_)) {
lock_ = nullptr;
validator_.ValidateRelease();
}
}
// Ordered lock constructor used by the nestable lock constructor above and
// by GuardMultiple.
template <typename Lockable, typename... Args>
Guard(OrderedLockTag, Lockable* lock,
uintptr_t order, Args&&... state_args)
__TA_ACQUIRE_SHARED(lock) __TA_ACQUIRE_SHARED(lock->capability())
: validator_{lock->id(), order}, lock_{&lock->lock()},
state_{std::forward<Args>(state_args)...} { ValidateAndAcquire(); }
// Validator type used when lock validation is enabled. Provides the
// AcquiredLockEntry instance and bookkeeping calls required by
// ThreadLockState.
struct LockValidator {
LockValidator(LockClassId id, uintptr_t order = 0)
: lock_entry{id, order} {}
void ValidateAcquire() {
ThreadLockState::Get()->Acquire(&lock_entry);
}
void ValidateRelease() {
ThreadLockState::Get()->Release(&lock_entry);
}
AcquiredLockEntry lock_entry;
};
// Validator type used when lock validation is disabled.
struct DummyValidator {
DummyValidator(LockClassId, uintptr_t = 0) {}
void ValidateAcquire() {}
void ValidateRelease() {}
};
// Alias of the configured validator.
using Validator = IfLockValidationEnabled<LockValidator, DummyValidator>;
// The validator to use when acquiring and releasing the lock.
Validator validator_;
// Pointer to the acquired lock.
LockType* lock_;
// State to store in the guard as specified by the lock policy. For example,
// this may be used to save IRQ state for spinlocks.
typename LockPolicy<LockType, Option>::State state_;
};
} // namespace lockdep