blob: f57c7d3741093f24b1261d86cd5adaa6e0baf1fa [file] [log] [blame]
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <atomic>
#include <cstddef>
#include <type_traits>
#include <utility>
namespace ipcz {
// Base class for any kind of ref-counted thing whose ownership will be shared
// by one or more Ref<T> objects.
class RefCounted {
enum { kAdoptExistingRef };
virtual ~RefCounted();
void AcquireRef();
void ReleaseRef();
std::atomic_int ref_count_{1};
// Base class for every Ref<T>, providing a common implementation for ref count
// management, assignment, etc.
class GenericRef {
constexpr GenericRef() = default;
// Does not increase the ref count, effectively assuming ownership of a
// previously acquired ref.
GenericRef(decltype(RefCounted::kAdoptExistingRef), RefCounted* ptr);
// Constructs a new reference to `ptr`, increasing its ref count by 1.
explicit GenericRef(RefCounted* ptr);
GenericRef(GenericRef&& other);
GenericRef& operator=(GenericRef&& other);
GenericRef(const GenericRef& other);
GenericRef& operator=(const GenericRef& other);
explicit operator bool() const { return ptr_ != nullptr; }
void reset();
void* ReleaseImpl();
RefCounted* ptr_ = nullptr;
// A smart pointer which can be used to share ownership of an instance of T,
// where T is any type derived from RefCounted above.
// This is used instead of std::shared_ptr so that ipcz can release and
// re-acquire ownership of individual references, as some object references may
// be conceptually owned by the embedding application via an IpczHandle across
// ipcz ABI boundary. std::shared_ptr design does not allow for such manual ref
// manipulation without additional indirection.
template <typename T>
class Ref : public GenericRef {
constexpr Ref() = default;
constexpr Ref(std::nullptr_t) {}
explicit Ref(T* ptr) : GenericRef(ptr) {}
Ref(decltype(RefCounted::kAdoptExistingRef), T* ptr)
: GenericRef(RefCounted::kAdoptExistingRef, ptr) {}
template <typename U>
using EnableIfConvertible =
typename std::enable_if<std::is_convertible<U*, T*>::value>::type;
template <typename U, typename = EnableIfConvertible<U>>
Ref(const Ref<U>& other) : Ref(other.get()) {}
template <typename U, typename = EnableIfConvertible<U>>
Ref(Ref<U>&& other) noexcept
: Ref(RefCounted::kAdoptExistingRef, other.release()) {}
T* get() const { return static_cast<T*>(ptr_); }
T* operator->() const { return get(); }
T& operator*() const { return *get(); }
bool operator==(const T* ptr) const { return ptr_ == ptr; }
bool operator!=(const T* ptr) const { return ptr_ != ptr; }
bool operator==(const Ref<T>& other) const { return ptr_ == other.ptr_; }
bool operator!=(const Ref<T>& other) const { return ptr_ != other.ptr_; }
T* release() { return static_cast<T*>(ReleaseImpl()); }
template <typename H>
friend H AbslHashValue(H h, const Ref<T>& ref) {
return H::combine(std::move(h), ref.get());
// Wraps `ptr` as a Ref<T>, increasing the refcount by 1.
template <typename T>
Ref<T> WrapRefCounted(T* ptr) {
return Ref<T>(ptr);
// Wraps `ptr` as a Ref<T>, assuming ownership of an existing reference. Does
// not change the object's refcount.
template <typename T>
Ref<T> AdoptRef(T* ptr) {
return Ref<T>(RefCounted::kAdoptExistingRef, ptr);
template <typename T, typename... Args>
Ref<T> MakeRefCounted(Args&&... args) {
return AdoptRef(new T(std::forward<Args>(args)...));
} // namespace ipcz