All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
stacked.h
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 <boost/mpl/if.hpp>
33 #include <boost/noncopyable.hpp>
34 
35 #include <tbb/enumerable_thread_specific.h>
36 
37 #include <atomic>
38 #include <vector>
39 
40 PXR_NAMESPACE_OPEN_SCOPE
41 
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>
58 class Tf_StackedStorageType {
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 boost::mpl::if_c<
99  PerThread, _PerThreadStackStorage, _GlobalStackStorage
100  >::type Type;
101 };
102 
103 // Detail for TfStacked storage. This exists so we can specialize it
104 // with exported storage.
105 template <typename T, bool PerThread>
106 struct Tf_ExportedStackedStorage { };
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>
112 struct Tf_StackedStorage {
113  typedef typename Tf_StackedStorageType<T, PerThread>::Stack Stack;
114  typedef typename Tf_StackedStorageType<T, PerThread>::Type Type;
115  static std::atomic<Type*> value;
116 };
117 
137 template <class Derived, bool PerThread = true,
138  class Holder = Tf_StackedStorage<Derived, PerThread>>
139 class TfStacked : boost::noncopyable {
140  typedef typename Holder::Type _StorageType;
141 public:
142  typedef Holder Storage;
143  typedef typename Storage::Stack Stack;
144 
147  _Push(_AsDerived());
148  }
149 
152  _Pop(_AsDerived());
153  }
154 
159  static Derived const *GetStackTop() {
160  Stack const &stack = GetStack();
161  return stack.empty() ? 0 : stack.back();
162  }
163 
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 
176  static Stack const &GetStack() {
177  return _GetStack();
178  }
179 
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 
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 
252 PXR_NAMESPACE_CLOSE_SCOPE
253 
254 #endif // TF_STACKED_H
A TfStacked is used where a class needs to keep a stack of the objects currently in existence...
Definition: stacked.h:139
TfStacked()
Pushes this stacked object onto the stack.
Definition: stacked.h:146
static Derived const * GetStackTop()
Returns the top of the stack.
Definition: stacked.h:159
#define TF_FATAL_ERROR(fmt, args)
Issue a fatal error and end the program.
Definition: diagnostic.h:111
Classes that derive TfStacked may befriend TfStackedAccess if they wish to customize aspects TfStacke...
Definition: stacked.h:48
static Derived const * GetStackPrevious()
Returns the element under the top of the stack.
Definition: stacked.h:169
~TfStacked()
Pops this stacked object from the stack.
Definition: stacked.h:151
static Stack const & GetStack()
Returns a const reference to the entire stack.
Definition: stacked.h:176
static bool IsStackTop(Derived const *p)
Returns true if p is the top of the stack.
Definition: stacked.h:181