blob: 28c680bf139e9f0db7f5ab16ac8a8d3fcd4cb629 [file] [log] [blame]
// Copyright 2016 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 FBL_REF_PTR_H_
#define FBL_REF_PTR_H_
#include <zircon/assert.h>
#include <zircon/compiler.h>
#include <type_traits>
#include <utility>
#include <fbl/alloc_checker.h>
#include <fbl/recycler.h>
namespace fbl {
template <typename T>
class RefPtr;
template <typename T>
RefPtr<T> AdoptRef(T* ptr);
template <typename T>
RefPtr<T> ImportFromRawPtr(T*);
namespace internal {
template <typename T>
RefPtr<T> MakeRefPtrNoAdopt(T* ptr);
template <typename T>
T* LeakToRawPtr(RefPtr<T>* ptr) __WARN_UNUSED_RESULT;
} // namespace internal
// RefPtr<T> holds a reference to an intrusively-refcounted object of type
// T that deletes the object when the refcount drops to 0.
//
// T should be a subclass of fbl::RefCounted<>, or something that adheres to
// the same contract for AddRef() and Release().
//
// Except for initial construction (see below), this generally adheres to a
// subset of the interface for std::shared_ptr<>. Unlike std::shared_ptr<> this
// type does not support vending weak pointers, introspecting the reference
// count, or any operations that would result in allocating memory (unless
// T::AddRef or T::Release allocate memory).
// TODO(https://fxbug.dev/42144534): Align RefPtr more closely with standard library smart
// pointers.
//
// Construction: To create a RefPtr around a freshly created object, use the
// AdoptRef free function at the bottom of this header. To construct a RefPtr
// to hold a reference to an object that already exists use the copy or move
// constructor or assignment operator.
template <typename T>
class RefPtr final {
public:
using element_type = T;
// Constructors
constexpr RefPtr() : ptr_(nullptr) {}
constexpr RefPtr(decltype(nullptr)) : RefPtr() {}
// Constructs a RefPtr from a pointer that has already been adopted. See
// AdoptRef() below for constructing the very first RefPtr to an object.
explicit RefPtr(T* p) : ptr_(p) {
if (ptr_)
ptr_->AddRef();
}
// Copy construction.
RefPtr(const RefPtr& r) : RefPtr(r.ptr_) {}
// Implicit upcast via copy construction.
//
// @see the notes in unique_ptr.h
template <typename U, typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>
RefPtr(const RefPtr<U>& r) : RefPtr(r.ptr_) {
static_assert((std::is_class_v<T> == std::is_class_v<U>)&&(!std::is_class_v<T> ||
std::has_virtual_destructor_v<T> ||
std::is_same_v<T, const U>),
"Cannot convert RefPtr<U> to RefPtr<T> unless neither T "
"nor U are class/struct types, or T has a virtual destructor,"
"or T == const U.");
}
// Assignment
RefPtr& operator=(const RefPtr& r) {
// Ref first so self-assignments work.
if (r.ptr_) {
r.ptr_->AddRef();
}
T* old = ptr_;
ptr_ = r.ptr_;
if (old && old->Release()) {
recycle(old);
}
return *this;
}
// Move construction
RefPtr(RefPtr&& r) : ptr_(r.ptr_) { r.ptr_ = nullptr; }
// Implicit upcast via move construction.
//
// @see the notes in RefPtr.h
template <typename U, typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>
RefPtr(RefPtr<U>&& r) : ptr_(r.ptr_) {
static_assert((std::is_class_v<T> == std::is_class_v<U>)&&(!std::is_class_v<T> ||
std::has_virtual_destructor_v<T> ||
std::is_same_v<T, const U>),
"Cannot convert RefPtr<U> to RefPtr<T> unless neither T "
"nor U are class/struct types, or T has a virtual destructor,"
"or T == const U");
r.ptr_ = nullptr;
}
// Move assignment
RefPtr& operator=(RefPtr&& r) {
RefPtr(std::move(r)).swap(*this);
return *this;
}
// Construct via explicit downcast.
// ptr must be the same object as base.ptr_.
template <typename BaseType>
RefPtr(T* ptr, RefPtr<BaseType>&& base) : ptr_(ptr) {
ZX_ASSERT(static_cast<BaseType*>(ptr_) == base.ptr_);
base.ptr_ = nullptr;
}
// Downcast via static method invocation. Depending on use case, the syntax
// should look something like...
//
// fbl::RefPtr<MyBase> foo = MakeBase();
// auto bar_copy = fbl::RefPtr<MyDerived>::Downcast(foo);
// auto bar_move = fbl::RefPtr<MyDerived>::Downcast(std::move(foo));
//
template <typename BaseRefPtr>
static RefPtr Downcast(BaseRefPtr base) {
// Make certain that BaseRefPtr is some form of RefPtr<T>
static_assert(std::is_same_v<BaseRefPtr, RefPtr<typename BaseRefPtr::element_type>>,
"BaseRefPtr must be a RefPtr<T>!");
if (base != nullptr)
return ImportFromRawPtr<T>(static_cast<T*>(internal::LeakToRawPtr(&base)));
return nullptr;
}
~RefPtr() {
T* ptr = ptr_;
// Clear ptr_ to help detect re-entrancy in ~T.
ptr_ = nullptr;
if (ptr && ptr->Release()) {
recycle(ptr);
}
}
void reset(T* ptr = nullptr) { RefPtr(ptr).swap(*this); }
void swap(RefPtr& r) {
T* p = ptr_;
ptr_ = r.ptr_;
r.ptr_ = p;
}
T* get() const { return ptr_; }
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
explicit operator bool() const { return !!ptr_; }
// Comparison against nullptr operators (of the form, myptr == nullptr).
bool operator==(decltype(nullptr)) const { return (ptr_ == nullptr); }
bool operator!=(decltype(nullptr)) const { return (ptr_ != nullptr); }
bool operator==(const RefPtr<T>& other) const { return ptr_ == other.ptr_; }
bool operator!=(const RefPtr<T>& other) const { return ptr_ != other.ptr_; }
private:
template <typename U>
friend class RefPtr;
friend RefPtr<T> AdoptRef<T>(T*);
friend RefPtr<T> ImportFromRawPtr<>(T*);
friend RefPtr<T> internal::MakeRefPtrNoAdopt<>(T* ptr);
friend T* internal::LeakToRawPtr<>(RefPtr<T>*);
enum AdoptTag { ADOPT };
enum NoAdoptTag { NO_ADOPT };
RefPtr(T* ptr, AdoptTag) : ptr_(ptr) {
if (ptr_) {
ptr_->Adopt();
}
}
RefPtr(T* ptr, NoAdoptTag) : ptr_(ptr) {}
static void recycle(T* ptr) {
if constexpr (::fbl::internal::has_fbl_recycle_v<T>) {
::fbl::internal::recycler<T>::recycle(ptr);
} else {
delete ptr;
}
}
T* ptr_;
};
// Comparison against nullptr operator (of the form, nullptr == myptr)
template <typename T>
static inline bool operator==(decltype(nullptr), const RefPtr<T>& ptr) {
return (ptr.get() == nullptr);
}
template <typename T>
static inline bool operator!=(decltype(nullptr), const RefPtr<T>& ptr) {
return (ptr.get() != nullptr);
}
// Constructs a RefPtr from a fresh object that has not been referenced before.
// Use like:
//
// RefPtr<Happy> h = AdoptRef(new Happy);
// if (!h)
// // Deal with allocation failure here
// h->DoStuff();
template <typename T>
inline RefPtr<T> AdoptRef(T* ptr) {
return RefPtr<T>(ptr, RefPtr<T>::ADOPT);
}
// Export a pointer from a smart pointer to a raw pointer without modifying its
// reference count. The caller is responsible for maintaining the reference
// count, likely by calling ImportFromRawPtr() later on.
//
// Use this to store a pointer in code that can't use the C++
// type directly, such as in pure C code.
template <typename T>
inline T* ExportToRawPtr(RefPtr<T>* ptr) {
return internal::LeakToRawPtr(ptr);
}
// Imports from a raw pointer to a RefPtr. Does not modify the reference count
// of the object. This should be used on values that have previously been
// exported with ExportToRawPtr().
template <typename T>
inline RefPtr<T> ImportFromRawPtr(T* ptr) {
return internal::MakeRefPtrNoAdopt(ptr);
}
namespace internal {
// Constructs a RefPtr from a T* without attempt to either AddRef or Adopt the
// pointer. Used by the internals of some intrusive container classes to store
// sentinels (special invalid pointers) in RefPtr<>s.
template <typename T>
inline RefPtr<T> MakeRefPtrNoAdopt(T* ptr) {
return RefPtr<T>(ptr, RefPtr<T>::NO_ADOPT);
}
// Leaks the internal value to a raw pointer and resets the RefPtr to null.
// Does not change the reference count of the object.
template <typename T>
inline T* LeakToRawPtr(RefPtr<T>* ptr) {
T* ret = ptr->ptr_;
ptr->ptr_ = nullptr;
return ret;
}
// This is a wrapper class that can be friended for a particular |T|, if you
// want to make |T|'s constructor private, but still use |MakeRefCounted()|
// (below). (You can't friend partial specializations.)
template <typename T>
class MakeRefCountedHelper final {
public:
template <typename... Args>
static RefPtr<T> MakeRefCounted(Args&&... args) {
return AdoptRef<T>(new T(std::forward<Args>(args)...));
}
template <typename... Args>
static RefPtr<T> MakeRefCountedChecked(AllocChecker* ac, Args&&... args) {
return AdoptRef<T>(new (ac) T(std::forward<Args>(args)...));
}
};
} // namespace internal
template <typename T, typename... Args>
RefPtr<T> MakeRefCounted(Args&&... args) {
return internal::MakeRefCountedHelper<T>::MakeRefCounted(std::forward<Args>(args)...);
}
template <typename T, typename... Args>
RefPtr<T> MakeRefCountedChecked(AllocChecker* ac, Args&&... args) {
return internal::MakeRefCountedHelper<T>::MakeRefCountedChecked(ac, std::forward<Args>(args)...);
}
} // namespace fbl
#endif // FBL_REF_PTR_H_