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