HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_COWValue.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_COWValue.h
7  *
8  * COMMENTS:
9  *
10  *
11  */
12 
13 #ifndef __UT_COWVALUE_H__
14 #define __UT_COWVALUE_H__
15 
16 #include "UT_API.h"
17 
18 #include "UT_ArraySet.h"
19 #include "UT_Assert.h"
20 #include "UT_Tracing.h"
21 
22 #include <SYS/SYS_AtomicInt.h>
23 #include <SYS/SYS_TypeDecorate.h>
24 
25 #include <stddef.h>
26 
27 /// Implements a generic copy on write mechanism for types. There is
28 /// always a valid object stored within a COWValue -- moving from it resets it
29 /// to a default-constructed T, not a nullptr.
30 template <typename T>
32 {
33 public:
35  UT_COWValue(T &&data) : myRep(new Rep(std::move(data))) {}
36  template <typename... Args>
37  UT_COWValue(std::in_place_t, Args &&...args)
38  : myRep(new Rep(std::in_place, std::forward<Args>(args)...))
39  {
40  }
41 
43  {
44  utZoneScopedN("cow handle copy assign");
45  // UTdebugPrint("copy assign cow handle");
46  if (this != &other)
47  {
48  UT_COWValue(other).swap(*this);
49  }
50  return *this;
51  }
52 
53  UT_COWValue(const UT_COWValue &other)
54  {
55  other.safeIncRef();
56 
57  myConstRep = other.myConstRep;
58  }
59 
60  UT_COWValue &operator=(UT_COWValue &&other) noexcept
61  {
62  // UTdebugPrint("move cow handle");
63  if (this != &other)
64  {
65  safeDecRef();
67  other.myConstRep, Rep::getStaticEmpty());
68  }
69  return *this;
70  }
71 
72  UT_COWValue(UT_COWValue &&other) noexcept
73  : myConstRep(
74  std::exchange(other.myConstRep, Rep::getStaticEmpty()))
75  {
76  }
77 
79 
80  /// Returns true if the data referenced by this UT_COWValue is not
81  /// shared (i.e. whether accessing it will not create a copy).
82  bool isUnique() const { return myConstRep->isUnique(); }
83  /// Returns true if this UT_COWValue is referencing the global
84  /// default-constructed version of T.
85  bool isImmortal() const { return myConstRep->isImmortal(); }
86  /// Return the number of UT_COWValues sharing data with us, including us.
87  /// May return a negative number to indicate an unknown quantity.
88  exint refCount() const { return myConstRep->refCount(); }
89 
90  /// Ensure the data referenced by this UT_COWValue is only referenced by
91  /// it.
92  void makeUnique()
93  {
94  // Check if we have multiple objects referencing this handle
95  if (!isUnique())
96  {
97  utZoneScopedN("cow handle deep copy");
98  // UTdebugPrint("cow handle deep copy");
99  *this = UT_COWValue(std::in_place, *myRep->data());
100  }
101  }
102 
104  {
105  UT_ASSERT(myRep);
106  makeUnique();
107  return *myRep->data();
108  }
109  const T &operator*() const
110  {
111  UT_ASSERT(myRep);
112  return *myConstRep->data();
113  }
114 
116  {
117  UT_ASSERT(myRep);
118  makeUnique();
119  return myRep->data();
120  }
121  const T *operator->() const
122  {
123  UT_ASSERT(myRep);
124  return myConstRep->data();
125  }
126 
127  /// Return a const reference to the stored data. Never copies.
128  /// NB: This allows a non-unique, non-const, UT_COWValue<T>, to read the
129  /// data without first forcing the value to be hardened.
130  const T &peek() const { return *myConstRep->data(); }
131 
132  void swap(UT_COWValue<T>& other)
133  {
134  std::swap(myRep, other.myRep);
135  }
136 
137  /// Return the global static immortal default-constructed UT_COWValue for
138  /// the type.
140  {
141  static const UT_COWValue<T> theInstance{};
142  return theInstance;
143  }
144 
145 protected:
146  /// The actual control block for a value referenced by (potentially
147  /// multiple) UT_COWValues
148  ///
149  /// These can be in one of three states:
150  /// - unique: only one UT_COWValue has mutable access to the data
151  /// stored
152  /// - nonunique: multiple UT_COWValues have const access to the data
153  /// stored
154  /// - immortal: an arbitrary number of UT_COWValues have const access
155  /// to the read-only data stored
156  ///
157  /// Immortal Reps are used to provide a cheap global
158  /// default-constructed version of the contained type.
159  class Rep
160  {
161  friend class UT_COWValue<T>;
162 
163  struct ImmortalTag
164  {
165  };
166 
167  /// Stored as the myRefCount for an immortal Rep.
168  inline const static exint IMMORTAL_REF_COUNT = -1;
169 
170  protected:
171  typedef T element_type;
172 
173  Rep() : myData{}, myRefCount(1) {}
174  Rep(const T &data) : myData(T(data)), myRefCount(1) {}
175  Rep(T &&data) : myData(std::move(data)), myRefCount(1) {}
176  template <typename... Args>
177  Rep(std::in_place_t, Args &&...args)
178  : myData(std::forward<Args>(args)...), myRefCount(1)
179  {
180  }
181  /// Construct the global immortal Rep for this type -- should
182  /// only be called by getStaticEmpty().
183  explicit Rep(ImmortalTag) : myData{}, myRefCount{IMMORTAL_REF_COUNT} {}
184 
185  // TODO: should there be emplace-style construct-in-place constructors?
186 
187  Rep(const Rep &other) = delete;
188  Rep &operator=(const Rep &other) = delete;
189 
190  ~Rep() { UT_ASSERT(isImmortal() || myRefCount.relaxedLoad() == 0); }
191 
192  bool isUnique() const
193  {
194  return myRefCount.load(SYS_MEMORY_ORDER_ACQUIRE) == 1;
195  }
196  bool isImmortal() const
197  {
198  return myRefCount.relaxedLoad() == IMMORTAL_REF_COUNT;
199  }
200  exint refCount() const
201  {
202  return myRefCount.load(SYS_MEMORY_ORDER_ACQUIRE);
203  }
204 
205  T *data() { return &myData; }
206  const T *data() const { return &myData; }
207 
208  /// Return the global static immortal default-constructed Rep for the
209  /// type.
210  static const Rep *getStaticEmpty()
211  {
212  static const Rep theInstance{ImmortalTag{}};
213  return &theInstance;
214  }
215 
216  private:
217  void incRef()
218  {
219  UT_ASSERT(!isImmortal());
220  myRefCount.add(1, SYS_MEMORY_ORDER_RELAXED);
221  }
222 
223  void decRef()
224  {
225  UT_ASSERT(!isImmortal());
226  if (myRefCount.add(-1, SYS_MEMORY_ORDER_ACQ_REL)
227  == 0)
228  delete this;
229  }
230 
231  SYS_AtomicInt32 myRefCount;
232  T myData;
233  };
234 
235  void safeIncRef() const
236  {
237  if (!myConstRep->isImmortal())
238  myRep->incRef();
239  }
240 
241  void safeDecRef()
242  {
243  if (!myConstRep->isImmortal())
244  myRep->decRef();
245  }
246 
247  // This union is used to make it clear where
248  // we expect to have a possibly-const handleref.
249  union
250  {
251  const Rep *myConstRep;
252  Rep *myRep;
253  };
254 };
255 
256 namespace UT
257 {
258 template <typename T>
259 struct DefaultClearer;
260 
261 template <typename S>
263 {
264  static void clear(UT_COWValue<S>& v) { v = UT_COWValue<S>(); }
265  static bool isClear(const UT_COWValue<S>& v) { return v.isImmortal(); }
267  {
268  new ((void*)p) UT_COWValue<S>();
269  }
270  static const bool clearNeedsDestruction = false;
271 };
272 }
273 
274 #endif // __UT_COWVALUE_H__
bool isUnique() const
Definition: UT_COWValue.h:82
static void clearConstruct(UT_COWValue< S > *p)
Definition: UT_COWValue.h:266
UT_COWValue(T &&data)
Definition: UT_COWValue.h:35
UT_COWValue & operator=(const UT_COWValue &other)
Definition: UT_COWValue.h:42
void swap(UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &a, UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &b)
Definition: UT_ArraySet.h:1699
Rep(std::in_place_t, Args &&...args)
Definition: UT_COWValue.h:177
const GLdouble * v
Definition: glcorearb.h:837
void safeIncRef() const
Definition: UT_COWValue.h:235
void makeUnique()
Definition: UT_COWValue.h:92
static const UT_COWValue< T > & getStaticEmpty()
Definition: UT_COWValue.h:139
int64 exint
Definition: SYS_Types.h:125
static const Rep * getStaticEmpty()
Definition: UT_COWValue.h:210
Rep(const T &data)
Definition: UT_COWValue.h:174
T & operator*()
Definition: UT_COWValue.h:103
bool isImmortal() const
Definition: UT_COWValue.h:196
bool isUnique() const
Definition: UT_COWValue.h:192
static void clear(UT_COWValue< S > &v)
Definition: UT_COWValue.h:264
Rep(ImmortalTag)
Definition: UT_COWValue.h:183
bool isImmortal() const
Definition: UT_COWValue.h:85
constexpr SYS_MemoryOrder SYS_MEMORY_ORDER_ACQ_REL
UT_COWValue(UT_COWValue &&other) noexcept
Definition: UT_COWValue.h:72
#define utZoneScopedN(name)
Definition: UT_Tracing.h:222
UT_COWValue & operator=(UT_COWValue &&other) noexcept
Definition: UT_COWValue.h:60
const T * operator->() const
Definition: UT_COWValue.h:121
T load(SYS_MemoryOrder order=SYS_MEMORY_ORDER_SEQ_CST) const
UT_COWValue(const UT_COWValue &other)
Definition: UT_COWValue.h:53
UT_COWValue(std::in_place_t, Args &&...args)
Definition: UT_COWValue.h:37
void safeDecRef()
Definition: UT_COWValue.h:241
constexpr SYS_MemoryOrder SYS_MEMORY_ORDER_RELAXED
Any reordering the compiler or hardware chooses to do is okay.
exint refCount() const
Definition: UT_COWValue.h:200
const T * data() const
Definition: UT_COWValue.h:206
SYS_FORCE_INLINE T relaxedLoad() const
exint refCount() const
Definition: UT_COWValue.h:88
T * operator->()
Definition: UT_COWValue.h:115
VULKAN_HPP_CONSTEXPR_14 VULKAN_HPP_INLINE T exchange(T &obj, U &&newValue)
Definition: vulkan_raii.hpp:25
const Rep * myConstRep
Definition: UT_COWValue.h:251
**If you just want to fire and args
Definition: thread.h:618
T add(T val)
Atomically adds val to myValue, returning the new value of myValue.
Rep & operator=(const Rep &other)=delete
const T & operator*() const
Definition: UT_COWValue.h:109
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:165
void swap(UT_COWValue< T > &other)
Definition: UT_COWValue.h:132
constexpr SYS_MemoryOrder SYS_MEMORY_ORDER_ACQUIRE
static bool isClear(const UT_COWValue< S > &v)
Definition: UT_COWValue.h:265
Definition: format.h:1821
const T & peek() const
Definition: UT_COWValue.h:130