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 PXR_BASE_TF_STACKED_H
25 #define PXR_BASE_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 <tbb/enumerable_thread_specific.h>
33 
34 #include <atomic>
35 #include <type_traits>
36 #include <vector>
37 
39 
40 /// \class TfStackedAccess
41 ///
42 /// Classes that derive \a TfStacked may befriend TfStackedAccess if they wish
43 /// to customize aspects \a TfStacked's behavior. See \a TfStacked
44 /// documentation for more details.
45 ///
47 public:
48  template <class Derived>
49  static void InitializeStack() {
50  return Derived::_InitializeStack();
51  }
52 };
53 
54 // Detail for TfStacked storage types.
55 template <typename T, bool PerThread>
57 public:
58  typedef std::vector<T const *> Stack;
59 
60 private:
61  /* This is a wrapper around Stack that makes sure we call InitializeStack */
62  /* once per stack instance. */
63  class _StackHolder {
64  public:
65  _StackHolder() : _initialized(false) { }
66 
67  Stack &Get() {
68  if (!_initialized) {
69  _initialized = true;
70  TfStackedAccess::InitializeStack<T>();
71  }
72  return _stack;
73  }
74 
75  private:
76  Stack _stack;
77  bool _initialized;
78  };
79 
80  struct _PerThreadStackStorage {
81  tbb::enumerable_thread_specific<_StackHolder> stack;
82  Stack &Get() {
83  return stack.local().Get();
84  }
85  };
86 
87  struct _GlobalStackStorage {
88  _StackHolder stack;
89  Stack &Get() {
90  return stack.Get();
91  }
92  };
93 
94 public:
95  /* Choose the stack storage type based on thea PerThread argument. */
96  typedef typename std::conditional<
97  PerThread, _PerThreadStackStorage, _GlobalStackStorage
99 };
100 
101 // Detail for TfStacked storage. This exists so we can specialize it
102 // with exported storage.
103 template <typename T, bool PerThread>
105 
106 // Detail for TfStacked storage. This is for the case we don't need
107 // exported storage. This is the default when simply subclassing
108 // TfStacked without using TF_DEFINE_STACKED.
109 template <typename T, bool PerThread>
113  static std::atomic<Type*> value;
114 };
115 
116 /// \class TfStacked
117 ///
118 /// A TfStacked is used where a class needs to keep a stack of the objects
119 /// currently in existence. This class follows the CRTP and is a base class
120 /// that is parameterized on its derived classes.
121 ///
122 /// TfStacked is thread-safe by default and each thread will get its own stack.
123 /// This behavior may be disabled by passing \a false for the \a PerThread
124 /// template parameter.
125 ///
126 /// Derived classes must instantiate the stack themselves by putting
127 ///
128 /// TF_INSTANTIATE_STACKED(YourStackedClass)
129 ///
130 /// in a single .cpp file.
131 ///
132 /// Note that \a Stacked objects that differ only by \a PerThread will not
133 /// share stacks.
134 ///
135 template <class Derived, bool PerThread = true,
137 class TfStacked {
138  TfStacked(TfStacked const &) = delete;
139  TfStacked& operator=(TfStacked const &) = delete;
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 // PXR_BASE_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
TfStacked()
Pushes this stacked object onto the stack.
Definition: stacked.h:146
static void InitializeStack()
Definition: stacked.h:49
Storage::Stack Stack
Definition: stacked.h:143
GLsizei const GLfloat * value
Definition: glcorearb.h:824
Tf_StackedStorageType< T, PerThread >::Type Type
Definition: stacked.h:112
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
static Derived const * GetStackPrevious()
Definition: stacked.h:169
#define TF_FATAL_ERROR
std::conditional< PerThread, _PerThreadStackStorage, _GlobalStackStorage >::type Type
Definition: stacked.h:98
GLsizeiptr size
Definition: glcorearb.h:664
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1441
Holder Storage
Definition: stacked.h:142
static std::atomic< Type * > value
Definition: stacked.h:113
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
std::vector< T const * > Stack
Definition: stacked.h:58
Tf_StackedStorageType< T, PerThread >::Stack Stack
Definition: stacked.h:111
type
Definition: core.h:1059