blob: 2bf10ccc72d9503f1a32032dd5871f0bf99db596 [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.
#ifndef LOCKDEP_LOCK_CLASS_H_
#define LOCKDEP_LOCK_CLASS_H_
#include <lib/fxt/interned_string.h>
#include <zircon/compiler.h>
#include <type_traits>
#include <utility>
#include <fbl/macros.h>
#include <fbl/type_info.h>
#include <lockdep/common.h>
#include <lockdep/global_reference.h>
#include <lockdep/lock_class_state.h>
#include <lockdep/lock_name_helper.h>
#include <lockdep/lock_traits.h>
#include <lockdep/thread_lock_state.h>
namespace lockdep {
// A fbl helper which can be used to test to see if an object's definition includes a method of the
// form:
//
// ```
// void SetLockClassId(LockClassId id)
// ```
//
// Used by the Lock's state wrappers to inform an underlying lock implementation
// of its class ID via a set method when supported.
//
DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_set_lock_class_id, SetLockClassId,
void (C::*)(LockClassId));
// Looks up a lock traits type using ADL to perform cross-namespace matching.
// This alias works in concert with the macro LOCK_DEP_TRAITS(type, flags) to
// find the defined lock traits for a lock, even when defined in a different
// namespace.
template <typename T>
using LookupLockTraits = decltype(LOCK_DEP_GetLockTraits(static_cast<T*>(nullptr)));
// Base lock traits type. If a type has not been tagged with the
// LOCK_DEP_TRAITS() then this base type provides the default flags for the lock
// type.
template <typename T, typename = void>
struct LockTraits {
static constexpr LockFlags Flags = LockFlagsNone;
};
// Returns the flags for a lock type when the type is tagged with
// LOCK_DEP_TRAITS(type, flags).
template <typename T>
struct LockTraits<T, std::void_t<LookupLockTraits<T>>> {
static constexpr LockFlags Flags = LookupLockTraits<T>::Flags;
};
// Forward declarations.
template <typename Class, typename LockType, size_t Index, LockFlags Flags>
class LockClass;
namespace internal {
// Generates the global storage for runtime validation state and/or lock
// metadata, depending on the compile-time configuration.
template <bool Enabled, typename Class, typename LockType, size_t Index, LockFlags Flags>
class LockClassStateStorage {
public:
static constexpr LockClassId id() { return lock_class_state_.id(); }
private:
// Returns the name of this lock class.
constexpr static const char* GetName() { return internal::LockNameHelper<Class, Index>::Name(); }
// Select whether to generate full validation state or only metadata.
using LockClassStateType =
IfLockValidationEnabled<ValidatorLockClassState, MetadataLockClassState>;
// The name of this lock class is deduced from its template parameters, and the interned structure
// storage is a member of the templated class, the LockClassState will simply hold a pointer back
// to this storage..
static inline fxt::InternedString interned_name_ FXT_INTERNED_STRING_SECTION{GetName()};
// This static member serves a dual role:
// 1. It generates storage for the lock class metadata and validation state.
// 2. The address of this member serves as the unique id for this lock class.
inline static LockClassStateType lock_class_state_{
interned_name_, static_cast<LockFlags>(LockTraits<LockType>::Flags | Flags)};
};
// Empty storage case that does not generate runtime validation or metadata
// storage when both are disabled at compile-time.
template <typename Class, typename LockType, size_t Index, LockFlags Flags>
class LockClassStateStorage<false, Class, LockType, Index, Flags> {
public:
static constexpr LockClassId id() { return kInvalidLockClassId; }
};
} // namespace internal
// Singleton type representing a lock class. Each unique template instantiation
// represents an independent, unique lock class. This type generates global lock
// metadata and runtime validation state structures when these respective
// features are enabled.
template <typename Class, typename LockType, size_t Index, LockFlags Flags>
class LockClass : public internal::LockClassStateStorage<kLockMetadataAvailable, Class, LockType,
Index, Flags> {};
// Utility to normalize the LockClass type by removing global references from
// the LockType.
template <typename Class, typename LockType, size_t Index, LockFlags Flags>
using NormalizedLockClass = LockClass<Class, RemoveGlobalReference<LockType>, Index, Flags>;
// Base lock wrapper type that provides the essential interface required by
// Guard<LockType, Option> to perform locking and validation. This type wraps
// an instance of LockType that is used to perform the actual synchronization.
// When lock validation is enabled this type also stores the LockClassId for
// the lock class this lock belongs to.
//
// The "lock class" that each lock belongs to is created by each unique
// instantiation of the types LockDep<Class, LockType, Index> or
// SingletonLockDep<Class, LockType, LockFlags> below. These types subclass
// Lock<LockType> to provide type erasure required when virtual accessors are
// used to specify capabilities to Clang static lock analysis.
//
// For example, the lock_ members of LockableType and AnotherLockableType below
// are different types due to how LockDep<> instruments the lock members.
// Both unique LockDep<> instantiations derive from the same Lock<LockType>,
// providing a common capability type (erasing the LockDep<> type) that may be
// used in static lock annotations with the expression "get_lock()".
//
// struct LockableInterface {
// virtual ~LockableInterface = 0;
// virtual Lock<fbl::Mutex>* get_lock() = 0;
// virtual void Clear() __TA_REQUIRES(get_lock()) = 0;
// };
//
// class LockableType : public LockableInterface {
// public:
// LockableType() = default;
// ~LockableType() override = default;
//
// Lock<fbl::Mutex>* get_lock() override { return &lock_; }
//
// void Clear() override { count_ = 0; }
//
// void Increment() {
// Guard<fbl::Mutex> guard{get_lock()};
// count_++;
// }
//
//
// private:
// LOCK_DEP_INSTRUMENT(LockableType, fbl::Mutex) lock_;
// int count_ __TA_GUARDED(get_lock()) {0};
// };
//
// class AnotherLockableType : public LockableInterface {
// public:
// AnotherLockableType() = default;
// ~AnotherLockableType() override = default;
//
// Lock<fbl::Mutex>* get_lock() override { return &lock_; }
//
// void Clear() override { test_.clear(); }
//
// void Append(const std::string& string) {
// Guard<fbl::Mutex> guard{get_lock()};
// text_.append(string)
// }
//
//
// private:
// LOCK_DEP_INSTRUMENT(AnotherLockableType, fbl::Mutex) lock_;
// std::string text_ __TA_GUARDED(get_lock()) {};
// };
//
template <typename LockType>
class __TA_CAPABILITY("mutex") Lock {
public:
Lock(Lock&&) = delete;
Lock(const Lock&) = delete;
Lock& operator=(Lock&&) = delete;
Lock& operator=(const Lock&) = delete;
~Lock() = default;
// Provides direct access to the underlying lock. Care should be taken when
// manipulating the underlying lock. Incorrect manipulation could confuse
// the validator, trigger lock assertions, and/or deadlock.
LockType& lock() __TA_RETURN_CAPABILITY(*this) { return state_.lock_; }
const LockType& lock() const __TA_RETURN_CAPABILITY(*this) { return state_.lock_; }
// Returns the capability of the underlying lock. This is expected by Guard
// as an additional static assertion target.
LockType& capability() __TA_RETURN_CAPABILITY(state_.lock_) { return state_.lock_; }
const LockType& capability() const __TA_RETURN_CAPABILITY(state_.lock_) { return state_.lock_; }
// Returns the LockClassId of the lock class this lock belongs to.
constexpr LockClassId id() const { return state_.id(); }
protected:
// Initializes the Lock instance with the given LockClassId and passes any
// additional arguments to the underlying lock constructor.
template <typename... Args>
explicit constexpr Lock(LockClassId id, Args&&... args)
: state_{id, std::forward<Args>(args)...} {}
private:
template <typename, typename, typename>
friend class Guard;
template <size_t, typename, typename>
friend class GuardMultiple;
// State type when lock validation or metadata is enabled.
struct AvailableState {
template <typename... Args>
explicit AvailableState(LockClassId id, Args&&... args)
: id_{id}, lock_(std::forward<Args>(args)...) {
if constexpr (has_set_lock_class_id_v<LockType>) {
lock_.SetLockClassId(id);
}
}
LockClassId id() const { return id_; }
LockClassId id_;
LockType lock_;
};
// State type when validation and metadata is disabled.
struct UnavailableState {
template <typename... Args>
explicit UnavailableState(LockClassId id, Args&&... args) : lock_(std::forward<Args>(args)...) {
if constexpr (has_set_lock_class_id_v<LockType>) {
lock_.SetLockClassId(id);
}
}
constexpr LockClassId id() const { return kInvalidLockClassId; }
LockType lock_;
};
// Selects which state type to use based on whether metadata or validation is
// enabled.
using State = IfLockMetadataAvailable<AvailableState, UnavailableState>;
// State instance holding the underlying lock and, when validation is enabled,
// the lock class id.
State state_;
};
// Specialization of Lock<LockType> that wraps a static/global raw lock. This
// type permits creating a tracked alias of a raw lock of type LockType that
// has static storage duration, with either external or internal linkage.
//
// This type supports transitioning from C-compatible APIs to full C++.
template <typename LockType, LockType& Reference>
class __TA_CAPABILITY("mutex") Lock<GlobalReference<LockType, Reference>> {
public:
Lock(Lock&&) = delete;
Lock(const Lock&) = delete;
Lock& operator=(Lock&&) = delete;
Lock& operator=(const Lock&) = delete;
~Lock() = default;
// Provides direct access to the underlying lock. Care should be taken when
// manipulating the underlying lock. Incorrect manipulation could confuse
// the validator, trigger lock assertions, and/or deadlock.
LockType& lock() __TA_RETURN_CAPABILITY(*this) { return Reference; }
const LockType& lock() const __TA_RETURN_CAPABILITY(*this) { return Reference; }
// Returns the LockClassId of the lock class this lock belongs to.
LockClassId id() const { return id_.value(); }
protected:
explicit constexpr Lock(LockClassId id) : id_{id} {
// Do not interact with the lock at this point in time (not even to try to
// set its lock class ID). This specialized version of |Lock| does not
// encapsulate the actual lock, it only holds a reference to it.
// Additionally, this wrapper is used in situations where the lock in
// question is almost certainly a global lock.
//
// So, we have a global ctor race. We do not know who is going to be
// constructed first, the lock we hold a reference to, or the wrapper (aka,
// |this|). Attempting to interact with the lock via reference at this
// point runs the risk of interacting with an object before it has been
// constructed, which is clearly no good.
}
private:
template <typename, typename, typename>
friend class Guard;
template <size_t, typename, typename>
friend class GuardMultiple;
struct Value {
LockClassId value_;
LockClassId value() const { return value_; }
};
struct Disabled {
constexpr Disabled(LockClassId) {}
LockClassId constexpr value() const { return kInvalidLockClassId; }
};
using IdValue = IfLockMetadataAvailable<Value, Disabled>;
// Stores the lock class id of this lock when validation is enabled.
IdValue id_;
};
// Lock wrapper class that implements lock dependency checks. The template
// argument |Class| should be a type that uniquely defines the class, such as
// the type of the containing scope. The template argument |LockType| is the
// type of the lock to wrap. The template argument |Index| may be used to
// differentiate lock classes between multiple locks within the same scope.
//
// For example:
//
// struct MyType {
// LockDep<MyType, Mutex, 0> lock_a;
// LockDep<MyType, Mutex, 1> lock_b;
// // ...
// };
//
template <typename Class, typename LockType_, size_t Index = 0, LockFlags Flags = LockFlagsNone>
class LockDep : public Lock<LockType_> {
public:
// Expose the wrapped lock type to user via an alias.
using LockType = LockType_;
// Alias that may be used by subclasses to simplify constructor
// expressions.
using Base = LockDep;
// Alias of the lock class that this wrapper represents.
using LockClass = NormalizedLockClass<Class, LockType, Index, Flags>;
// Constructor that initializes the underlying lock with the additional
// arguments.
template <typename... Args>
constexpr LockDep(Args&&... args)
: Lock<LockType>(LockClass::id(), std::forward<Args>(args)...) {}
};
// Singleton version of the lock wrapper above. This type is appropriate for
// global locks. This type is used by the macros LOCK_DEP_SINGLETON_LOCK and
// LOCK_DEP_SINGLETON_LOCK_WRAPPER to define instrumented global locks.
template <typename Class, typename LockType_, LockFlags Flags = LockFlagsNone>
class SingletonLockDep : public Lock<LockType_> {
public:
// Expose the wrapped lock type to user via an alias.
using LockType = LockType_;
// Alias of the lock class that this wrapper represents.
using LockClass = NormalizedLockClass<Class, LockType, 0, Flags>;
// Returns a pointer to the singleton object.
static Class* Get() { return &global_lock_; }
protected:
// Initializes the base Lock<LockType> with lock class id for this lock. The
// additional arguments are pass to the underlying lock.
template <typename... Args>
constexpr SingletonLockDep(Args&&... args)
: Lock<LockType>(LockClass::id(), std::forward<Args>(args)...) {}
private:
inline static Class global_lock_;
};
} // namespace lockdep
#endif // LOCKDEP_LOCK_CLASS_H_