Move most of Ref's functionality in RefBase for reuse.

This will allow using the same logic for other kinds of smartpointers,
like NSRef<>

Bug: dawn:89
Change-Id: Idbe08208fdb38b236f52635bc913162e60baf0f0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/32160
Reviewed-by: Stephen White <senorblanco@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Corentin Wallez 2020-11-12 11:47:59 +00:00 committed by Commit Bot service account
parent b70a5b02e9
commit 2089adc62a
6 changed files with 209 additions and 148 deletions

View File

@ -169,6 +169,7 @@ if (is_win || is_linux || is_chromeos || is_mac || is_fuchsia || is_android) {
"Math.h", "Math.h",
"PlacementAllocated.h", "PlacementAllocated.h",
"Platform.h", "Platform.h",
"RefBase.h",
"RefCounted.cpp", "RefCounted.cpp",
"RefCounted.h", "RefCounted.h",
"Result.cpp", "Result.cpp",

View File

@ -31,6 +31,7 @@ target_sources(dawn_common PRIVATE
"Math.h" "Math.h"
"PlacementAllocated.h" "PlacementAllocated.h"
"Platform.h" "Platform.h"
"RefBase.h"
"RefCounted.cpp" "RefCounted.cpp"
"RefCounted.h" "RefCounted.h"
"Result.cpp" "Result.cpp"

189
src/common/RefBase.h Normal file
View File

@ -0,0 +1,189 @@
// Copyright 2020 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef COMMON_REFBASE_H_
#define COMMON_REFBASE_H_
#include "common/Compiler.h"
#include <type_traits>
// A common class for various smart-pointers acting on referenceable/releasable pointer-like
// objects. Logic for each specialization can be customized using a Traits type that looks
// like the following:
//
// struct {
// static constexpr T kNullValue = ...;
// static void Reference(T value) { ... }
// static void Release(T value) { ... }
// };
//
// RefBase supports
template <typename T, typename Traits>
class RefBase {
private:
static constexpr T kNullValue = Traits::kNullValue;
public:
// Default constructor and destructor.
RefBase() : mValue(kNullValue) {
}
~RefBase() {
Release();
mValue = kNullValue;
}
// Constructors from nullptr.
constexpr RefBase(std::nullptr_t) : RefBase() {
}
RefBase<T, Traits>& operator=(std::nullptr_t) {
Release();
mValue = kNullValue;
return *this;
}
// Constructors from a value T.
RefBase(T value) : mValue(value) {
Reference();
}
RefBase<T, Traits>& operator=(const T& value) {
mValue = value;
Reference();
return *this;
}
// Constructors from a RefBase<T>
RefBase(const RefBase<T, Traits>& other) : mValue(other.mValue) {
Reference();
}
RefBase<T, Traits>& operator=(const RefBase<T, Traits>& other) {
if (&other != this) {
other.Reference();
Release();
mValue = other.mValue;
}
return *this;
}
RefBase(RefBase<T, Traits>&& other) {
mValue = other.mValue;
other.mValue = kNullValue;
}
RefBase<T, Traits>& operator=(RefBase<T, Traits>&& other) {
if (&other != this) {
Release();
mValue = other.mValue;
other.mValue = kNullValue;
}
return *this;
}
// Constructors from a RefBase<U>. Note that in the *-assignment operators this cannot be the
// same as `other` because overload resolution rules would have chosen the *-assignement
// operators defined with `other` == RefBase<T, Traits>.
template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type>
RefBase(const RefBase<U, UTraits>& other) : mValue(other.mValue) {
Reference();
}
template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type>
RefBase<T, Traits>& operator=(const RefBase<U, UTraits>& other) {
other.Reference();
Release();
mValue = other.mValue;
return *this;
}
template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type>
RefBase(RefBase<U, UTraits>&& other) {
mValue = other.Detach();
}
template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type>
RefBase<T, Traits>& operator=(RefBase<U, UTraits>&& other) {
Release();
mValue = other.Detach();
return *this;
}
// Comparison operators.
bool operator==(const T& other) const {
return mValue == other;
}
bool operator!=(const T& other) const {
return mValue != other;
}
operator bool() {
return mValue != kNullValue;
}
// Operator * and -> to act like a pointer.
const typename Traits::PointedType& operator*() const {
return *mValue;
}
typename Traits::PointedType& operator*() {
return *mValue;
}
const T operator->() const {
return mValue;
}
T operator->() {
return mValue;
}
// Smart pointer methods.
const T& Get() const {
return mValue;
}
T& Get() {
return mValue;
}
T Detach() DAWN_NO_DISCARD {
T value = mValue;
mValue = kNullValue;
return value;
}
private:
// Friend is needed so that instances of RefBase<U> can call Reference and Release on
// RefBase<T>.
template <typename U, typename UTraits>
friend class RefBase;
void Reference() const {
if (mValue != kNullValue) {
Traits::Reference(mValue);
}
}
void Release() const {
if (mValue != kNullValue) {
Traits::Release(mValue);
}
}
T mValue;
};
#endif // COMMON_REFBASE_H_

View File

@ -15,6 +15,8 @@
#ifndef COMMON_REFCOUNTED_H_ #ifndef COMMON_REFCOUNTED_H_
#define COMMON_REFCOUNTED_H_ #define COMMON_REFCOUNTED_H_
#include "common/RefBase.h"
#include <atomic> #include <atomic>
#include <cstdint> #include <cstdint>
@ -39,146 +41,21 @@ class RefCounted {
}; };
template <typename T> template <typename T>
class Ref { struct RefCountedTraits {
using PointedType = T;
static constexpr T* kNullValue = nullptr;
static void Reference(T* value) {
value->Reference();
}
static void Release(T* value) {
value->Release();
}
};
template <typename T>
class Ref : public RefBase<T*, RefCountedTraits<T>> {
public: public:
Ref() = default; using RefBase<T*, RefCountedTraits<T>>::RefBase;
constexpr Ref(std::nullptr_t) {
}
Ref& operator=(std::nullptr_t) {
Release();
mPointee = nullptr;
return *this;
}
template <typename U>
Ref(U* p) : mPointee(p) {
static_assert(std::is_convertible<U*, T*>::value, "");
Reference();
}
Ref(const Ref<T>& other) : mPointee(other.mPointee) {
Reference();
}
template <typename U>
Ref(const Ref<U>& other) : mPointee(other.mPointee) {
static_assert(std::is_convertible<U*, T*>::value, "");
Reference();
}
Ref<T>& operator=(const Ref<T>& other) {
if (&other == this)
return *this;
other.Reference();
Release();
mPointee = other.mPointee;
return *this;
}
template <typename U>
Ref<T>& operator=(const Ref<U>& other) {
static_assert(std::is_convertible<U*, T*>::value, "");
other.Reference();
Release();
mPointee = other.mPointee;
return *this;
}
template <typename U>
Ref(Ref<U>&& other) {
static_assert(std::is_convertible<U*, T*>::value, "");
mPointee = other.mPointee;
other.mPointee = nullptr;
}
Ref<T>& operator=(Ref<T>&& other) {
if (&other == this)
return *this;
Release();
mPointee = other.mPointee;
other.mPointee = nullptr;
return *this;
}
template <typename U>
Ref<T>& operator=(Ref<U>&& other) {
static_assert(std::is_convertible<U*, T*>::value, "");
Release();
mPointee = other.mPointee;
other.mPointee = nullptr;
return *this;
}
~Ref() {
Release();
mPointee = nullptr;
}
bool operator==(const T* other) const {
return mPointee == other;
}
bool operator!=(const T* other) const {
return mPointee != other;
}
operator bool() {
return mPointee != nullptr;
}
const T& operator*() const {
return *mPointee;
}
T& operator*() {
return *mPointee;
}
const T* operator->() const {
return mPointee;
}
T* operator->() {
return mPointee;
}
const T* Get() const {
return mPointee;
}
T* Get() {
return mPointee;
}
T* Detach() {
T* pointee = mPointee;
mPointee = nullptr;
return pointee;
}
private:
// Friend is needed so that instances of Ref<U> can assign mPointee
// members of Ref<T>.
template <typename U>
friend class Ref;
void Reference() const {
if (mPointee != nullptr) {
mPointee->Reference();
}
}
void Release() const {
if (mPointee != nullptr) {
mPointee->Release();
}
}
T* mPointee = nullptr;
}; };
template <typename T> template <typename T>

View File

@ -483,7 +483,7 @@ namespace dawn_native { namespace vulkan {
Ref<Texture> texture = Ref<Texture> texture =
AcquireRef(new Texture(device, descriptor, TextureState::OwnedExternal)); AcquireRef(new Texture(device, descriptor, TextureState::OwnedExternal));
texture->InitializeForSwapChain(nativeImage); texture->InitializeForSwapChain(nativeImage);
return std::move(texture); return texture;
} }
MaybeError Texture::InitializeAsInternalTexture(VkImageUsageFlags extraUsages) { MaybeError Texture::InitializeAsInternalTexture(VkImageUsageFlags extraUsages) {

View File

@ -43,14 +43,7 @@ class RCTest : public RefCounted {
}; };
struct RCTestDerived : public RCTest { struct RCTestDerived : public RCTest {
RCTestDerived() : RCTest() { using RCTest::RCTest;
}
RCTestDerived(uint64_t payload) : RCTest(payload) {
}
RCTestDerived(bool* deleted) : RCTest(deleted) {
}
}; };
// Test that RCs start with one ref, and removing it destroys the object. // Test that RCs start with one ref, and removing it destroys the object.