All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
stacked.h
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"
14
15#include <tbb/enumerable_thread_specific.h>
16
17#include <atomic>
18#include <type_traits>
19#include <vector>
20
21PXR_NAMESPACE_OPEN_SCOPE
22
30public:
31 template <class Derived>
32 static void InitializeStack() {
33 return Derived::_InitializeStack();
34 }
35};
36
37// Detail for TfStacked storage types.
38template <typename T, bool PerThread>
39class Tf_StackedStorageType {
40public:
41 typedef std::vector<T const *> Stack;
42
43private:
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
77public:
78 /* Choose the stack storage type based on thea PerThread argument. */
79 typedef typename std::conditional<
80 PerThread, _PerThreadStackStorage, _GlobalStackStorage
81 >::type Type;
82};
83
84// Detail for TfStacked storage. This exists so we can specialize it
85// with exported storage.
86template <typename T, bool PerThread>
87struct Tf_ExportedStackedStorage { };
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.
92template <typename T, bool PerThread>
93struct Tf_StackedStorage {
94 typedef typename Tf_StackedStorageType<T, PerThread>::Stack Stack;
95 typedef typename Tf_StackedStorageType<T, PerThread>::Type Type;
96 static std::atomic<Type*> value;
97};
98
118template <class Derived, bool PerThread = true,
119 class Holder = Tf_StackedStorage<Derived, PerThread>>
121 TfStacked(TfStacked const &) = delete;
122 TfStacked& operator=(TfStacked const &) = delete;
123 typedef typename Holder::Type _StorageType;
124public:
125 typedef Holder Storage;
126 typedef typename Storage::Stack Stack;
127
130 _Push(_AsDerived());
131 }
132
135 _Pop(_AsDerived());
136 }
137
142 static Derived const *GetStackTop() {
143 Stack const &stack = GetStack();
144 return stack.empty() ? 0 : stack.back();
145 }
146
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
159 static Stack const &GetStack() {
160 return _GetStack();
161 }
162
164 static bool IsStackTop(Derived const *p) {
165 return GetStackTop() == p;
166 }
167
168private:
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
223#define TF_DEFINE_STACKED(Derived, IsPerThread, eiAPI) \
224class Derived; \
225template <> \
226struct 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}; \
231class Derived : \
232 public TfStacked<Derived, IsPerThread, \
233 Tf_ExportedStackedStorage<Derived, IsPerThread>>
234
235PXR_NAMESPACE_CLOSE_SCOPE
236
237#endif // PXR_BASE_TF_STACKED_H
Low-level utilities for informing users of various internal and external diagnostic conditions.
Classes that derive TfStacked may befriend TfStackedAccess if they wish to customize aspects TfStacke...
Definition: stacked.h:29
A TfStacked is used where a class needs to keep a stack of the objects currently in existence.
Definition: stacked.h:120
~TfStacked()
Pops this stacked object from the stack.
Definition: stacked.h:134
static bool IsStackTop(Derived const *p)
Returns true if p is the top of the stack.
Definition: stacked.h:164
static Stack const & GetStack()
Returns a const reference to the entire stack.
Definition: stacked.h:159
static Derived const * GetStackTop()
Returns the top of the stack.
Definition: stacked.h:142
static Derived const * GetStackPrevious()
Returns the element under the top of the stack.
Definition: stacked.h:152
TfStacked()
Pushes this stacked object onto the stack.
Definition: stacked.h:129
Demangle C++ typenames generated by the typeid() facility.
#define TF_FATAL_ERROR(fmt, args)
Issue a fatal error and end the program.
Definition: diagnostic.h:91