HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
instanceRegistry.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_IMAGING_HD_INSTANCE_REGISTRY_H
25 #define PXR_IMAGING_HD_INSTANCE_REGISTRY_H
26 
27 #include "pxr/pxr.h"
28 #include "pxr/imaging/hd/api.h"
29 #include "pxr/imaging/hd/version.h"
30 #include "pxr/imaging/hd/perfLog.h"
31 #include "pxr/imaging/hf/perfLog.h"
32 
33 #include <tbb/concurrent_unordered_map.h>
34 
35 #include <memory>
36 #include <mutex>
37 
39 
40 
41 /// \class HdInstance
42 ///
43 /// This class is used as an interface to a shared instance in
44 /// HdInstanceRegistry.
45 ///
46 /// KeyType is a hashable index type and VALUE is shared_ptr. In most use
47 /// cases, the client computes a hash key which represents large bulky data
48 /// (like topology, primvars) and registers it into HdInstanceRegistry. If the
49 /// key has already been registered, the registry returns HdInstance and the
50 /// client can use GetValue() without setting/computing actual bulky data. If
51 /// it doesn't exist, IsFirstInstance() returns true for the first instance
52 /// and the client needs to populate an appropriate data VALUE into the
53 /// instance by SetValue().
54 ///
55 /// In order to support concurrent access to HdInstanceRegistry, this
56 /// class holds a lock to a mutex in HdInstanceRegistry. This lock will
57 /// be held until the instance of this interface class is destroyed.
58 ///
59 template <typename VALUE>
60 class HdInstance {
61 public:
62  typedef uint64_t KeyType;
63  typedef VALUE ValueType;
64 
65  typedef KeyType ID;
66 
67  struct ValueHolder {
69  : value(value)
70  , recycleCounter(0)
71  { }
73  recycleCounter = 0;
74  }
75 
78  };
79  typedef tbb::concurrent_unordered_map<KeyType, ValueHolder> Dictionary;
80 
81  typedef std::mutex RegistryMutex;
82  typedef std::unique_lock<RegistryMutex> RegistryLock;
83 
84  HdInstance() = delete;
85 
86  /// Construct an instance holding a registry lock, representing a value
87  /// held in a registry container.
88  explicit HdInstance(KeyType const &key,
89  ValueType const &value,
90  RegistryLock &&registryLock,
91  Dictionary *container)
92  : _key(key)
93  , _value(value)
94  , _registryLock(std::move(registryLock))
95  , _container(container)
96  , _isFirstInstance(!bool(_value))
97  { }
98 
99  /// Construct an instance with no lock or registry container. This
100  /// is used to present a consistent interface to clients in cases
101  /// where shared resource registration is disabled.
102  explicit HdInstance(KeyType const &key)
103  : _key(key)
104  , _value(ValueType())
105  , _registryLock()
106  , _container(nullptr)
107  , _isFirstInstance(!bool(_value))
108  { }
109 
110  /// Returns the key
111  KeyType const &GetKey() const { return _key; }
112 
113  /// Returns the value
114  ValueType const &GetValue() const { return _value; }
115 
116  /// Update the value in dictionary indexed by the key.
117  void SetValue(ValueType const &value) {
118  if (_container) (*_container)[_key] = ValueHolder(value);
119  _value = value;
120  }
121 
122  /// Returns true if the value has not been initialized.
123  bool IsFirstInstance() const {
124  return _isFirstInstance;
125  }
126 
127 private:
128  KeyType _key;
129  ValueType _value;
130  RegistryLock _registryLock;
131  Dictionary *_container;
132  bool _isFirstInstance;
133 };
134 
135 /// \class HdInstanceRegistry
136 ///
137 /// HdInstanceRegistry is a dictionary container of HdInstance.
138 /// This class is almost just a dictionary from key to value.
139 /// For cleaning unused entries, it provides GarbageCollect() API.
140 /// It sweeps all entries in the dictionary and erase unreferenced entries.
141 /// When HdInstance::ValueType is shared_ptr, it is regarded as unreferenced
142 /// if the shared_ptr is unique (use_count==1). Note that Key is not
143 /// involved to determine the lifetime of entries.
144 ///
145 template <typename VALUE>
147 public:
149 
150  HdInstanceRegistry() = default;
151 
152  /// Copy constructor. Need as HdInstanceRegistry is placed in a map
153  /// and mutex is not copy constructable, so can't use default
155  : _dictionary(other._dictionary)
156  , _registryMutex() // mutex is not copied
157  { }
158 
159  /// Returns a shared instance for given key.
161  typename InstanceType::KeyType const &key);
162 
163  /// Returns a shared instance for a given key
164  /// only if the key exists in the dictionary.
166  typename InstanceType::KeyType const &key, bool *found);
167 
168  /// Removes unreferenced entries and returns the count
169  /// of remaining entries. When recycleCount is greater than zero,
170  /// unreferenced entries will not be removed until GarbageCollect() is
171  /// called that many more times, i.e. allowing unreferenced entries to
172  /// be recycled if they are needed again.
173  size_t GarbageCollect(int recycleCount = 0);
174 
175  /// Removes unreferenced entries and returns the count
176  /// of remaining entries. If an entry is to be removed, callback function
177  /// "callback" will be called on the entry before removal. When
178  /// recycleCount is greater than zero, unreferenced entries will not be
179  /// removed until GarbageCollect() is called that many more times, i.e.
180  /// allowing unreferenced entries to be recycled if they are needed again.
181  template <typename Callback>
182  size_t GarbageCollect(Callback &&callback, int recycleCount = 0);
183 
184  /// Returns a const iterator being/end of dictionary. Mainly used for
185  /// resource auditing.
186  typedef typename InstanceType::Dictionary::const_iterator const_iterator;
187  const_iterator begin() const { return _dictionary.begin(); }
188  const_iterator end() const { return _dictionary.end(); }
189 
190  size_t size() const { return _dictionary.size(); }
191 
192  void Invalidate();
193 
194 private:
195  template <typename T>
196  static bool _IsUnique(std::shared_ptr<T> const &value) {
197  return value.unique();
198  }
199 
200  typename InstanceType::Dictionary _dictionary;
201  typename InstanceType::RegistryMutex _registryMutex;
202 
203  HdInstanceRegistry &operator =(HdInstanceRegistry &) = delete;
204 };
205 
206 // ---------------------------------------------------------------------------
207 // instance registry impl
208 
209 template <typename VALUE>
212  typename HdInstance<VALUE>::KeyType const &key)
213 {
216 
217  // Grab Registry lock
218  // (and don't release it in this function, return it instead)
219  typename InstanceType::RegistryLock lock(_registryMutex);
220 
221  typename InstanceType::Dictionary::iterator it = _dictionary.find(key);
222  if (it == _dictionary.end()) {
223  // not found. create new one
224  it = _dictionary.insert(
225  std::make_pair(key, typename InstanceType::ValueHolder())).first;
226  }
227 
228  it->second.ResetRecycleCounter();
229  return InstanceType(key, it->second.value, std::move(lock), &_dictionary);
230 }
231 
232 template <typename VALUE>
235  typename HdInstance<VALUE>::KeyType const &key, bool *found)
236 {
239 
240  // Grab Registry lock
241  // (and don't release it in this function, return it instead)
242  typename InstanceType::RegistryLock lock(_registryMutex);
243 
244  typename InstanceType::Dictionary::iterator it = _dictionary.find(key);
245  if (it == _dictionary.end()) {
246  *found = false;
247  return InstanceType(key, VALUE(), std::move(lock), nullptr);
248  } else {
249  *found = true;
250  it->second.ResetRecycleCounter();
251  return InstanceType(key, it->second.value,std::move(lock),&_dictionary);
252  }
253 }
254 
255 template <typename VALUE>
256 size_t
258 {
259  // Call GarbageCollect with empty callback function
260  return GarbageCollect([](void*){}, recycleCount);
261 }
262 
263 template <typename VALUE>
264 template <typename Callback>
265 size_t
266 HdInstanceRegistry<VALUE>::GarbageCollect(Callback &&callback, int recycleCount)
267 {
270 
271  // Skip garbage collection entirely when then the recycleCount is < 0
272  if (recycleCount < 0) {
273  return _dictionary.size();
274  }
275 
276  size_t inUseCount = 0;
277  for (typename InstanceType::Dictionary::iterator it = _dictionary.begin();
278  it != _dictionary.end();) {
279 
280  // erase instance which isn't referred from anyone
281  bool isUnique = _IsUnique(it->second.value);
282  if (isUnique && (++it->second.recycleCounter > recycleCount)) {
283  std::forward<Callback>(callback)(it->second.value.get());
284  it = _dictionary.unsafe_erase(it);
285  } else {
286  ++it;
287  ++inUseCount;
288  }
289  }
290  return inUseCount;
291 }
292 
293 template <typename VALUE>
294 void
296 {
299 
300  _dictionary.clear();
301 }
302 
303 
305 
306 #endif // PXR_IMAGING_HD_INSTANCE_REGISTRY_H
size_t GarbageCollect(int recycleCount=0)
tbb::concurrent_unordered_map< KeyType, ValueHolder > Dictionary
KeyType const & GetKey() const
Returns the key.
HdInstance()=delete
GLsizei const GLfloat * value
Definition: glcorearb.h:824
#define HF_MALLOC_TAG_FUNCTION()
Definition: perfLog.h:37
uint64_t KeyType
const_iterator begin() const
bool IsFirstInstance() const
Returns true if the value has not been initialized.
HdInstanceRegistry()=default
HdInstance< VALUE > InstanceType
HdInstance(KeyType const &key)
void SetValue(ValueType const &value)
Update the value in dictionary indexed by the key.
HdInstance(KeyType const &key, ValueType const &value, RegistryLock &&registryLock, Dictionary *container)
InstanceType::Dictionary::const_iterator const_iterator
#define HD_TRACE_FUNCTION()
Definition: perfLog.h:57
std::unique_lock< RegistryMutex > RegistryLock
HdInstanceRegistry(const HdInstanceRegistry &other)
InstanceType GetInstance(typename InstanceType::KeyType const &key)
Returns a shared instance for given key.
std::mutex RegistryMutex
size_t size() const
ValueType const & GetValue() const
Returns the value.
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1441
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
Definition: core.h:1131
InstanceType FindInstance(typename InstanceType::KeyType const &key, bool *found)
ValueHolder(ValueType const &value=ValueType())
const_iterator end() const