/** * @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> /** * 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 { StaticBase(): sIterationNestDepth(0) {} 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 { 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) { getStatic().incrementDepth(); } ~instance_iter() { getStatic().decrementDepth(); } 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) { getStatic().incrementDepth(); } key_iter(const key_iter& other) : mIterator(other.mIterator) { getStatic().incrementDepth(); } ~key_iter() { getStatic().decrementDepth(); } 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() { // it's unsafe to delete instances of this type while all instances are being iterated over. llassert_always(getStatic().getDepth() == 0); 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 { 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) { getStatic().incrementDepth(); } instance_iter(const instance_iter& other) : mIterator(other.mIterator) { getStatic().incrementDepth(); } ~instance_iter() { getStatic().decrementDepth(); } 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() { // it's unsafe to delete instances of this type while all instances are being iterated over. llassert_always(getStatic().getDepth() == 0); getSet_().erase(static_cast<T*>(this)); } LLInstanceTracker(const LLInstanceTracker& other) { getSet_().insert(static_cast<T*>(this)); } }; #endif