/** * @file llsingleton.cpp * @author Brad Kittenbrink * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" #include "llsingleton.h" #include "llerror.h" #include "llerrorcontrol.h" // LLError::is_available() #include "lldependencies.h" #include "llcoro_get_id.h" #include #include #include #include // std::cerr in dire emergency #include #include namespace { void log(LLError::ELevel level, const char* p1, const char* p2, const char* p3, const char* p4); void logdebugs(const char* p1="", const char* p2="", const char* p3="", const char* p4=""); bool oktolog(); } // anonymous namespace // Our master list of all LLSingletons is itself an LLSingleton. We used to // store it in a function-local static, but that could get destroyed before // the last of the LLSingletons -- and ~LLSingletonBase() definitely wants to // remove itself from the master list. Since the whole point of this master // list is to help track inter-LLSingleton dependencies, and since we have // this implicit dependency from every LLSingleton to the master list, make it // an LLSingleton. class LLSingletonBase::MasterList: public LLSingleton { LLSINGLETON_EMPTY_CTOR(MasterList); public: // No need to make this private with accessors; nobody outside this source // file can see it. // This is the master list of all instantiated LLSingletons (save the // MasterList itself) in arbitrary order. You MUST call dep_sort() before // traversing this list. LLSingletonBase::list_t mMaster; // We need to maintain a stack of LLSingletons currently being // initialized, either in the constructor or in initSingleton(). However, // managing that as a stack depends on having a DISTINCT 'initializing' // stack for every C++ stack in the process! And we have a distinct C++ // stack for every running coroutine. It would be interesting and cool to // implement a generic coroutine-local-storage mechanism and use that // here. The trouble is that LLCoros is itself an LLSingleton, so // depending on LLCoros functionality could dig us into infinite // recursion. (Moreover, when we reimplement LLCoros on top of // Boost.Fiber, that library already provides fiber_specific_ptr -- so // it's not worth a great deal of time and energy implementing a generic // equivalent on top of boost::dcoroutine, which is on its way out.) // Instead, use a map of llcoro::id to select the appropriate // coro-specific 'initializing' stack. llcoro::get_id() is carefully // implemented to avoid requiring LLCoros. typedef boost::unordered_map InitializingMap; InitializingMap mInitializing; // non-static method, cf. LLSingletonBase::get_initializing() list_t& get_initializing_() { // map::operator[] has find-or-create semantics, exactly what we need // here. It returns a reference to the selected mapped_type instance. return mInitializing[llcoro::get_id()]; } void cleanup_initializing_() { InitializingMap::iterator found = mInitializing.find(llcoro::get_id()); if (found != mInitializing.end()) { mInitializing.erase(found); } } }; //static LLSingletonBase::list_t& LLSingletonBase::get_master() { return LLSingletonBase::MasterList::instance().mMaster; } void LLSingletonBase::add_master() { // As each new LLSingleton is constructed, add to the master list. get_master().push_back(this); } void LLSingletonBase::remove_master() { // When an LLSingleton is destroyed, remove from master list. // add_master() used to capture the iterator to the newly-added list item // so we could directly erase() it from the master list. Unfortunately // that runs afoul of destruction-dependency order problems. So search the // master list, and remove this item IF FOUND. We have few enough // LLSingletons, and they are so rarely destroyed (once per run), that the // cost of a linear search should not be an issue. get_master().remove(this); } //static LLSingletonBase::list_t& LLSingletonBase::get_initializing() { return LLSingletonBase::MasterList::instance().get_initializing_(); } //static LLSingletonBase::list_t& LLSingletonBase::get_initializing_from(MasterList* master) { return master->get_initializing_(); } LLSingletonBase::~LLSingletonBase() {} void LLSingletonBase::push_initializing(const char* name) { // log BEFORE pushing so logging singletons don't cry circularity log_initializing("Pushing", name); get_initializing().push_back(this); } void LLSingletonBase::pop_initializing() { list_t& list(get_initializing()); if (list.empty()) { logerrs("Underflow in stack of currently-initializing LLSingletons at ", demangle(typeid(*this).name()).c_str(), "::getInstance()"); } // Now we know list.back() exists: capture it LLSingletonBase* back(list.back()); // and pop it list.pop_back(); // The viewer launches an open-ended number of coroutines. While we don't // expect most of them to initialize LLSingleton instances, our present // get_initializing() logic could lead to an open-ended number of map // entries. So every time we pop the stack back to empty, delete the entry // entirely. if (list.empty()) { MasterList::instance().cleanup_initializing_(); } // Now validate the newly-popped LLSingleton. if (back != this) { logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ", demangle(typeid(*this).name()).c_str(), "::getInstance() trying to pop ", demangle(typeid(*back).name()).c_str()); } // log AFTER popping so logging singletons don't cry circularity log_initializing("Popping", typeid(*back).name()); } //static void LLSingletonBase::log_initializing(const char* verb, const char* name) { if (oktolog()) { LL_DEBUGS("LLSingleton") << verb << ' ' << demangle(name) << ';'; list_t& list(get_initializing()); for (list_t::const_reverse_iterator ri(list.rbegin()), rend(list.rend()); ri != rend; ++ri) { LLSingletonBase* sb(*ri); LL_CONT << ' ' << demangle(typeid(*sb).name()); } LL_ENDL; } } void LLSingletonBase::capture_dependency(list_t& initializing, EInitState initState) { // Did this getInstance() call come from another LLSingleton, or from // vanilla application code? Note that although this is a nontrivial // method, the vast majority of its calls arrive here with initializing // empty(). if (! initializing.empty()) { // getInstance() is being called by some other LLSingleton. But -- is // this a circularity? That is, does 'this' already appear in the // initializing stack? // For what it's worth, normally 'initializing' should contain very // few elements. list_t::const_iterator found = std::find(initializing.begin(), initializing.end(), this); if (found != initializing.end()) { list_t::const_iterator it_next = found; it_next++; // Report the circularity. Requiring the coder to dig through the // logic to diagnose exactly how we got here is less than helpful. std::ostringstream out; for ( ; found != initializing.end(); ++found) { // 'found' is an iterator; *found is an LLSingletonBase*; **found // is the actual LLSingletonBase instance. LLSingletonBase* foundp(*found); out << demangle(typeid(*foundp).name()) << " -> "; } // We promise to capture dependencies from both the constructor // and the initSingleton() method, so an LLSingleton's instance // pointer is on the initializing list during both. Now that we've // detected circularity, though, we must distinguish the two. If // the recursive call is from the constructor, we CAN'T honor it: // otherwise we'd be returning a pointer to a partially- // constructed object! But from initSingleton() is okay: that // method exists specifically to support circularity. // Decide which log helper to call. if (initState == CONSTRUCTING) { logerrs("LLSingleton circularity in Constructor: ", out.str().c_str(), demangle(typeid(*this).name()).c_str(), ""); } else if (it_next == initializing.end()) { // Points to self after construction, but during initialization. // Singletons can initialize other classes that depend onto them, // so this is expected. // // Example: LLNotifications singleton initializes default channels. // Channels register themselves with singleton once done. logdebugs("LLSingleton circularity: ", out.str().c_str(), demangle(typeid(*this).name()).c_str(), ""); } else { // Actual circularity with other singleton (or single singleton is used extensively). // Dependency can be unclear. logwarns("LLSingleton circularity: ", out.str().c_str(), demangle(typeid(*this).name()).c_str(), ""); } } else { // Here 'this' is NOT already in the 'initializing' stack. Great! // Record the dependency. // initializing.back() is the LLSingletonBase* currently being // initialized. Store 'this' in its mDepends set. LLSingletonBase* current(initializing.back()); if (current->mDepends.insert(this).second) { // only log the FIRST time we hit this dependency! logdebugs(demangle(typeid(*current).name()).c_str(), " depends on ", demangle(typeid(*this).name()).c_str()); } } } } //static LLSingletonBase::vec_t LLSingletonBase::dep_sort() { // While it would theoretically be possible to maintain a static // SingletonDeps through the life of the program, dynamically adding and // removing LLSingletons as they are created and destroyed, in practice // it's less messy to construct it on demand. The overhead of doing so // should happen basically twice: once for cleanupAll(), once for // deleteAll(). typedef LLDependencies SingletonDeps; SingletonDeps sdeps; list_t& master(get_master()); BOOST_FOREACH(LLSingletonBase* sp, master) { // Build the SingletonDeps structure by adding, for each // LLSingletonBase* sp in the master list, sp itself. It has no // associated value type in our SingletonDeps, hence the 0. We don't // record the LLSingletons it must follow; rather, we record the ones // it must precede. Copy its mDepends to a KeyList to express that. sdeps.add(sp, 0, SingletonDeps::KeyList(), SingletonDeps::KeyList(sp->mDepends.begin(), sp->mDepends.end())); } vec_t ret; ret.reserve(master.size()); // We should be able to effect this with a transform_iterator that // extracts just the first (key) element from each sorted_iterator, then // uses vec_t's range constructor... but frankly this is more // straightforward, as long as we remember the above reserve() call! BOOST_FOREACH(SingletonDeps::sorted_iterator::value_type pair, sdeps.sort()) { ret.push_back(pair.first); } // The master list is not itself pushed onto the master list. Add it as // the very last entry -- it is the LLSingleton on which ALL others // depend! -- so our caller will process it. ret.push_back(MasterList::getInstance()); return ret; } //static void LLSingletonBase::cleanupAll() { // It's essential to traverse these in dependency order. BOOST_FOREACH(LLSingletonBase* sp, dep_sort()) { // Call cleanupSingleton() only if we haven't already done so for this // instance. if (! sp->mCleaned) { sp->mCleaned = true; logdebugs("calling ", demangle(typeid(*sp).name()).c_str(), "::cleanupSingleton()"); try { sp->cleanupSingleton(); } catch (const std::exception& e) { logwarns("Exception in ", demangle(typeid(*sp).name()).c_str(), "::cleanupSingleton(): ", e.what()); } catch (...) { logwarns("Unknown exception in ", demangle(typeid(*sp).name()).c_str(), "::cleanupSingleton()"); } } } } //static void LLSingletonBase::deleteAll() { // It's essential to traverse these in dependency order. BOOST_FOREACH(LLSingletonBase* sp, dep_sort()) { // Capture the class name first: in case of exception, don't count on // being able to extract it later. const std::string name = demangle(typeid(*sp).name()); try { // Call static method through instance function pointer. if (! sp->mDeleteSingleton) { // This Should Not Happen... but carry on. logwarns(name.c_str(), "::mDeleteSingleton not initialized!"); } else { // properly initialized: call it. logdebugs("calling ", name.c_str(), "::deleteSingleton()"); // From this point on, DO NOT DEREFERENCE sp! sp->mDeleteSingleton(); } } catch (const std::exception& e) { logwarns("Exception in ", name.c_str(), "::deleteSingleton(): ", e.what()); } catch (...) { logwarns("Unknown exception in ", name.c_str(), "::deleteSingleton()"); } } } /*---------------------------- Logging helpers -----------------------------*/ namespace { bool oktolog() { // See comments in log() below. return LLError::is_available(); } void log(LLError::ELevel level, const char* p1, const char* p2, const char* p3, const char* p4) { // The is_available() test below ensures that we'll stop logging once // LLError has been cleaned up. If we had a similar portable test for // std::cerr, this would be a good place to use it. // Check LLError::is_available() because some of LLError's infrastructure // is itself an LLSingleton. If that LLSingleton has not yet been // initialized, trying to log will engage LLSingleton machinery... and // around and around we go. if (LLError::is_available()) { LL_VLOGS(level, "LLSingleton") << p1 << p2 << p3 << p4 << LL_ENDL; } else { // Caller may be a test program, or something else whose stderr is // visible to the user. std::cerr << p1 << p2 << p3 << p4 << std::endl; } } void logdebugs(const char* p1, const char* p2, const char* p3, const char* p4) { log(LLError::LEVEL_DEBUG, p1, p2, p3, p4); } } // anonymous namespace //static void LLSingletonBase::logwarns(const char* p1, const char* p2, const char* p3, const char* p4) { log(LLError::LEVEL_WARN, p1, p2, p3, p4); } //static void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4) { log(LLError::LEVEL_ERROR, p1, p2, p3, p4); // The other important side effect of LL_ERRS() is // https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG) LLError::crashAndLoop(std::string()); } std::string LLSingletonBase::demangle(const char* mangled) { return LLError::Log::demangle(mangled); }