From 9f962c03bf9080d67a8ea10aa53289c841fea781 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 27 May 2015 16:41:10 -0400 Subject: MAINT-5232: Extract LLInitClass, LLDestroyClass from llui/llui.h to a new llcommon/llinitdestroyclass.h. This mechanism is so general -- but has so many related moving parts -- that (a) it deserves to be in a header file all its own, instead of conflated with llui.h, and (b) it should be in llcommon where anyone can use it. It has no dependencies whatsoever on llui or anything viewer-specific. In this very changeset we changed one #include "llui.h" whose comment admits that it was only dragged in for LLDestroyClass. --- indra/llcommon/llinitdestroyclass.h | 134 ++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 indra/llcommon/llinitdestroyclass.h (limited to 'indra/llcommon/llinitdestroyclass.h') diff --git a/indra/llcommon/llinitdestroyclass.h b/indra/llcommon/llinitdestroyclass.h new file mode 100644 index 0000000000..ca5c3f07de --- /dev/null +++ b/indra/llcommon/llinitdestroyclass.h @@ -0,0 +1,134 @@ +/** + * @file llinitdestroyclass.h + * @author Nat Goodspeed + * @date 2015-05-27 + * @brief LLInitClass / LLDestroyClass mechanism + * + * The LLInitClass template, extracted from llui.h, ensures that control will + * reach a static initClass() method. LLDestroyClass does the same for a + * static destroyClass() method. + * + * The distinguishing characteristics of these templates are: + * + * - All LLInitClass::initClass() methods are triggered by an explicit call + * to LLInitClassList::instance().fireCallbacks(). Presumably this call + * happens sometime after all static objects in the program have been + * initialized. In other words, each LLInitClass::initClass() method + * should be able to make some assumptions about global program state. + * + * - Similarly, LLDestroyClass::destroyClass() methods are triggered by + * LLDestroyClassList::instance().fireCallbacks(). Again, presumably this + * happens at a well-defined moment in the program's shutdown sequence. + * + * - The initClass() calls happen in an unspecified sequence. You may not rely + * on the relative ordering of LLInitClass::initClass() versus another + * LLInitClass::initClass() method. If you need such a guarantee, use + * LLSingleton instead and make the dependency explicit. + * + * - Similarly, LLDestroyClass::destroyClass() may happen either before or + * after LLDestroyClass::destroyClass(). You cannot rely on that order. + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Copyright (c) 2015, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLINITDESTROYCLASS_H) +#define LL_LLINITDESTROYCLASS_H + +#include "llerror.h" +#include "llsingleton.h" +#include +#include +#include + +class LLCallbackRegistry +{ +public: + typedef boost::signals2::signal callback_signal_t; + + void registerCallback(const callback_signal_t::slot_type& slot) + { + mCallbacks.connect(slot); + } + + void fireCallbacks() + { + mCallbacks(); + } + +private: + callback_signal_t mCallbacks; +}; + +class LLInitClassList : + public LLCallbackRegistry, + public LLSingleton +{ + friend class LLSingleton; +private: + LLInitClassList() {} +}; + +class LLDestroyClassList : + public LLCallbackRegistry, + public LLSingleton +{ + friend class LLSingleton; +private: + LLDestroyClassList() {} +}; + +template +class LLRegisterWith +{ +public: + LLRegisterWith(boost::function func) + { + T::instance().registerCallback(func); + } + + // this avoids a MSVC bug where non-referenced static members are "optimized" away + // even if their constructors have side effects + S32 reference() + { + S32 dummy; + dummy = 0; + return dummy; + } +}; + +template +class LLInitClass +{ +public: + LLInitClass() { sRegister.reference(); } + + static LLRegisterWith sRegister; +private: + + static void initClass() + { + LL_ERRS() << "No static initClass() method defined for " << typeid(T).name() << LL_ENDL; + } +}; + +template +class LLDestroyClass +{ +public: + LLDestroyClass() { sRegister.reference(); } + + static LLRegisterWith sRegister; +private: + + static void destroyClass() + { + LL_ERRS() << "No static destroyClass() method defined for " << typeid(T).name() << LL_ENDL; + } +}; + +template LLRegisterWith LLInitClass::sRegister(&T::initClass); +template LLRegisterWith LLDestroyClass::sRegister(&T::destroyClass); + +#endif /* ! defined(LL_LLINITDESTROYCLASS_H) */ -- cgit v1.2.3 From d15ec90bc958a810c7129a66b4924b91267f4fa1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 28 May 2015 11:57:46 -0400 Subject: MAINT-5232: Provide better commentation for llinitdestroyclass.h. --- indra/llcommon/llinitdestroyclass.h | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'indra/llcommon/llinitdestroyclass.h') diff --git a/indra/llcommon/llinitdestroyclass.h b/indra/llcommon/llinitdestroyclass.h index ca5c3f07de..49bcefc33d 100644 --- a/indra/llcommon/llinitdestroyclass.h +++ b/indra/llcommon/llinitdestroyclass.h @@ -42,6 +42,11 @@ #include #include +/** + * LLCallbackRegistry is an implementation detail base class for + * LLInitClassList and LLDestroyClassList. It's a very thin wrapper around a + * Boost.Signals2 signal object. + */ class LLCallbackRegistry { public: @@ -61,6 +66,13 @@ private: callback_signal_t mCallbacks; }; +/** + * LLInitClassList is the LLCallbackRegistry for LLInitClass. It stores the + * registered initClass() methods. It must be an LLSingleton because + * LLInitClass registers its initClass() method at static construction time + * (before main()), requiring LLInitClassList to be fully constructed on + * demand regardless of module initialization order. + */ class LLInitClassList : public LLCallbackRegistry, public LLSingleton @@ -70,6 +82,13 @@ private: LLInitClassList() {} }; +/** + * LLDestroyClassList is the LLCallbackRegistry for LLDestroyClass. It stores + * the registered destroyClass() methods. It must be an LLSingleton because + * LLDestroyClass registers its destroyClass() method at static construction + * time (before main()), requiring LLDestroyClassList to be fully constructed + * on demand regardless of module initialization order. + */ class LLDestroyClassList : public LLCallbackRegistry, public LLSingleton @@ -79,6 +98,12 @@ private: LLDestroyClassList() {} }; +/** + * LLRegisterWith is an implementation detail for LLInitClass and + * LLDestroyClass. It is intended to be used as a static class member whose + * constructor registers the specified callback with the LLMumbleClassList + * singleton registry specified as the template argument. + */ template class LLRegisterWith { @@ -98,37 +123,68 @@ public: } }; +/** + * Derive MyClass from LLInitClass (the Curiously Recurring Template + * Pattern) to ensure that the static method MyClass::initClass() will be + * called (along with all other LLInitClass subclass initClass() methods) + * when someone calls LLInitClassList::instance().fireCallbacks(). This gives + * the application specific control over the timing of all such + * initializations, without having to insert calls for every such class into + * generic application code. + */ template class LLInitClass { public: LLInitClass() { sRegister.reference(); } + // When this static member is initialized, the subclass initClass() method + // is registered on LLInitClassList. See sRegister definition below. static LLRegisterWith sRegister; private: + // Provide a default initClass() method in case subclass misspells (or + // omits) initClass(). This turns a potential build error into a fatal + // runtime error. static void initClass() { LL_ERRS() << "No static initClass() method defined for " << typeid(T).name() << LL_ENDL; } }; +/** + * Derive MyClass from LLDestroyClass (the Curiously Recurring + * Template Pattern) to ensure that the static method MyClass::destroyClass() + * will be called (along with other LLDestroyClass subclass destroyClass() + * methods) when someone calls LLDestroyClassList::instance().fireCallbacks(). + * This gives the application specific control over the timing of all such + * cleanup calls, without having to insert calls for every such class into + * generic application code. + */ template class LLDestroyClass { public: LLDestroyClass() { sRegister.reference(); } + // When this static member is initialized, the subclass destroyClass() + // method is registered on LLInitClassList. See sRegister definition + // below. static LLRegisterWith sRegister; private: + // Provide a default destroyClass() method in case subclass misspells (or + // omits) destroyClass(). This turns a potential build error into a fatal + // runtime error. static void destroyClass() { LL_ERRS() << "No static destroyClass() method defined for " << typeid(T).name() << LL_ENDL; } }; +// Here's where LLInitClass specifies the subclass initClass() method. template LLRegisterWith LLInitClass::sRegister(&T::initClass); +// Here's where LLDestroyClass specifies the subclass destroyClass() method. template LLRegisterWith LLDestroyClass::sRegister(&T::destroyClass); #endif /* ! defined(LL_LLINITDESTROYCLASS_H) */ -- cgit v1.2.3 From dcdccb3ef1b42b36042fa6f3fe61849124e1728f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 30 Aug 2016 16:28:21 -0400 Subject: MAINT-5232: Move "llerror.h" out of llcleanup.h, llinitdestroyclass.h Introduce corresponding llcleanup.cpp, llinitdestroyclass.cpp modules to contain code that performs logging calls. Track class::method names for LLInitClass and LLDestroyClass subclasses, and log them when called. The order in which these calls occur could be relevant to bugs, and could surface the need to convert to LLSingleton dependencies. --- indra/llcommon/llinitdestroyclass.h | 59 +++++++++++++++---------------------- 1 file changed, 24 insertions(+), 35 deletions(-) (limited to 'indra/llcommon/llinitdestroyclass.h') diff --git a/indra/llcommon/llinitdestroyclass.h b/indra/llcommon/llinitdestroyclass.h index 49bcefc33d..9c66211475 100644 --- a/indra/llcommon/llinitdestroyclass.h +++ b/indra/llcommon/llinitdestroyclass.h @@ -36,34 +36,35 @@ #if ! defined(LL_LLINITDESTROYCLASS_H) #define LL_LLINITDESTROYCLASS_H -#include "llerror.h" #include "llsingleton.h" #include -#include #include +#include +#include // std::pair /** * LLCallbackRegistry is an implementation detail base class for - * LLInitClassList and LLDestroyClassList. It's a very thin wrapper around a - * Boost.Signals2 signal object. + * LLInitClassList and LLDestroyClassList. It accumulates the initClass() or + * destroyClass() callbacks for registered classes. */ class LLCallbackRegistry { public: - typedef boost::signals2::signal callback_signal_t; - - void registerCallback(const callback_signal_t::slot_type& slot) - { - mCallbacks.connect(slot); - } + typedef boost::function func_t; - void fireCallbacks() + void registerCallback(const std::string& name, const func_t& func) { - mCallbacks(); + mCallbacks.push_back(FuncList::value_type(name, func)); } + void fireCallbacks() const; + private: - callback_signal_t mCallbacks; + // Arguably this should be a boost::signals2::signal, which is, after all, + // a sequence of callables. We manage it by hand so we can log a name for + // each registered function we call. + typedef std::vector< std::pair > FuncList; + FuncList mCallbacks; }; /** @@ -108,9 +109,9 @@ template class LLRegisterWith { public: - LLRegisterWith(boost::function func) + LLRegisterWith(const std::string& name, const LLCallbackRegistry::func_t& func) { - T::instance().registerCallback(func); + T::instance().registerCallback(name, func); } // this avoids a MSVC bug where non-referenced static members are "optimized" away @@ -141,15 +142,6 @@ public: // When this static member is initialized, the subclass initClass() method // is registered on LLInitClassList. See sRegister definition below. static LLRegisterWith sRegister; -private: - - // Provide a default initClass() method in case subclass misspells (or - // omits) initClass(). This turns a potential build error into a fatal - // runtime error. - static void initClass() - { - LL_ERRS() << "No static initClass() method defined for " << typeid(T).name() << LL_ENDL; - } }; /** @@ -171,20 +163,17 @@ public: // method is registered on LLInitClassList. See sRegister definition // below. static LLRegisterWith sRegister; -private: - - // Provide a default destroyClass() method in case subclass misspells (or - // omits) destroyClass(). This turns a potential build error into a fatal - // runtime error. - static void destroyClass() - { - LL_ERRS() << "No static destroyClass() method defined for " << typeid(T).name() << LL_ENDL; - } }; // Here's where LLInitClass specifies the subclass initClass() method. -template LLRegisterWith LLInitClass::sRegister(&T::initClass); +template +LLRegisterWith +LLInitClass::sRegister(std::string(typeid(T).name()) + "::initClass", + &T::initClass); // Here's where LLDestroyClass specifies the subclass destroyClass() method. -template LLRegisterWith LLDestroyClass::sRegister(&T::destroyClass); +template +LLRegisterWith +LLDestroyClass::sRegister(std::string(typeid(T).name()) + "::destroyClass", + &T::destroyClass); #endif /* ! defined(LL_LLINITDESTROYCLASS_H) */ -- cgit v1.2.3