HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
stacked.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_TF_STACKED_H
8 #define PXR_BASE_TF_STACKED_H
9 
10 #include "pxr/pxr.h"
11 #include "pxr/base/tf/api.h"
12 #include "pxr/base/tf/diagnostic.h"
13 #include "pxr/base/arch/demangle.h"
14 
15 #include <tbb/enumerable_thread_specific.h>
16 
17 #include <atomic>
18 #include <type_traits>
19 #include <vector>
20 
22 
23 /// \class TfStackedAccess
24 ///
25 /// Classes that derive \a TfStacked may befriend TfStackedAccess if they wish
26 /// to customize aspects \a TfStacked's behavior. See \a TfStacked
27 /// documentation for more details.
28 ///
30 public:
31  template <class Derived>
32  static void InitializeStack() {
33  return Derived::_InitializeStack();
34  }
35 };
36 
37 // Detail for TfStacked storage types.
38 template <typename T, bool PerThread>
40 public:
41  typedef std::vector<T const *> Stack;
42 
43 private:
44  /* This is a wrapper around Stack that makes sure we call InitializeStack */
45  /* once per stack instance. */
46  class _StackHolder {
47  public:
48  _StackHolder() : _initialized(false) { }
49 
50  Stack &Get() {
51  if (!_initialized) {
52  _initialized = true;
53  TfStackedAccess::InitializeStack<T>();
54  }
55  return _stack;
56  }
57 
58  private:
59  Stack _stack;
60  bool _initialized;
61  };
62 
63  struct _PerThreadStackStorage {
64  tbb::enumerable_thread_specific<_StackHolder> stack;
65  Stack &Get() {
66  return stack.local().Get();
67  }
68  };
69 
70  struct _GlobalStackStorage {
71  _StackHolder stack;
72  Stack &Get() {
73  return stack.Get();
74  }
75  };
76 
77 public:
78  /* Choose the stack storage type based on thea PerThread argument. */
79  typedef typename std::conditional<
80  PerThread, _PerThreadStackStorage, _GlobalStackStorage
82 };
83 
84 // Detail for TfStacked storage. This exists so we can specialize it
85 // with exported storage.
86 template <typename T, bool PerThread>
88 
89 // Detail for TfStacked storage. This is for the case we don't need
90 // exported storage. This is the default when simply subclassing
91 // TfStacked without using TF_DEFINE_STACKED.
92 template <typename T, bool PerThread>
96  static std::atomic<Type*> value;
97 };
98 
99 /// \class TfStacked
100 ///
101 /// A TfStacked is used where a class needs to keep a stack of the objects
102 /// currently in existence. This class follows the CRTP and is a base class
103 /// that is parameterized on its derived classes.
104 ///
105 /// TfStacked is thread-safe by default and each thread will get its own stack.
106 /// This behavior may be disabled by passing \a false for the \a PerThread
107 /// template parameter.
108 ///
109 /// Derived classes must instantiate the stack themselves by putting
110 ///
111 /// TF_INSTANTIATE_STACKED(YourStackedClass)
112 ///
113 /// in a single .cpp file.
114 ///
115 /// Note that \a Stacked objects that differ only by \a PerThread will not
116 /// share stacks.
117 ///
118 template <class Derived, bool PerThread = true,
120 class TfStacked {
121  TfStacked(TfStacked const &) = delete;
122  TfStacked& operator=(TfStacked const &) = delete;
123  typedef typename Holder::Type _StorageType;
124 public:
125  typedef Holder Storage;
126  typedef typename Storage::Stack Stack;
127 
128  /// Pushes this stacked object onto the stack.
130  _Push(_AsDerived());
131  }
132 
133  /// Pops this stacked object from the stack.
135  _Pop(_AsDerived());
136  }
137 
138  /// Returns the top of the stack. If the stack is empty, returns 0.
139  /// Derived classes can befriend TfStackedAccess and hide (override)
140  /// \a _InitializeStack() to pre-populate the stack if desired. This way,
141  /// a stack can be made never to be empty.
142  static Derived const *GetStackTop() {
143  Stack const &stack = GetStack();
144  return stack.empty() ? 0 : stack.back();
145  }
146 
147  /// Returns the element under the top of the stack. If the stack contains
148  /// only one element, or is empty, returns 0. Derived classes can befriend
149  /// TfStackedAccess and hide (override) \a _InitializeStack() to
150  /// pre-populate the stack if desired. This way, a stack can be made never
151  /// to be empty.
152  static Derived const *GetStackPrevious() {
153  Stack const &stack = GetStack();
154  size_t size = stack.size();
155  return size <= 1 ? 0 : stack[size-2];
156  }
157 
158  /// Returns a const reference to the entire stack.
159  static Stack const &GetStack() {
160  return _GetStack();
161  }
162 
163  /// Returns true if \a p is the top of the stack.
164  static bool IsStackTop(Derived const *p) {
165  return GetStackTop() == p;
166  }
167 
168 private:
169  friend class TfStackedAccess;
170 
171  // This function may be hidden (overridden) by derived classes to initialize
172  // (pre-populate) the stack with some items. One way to do this is to
173  // allocate objects on the heap, never to be freed. By default, no
174  // initialization is performed.
175  static void _InitializeStack() {}
176 
177  // Push p on the stack. Only the constructor should call this.
178  static void _Push(Derived const *p) {
179  _GetStack().push_back(p);
180  }
181 
182  // Pop p off the stack. Only the destructor should call this.
183  static void _Pop(Derived const *p) {
184  // Make sure we pop in reverse order.
185  if (ARCH_LIKELY(IsStackTop(p))) {
186  _GetStack().pop_back();
187  } else {
188  // CODE_COVERAGE_OFF
189  TF_FATAL_ERROR("Destroyed %s out of stack order.",
190  ArchGetDemangled<Derived>().c_str());
191  // CODE_COVERAGE_ON
192  }
193  }
194 
195  static Stack &_GetStack() {
196  // Technically unsafe double-checked lock to initialize the stack.
197  if (ARCH_UNLIKELY(Storage::value.load() == nullptr)) {
198  // Make a new stack and try to set it.
199  _StorageType *old = nullptr;
200  _StorageType *tmp = new _StorageType;
201  // Attempt to set the stack.
202  if (!Storage::value.compare_exchange_strong(old, tmp)) {
203  // Another caller won the race.
204  delete tmp;
205  }
206  }
207  return Storage::value.load(std::memory_order_relaxed)->Get();
208  }
209 
210  Derived *_AsDerived() {
211  return static_cast<Derived *>(this);
212  }
213 
214  Derived const *_AsDerived() const {
215  return static_cast<Derived const *>(this);
216  }
217 };
218 
219 /// Define the class \p Derived that subclasses from TfStacked.
220 /// \p IsPerThread selected thread safety and \p eiAPI is used to export
221 /// the storage for the stack. Use \c TF_INSTANTIATE_DEFINED_STACKED
222 /// to define the storage.
223 #define TF_DEFINE_STACKED(Derived, IsPerThread, eiAPI) \
224 class Derived; \
225 template <> \
226 struct Tf_ExportedStackedStorage<Derived, IsPerThread> { \
227  typedef typename Tf_StackedStorageType<Derived, IsPerThread>::Stack Stack; \
228  typedef typename Tf_StackedStorageType<Derived, IsPerThread>::Type Type; \
229  static eiAPI std::atomic<Type*> value; \
230 }; \
231 class Derived : \
232  public TfStacked<Derived, IsPerThread, \
233  Tf_ExportedStackedStorage<Derived, IsPerThread>>
234 
236 
237 #endif // PXR_BASE_TF_STACKED_H
#define ARCH_LIKELY(x)
Definition: hints.h:29
static bool IsStackTop(Derived const *p)
Returns true if p is the top of the stack.
Definition: stacked.h:164
~TfStacked()
Pops this stacked object from the stack.
Definition: stacked.h:134
TfStacked()
Pushes this stacked object onto the stack.
Definition: stacked.h:129
static void InitializeStack()
Definition: stacked.h:32
Storage::Stack Stack
Definition: stacked.h:126
GLsizei const GLfloat * value
Definition: glcorearb.h:824
Tf_StackedStorageType< T, PerThread >::Type Type
Definition: stacked.h:95
static Derived const * GetStackTop()
Definition: stacked.h:142
static Stack const & GetStack()
Returns a const reference to the entire stack.
Definition: stacked.h:159
#define ARCH_UNLIKELY(x)
Definition: hints.h:30
GLint GLint GLsizei GLint GLenum GLenum type
Definition: glcorearb.h:108
static Derived const * GetStackPrevious()
Definition: stacked.h:152
#define TF_FATAL_ERROR
std::conditional< PerThread, _PerThreadStackStorage, _GlobalStackStorage >::type Type
Definition: stacked.h:81
GLsizeiptr size
Definition: glcorearb.h:664
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
Holder Storage
Definition: stacked.h:125
static std::atomic< Type * > value
Definition: stacked.h:96
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
OIIO_UTIL_API const char * c_str(string_view str)
std::vector< T const * > Stack
Definition: stacked.h:41
Tf_StackedStorageType< T, PerThread >::Stack Stack
Definition: stacked.h:94