Line data Source code
1 : // Author(s): Maurice Laveaux 2 : // Copyright: see the accompanying file COPYING or copy at 3 : // https://github.com/mCRL2org/mCRL2/blob/master/COPYING 4 : // 5 : // Distributed under the Boost Software License, Version 1.0. 6 : // (See accompanying file LICENSE_1_0.txt or copy at 7 : // http://www.boost.org/LICENSE_1_0.txt) 8 : // 9 : 10 : #ifndef MCRL2_UTILITIES_SHARED_REFERENCE_H_ 11 : #define MCRL2_UTILITIES_SHARED_REFERENCE_H_ 12 : 13 : #include "tagged_pointer.h" 14 : 15 : #include <cassert> 16 : #include <atomic> 17 : #include <type_traits> 18 : 19 : #include "mcrl2/utilities/tagged_pointer.h" 20 : 21 : namespace mcrl2 22 : { 23 : namespace utilities 24 : { 25 : 26 : /// \brief Enable to count the number of reference count changes. 27 : constexpr static bool EnableReferenceCountMetrics = false; 28 : 29 : /// \brief Stores a reference count that can be incremented and decremented. 30 : /// \details The template variable is such that reference_count_changes corresponds 31 : /// to the amount of times the reference count of type T changed. 32 : template<typename T, bool ThreadSafe = false> 33 : class shared_reference_counted 34 : { 35 : public: 36 75817 : shared_reference_counted() 37 75817 : : m_reference_count(0) 38 75817 : {} 39 : 40 : /// \brief Obtain the reference count. 41 29604 : std::size_t reference_count() const 42 : { 43 29604 : return m_reference_count; 44 : } 45 : 46 : /// \brief Increment the reference count by one. 47 3169404 : void increment_reference_count() const 48 : { 49 : if constexpr (ThreadSafe) 50 : { 51 3169404 : m_reference_count.fetch_add(1, std::memory_order_relaxed); 52 : } 53 : else 54 : { 55 : ++m_reference_count; 56 : } 57 3169404 : count_reference_count_changes(); 58 3169404 : } 59 : 60 : /// \brief Decrement the reference count by one. 61 2266857 : void decrement_reference_count() const 62 : { 63 : if constexpr (ThreadSafe) 64 : { 65 2266857 : m_reference_count.fetch_sub(1, std::memory_order_release); 66 : } 67 : else 68 : { 69 : --m_reference_count; 70 : } 71 2266857 : count_reference_count_changes(); 72 2266857 : } 73 : 74 : /// \brief Obtain the number of times that this reference count has changed. 75 : static std::atomic<std::size_t>& reference_count_changes() 76 : { 77 : static std::atomic<std::size_t> g_reference_count_changes; 78 : return g_reference_count_changes; 79 : } 80 : 81 : /// \brief Increment the number of reference count changes. 82 5436261 : static void count_reference_count_changes() 83 : { 84 : if constexpr (EnableReferenceCountMetrics) 85 : { 86 : ++reference_count_changes(); 87 : } 88 5436261 : } 89 : 90 : protected: 91 : using SizeType = typename std::conditional<ThreadSafe, std::atomic<std::size_t>, std::size_t>::type; 92 : 93 : // The underlying reference counter can always be changed. 94 : mutable SizeType m_reference_count; 95 : }; 96 : 97 : /// \brief A reference counted reference to a shared_reference_counted object. 98 : /// \details Similar to a shared_ptr except that reference counts are only atomic when 99 : /// thread safety is desired and that it stores the reference count in the 100 : /// inherited object. 101 : template<typename T> 102 : class shared_reference 103 : { 104 : public: 105 : 106 : /// \brief The default constructor. 107 15715 : shared_reference() noexcept 108 15715 : : m_reference(nullptr) 109 15715 : {} 110 : 111 : /// \brief Takes ownership of the passed reference, which means 112 : /// that its reference count is incremented. 113 1095235 : shared_reference(T* reference) noexcept 114 1095235 : : m_reference(reference) 115 : { 116 1095235 : assert(defined()); 117 1095235 : m_reference->increment_reference_count(); 118 1095235 : } 119 : 120 : /// \brief Copy constructor. 121 2071644 : shared_reference(const shared_reference<T>& other) noexcept 122 2071644 : : m_reference(other.m_reference) 123 : { 124 2071644 : if (defined()) 125 : { 126 2071581 : m_reference->increment_reference_count(); 127 : } 128 2071644 : } 129 : 130 : /// \brief Move constructor. 131 1095235 : shared_reference(shared_reference<T>&& other) noexcept 132 1095235 : : m_reference(other.m_reference) 133 : { 134 1095235 : other.m_reference = nullptr; 135 1095235 : } 136 : 137 3374799 : ~shared_reference() 138 : { 139 3374799 : if (defined()) 140 : { 141 2266856 : m_reference->decrement_reference_count(); 142 : } 143 3374799 : } 144 : 145 : /// \brief Copy assignment constructor. 146 2588 : shared_reference<T>& operator=(const shared_reference<T>& other) noexcept 147 : { 148 : // Increment first to prevent the same reference from getting a reference count of zero temporarily. 149 2588 : if (other.defined()) 150 : { 151 2588 : other.m_reference->increment_reference_count(); 152 : } 153 : 154 : // Decrement the reference from the reference that is currently referred to. 155 2588 : if (defined()) 156 : { 157 0 : m_reference->decrement_reference_count(); 158 : } 159 : 160 2588 : m_reference=other.m_reference; 161 2588 : return *this; 162 : } 163 : 164 : /// \brief Move assignment constructor. 165 12405 : shared_reference<T>& operator=(shared_reference<T>&& other) noexcept 166 : { 167 12405 : if (defined()) 168 : { 169 1 : m_reference->decrement_reference_count(); 170 : } 171 : 172 12405 : m_reference = other.m_reference; 173 12405 : other.m_reference = nullptr; 174 12405 : return *this; 175 : } 176 : 177 : /// \brief Check whether the shared_reference has a valid reference. 178 13101084348 : bool defined() const 179 : { 180 13101084348 : return m_reference.get() != nullptr; 181 : } 182 : 183 70186438114 : bool operator ==(const shared_reference<T>& other) const noexcept 184 : { 185 70186438114 : return m_reference == other.m_reference; 186 : } 187 : 188 : bool operator <(const shared_reference<T>& other) const noexcept 189 : { 190 : return m_reference < other.m_reference; 191 : } 192 : 193 : // Comparison operators follow from equivalence and less than. 194 6370075682 : bool operator !=(const shared_reference<T>& other) const noexcept 195 : { 196 6370075682 : return m_reference != other.m_reference; 197 : } 198 : 199 : bool operator <=(const shared_reference<T>& other) const noexcept 200 : { 201 : return m_reference <= other.m_reference; 202 : } 203 : 204 : bool operator >(const shared_reference<T>& other) const noexcept 205 : { 206 : return m_reference > other.m_reference; 207 : } 208 : 209 : bool operator >=(const shared_reference& other) const noexcept 210 : { 211 : return m_reference >= other.m_reference; 212 : } 213 : 214 12932941156 : T* operator->() const noexcept 215 : { 216 12932941156 : assert(defined()); 217 12932941156 : return m_reference.get(); 218 : } 219 : 220 159265419 : T* get() const noexcept 221 : { 222 159265419 : assert(defined()); 223 159265419 : return m_reference.get(); 224 : } 225 : 226 : T& operator*() const noexcept 227 : { 228 : assert(defined()); 229 : return *m_reference; 230 : } 231 : 232 : /// \brief Swaps *this with the other shared reference. 233 : void swap(shared_reference<T>& other) 234 : { 235 : m_reference.swap(other.m_reference); 236 : } 237 : 238 6546423 : bool tagged() const noexcept 239 : { 240 6546423 : return m_reference.tagged(); 241 : } 242 : 243 48405 : void tag() const 244 : { 245 48405 : m_reference.tag(); 246 48405 : } 247 : 248 29474 : void untag() const 249 : { 250 29474 : m_reference.untag(); 251 29474 : } 252 : 253 : private: 254 : mutable utilities::tagged_pointer<T> m_reference; 255 : }; 256 : 257 : } // namespace utilities 258 : } // namespace mcrl2 259 : 260 : namespace std 261 : { 262 : 263 : template<typename T> 264 : void swap(mcrl2::utilities::shared_reference<T>& a, mcrl2::utilities::shared_reference<T>& b) noexcept 265 : { 266 : a.swap(b); 267 : } 268 : 269 : } // namespace std 270 : 271 : #endif // MCRL2_UTILITIES_SHARED_REFERENCE_H_