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, even if it's not unique.
128  /// Never copies.
129  const T &peek() const { return *myConstRep->data(); }
130 
131  void swap(UT_COWValue<T>& other)
132  {
133  std::swap(myRep, other.myRep);
134  }
135 
136  /// Return the global static immortal default-constructed UT_COWValue for
137  /// the type.
139  {
140  static const UT_COWValue<T> theInstance{};
141  return theInstance;
142  }
143 
144 protected:
145  /// The actual control block for a value referenced by (potentially
146  /// multiple) UT_COWValues
147  ///
148  /// These can be in one of three states:
149  /// - unique: only one UT_COWValue has mutable access to the data
150  /// stored
151  /// - nonunique: multiple UT_COWValues have const access to the data
152  /// stored
153  /// - immortal: an arbitrary number of UT_COWValues have const access
154  /// to the read-only data stored
155  ///
156  /// Immortal Reps are used to provide a cheap global
157  /// default-constructed version of the contained type.
158  class Rep
159  {
160  friend class UT_COWValue<T>;
161 
162  struct ImmortalTag
163  {
164  };
165 
166  /// Stored as the myRefCount for an immortal Rep.
167  inline const static exint IMMORTAL_REF_COUNT = -1;
168 
169  protected:
170  typedef T element_type;
171 
172  Rep() : myData{}, myRefCount(1) {}
173  Rep(const T &data) : myData(T(data)), myRefCount(1) {}
174  Rep(T &&data) : myData(std::move(data)), myRefCount(1) {}
175  template <typename... Args>
176  Rep(std::in_place_t, Args &&...args)
177  : myData(std::forward<Args>(args)...), myRefCount(1)
178  {
179  }
180  /// Construct the global immortal Rep for this type -- should
181  /// only be called by getStaticEmpty().
182  explicit Rep(ImmortalTag) : myData{}, myRefCount{IMMORTAL_REF_COUNT} {}
183 
184  // TODO: should there be emplace-style construct-in-place constructors?
185 
186  Rep(const Rep &other) = delete;
187  Rep &operator=(const Rep &other) = delete;
188 
189  ~Rep() { UT_ASSERT(isImmortal() || myRefCount.relaxedLoad() == 0); }
190 
191  bool isUnique() const
192  {
193  return myRefCount.load(SYS_MEMORY_ORDER_ACQUIRE) == 1;
194  }
195  bool isImmortal() const
196  {
197  return myRefCount.relaxedLoad() == IMMORTAL_REF_COUNT;
198  }
199  exint refCount() const
200  {
201  return myRefCount.load(SYS_MEMORY_ORDER_ACQUIRE);
202  }
203 
204  T *data() { return &myData; }
205  const T *data() const { return &myData; }
206 
207  /// Return the global static immortal default-constructed Rep for the
208  /// type.
209  static const Rep *getStaticEmpty()
210  {
211  static const Rep theInstance{ImmortalTag{}};
212  return &theInstance;
213  }
214 
215  private:
216  void incRef()
217  {
218  UT_ASSERT(!isImmortal());
219  myRefCount.add(1, SYS_MEMORY_ORDER_RELAXED);
220  }
221 
222  void decRef()
223  {
224  UT_ASSERT(!isImmortal());
225  if (myRefCount.add(-1, SYS_MEMORY_ORDER_ACQ_REL)
226  == 0)
227  delete this;
228  }
229 
230  SYS_AtomicInt32 myRefCount;
231  T myData;
232  };
233 
234  void safeIncRef() const
235  {
236  if (!myConstRep->isImmortal())
237  myRep->incRef();
238  }
239 
240  void safeDecRef()
241  {
242  if (!myConstRep->isImmortal())
243  myRep->decRef();
244  }
245 
246  // This union is used to make it clear where
247  // we expect to have a possibly-const handleref.
248  union
249  {
250  const Rep *myConstRep;
251  Rep *myRep;
252  };
253 };
254 
255 namespace UT
256 {
257 template <typename T>
258 struct DefaultClearer;
259 
260 template <typename S>
262 {
263  static void clear(UT_COWValue<S>& v) { v = UT_COWValue<S>(); }
264  static bool isClear(const UT_COWValue<S>& v) { return v.isImmortal(); }
266  {
267  new ((void*)p) UT_COWValue<S>();
268  }
269  static const bool clearNeedsDestruction = false;
270 };
271 }
272 
273 #endif // __UT_COWVALUE_H__
bool isUnique() const
Definition: UT_COWValue.h:82
static void clearConstruct(UT_COWValue< S > *p)
Definition: UT_COWValue.h:265
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:176
const GLdouble * v
Definition: glcorearb.h:837
void safeIncRef() const
Definition: UT_COWValue.h:234
void makeUnique()
Definition: UT_COWValue.h:92
static const UT_COWValue< T > & getStaticEmpty()
Definition: UT_COWValue.h:138
int64 exint
Definition: SYS_Types.h:125
static const Rep * getStaticEmpty()
Definition: UT_COWValue.h:209
Rep(const T &data)
Definition: UT_COWValue.h:173
T & operator*()
Definition: UT_COWValue.h:103
bool isImmortal() const
Definition: UT_COWValue.h:195
bool isUnique() const
Definition: UT_COWValue.h:191
static void clear(UT_COWValue< S > &v)
Definition: UT_COWValue.h:263
Rep(ImmortalTag)
Definition: UT_COWValue.h:182
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:212
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:240
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:199
const T * data() const
Definition: UT_COWValue.h:205
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:250
**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:156
void swap(UT_COWValue< T > &other)
Definition: UT_COWValue.h:131
constexpr SYS_MemoryOrder SYS_MEMORY_ORDER_ACQUIRE
static bool isClear(const UT_COWValue< S > &v)
Definition: UT_COWValue.h:264
Definition: format.h:1821
const T & peek() const
Definition: UT_COWValue.h:129