blob: 677297327ca20b393a8f39178b2081deadd360f1 [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/compiler.h>
#include <type_traits>
#include <utility>
#include <fbl/type_info.h>
#include <lockdep/common.h>
#include <lockdep/global_reference.h>
#include <lockdep/lock_class_state.h>
#include <lockdep/lock_traits.h>
#include <lockdep/thread_lock_state.h>
namespace lockdep {
// 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;
};
// Singleton type that represents a lock class. Each template instantiation
// represents an independent, unique lock class. This type maintains a global
// dependency set that tracks which other lock classes have been observed
// being held prior to acquisitions of this lock class. This type is only used
// when lock validation is enabled, otherwise DummyLockClass takes its place.
template <typename Class, typename LockType, size_t Index, LockFlags Flags>
class LockClass {
public:
// Returns the unique lock class id for this lock class.
static constexpr LockClassId Id() { return lock_class_state_.id(); }
// Returns the LockClassState instance for this lock class.
static LockClassState* GetLockClassState() { return &lock_class_state_; }
private:
LockClass() = default;
~LockClass() = default;
LockClass(const LockClass&) = delete;
void operator=(const LockClass&) = delete;
// Returns the name of this lock class.
constexpr static const char* GetName() { return fbl::TypeInfo<LockClass>::Name(); }
// Lock free dependency set that tracks which lock classes have been observed
// being held prior to acquisitions of this lock class.
static LockDependencySet dependency_set_;
// This static member serves a dual role:
// 1. Stores the id, name, and pointer to dependency set for this lock class.
// 2. The address of this member serves as the unique id for this lock class.
static LockClassState lock_class_state_;
};
// Initializes the value of the lock class state with the id, typeid name, and
// dependency set of the lock class. This results in a global initializer that
// performs trivial assignment.
template <typename Class, typename LockType, size_t Index, LockFlags Flags>
LockClassState LockClass<Class, LockType, Index, Flags>::lock_class_state_ = {
LockClass<Class, LockType, Index, Flags>::GetName(),
&LockClass<Class, LockType, Index, Flags>::dependency_set_,
static_cast<LockFlags>(LockTraits<LockType>::Flags | Flags)};
// The storage for this static member is allocated as zeroed memory.
template <typename Class, typename LockType, size_t Index, LockFlags Flags>
LockDependencySet LockClass<Class, LockType, Index, Flags>::dependency_set_;
// Dummy type used in place of LockClass when validation is disabled. This type
// does not create static dependency tracking structures that LockClass does.
struct DummyLockClass {
static constexpr LockClassId Id() { return kInvalidLockClassId; }
};
// Alias that selects LockClass<Class, LockType, Index, Flags> when validation
// is enabled or DummyLockClass when validation is disabled.
template <typename Class, typename LockType, size_t Index, LockFlags Flags>
using ConditionalLockClass =
IfLockValidationEnabled<LockClass<Class, LockType, Index, Flags>, DummyLockClass>;
// 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() { return lock_; }
const LockType& lock() const { return lock_; }
// Returns the capability of the underlying lock. This is expected by Guard
// as an additional static assertion target.
LockType& capability() __TA_RETURN_CAPABILITY(lock_) { return lock_; }
const LockType& capability() const __TA_RETURN_CAPABILITY(lock_) { return lock_; }
// Returns the LockClassId of the lock class this lock belongs to.
LockClassId id() const { return id_.value(); }
protected:
// Initializes the Lock instance with the given LockClassId and passes any
// additional arguments to the underlying lock constructor.
template <typename... Args>
constexpr Lock(LockClassId id, Args&&... args) : id_{id}, lock_(std::forward<Args>(args)...) {}
private:
template <typename, typename, typename>
friend class Guard;
template <size_t, typename, typename>
friend class GuardMultiple;
// Value type that stores the LockClassId for this lock when validation is
// enabled.
struct Value {
LockClassId value_;
LockClassId value() const { return value_; }
};
// Dummy type that stores nothing when validation is disabled.
struct Dummy {
constexpr Dummy(LockClassId) {}
LockClassId constexpr value() const { return kInvalidLockClassId; }
};
// Selects between Value or Dummy based on whether validation is enabled.
using IdValue = IfLockValidationEnabled<Value, Dummy>;
// Stores the lock class id of this lock when validation is enabled.
IdValue id_;
// The underlying lock managed by this dependency tracking wrapper.
LockType lock_;
};
// 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() { return Reference; }
const LockType& lock() const { return Reference; }
// Returns the LockClassId of the lock class this lock belongs to.
LockClassId id() const { return id_.value(); }
protected:
constexpr Lock(LockClassId id) : id_{id} {}
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 Dummy {
constexpr Dummy(LockClassId) {}
LockClassId constexpr value() const { return kInvalidLockClassId; }
};
using IdValue = IfLockValidationEnabled<Value, Dummy>;
// 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:
// 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 = ConditionalLockClass<Class, RemoveGlobalReference<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:
// Alias of the lock class that this wrapper represents.
using LockClass = ConditionalLockClass<Class, RemoveGlobalReference<LockType>, 0, Flags>;
// Returns a pointer to the singleton object.
static Class* Get() {
// The singleton instance of the global lock.
// Note: This must be a local static variable to avoid global initialization
// sequencing issues in environments that may acquire locks before ctors are
// executed (i.e. the kernel).
static Class global_lock;
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)...} {}
};
} // namespace lockdep