HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
instantiateSingleton.h
Go to the documentation of this file.
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 /*
8  * This header is not meant to be included in a .h file.
9  * Complain if we see this header twice through.
10  */
11 
12 #ifdef PXR_BASE_TF_INSTANTIATE_SINGLETON_H
13 #error This file should only be included once in any given source (.cpp) file.
14 #endif
15 
16 #define PXR_BASE_TF_INSTANTIATE_SINGLETON_H
17 
18 /// \file tf/instantiateSingleton.h
19 /// \ingroup group_tf_ObjectCreation
20 /// Manage a single instance of an object.
21 
22 #include "pxr/pxr.h"
23 #include "pxr/base/tf/singleton.h"
25 #include "pxr/base/tf/mallocTag.h"
26 #include "pxr/base/arch/demangle.h"
27 
28 #include <memory>
29 #include <thread>
30 
32 
34 {
36  void * const initInstance;
37 };
38 
39 // This GIL-releasing helper is implemented in singleton.cpp. We do it this way
40 // to avoid including the Python headers here.
42 {
43  TF_API
45  TF_API
47 private:
48 #ifdef PXR_PYTHON_SUPPORT_ENABLED
49  std::unique_ptr<class TfPyLock> _pyLock;
50 #endif // PXR_PYTHON_SUPPORT_ENABLED
51 };
52 
53 template <class T>
54 std::atomic<T *> TfSingleton<T>::_instance;
55 
56 template <class T>
57 std::atomic<Tf_SingletonInitState *> TfSingleton<T>::_initState;
58 
59 template <class T>
60 void
62 {
63  // Capture the initializing thread id so that it can call GetInstance()
64  // successfully while other threads wait for full construction to complete.
65  std::unique_ptr<Tf_SingletonInitState> initState(
67  std::this_thread::get_id(), static_cast<void *>(&instance)
68  });
69 
70  if (_instance.load() || _initState.exchange(initState.get()) != nullptr) {
71  TF_FATAL_ERROR("this function may not be called after "
72  "GetInstance() or another SetInstanceConstructed() "
73  "has completed");
74  }
75  initState.release();
76 }
77 
78 template <class T>
79 T *
80 TfSingleton<T>::_CreateOrWaitForInstance(std::atomic<T *> &instance)
81 {
82  static std::atomic<bool> isInitializing;
83 
84  TfAutoMallocTag tag("Tf", "TfSingleton::_CreateOrWaitForInstance",
85  "Create Singleton " + ArchGetDemangled<T>());
86 
87  // Check to see if we're the thread that's currently initializing the
88  // singleton. If so, return the initializing instance. Other threads will
89  // wait for initialization to complete.
90  if (Tf_SingletonInitState *initState = _initState.load()) {
91  if (initState->initThreadId == std::this_thread::get_id()) {
92  TF_AXIOM(initState->initInstance);
93  return static_cast<T *>(initState->initInstance);
94  }
95  // Otherwise fall through and wait for the instance to appear.
96  }
97 
98  // Drop the GIL if we have it, before possibly locking to create the
99  // singleton instance.
100  Tf_SingletonPyGILDropper dropGIL;
101 
102  // Try to take isInitializing false -> true. If we do it, then check to see
103  // if we don't yet have an instance. If we don't, then we get to create it.
104  // Otherwise we just wait until the instance shows up.
105  if (isInitializing.exchange(true) == false) {
106  // Do we not yet have an instance?
107  if (!instance) {
108  // Try to create the instance.
109  T *newInst;
110  try {
111  newInst = new T;
112  }
113  catch (...) {
114  // Ensure we clean up the _initState if it was set by a call to
115  // SetInstanceConstructed() in T's ctor.
116  delete _initState.exchange(nullptr);
117  throw;
118  }
119  T *curInst = nullptr;
120  if (Tf_SingletonInitState *initState = _initState.load()) {
121  TF_AXIOM(initState->initInstance);
122  curInst = static_cast<T *>(initState->initInstance);
123  }
124  if (curInst && (curInst != newInst)) {
125  TF_FATAL_ERROR("race detected setting singleton instance");
126  }
127  TF_AXIOM(instance.exchange(newInst) == nullptr);
128  }
129  isInitializing = false;
130  }
131  else {
132  while (!instance) {
134  }
135  }
136 
137  return instance.load();
138 }
139 
140 template <typename T>
141 void
143 {
144  // Try to swap out a non-null instance for nullptr -- if we do it, we delete
145  // it.
146  T *instance = _instance.load();
147  if (instance && _instance.compare_exchange_strong(instance, nullptr)) {
148  delete instance;
149  delete _initState.exchange(nullptr);
150  }
151 }
152 
153 /// Source file definition that a type is being used as a singleton.
154 ///
155 /// To use a type \c T in conjunction with \c TfSingleton, add
156 /// TF_INSTANTIATE_SINGLETON(T) in one source file (typically the .cpp) file
157 /// for class \c T.
158 ///
159 /// \hideinitializer
160 #define TF_INSTANTIATE_SINGLETON(T) \
161  template class PXR_NS_GLOBAL::TfSingleton<T>
162 
163 
#define TF_API
Definition: api.h:23
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
const std::thread::id initThreadId
#define TF_FATAL_ERROR
GLuint id
Definition: glcorearb.h:655
static void SetInstanceConstructed(T &instance)
#define TF_AXIOM(cond)
void yield() noexcept
Definition: thread.h:94
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
static void DeleteInstance()
TF_API ~Tf_SingletonPyGILDropper()