Loading...
Searching...
No Matches
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 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"
31
32#include <tbb/enumerable_thread_specific.h>
33
34#include <atomic>
35#include <type_traits>
36#include <vector>
37
38PXR_NAMESPACE_OPEN_SCOPE
39
47public:
48 template <class Derived>
49 static void InitializeStack() {
50 return Derived::_InitializeStack();
51 }
52};
53
54// Detail for TfStacked storage types.
55template <typename T, bool PerThread>
56class Tf_StackedStorageType {
57public:
58 typedef std::vector<T const *> Stack;
59
60private:
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
94public:
95 /* Choose the stack storage type based on thea PerThread argument. */
96 typedef typename std::conditional<
97 PerThread, _PerThreadStackStorage, _GlobalStackStorage
98 >::type Type;
99};
100
101// Detail for TfStacked storage. This exists so we can specialize it
102// with exported storage.
103template <typename T, bool PerThread>
104struct Tf_ExportedStackedStorage { };
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.
109template <typename T, bool PerThread>
110struct Tf_StackedStorage {
111 typedef typename Tf_StackedStorageType<T, PerThread>::Stack Stack;
112 typedef typename Tf_StackedStorageType<T, PerThread>::Type Type;
113 static std::atomic<Type*> value;
114};
115
135template <class Derived, bool PerThread = true,
136 class Holder = Tf_StackedStorage<Derived, PerThread>>
138 TfStacked(TfStacked const &) = delete;
139 TfStacked& operator=(TfStacked const &) = delete;
140 typedef typename Holder::Type _StorageType;
141public:
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
185private:
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) \
241class Derived; \
242template <> \
243struct 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}; \
248class Derived : \
249 public TfStacked<Derived, IsPerThread, \
250 Tf_ExportedStackedStorage<Derived, IsPerThread>>
251
252PXR_NAMESPACE_CLOSE_SCOPE
253
254#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:46
A TfStacked is used where a class needs to keep a stack of the objects currently in existence.
Definition: stacked.h:137
~TfStacked()
Pops this stacked object from the stack.
Definition: stacked.h:151
static bool IsStackTop(Derived const *p)
Returns true if p is the top of the stack.
Definition: stacked.h:181
static Stack const & GetStack()
Returns a const reference to the entire stack.
Definition: stacked.h:176
static Derived const * GetStackTop()
Returns the top of the stack.
Definition: stacked.h:159
static Derived const * GetStackPrevious()
Returns the element under the top of the stack.
Definition: stacked.h:169
TfStacked()
Pushes this stacked object onto the stack.
Definition: stacked.h:146
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:108