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