1 // Copyright 2020 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef COMMON_REFBASE_H_ 16 #define COMMON_REFBASE_H_ 17 18 #include "common/Assert.h" 19 #include "common/Compiler.h" 20 21 #include <type_traits> 22 #include <utility> 23 24 // A common class for various smart-pointers acting on referenceable/releasable pointer-like 25 // objects. Logic for each specialization can be customized using a Traits type that looks 26 // like the following: 27 // 28 // struct { 29 // static constexpr T kNullValue = ...; 30 // static void Reference(T value) { ... } 31 // static void Release(T value) { ... } 32 // }; 33 // 34 // RefBase supports 35 template <typename T, typename Traits> 36 class RefBase { 37 public: 38 // Default constructor and destructor. RefBase()39 RefBase() : mValue(Traits::kNullValue) { 40 } 41 ~RefBase()42 ~RefBase() { 43 Release(mValue); 44 } 45 46 // Constructors from nullptr. RefBase(std::nullptr_t)47 constexpr RefBase(std::nullptr_t) : RefBase() { 48 } 49 50 RefBase<T, Traits>& operator=(std::nullptr_t) { 51 Set(Traits::kNullValue); 52 return *this; 53 } 54 55 // Constructors from a value T. RefBase(T value)56 RefBase(T value) : mValue(value) { 57 Reference(value); 58 } 59 60 RefBase<T, Traits>& operator=(const T& value) { 61 Set(value); 62 return *this; 63 } 64 65 // Constructors from a RefBase<T> RefBase(const RefBase<T,Traits> & other)66 RefBase(const RefBase<T, Traits>& other) : mValue(other.mValue) { 67 Reference(other.mValue); 68 } 69 70 RefBase<T, Traits>& operator=(const RefBase<T, Traits>& other) { 71 Set(other.mValue); 72 return *this; 73 } 74 RefBase(RefBase<T,Traits> && other)75 RefBase(RefBase<T, Traits>&& other) { 76 mValue = other.Detach(); 77 } 78 79 RefBase<T, Traits>& operator=(RefBase<T, Traits>&& other) { 80 if (&other != this) { 81 Release(mValue); 82 mValue = other.Detach(); 83 } 84 return *this; 85 } 86 87 // Constructors from a RefBase<U>. Note that in the *-assignment operators this cannot be the 88 // same as `other` because overload resolution rules would have chosen the *-assignement 89 // operators defined with `other` == RefBase<T, Traits>. 90 template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type> RefBase(const RefBase<U,UTraits> & other)91 RefBase(const RefBase<U, UTraits>& other) : mValue(other.mValue) { 92 Reference(other.mValue); 93 } 94 95 template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type> 96 RefBase<T, Traits>& operator=(const RefBase<U, UTraits>& other) { 97 Set(other.mValue); 98 return *this; 99 } 100 101 template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type> RefBase(RefBase<U,UTraits> && other)102 RefBase(RefBase<U, UTraits>&& other) { 103 mValue = other.Detach(); 104 } 105 106 template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type> 107 RefBase<T, Traits>& operator=(RefBase<U, UTraits>&& other) { 108 Release(mValue); 109 mValue = other.Detach(); 110 return *this; 111 } 112 113 // Comparison operators. 114 bool operator==(const T& other) const { 115 return mValue == other; 116 } 117 118 bool operator!=(const T& other) const { 119 return mValue != other; 120 } 121 122 const T operator->() const { 123 return mValue; 124 } 125 T operator->() { 126 return mValue; 127 } 128 129 // Smart pointer methods. Get()130 const T& Get() const { 131 return mValue; 132 } Get()133 T& Get() { 134 return mValue; 135 } 136 Detach()137 T Detach() DAWN_NO_DISCARD { 138 T value{std::move(mValue)}; 139 mValue = Traits::kNullValue; 140 return value; 141 } 142 Acquire(T value)143 void Acquire(T value) { 144 Release(mValue); 145 mValue = value; 146 } 147 InitializeInto()148 T* InitializeInto() DAWN_NO_DISCARD { 149 ASSERT(mValue == Traits::kNullValue); 150 return &mValue; 151 } 152 153 private: 154 // Friend is needed so that instances of RefBase<U> can call Reference and Release on 155 // RefBase<T>. 156 template <typename U, typename UTraits> 157 friend class RefBase; 158 Reference(T value)159 static void Reference(T value) { 160 if (value != Traits::kNullValue) { 161 Traits::Reference(value); 162 } 163 } Release(T value)164 static void Release(T value) { 165 if (value != Traits::kNullValue) { 166 Traits::Release(value); 167 } 168 } 169 Set(T value)170 void Set(T value) { 171 if (mValue != value) { 172 // Ensure that the new value is referenced before the old is released to prevent any 173 // transitive frees that may affect the new value. 174 Reference(value); 175 Release(mValue); 176 mValue = value; 177 } 178 } 179 180 T mValue; 181 }; 182 183 #endif // COMMON_REFBASE_H_ 184