Kodi Documentation 22.0
Kodi is an open source media player and entertainment hub.
|
Namespaces | |
namespace | test |
Classes | |
class | GlobalsSingleton |
class | InitFlag |
This file contains the pattern for moving "globals" from the BSS Segment to the heap. A note on usage of this pattern for globals replacement:
This pattern uses a singleton pattern and some compiler/C preprocessor sugar to allow "global" variables to be lazy instantiated and initialized and moved from the BSS segment to the heap (that is, they are instantiated on the heap when they are first used rather than relying on the startup code to initialize the BSS segment). This eliminates the problem associated with global variable dependencies across compilation units.
Reference counting from the BSS segment is used to destruct these globals at the time the last compilation unit that knows about it is finalized by the post-main shutdown. The book keeping is done by smuggling a smart pointer into every file that references a particular "global class" through the use of a 'static' declaration of an instance of that smart pointer in the header file of the global class (did you ever think you'd see a file scope 'static' variable in a header file - on purpose?)
There are two different ways to use this pattern when replacing global variables. The selection of which one to use depends on whether or not there is a possibility that the code in the .cpp file for the global can be executed from a static method somewhere. This may take some explanation.
The (at least) two ways to do this:
1) You can use the reference object std::shared_ptr to access the global variable.
This would be the preferred means since it is (very slightly) more efficient than the alternative. To use this pattern you replace standard static references to the global with access through the reference. If you use the C preprocessor to do this for you can put the following code in the header file where the global's class is declared:
static std::shared_ptr<GlobalVariableClass> g_globalVariableRef(xbmcutil::GlobalsSingleton<GlobalVariableClass>::getInstance()); #define g_globalVariable (*(g_globalVariableRef.get()))
Note what this does. In every file that includes this header there will be a static instance of the std::shared_ptr<GlobalVariableClass> smart pointer. This effectively reference counts the singleton from every compilation unit (ie, object code file that results from a compilation of a .c/.cpp file) that references this global directly.
There is a problem with this, however. Keep in mind that the instance of the smart pointer (being in the BSS segment of the compilation unit) is ITSELF an object that depends on the BSS segment initialization in order to be initialized with an instance from the singleton. That means, depending on the code structure, it is possible to get into a circumstance where the above #define could be exercised PRIOR TO the setting of the value of the smart pointer.
Some reflection on this should lead you to the conclusion that the only way for this to happen is if access to this global can take place through a static/global method, directly or indirectly (ie, the static/global method can call another method that uses the reference), where that static is called from initialization code exercised prior to the start of 'main.'
Because of the "indirectly" in the above statement, this situation can be difficult to determine beforehand.
2) Alternatively, when you KNOW that the global variable can suffer from the above described problem, you can restrict all access to the variable to the singleton by changing the #define to:
#define g_globalVariable (*(xbmcutil::Singleton<GlobalVariableClass>::getInstance()))
A few things to note about this. First, this separates the reference counting aspect from the access aspect of this solution. The smart pointers are no longer used for access, only for reference counting. Secondly, all access is through the singleton directly so there is no reliance on the state of the BSS segment for the code to operate correctly.
This solution is required for g_Windowing because it's accessed (both directly and indirectly) from the static methods of CLog which are called repeatedly from code exercised during the initialization of the BSS segment.