/** 
 * @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"
#include "lldependencies.h"
#include "llexception.h"
#include "llcoros.h"
#include "llmutex.h"
#include <algorithm>
#include <iostream>                 // std::cerr in dire emergency
#include <sstream>
#include <stdexcept>

namespace {
void log(ALLog::ELevel level,
         const char* p1, const char* p2, const char* p3, const char* p4);
} // 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 final :
    public LLSingleton<LLSingletonBase::MasterList>
{
private:
    LLSINGLETON_EMPTY_CTOR(MasterList);

    // Independently of the LLSingleton locks governing construction,
    // destruction and other state changes of the MasterList instance itself,
    // we must also defend each of the data structures owned by the
    // MasterList.
    // This must be a recursive_mutex because, while the lock is held for
    // manipulating some data in the master list, we must also check whether
    // it's safe to log -- which involves querying a different LLSingleton --
    // which requires accessing the master list.
    typedef LLMutex mutex_t;
    typedef LLMutexLock lock_t;

    mutex_t mMutex;

public:
    // Instantiate this to both obtain a reference to MasterList::instance()
    // and lock its mutex for the lifespan of this Lock instance.
    class Lock
    {
    public:
        Lock():
            mMasterList(MasterList::instance()),
            mLock(&mMasterList.mMutex)
        {}
        Lock(const Lock&) = delete;
        Lock& operator=(const Lock&) = delete;
        MasterList& get() const { return mMasterList; }
        operator MasterList&() const { return get(); }

    protected:
        MasterList& mMasterList;
        MasterList::lock_t mLock;
    };

private:
    // 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.
    list_t mMaster;

public:
    // Instantiate this to obtain a reference to MasterList::mMaster and to
    // hold the MasterList lock for the lifespan of this LockedMaster
    // instance.
    struct LockedMaster: public Lock
    {
        list_t& get() const { return mMasterList.mMaster; }
        operator list_t&() const { return get(); }
    };

private:
    // 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. Therefore this stack must be based
    // on a coroutine-local pointer.
    // This local_ptr isn't static because it's a member of an LLSingleton.
    LLCoros::local_ptr<list_t> mInitializing;

public:
    // Instantiate this to obtain a reference to the coroutine-specific
    // initializing list and to hold the MasterList lock for the lifespan of
    // this LockedInitializing instance.
    struct LockedInitializing: public Lock
    {
    public:
        LockedInitializing():
            // only do the lookup once, cache the result
            // note that the lock is already locked during this lookup
            mList(&mMasterList.get_initializing_())
        {}
        list_t& get() const
        {
            if (! mList)
            {
                LLTHROW(LLException("Trying to use LockedInitializing "
                                    "after cleanup_initializing()"));
            }
            return *mList;
        }
        operator list_t&() const { return get(); }
        void log(const char* verb, const char* name);
        void cleanup_initializing()
        {
            mMasterList.cleanup_initializing_();
            mList = nullptr;
        }

    private:
        // Store pointer since cleanup_initializing() must clear it.
        list_t* mList;
    };

private:
    list_t& get_initializing_()
    {
        LLSingletonBase::list_t* current = mInitializing.get();
        if (! current)
        {
            // If the running coroutine doesn't already have an initializing
            // stack, allocate a new one and save it for future reference.
            current = new LLSingletonBase::list_t();
            mInitializing.reset(current);
        }
        return *current;
    }

    // By the time mInitializing is destroyed, its value for every coroutine
    // except the running one must have been reset() to nullptr. So every time
    // we pop the list to empty, reset() the running coroutine's local_ptr.
    void cleanup_initializing_()
    {
        mInitializing.reset(nullptr);
    }
};

void LLSingletonBase::add_master()
{
    // As each new LLSingleton is constructed, add to the master list.
    // This temporary LockedMaster should suffice to hold the MasterList lock
    // during the push_back() call.
    MasterList::LockedMaster().get().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.
    // This temporary LockedMaster should suffice to hold the MasterList lock
    // during the remove() call.
    MasterList::LockedMaster().get().remove(this);
}

//static
LLSingletonBase::list_t::size_type LLSingletonBase::get_initializing_size()
{
    return MasterList::LockedInitializing().get().size();
}

void LLSingletonBase::push_initializing(const char* name)
{
    MasterList::LockedInitializing locked_list;
    // log BEFORE pushing so logging singletons don't cry circularity
    locked_list.log("Pushing", name);
    locked_list.get().push_back(this);
}

void LLSingletonBase::pop_initializing()
{
    // Lock the MasterList for the duration of this call
    MasterList::LockedInitializing locked_list;
    list_t& list(locked_list.get());

    if (list.empty())
    {
        logerrs("Underflow in stack of currently-initializing LLSingletons at ",
                classname(this).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())
    {
        locked_list.cleanup_initializing();
    }

    // Now validate the newly-popped LLSingleton.
    if (back != this)
    {
        logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ",
                classname(this).c_str(), "::getInstance() trying to pop ",
                classname(back).c_str());
    }

    // log AFTER popping so logging singletons don't cry circularity
    locked_list.log("Popping", typeid(*back).name());
}

void LLSingletonBase::reset_initializing(list_t::size_type size)
{
    // called for cleanup in case the LLSingleton subclass constructor throws
    // an exception

    // The tricky thing about this, the reason we have a separate method
    // instead of just calling pop_initializing(), is (hopefully remote)
    // possibility that the exception happened *before* the
    // push_initializing() call in LLSingletonBase's constructor. So only
    // remove the stack top if in fact we've pushed something more than the
    // previous size.
    MasterList::LockedInitializing locked_list;
    list_t& list(locked_list.get());

    while (list.size() > size)
    {
        list.pop_back();
    }

    // as in pop_initializing()
    if (list.empty())
    {
        locked_list.cleanup_initializing();
    }
}

void LLSingletonBase::MasterList::LockedInitializing::log(const char* verb, const char* name)
{
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
        std::ostringstream ostream;
        if (mList)
        {
            for (list_t::const_reverse_iterator ri(mList->rbegin()), rend(mList->rend());
                 ri != rend; ++ri)
            {
                LLSingletonBase* sb(*ri);
                ostream << ' ' << classname(sb);
            }
        }
        ALOG_DEBUG(FMT_COMPILE("{:s} {:s};{}"), verb, demangle(name), ostream.str());
#endif
}

void LLSingletonBase::capture_dependency()
{
    MasterList::LockedInitializing locked_list;
    list_t& initializing(locked_list.get());
    // 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 << classname(foundp) << " -> ";
            }
            // Decide which log helper to call.
            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(),
                    classname(this).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(),
                    classname(this).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(classname(current).c_str(),
                          " depends on ", classname(this).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 once: for deleteAll().
    typedef LLDependencies<LLSingletonBase*> SingletonDeps;
    SingletonDeps sdeps;
    // Lock while traversing the master list 
    MasterList::LockedMaster master;
    for (LLSingletonBase* sp : master.get())
    {
        // 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.get().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!
    for (const 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(&master.Lock::get());
    return ret;
}

void LLSingletonBase::cleanup_()
{
    logdebugs("calling ", classname(this).c_str(), "::cleanupSingleton()");
    try
    {
        cleanupSingleton();
    }
    catch (...)
    {
        LOG_UNHANDLED_EXCEPTION(classname(this) + "::cleanupSingleton()");
    }
}

//static
void LLSingletonBase::deleteAll()
{
    // It's essential to traverse these in dependency order.
    for (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 = classname(sp);
        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 {

void log(ALLog::ELevel level,
         const char* p1, const char* p2, const char* p3, const char* p4)
{
    ALOG_CALL(level, FMT_STRING("{:s}{:s}{:s}{:s}"), p1, p2, p3, p4);
}

} // anonymous namespace        

//static
void LLSingletonBase::logwarns(const char* p1, const char* p2, const char* p3, const char* p4)
{
    log(ALLog::ELevel::warn, p1, p2, p3, p4);
}

//static
void LLSingletonBase::loginfos(const char* p1, const char* p2, const char* p3, const char* p4)
{
    log(ALLog::ELevel::info, p1, p2, p3, p4);
}

//static
void LLSingletonBase::logdebugs(const char* p1, const char* p2, const char* p3, const char* p4)
{
    log(ALLog::ELevel::debug, p1, p2, p3, p4);
}

//static
void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4)
{
    log(ALLog::ELevel::critical, p1, p2, p3, p4);
    // The other important side effect of LL_ERRS() is
    // https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG)
    std::ostringstream out;
    out << p1 << p2 << p3 << p4;
    auto crash = ALLog::getFatalFunction();
    if (crash)
    {
        crash(out.str());
    }
    else
    {
        ALLog::crashAndLoop(out.str());
    }
}

std::string LLSingletonBase::demangle(const char* mangled)
{
    return LLError::Log::demangle(mangled);
}
