166 lines
5.5 KiB
C++
166 lines
5.5 KiB
C++
// Copyright 2021 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef BASE_MEMORY_SAFE_REF_H_
|
|
#define BASE_MEMORY_SAFE_REF_H_
|
|
|
|
#include "base/check.h"
|
|
#include "base/memory/weak_ptr.h"
|
|
|
|
#include <utility>
|
|
|
|
namespace base {
|
|
|
|
// SafeRef smart pointers are used to represent a non-owning pointer to an
|
|
// object, where the pointer is always intended to be valid. These are useful in
|
|
// the same cases that a raw pointer `T*` (or a `T&`) would traditionally be
|
|
// used, as the owner of the SafeRef knows the lifetime of the pointed-to object
|
|
// from other means and will not use the pointer after the pointed-to object is
|
|
// destroyed. However, unlike a `T*` or `T&`, a logic bug will manifest as a
|
|
// benign crash instead of as a Use-after-Free.
|
|
//
|
|
// SafeRef pointers can not be null (as expressed by the "Ref" suffix instead of
|
|
// "Ptr"). A SafeRef can be wrapped in an absl::optional if it should not always
|
|
// point to something valid. (A SafePtr sibling type can be introduced if this
|
|
// is problematic, or if consuming moves are needed!)
|
|
//
|
|
// If code wants to track the lifetime of the object directly through its
|
|
// pointer, and dynamically handle the case of the pointer outliving the object
|
|
// it points to, then base::WeakPtr should be used instead.
|
|
//
|
|
// The SafeRef pointer is constructed from a base::WeakPtrFactory's GetSafeRef()
|
|
// method. Since it is tied to the base::WeakPtrFactory, it will consider its
|
|
// pointee invalid when the base::WeakPtrFactory is invalidated, in the same way
|
|
// as base::WeakPtr does, including after a call to InvalidateWeakPtrs().
|
|
//
|
|
// THREAD SAFETY: SafeRef pointers (like base::WeakPtr) may only be used on the
|
|
// sequence (or thread) where the associated base::WeakPtrFactory will be
|
|
// invalidated and/or destroyed. They are safe to passively hold or to destroy
|
|
// on any thread though.
|
|
//
|
|
// This class is expected to one day be replaced by a more flexible and safe
|
|
// smart pointer abstraction which is not tied to base::WeakPtrFactory, such as
|
|
// raw_ptr<T> from the MiraclePtr project (though perhaps a non-nullable raw_ref
|
|
// equivalent).
|
|
template <typename T>
|
|
class SafeRef {
|
|
public:
|
|
// No default constructor, since there's no null state. Use an optional
|
|
// SafeRef if the pointer may not be present.
|
|
|
|
// Copy construction and assignment.
|
|
SafeRef(const SafeRef& other) : ref_(other.ref_), ptr_(other.ptr_) {
|
|
// Avoid use-after-move.
|
|
CHECK(ref_.IsValid());
|
|
}
|
|
SafeRef& operator=(const SafeRef& other) {
|
|
ref_ = other.ref_;
|
|
ptr_ = other.ptr_;
|
|
// Avoid use-after-move.
|
|
CHECK(ref_.IsValid());
|
|
return *this;
|
|
}
|
|
|
|
// Move construction and assignment.
|
|
SafeRef(SafeRef&& other)
|
|
: ref_(std::move(other.ref_)), ptr_(std::move(other.ptr_)) {
|
|
// Avoid use-after-move.
|
|
CHECK(ref_.IsValid());
|
|
}
|
|
SafeRef& operator=(SafeRef&& other) {
|
|
ref_ = std::move(other.ref_);
|
|
ptr_ = std::move(other.ptr_);
|
|
// Avoid use-after-move.
|
|
CHECK(ref_.IsValid());
|
|
return *this;
|
|
}
|
|
|
|
// Copy conversion from SafeRef<U>.
|
|
template <typename U,
|
|
typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
SafeRef(const SafeRef<U>& other)
|
|
: ref_(other.ref_),
|
|
ptr_(other.ptr_) // raw_ptr<U> converts to raw_ptr<T>.
|
|
{
|
|
// Avoid use-after-move.
|
|
CHECK(ref_.IsValid());
|
|
}
|
|
template <typename U>
|
|
SafeRef& operator=(const SafeRef<U>& other) {
|
|
ref_ = other.ref_;
|
|
ptr_ = other.ptr_; // raw_ptr<U> converts to raw_ptr<T>.
|
|
// Avoid use-after-move.
|
|
CHECK(ref_.IsValid());
|
|
return *this;
|
|
}
|
|
|
|
// Move conversion from SafeRef<U>.
|
|
template <typename U>
|
|
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
SafeRef(SafeRef<U>&& other)
|
|
: ref_(std::move(other.ref_)),
|
|
ptr_(std::move(other.ptr_)) // raw_ptr<U> converts to raw_ptr<T>.
|
|
{
|
|
// Avoid use-after-move.
|
|
CHECK(ref_.IsValid());
|
|
}
|
|
template <typename U>
|
|
SafeRef& operator=(SafeRef<U>&& other) {
|
|
ref_ = std::move(other.ref_);
|
|
ptr_ = std::move(other.ptr_); // raw_ptr<U> converts to raw_ptr<T>.
|
|
// Avoid use-after-move.
|
|
CHECK(ref_.IsValid());
|
|
return *this;
|
|
}
|
|
|
|
// Provide access to the underlying T as a reference. Will CHECK() if the T
|
|
// pointee is no longer alive.
|
|
T& operator*() const {
|
|
CHECK(ref_.IsValid());
|
|
return *ptr_;
|
|
}
|
|
|
|
// Used to call methods on the underlying T. Will CHECK() if the T pointee is
|
|
// no longer alive.
|
|
T* operator->() const {
|
|
CHECK(ref_.IsValid());
|
|
return &*ptr_;
|
|
}
|
|
|
|
private:
|
|
template <typename U>
|
|
friend class SafeRef;
|
|
template <typename U>
|
|
friend SafeRef<U> internal::MakeSafeRefFromWeakPtrInternals(
|
|
internal::WeakReference&& ref,
|
|
U* ptr);
|
|
|
|
// Construction from a from a WeakPtr's internals. Will CHECK() if the WeakPtr
|
|
// is already invalid.
|
|
explicit SafeRef(internal::WeakReference&& ref, T* ptr)
|
|
: ref_(std::move(ref)), ptr_(ptr) {
|
|
CHECK(ref_.IsValid());
|
|
}
|
|
|
|
internal::WeakReference ref_;
|
|
|
|
// This pointer is only valid when ref_.is_valid() is true. Otherwise, its
|
|
// value is undefined (as opposed to nullptr). Unlike WeakPtr, this raw_ptr is
|
|
// not allowed to dangle.
|
|
raw_ptr<T> ptr_;
|
|
};
|
|
|
|
namespace internal {
|
|
template <typename T>
|
|
SafeRef<T> MakeSafeRefFromWeakPtrInternals(internal::WeakReference&& ref,
|
|
T* ptr) {
|
|
return SafeRef<T>(std::move(ref), ptr);
|
|
}
|
|
} // namespace internal
|
|
|
|
} // namespace base
|
|
|
|
#endif // BASE_MEMORY_SAFE_REF_H_
|