1 /* 2 * Copyright (C) 2005-2018 Team Kodi 3 * This file is part of Kodi - https://kodi.tv 4 * 5 * SPDX-License-Identifier: GPL-2.0-or-later 6 * See LICENSES/README.md for more information. 7 */ 8 9 #pragma once 10 11 #include <memory> 12 13 /** 14 * This file contains the pattern for moving "globals" from the BSS Segment to the heap. 15 * A note on usage of this pattern for globals replacement: 16 * 17 * This pattern uses a singleton pattern and some compiler/C preprocessor sugar to allow 18 * "global" variables to be lazy instantiated and initialized and moved from the BSS segment 19 * to the heap (that is, they are instantiated on the heap when they are first used rather 20 * than relying on the startup code to initialize the BSS segment). This eliminates the 21 * problem associated with global variable dependencies across compilation units. 22 * 23 * Reference counting from the BSS segment is used to destruct these globals at the time the 24 * last compilation unit that knows about it is finalized by the post-main shutdown. The book 25 * keeping is done by smuggling a smart pointer into every file that references a particular 26 * "global class" through the use of a 'static' declaration of an instance of that smart 27 * pointer in the header file of the global class (did you ever think you'd see a file scope 28 * 'static' variable in a header file - on purpose?) 29 * 30 * There are two different ways to use this pattern when replacing global variables. 31 * The selection of which one to use depends on whether or not there is a possibility 32 * that the code in the .cpp file for the global can be executed from a static method 33 * somewhere. This may take some explanation. 34 * 35 * The (at least) two ways to do this: 36 * 37 * 1) You can use the reference object std::shared_ptr to access the global variable. 38 * 39 * This would be the preferred means since it is (very slightly) more efficient than 40 * the alternative. To use this pattern you replace standard static references to 41 * the global with access through the reference. If you use the C preprocessor to 42 * do this for you can put the following code in the header file where the global's 43 * class is declared: 44 * 45 * static std::shared_ptr<GlobalVariableClass> g_globalVariableRef(xbmcutil::GlobalsSingleton<GlobalVariableClass>::getInstance()); 46 * #define g_globalVariable (*(g_globalVariableRef.get())) 47 * 48 * Note what this does. In every file that includes this header there will be a *static* 49 * instance of the std::shared_ptr<GlobalVariableClass> smart pointer. This effectively 50 * reference counts the singleton from every compilation unit (ie, object code file that 51 * results from a compilation of a .c/.cpp file) that references this global directly. 52 * 53 * There is a problem with this, however. Keep in mind that the instance of the smart pointer 54 * (being in the BSS segment of the compilation unit) is ITSELF an object that depends on 55 * the BSS segment initialization in order to be initialized with an instance from the 56 * singleton. That means, depending on the code structure, it is possible to get into a 57 * circumstance where the above #define could be exercised PRIOR TO the setting of the 58 * value of the smart pointer. 59 * 60 * Some reflection on this should lead you to the conclusion that the only way for this to 61 * happen is if access to this global can take place through a static/global method, directly 62 * or indirectly (ie, the static/global method can call another method that uses the 63 * reference), where that static is called from initialization code exercised prior to 64 * the start of 'main.' 65 * 66 * Because of the "indirectly" in the above statement, this situation can be difficult to 67 * determine beforehand. 68 * 69 * 2) Alternatively, when you KNOW that the global variable can suffer from the above described 70 * problem, you can restrict all access to the variable to the singleton by changing 71 * the #define to: 72 * 73 * #define g_globalVariable (*(xbmcutil::Singleton<GlobalVariableClass>::getInstance())) 74 * 75 * A few things to note about this. First, this separates the reference counting aspect 76 * from the access aspect of this solution. The smart pointers are no longer used for 77 * access, only for reference counting. Secondly, all access is through the singleton directly 78 * so there is no reliance on the state of the BSS segment for the code to operate 79 * correctly. 80 * 81 * This solution is required for g_Windowing because it's accessed (both directly and 82 * indirectly) from the static methods of CLog which are called repeatedly from 83 * code exercised during the initialization of the BSS segment. 84 */ 85 86 namespace xbmcutil 87 { 88 /** 89 * This class is an implementation detail of the macros defined below and 90 * is NOT meant to be used as a general purpose utility. IOW, DO NOT USE THIS 91 * CLASS to support a general singleton design pattern, it's specialized 92 * for solving the initialization/finalization order/dependency problem 93 * with global variables and should only be used via the macros below. 94 * 95 * Currently THIS IS NOT THREAD SAFE! Why not just add a lock you ask? 96 * Because this singleton is used to initialize global variables and 97 * there is an issue with having the lock used prior to its 98 * initialization. No matter what, if this class is used as a replacement 99 * for global variables there's going to be a race condition if it's used 100 * anywhere else. So currently this is the only prescribed use. 101 * 102 * Therefore this hack depends on the fact that compilation unit global/static 103 * initialization is done in a single thread. 104 */ 105 template <class T> class GlobalsSingleton 106 { 107 /** 108 * This thing just deletes the shared_ptr when the 'instance' 109 * goes out of scope (when the bss segment of the compilation unit 110 * that 'instance' is sitting in is deinitialized). See the comment 111 * on 'instance' for more information. 112 */ 113 template <class K> class Deleter 114 { 115 public: 116 K* guarded; ~Deleter()117 ~Deleter() { if (guarded) delete guarded; } 118 }; 119 120 /** 121 * Is it possible that getInstance can be called prior to the shared_ptr 'instance' 122 * being initialized as a global? If so, then the shared_ptr constructor would 123 * effectively 'reset' the shared pointer after it had been set by the prior 124 * getInstance call, and a second instance would be created. We really don't 125 * want this to happen so 'instance' is a pointer to a smart pointer so that 126 * we can deterministically handle its construction. It is guarded by the 127 * Deleter class above so that when the bss segment that this static is 128 * sitting in is deinitialized, the shared_ptr pointer will be cleaned up. 129 */ 130 static Deleter<std::shared_ptr<T> > instance; 131 132 /** 133 * See 'getQuick' below. 134 */ 135 static T* quick; 136 public: 137 138 /** 139 * Retrieve an instance of the singleton using a shared pointer for 140 * reference counting. 141 */ getInstance()142 inline static std::shared_ptr<T> getInstance() 143 { 144 if (!instance.guarded) 145 { 146 if (!quick) 147 quick = new T; 148 instance.guarded = new std::shared_ptr<T>(quick); 149 } 150 return *(instance.guarded); 151 } 152 153 /** 154 * This is for quick access when using form (2) of the pattern. Before 'mdd' points 155 * it out, this might be a case of 'solving problems we don't have' but this access 156 * is used frequently within the event loop so any help here should benefit the 157 * overall performance and there is nothing complicated or tricky here and not 158 * a lot of code to maintain. 159 */ getQuick()160 inline static T* getQuick() 161 { 162 if (!quick) 163 quick = new T; 164 165 return quick; 166 } 167 168 }; 169 170 template <class T> typename GlobalsSingleton<T>::template Deleter<std::shared_ptr<T> > GlobalsSingleton<T>::instance; 171 template <class T> T* GlobalsSingleton<T>::quick; 172 173 /** 174 * This is another bit of hackery that will act as a flag for 175 * whether or not a global/static has been initialized yet. An instance 176 * should be placed in the cpp file after the static/global it's meant to 177 * monitor. 178 */ InitFlag(bool & flag)179 class InitFlag { public: explicit InitFlag(bool& flag) { flag = true; } }; 180 } 181 182 /** 183 * For pattern (2) above, you can use the following macro. This pattern is safe to 184 * use in all cases but may be very slightly less efficient. 185 * 186 * Also, you must also use a #define to replace the actual global variable since 187 * there's no way to use a macro to add a #define. An example would be: 188 * 189 * XBMC_GLOBAL_REF(CWinSystemWin32DX, g_Windowing); 190 * #define g_Windowing XBMC_GLOBAL_USE(CWinSystemWin32DX) 191 * 192 */ 193 #define XBMC_GLOBAL_REF(classname,g_variable) \ 194 static std::shared_ptr<classname> g_variable##Ref(xbmcutil::GlobalsSingleton<classname>::getInstance()) 195 196 /** 197 * This declares the actual use of the variable. It needs to be used in another #define 198 * of the form: 199 * 200 * #define g_variable XBMC_GLOBAL_USE(classname) 201 */ 202 #define XBMC_GLOBAL_USE(classname) (*(xbmcutil::GlobalsSingleton<classname>::getQuick())) 203