phoenix-firestorm/indra/llcommon/llinstancetracker.h

460 lines
13 KiB
C++

/**
* @file llinstancetracker.h
* @brief LLInstanceTracker is a mixin class that automatically tracks object
* instances with or without an associated key
*
* $LicenseInfo:firstyear=2000&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$
*/
#ifndef LL_LLINSTANCETRACKER_H
#define LL_LLINSTANCETRACKER_H
#include <map>
#include <typeinfo>
#include "llstringtable.h"
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/indirect_iterator.hpp>
// <FS:CR>
#ifdef LL_DEBUG
#include "llerror.h"
#endif
// </FS:CR>
// As of 2017-05-06, as far as nat knows, only clang supports __has_feature().
// Unfortunately VS2013's preprocessor shortcut logic doesn't prevent it from
// producing (fatal) warnings for defined(__clang__) && __has_feature(...).
// Have to work around that.
#if ! defined(__clang__)
#define __has_feature(x) 0
#endif // __clang__
#if defined(LL_TEST_llinstancetracker) && __has_feature(cxx_noexcept)
// ~LLInstanceTracker() performs llassert_always() validation. That's fine in
// production code, since the llassert_always() is implemented as an LL_ERRS
// message, which will crash-with-message. In our integration test executable,
// though, this llassert_always() throws an exception instead so we can test
// error conditions and continue running the test. However -- as of C++11,
// destructors are implicitly noexcept(true). Unless we mark
// ~LLInstanceTracker() noexcept(false), the test executable crashes even on
// the ATTEMPT to throw.
#define LLINSTANCETRACKER_DTOR_NOEXCEPT noexcept(false)
#else
// If we're building for production, or in fact building *any other* test, or
// we're using a compiler that doesn't support __has_feature(), or we're not
// compiling with a C++ version that supports noexcept -- don't specify it.
#define LLINSTANCETRACKER_DTOR_NOEXCEPT
#endif
// As of 2017-05-06, as far as nat knows, only clang supports __has_feature().
// Unfortunately VS2013's preprocessor shortcut logic doesn't prevent it from
// producing (fatal) warnings for defined(__clang__) && __has_feature(...).
// Have to work around that.
#if ! defined(__clang__)
#define __has_feature(x) 0
#endif // __clang__
#if defined(LL_TEST_llinstancetracker) && __has_feature(cxx_noexcept)
// ~LLInstanceTracker() performs llassert_always() validation. That's fine in
// production code, since the llassert_always() is implemented as an LL_ERRS
// message, which will crash-with-message. In our integration test executable,
// though, this llassert_always() throws an exception instead so we can test
// error conditions and continue running the test. However -- as of C++11,
// destructors are implicitly noexcept(true). Unless we mark
// ~LLInstanceTracker() noexcept(false), the test executable crashes even on
// the ATTEMPT to throw.
#define LLINSTANCETRACKER_DTOR_NOEXCEPT noexcept(false)
#else
// If we're building for production, or in fact building *any other* test, or
// we're using a compiler that doesn't support __has_feature(), or we're not
// compiling with a C++ version that supports noexcept -- don't specify it.
#define LLINSTANCETRACKER_DTOR_NOEXCEPT
#endif
/**
* Base class manages "class-static" data that must actually have singleton
* semantics: one instance per process, rather than one instance per module as
* sometimes happens with data simply declared static.
*/
class LL_COMMON_API LLInstanceTrackerBase
{
protected:
/// It's not essential to derive your STATICDATA (for use with
/// getStatic()) from StaticBase; it's just that both known
/// implementations do.
struct StaticBase
{
// <FS:ND> Only needed in debug builds
#ifdef LL_DEBUG
StaticBase()
: sIterationNestDepth(0)
{}
#else
StaticBase()
{}
#endif
// </FS:ND>
void incrementDepth();
void decrementDepth();
U32 getDepth();
private:
U32 sIterationNestDepth;
};
};
LL_COMMON_API void assert_main_thread();
enum EInstanceTrackerAllowKeyCollisions
{
LLInstanceTrackerErrorOnCollision,
LLInstanceTrackerReplaceOnCollision
};
/// This mix-in class adds support for tracking all instances of the specified class parameter T
/// The (optional) key associates a value of type KEY with a given instance of T, for quick lookup
/// If KEY is not provided, then instances are stored in a simple set
/// @NOTE: see explicit specialization below for default KEY==void case
/// @NOTE: this class is not thread-safe unless used as read-only
template<typename T, typename KEY = void, EInstanceTrackerAllowKeyCollisions KEY_COLLISION_BEHAVIOR = LLInstanceTrackerErrorOnCollision>
class LLInstanceTracker : public LLInstanceTrackerBase
{
protected:
typedef LLInstanceTracker<T, KEY> self_t;
typedef typename std::multimap<KEY, T*> InstanceMap;
struct StaticData: public StaticBase
{
InstanceMap sMap;
};
static StaticData& getStatic() { static StaticData sData; return sData;}
static InstanceMap& getMap_() { return getStatic().sMap; }
public:
class instance_iter : public boost::iterator_facade<instance_iter, T, boost::forward_traversal_tag>
{
public:
typedef boost::iterator_facade<instance_iter, T, boost::forward_traversal_tag> super_t;
instance_iter(const typename InstanceMap::iterator& it)
: mIterator(it)
{
// <FS:ND> Minimize calls to getStatic
#ifdef LL_DEBUG
getStatic().incrementDepth();
#endif
// </FS:ND>
}
~instance_iter()
{
// <FS:ND> Minimize calls to getStatic
#ifdef LL_DEBUG
getStatic().decrementDepth();
#endif
// </FS:ND>
}
private:
friend class boost::iterator_core_access;
void increment() { mIterator++; }
bool equal(instance_iter const& other) const
{
return mIterator == other.mIterator;
}
T& dereference() const
{
return *(mIterator->second);
}
typename InstanceMap::iterator mIterator;
};
class key_iter : public boost::iterator_facade<key_iter, KEY, boost::forward_traversal_tag>
{
public:
typedef boost::iterator_facade<key_iter, KEY, boost::forward_traversal_tag> super_t;
key_iter(typename InstanceMap::iterator it)
: mIterator(it)
{
// <FS:ND> Minimize calls to getStatic
#ifdef LL_DEBUG
getStatic().incrementDepth();
#endif
// </FS:ND>
}
key_iter(const key_iter& other)
: mIterator(other.mIterator)
{
// <FS:ND> Minimize calls to getStatic
#ifdef LL_DEBUG
getStatic().incrementDepth();
#endif
// </FS:ND>
}
~key_iter()
{
// <FS:ND> Minimize calls to getStatic
#ifdef LL_DEBUG
getStatic().decrementDepth();
#endif
// </FS:ND>
}
private:
friend class boost::iterator_core_access;
void increment() { mIterator++; }
bool equal(key_iter const& other) const
{
return mIterator == other.mIterator;
}
KEY& dereference() const
{
return const_cast<KEY&>(mIterator->first);
}
typename InstanceMap::iterator mIterator;
};
static T* getInstance(const KEY& k)
{
const InstanceMap& map(getMap_());
typename InstanceMap::const_iterator found = map.find(k);
return (found == map.end()) ? NULL : found->second;
}
static instance_iter beginInstances()
{
return instance_iter(getMap_().begin());
}
static instance_iter endInstances()
{
return instance_iter(getMap_().end());
}
static S32 instanceCount()
{
return getMap_().size();
}
static key_iter beginKeys()
{
return key_iter(getMap_().begin());
}
static key_iter endKeys()
{
return key_iter(getMap_().end());
}
protected:
LLInstanceTracker(const KEY& key)
{
// make sure static data outlives all instances
getStatic();
add_(key);
}
virtual ~LLInstanceTracker() LLINSTANCETRACKER_DTOR_NOEXCEPT
{
// it's unsafe to delete instances of this type while all instances are being iterated over.
// <FS:ND> Minimize calls to getStatic
#ifdef LL_DEBUG
llassert_always(getStatic().getDepth() == 0);
#endif
// </FS:ND>
remove_();
}
virtual void setKey(KEY key) { remove_(); add_(key); }
virtual const KEY& getKey() const { return mInstanceKey; }
private:
LLInstanceTracker( const LLInstanceTracker& );
const LLInstanceTracker& operator=( const LLInstanceTracker& );
void add_(const KEY& key)
{
mInstanceKey = key;
InstanceMap& map = getMap_();
typename InstanceMap::iterator insertion_point_it = map.lower_bound(key);
if (insertion_point_it != map.end()
&& insertion_point_it->first == key)
{ // found existing entry with that key
switch(KEY_COLLISION_BEHAVIOR)
{
case LLInstanceTrackerErrorOnCollision:
{
// use assert here instead of LL_ERRS(), otherwise the error will be ignored since this call is made during global object initialization
llassert_always_msg(false, "Instance with this same key already exists!");
break;
}
case LLInstanceTrackerReplaceOnCollision:
{
// replace pointer, but leave key (should have compared equal anyway)
insertion_point_it->second = static_cast<T*>(this);
break;
}
default:
break;
}
}
else
{ // new key
map.insert(insertion_point_it, std::make_pair(key, static_cast<T*>(this)));
}
}
void remove_()
{
InstanceMap& map = getMap_();
typename InstanceMap::iterator iter = map.find(mInstanceKey);
if (iter != map.end())
{
map.erase(iter);
}
}
private:
KEY mInstanceKey;
};
/// explicit specialization for default case where KEY is void
/// use a simple std::set<T*>
template<typename T, EInstanceTrackerAllowKeyCollisions KEY_COLLISION_BEHAVIOR>
class LLInstanceTracker<T, void, KEY_COLLISION_BEHAVIOR> : public LLInstanceTrackerBase
{
protected:
typedef LLInstanceTracker<T, void> self_t;
typedef typename std::set<T*> InstanceSet;
struct StaticData: public StaticBase
{
InstanceSet sSet;
};
static StaticData& getStatic() { static StaticData sData; return sData; }
static InstanceSet& getSet_() { return getStatic().sSet; }
public:
/**
* Does a particular instance still exist? Of course, if you already have
* a T* in hand, you need not call getInstance() to @em locate the
* instance -- unlike the case where getInstance() accepts some kind of
* key. Nonetheless this method is still useful to @em validate a
* particular T*, since each instance's destructor removes itself from the
* underlying set.
*/
static T* getInstance(T* k)
{
const InstanceSet& set(getSet_());
typename InstanceSet::const_iterator found = set.find(k);
return (found == set.end())? NULL : *found;
}
static S32 instanceCount() { return getSet_().size(); }
class instance_iter : public boost::iterator_facade<instance_iter, T, boost::forward_traversal_tag>
{
public:
instance_iter(const typename InstanceSet::iterator& it)
: mIterator(it)
{
// <FS:ND> Minimize calls to getStatic
#ifdef LL_DEBUG
getStatic().incrementDepth();
#endif
// </FS:ND>
}
instance_iter(const instance_iter& other)
: mIterator(other.mIterator)
{
// <FS:ND> Minimize calls to getStatic
#ifdef LL_DEBUG
getStatic().incrementDepth();
#endif
}
~instance_iter()
{
// <FS:ND> Minimize calls to getStatic
#ifdef LL_DEBUG
getStatic().decrementDepth();
#endif
// </FS:ND>
}
private:
friend class boost::iterator_core_access;
void increment() { mIterator++; }
bool equal(instance_iter const& other) const
{
return mIterator == other.mIterator;
}
T& dereference() const
{
return **mIterator;
}
typename InstanceSet::iterator mIterator;
};
static instance_iter beginInstances() { return instance_iter(getSet_().begin()); }
static instance_iter endInstances() { return instance_iter(getSet_().end()); }
protected:
LLInstanceTracker()
{
// make sure static data outlives all instances
getStatic();
getSet_().insert(static_cast<T*>(this));
}
virtual ~LLInstanceTracker() LLINSTANCETRACKER_DTOR_NOEXCEPT
{
// it's unsafe to delete instances of this type while all instances are being iterated over.
// <FS:ND> Minimize calls to getStatic
#ifdef LL_DEBUG
llassert_always(getStatic().getDepth() == 0);
#endif
// </FS:ND>
getSet_().erase(static_cast<T*>(this));
}
LLInstanceTracker(const LLInstanceTracker& other)
{
getSet_().insert(static_cast<T*>(this));
}
};
#endif