diff options
| -rw-r--r-- | indra/llcommon/llsd.cpp | 194 | ||||
| -rw-r--r-- | indra/llcommon/llsd.h | 82 | ||||
| -rw-r--r-- | indra/llmessage/llsdmessagereader.cpp | 1 | ||||
| -rw-r--r-- | indra/test/lltut.cpp | 5 | 
4 files changed, 248 insertions, 34 deletions
| diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp index 6ca0737445..862b6b5ebc 100644 --- a/indra/llcommon/llsd.cpp +++ b/indra/llcommon/llsd.cpp @@ -31,6 +31,7 @@  #include "../llmath/llmath.h"  #include "llformat.h"  #include "llsdserialize.h" +#include "stringize.h"  #ifndef LL_RELEASE_FOR_DOWNLOAD  #define NAME_UNNAMED_NAMESPACE @@ -50,6 +51,24 @@ namespace  using namespace LLSDUnnamedNamespace;  #endif + +// Normally undefined +#ifdef LLSD_DEBUG_INFO + +// statics +S32	LLSD::sLLSDAllocationCount = 0; +S32 LLSD::sLLSDNetObjects = 0; + +#define	ALLOC_LLSD_OBJECT			{ sLLSDNetObjects++;	sLLSDAllocationCount++;		} +#define	FREE_LLSD_OBJECT			{ sLLSDNetObjects--;								} + +#else + +#define	ALLOC_LLSD_OBJECT +#define	FREE_LLSD_OBJECT + +#endif +  class LLSD::Impl  	/**< This class is the abstract base class of the implementation of LLSD  		 It provides the reference counting implementation, and the default @@ -58,13 +77,10 @@ class LLSD::Impl  	*/  { -private: -	U32 mUseCount; -	  protected:  	Impl(); -	enum StaticAllocationMarker { STATIC }; +	enum StaticAllocationMarker { STATIC_USAGE_COUNT = 0xFFFFFFFF };  	Impl(StaticAllocationMarker);  		///< This constructor is used for static objects and causes the  		//   suppresses adjusting the debugging counters when they are @@ -72,7 +88,9 @@ protected:  	virtual ~Impl(); -	bool shared() const							{ return mUseCount > 1; } +	bool shared() const							{ return (mUseCount > 1) && (mUseCount != STATIC_USAGE_COUNT); } +	 +	U32 mUseCount;  public:  	static void reset(Impl*& var, Impl* impl); @@ -128,6 +146,9 @@ public:  	virtual LLSD::array_const_iterator beginArray() const { return endArray(); }  	virtual LLSD::array_const_iterator endArray() const { static const std::vector<LLSD> empty; return empty.end(); } +	virtual void dumpStats() const; +	virtual void calcStats(S32 type_counts[], S32 share_counts[]) const; +  	static const LLSD& undef();  	static U32 sAllocationCount; @@ -360,6 +381,9 @@ namespace  		LLSD::map_iterator endMap() { return mData.end(); }  		virtual LLSD::map_const_iterator beginMap() const { return mData.begin(); }  		virtual LLSD::map_const_iterator endMap() const { return mData.end(); } + +		virtual void dumpStats() const; +		virtual void calcStats(S32 type_counts[], S32 share_counts[]) const;  	};  	ImplMap& ImplMap::makeMap(LLSD::Impl*& var) @@ -414,6 +438,36 @@ namespace  		return i->second;  	} +	void ImplMap::dumpStats() const +	{ +		std::cout << "Map size: " << mData.size() << std::endl; + +		#ifdef LLSD_DEBUG_INFO +		std::cout << "LLSD Net Objects: " << LLSD::sLLSDNetObjects << std::endl; +		std::cout << "LLSD allocations: " << LLSD::sLLSDAllocationCount << std::endl; +		#endif + +		std::cout << "LLSD::Impl Net Objects: " << sOutstandingCount << std::endl; +		std::cout << "LLSD::Impl allocations: " << sAllocationCount << std::endl; + +		Impl::dumpStats(); +	} + +	void ImplMap::calcStats(S32 type_counts[], S32 share_counts[]) const +	{ +		LLSD::map_const_iterator iter = beginMap(); +		while (iter != endMap()) +		{ +			//std::cout << "  " << (*iter).first << ": " << (*iter).second << std::endl; +			(*iter).second.calcStats(type_counts, share_counts); +			iter++; +		} + +		// Add in the values for this map +		Impl::calcStats(type_counts, share_counts); +	} + +  	class ImplArray : public LLSD::Impl  	{  	private: @@ -449,6 +503,8 @@ namespace  		LLSD::array_iterator endArray() { return mData.end(); }  		virtual LLSD::array_const_iterator beginArray() const { return mData.begin(); }  		virtual LLSD::array_const_iterator endArray() const { return mData.end(); } + +		virtual void calcStats(S32 type_counts[], S32 share_counts[]) const;  	};  	ImplArray& ImplArray::makeArray(Impl*& var) @@ -490,12 +546,13 @@ namespace  	void ImplArray::insert(LLSD::Integer i, const LLSD& v)  	{ -		if (i < 0) { +		if (i < 0)  +		{  			return;  		}  		DataVector::size_type index = i; -		if (index >= mData.size()) +		if (index >= mData.size())	// tbd - sanity check limit for index ?  		{  			mData.resize(index + 1);  		} @@ -543,6 +600,19 @@ namespace  		return mData[index];  	} + +	void ImplArray::calcStats(S32 type_counts[], S32 share_counts[]) const +	{ +		LLSD::array_const_iterator iter = beginArray(); +		while (iter != endArray()) +		{	// Add values for all items held in the array +			(*iter).calcStats(type_counts, share_counts); +			iter++; +		} + +		// Add in the values for this array +		Impl::calcStats(type_counts, share_counts); +	}  }  LLSD::Impl::Impl() @@ -564,8 +634,11 @@ LLSD::Impl::~Impl()  void LLSD::Impl::reset(Impl*& var, Impl* impl)  { -	if (impl) ++impl->mUseCount; -	if (var  &&  --var->mUseCount == 0) +	if (impl && impl->mUseCount != STATIC_USAGE_COUNT)  +	{ +		++impl->mUseCount; +	} +	if (var  &&  var->mUseCount != STATIC_USAGE_COUNT && --var->mUseCount == 0)  	{  		delete var;  	} @@ -574,13 +647,13 @@ void LLSD::Impl::reset(Impl*& var, Impl* impl)  LLSD::Impl& LLSD::Impl::safe(Impl* impl)  { -	static Impl theUndefined(STATIC); +	static Impl theUndefined(STATIC_USAGE_COUNT);  	return impl ? *impl : theUndefined;  }  const LLSD::Impl& LLSD::Impl::safe(const Impl* impl)  { -	static Impl theUndefined(STATIC); +	static Impl theUndefined(STATIC_USAGE_COUNT);  	return impl ? *impl : theUndefined;  } @@ -656,6 +729,39 @@ const LLSD& LLSD::Impl::undef()  	return immutableUndefined;  } +void LLSD::Impl::dumpStats() const +{ +	S32 type_counts[LLSD::TypeLLSDNumTypes + 1]; +	memset(&type_counts, 0, LLSD::TypeLLSDNumTypes * sizeof(S32)); + +	S32 share_counts[LLSD::TypeLLSDNumTypes + 1]; +	memset(&share_counts, 0, LLSD::TypeLLSDNumTypes * sizeof(S32)); + +	// Add info from all the values this object has +	calcStats(type_counts, share_counts); + +	S32 type_index = LLSD::TypeLLSDTypeBegin; +	while (type_index != LLSD::TypeLLSDTypeEnd) +	{ +		std::cout << LLSD::typeString((LLSD::Type)type_index) << " type " +			<< type_counts[type_index] << " objects, " +			<< share_counts[type_index] << " shared" +			<< std::endl; +		type_index++; +	} +} + + +void LLSD::Impl::calcStats(S32 type_counts[], S32 share_counts[]) const +{  +	type_counts[(S32) type()]++;	 +	if (shared()) +	{ +		share_counts[(S32) type()]++; +	} +} + +  U32 LLSD::Impl::sAllocationCount = 0;  U32 LLSD::Impl::sOutstandingCount = 0; @@ -681,10 +787,10 @@ namespace  } -LLSD::LLSD()							: impl(0)	{ } -LLSD::~LLSD()							{ Impl::reset(impl, 0); } +LLSD::LLSD() : impl(0)					{ ALLOC_LLSD_OBJECT; } +LLSD::~LLSD()							{ FREE_LLSD_OBJECT; Impl::reset(impl, 0); } -LLSD::LLSD(const LLSD& other)			: impl(0) { assign(other); } +LLSD::LLSD(const LLSD& other) : impl(0) { ALLOC_LLSD_OBJECT;  assign(other); }  void LLSD::assign(const LLSD& other)	{ Impl::assign(impl, other.impl); } @@ -693,17 +799,17 @@ void LLSD::clear()						{ Impl::assignUndefined(impl); }  LLSD::Type LLSD::type() const			{ return safe(impl).type(); }  // Scaler Constructors -LLSD::LLSD(Boolean v)					: impl(0) { assign(v); } -LLSD::LLSD(Integer v)					: impl(0) { assign(v); } -LLSD::LLSD(Real v)						: impl(0) { assign(v); } -LLSD::LLSD(const UUID& v)				: impl(0) { assign(v); } -LLSD::LLSD(const String& v)				: impl(0) { assign(v); } -LLSD::LLSD(const Date& v)				: impl(0) { assign(v); } -LLSD::LLSD(const URI& v)				: impl(0) { assign(v); } -LLSD::LLSD(const Binary& v)				: impl(0) { assign(v); } +LLSD::LLSD(Boolean v) : impl(0)			{ ALLOC_LLSD_OBJECT;	assign(v); } +LLSD::LLSD(Integer v) : impl(0)			{ ALLOC_LLSD_OBJECT;	assign(v); } +LLSD::LLSD(Real v) : impl(0)			{ ALLOC_LLSD_OBJECT;	assign(v); } +LLSD::LLSD(const UUID& v) : impl(0)		{ ALLOC_LLSD_OBJECT;	assign(v); } +LLSD::LLSD(const String& v) : impl(0)	{ ALLOC_LLSD_OBJECT;	assign(v); } +LLSD::LLSD(const Date& v) : impl(0)		{ ALLOC_LLSD_OBJECT;	assign(v); } +LLSD::LLSD(const URI& v) : impl(0)		{ ALLOC_LLSD_OBJECT;	assign(v); } +LLSD::LLSD(const Binary& v) : impl(0)	{ ALLOC_LLSD_OBJECT;	assign(v); }  // Convenience Constructors -LLSD::LLSD(F32 v)						: impl(0) { assign((Real)v); } +LLSD::LLSD(F32 v) : impl(0)				{ ALLOC_LLSD_OBJECT;	assign((Real)v); }  // Scalar Assignment  void LLSD::assign(Boolean v)			{ safe(impl).assign(impl, v); } @@ -726,7 +832,7 @@ LLSD::URI		LLSD::asURI() const		{ return safe(impl).asURI(); }  LLSD::Binary	LLSD::asBinary() const	{ return safe(impl).asBinary(); }  // const char * helpers -LLSD::LLSD(const char* v)				: impl(0) { assign(v); } +LLSD::LLSD(const char* v) : impl(0)		{ ALLOC_LLSD_OBJECT;	assign(v); }  void LLSD::assign(const char* v)  {  	if(v) assign(std::string(v)); @@ -801,15 +907,9 @@ static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat)  	{  		std::ostringstream out;  		if (useXMLFormat) -		{ -			LLSDXMLStreamer xml_streamer(llsd); -			out << xml_streamer; -		} +			out << LLSDXMLStreamer(llsd);  		else -		{ -			LLSDNotationStreamer notation_streamer(llsd); -			out << notation_streamer; -		} +			out << LLSDNotationStreamer(llsd);  		out_string = out.str();  	}  	int len = out_string.length(); @@ -840,3 +940,33 @@ LLSD::array_iterator		LLSD::beginArray()		{ return makeArray(impl).beginArray();  LLSD::array_iterator		LLSD::endArray()		{ return makeArray(impl).endArray(); }  LLSD::array_const_iterator	LLSD::beginArray() const{ return safe(impl).beginArray(); }  LLSD::array_const_iterator	LLSD::endArray() const	{ return safe(impl).endArray(); } + + +// Diagnostic dump of contents in an LLSD object +void LLSD::dumpStats()	const						{ safe(impl).dumpStats();	} +void LLSD::calcStats(S32 type_counts[], S32 share_counts[]) const +													{ safe(impl).calcStats(type_counts, share_counts);	} + +// static +std::string		LLSD::typeString(Type type) +{ +	static const char * sTypeNameArray[] = { +		"Undefined", +		"Boolean", +		"Integer", +		"Real", +		"String", +		"UUID", +		"Date", +		"URI", +		"Binary", +		"Map", +		"Array" +	}; + +	if (0 <= type < (sizeof(sTypeNameArray)/sizeof(sTypeNameArray[0]))) +	{ +		return sTypeNameArray[type]; +	} +	return STRINGIZE("** invalid type value " << type); +} diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h index 90d0f97873..1ffa1d279b 100644 --- a/indra/llcommon/llsd.h +++ b/indra/llcommon/llsd.h @@ -80,9 +80,73 @@  	An array is a sequence of zero or more LLSD values. +	Thread Safety + +	In general, these LLSD classes offer *less* safety than STL container +	classes.  Implementations prior to this one were unsafe even when +	completely unrelated LLSD trees were in two threads due to reference +	sharing of special 'undefined' values that participated in the reference +	counting mechanism. + +	The dereference-before-refcount and aggressive tree sharing also make +	it impractical to share an LLSD across threads.  A strategy of passing +	ownership or a copy to another thread is still difficult due to a lack +	of a cloning interface but it can be done with some care. + +	One way of transferring ownership is as follows: + +		void method(const LLSD input) { +		... +		LLSD * xfer_tree = new LLSD(); +		{ +			// Top-level values +			(* xfer_tree)['label'] = "Some text"; +			(* xfer_tree)['mode'] = APP_MODE_CONSTANT; + +			// There will be a second-level +			LLSD subtree(LLSD::emptyMap()); +			(* xfer_tree)['subtree'] = subtree; + +			// Do *not* copy from LLSD objects via LLSD +			// intermediaries.  Only use plain-old-data +			// types as intermediaries to prevent reference +			// sharing. +			subtree['value1'] = input['value1'].asInteger(); +			subtree['value2'] = input['value2'].asString(); + +			// Close scope and drop 'subtree's reference. +			// Only xfer_tree has a reference to the second +			// level data. +		} +		... +		// Transfer the LLSD pointer to another thread.  Ownership +		// transfers, this thread no longer has a reference to any +		// part of the xfer_tree and there's nothing to free or +		// release here.  Receiving thread does need to delete the +		// pointer when it is done with the LLSD.  Transfer +		// mechanism must perform correct data ordering operations +		// as dictated by architecture. +		other_thread.sendMessageAndPointer("Take This", xfer_tree); +		xfer_tree = NULL; + + +	Avoid this pattern which provides half of a race condition: +	 +		void method(const LLSD input) { +		... +		LLSD xfer_tree(LLSD::emptyMap()); +		xfer_tree['label'] = "Some text"; +		xfer_tree['mode'] = APP_MODE_CONSTANT; +		... +		other_thread.sendMessageAndPointer("Take This", xfer_tree); + +	  	@nosubgrouping  */ +// Normally undefined, used for diagnostics +//#define LLSD_DEBUG_INFO	1 +  class LL_COMMON_API LLSD  {  public: @@ -266,7 +330,7 @@ public:  	/** @name Type Testing */  	//@{  		enum Type { -			TypeUndefined, +			TypeUndefined = 0,  			TypeBoolean,  			TypeInteger,  			TypeReal, @@ -276,7 +340,10 @@ public:  			TypeURI,  			TypeBinary,  			TypeMap, -			TypeArray +			TypeArray, +			TypeLLSDTypeEnd, +			TypeLLSDTypeBegin = TypeUndefined, +			TypeLLSDNumTypes = (TypeLLSDTypeEnd - TypeLLSDTypeBegin)  		};  		Type type() const; @@ -338,6 +405,17 @@ private:  		/// Returns Notation version of llsd -- only to be called from debugger  		static const char *dump(const LLSD &llsd);  	//@} + +public: +	void			dumpStats() const;					// Output information on object and usage +	void			calcStats(S32 type_counts[], S32 share_counts[]) const;		// Calculate the number of LLSD objects used by this value + +	static std::string		typeString(Type type);		// Return human-readable type as a string + +#ifdef LLSD_DEBUG_INFO +	static S32		sLLSDAllocationCount;		// Number of LLSD objects ever created +	static S32		sLLSDNetObjects;			// Number of LLSD objects that exist +#endif  };  struct llsd_select_bool : public std::unary_function<LLSD, LLSD::Boolean> diff --git a/indra/llmessage/llsdmessagereader.cpp b/indra/llmessage/llsdmessagereader.cpp index 304a692cdf..3ab62a8c57 100644 --- a/indra/llmessage/llsdmessagereader.cpp +++ b/indra/llmessage/llsdmessagereader.cpp @@ -291,6 +291,7 @@ S32 getElementSize(const LLSD& llsd)  	case LLSD::TypeMap:  	case LLSD::TypeArray:  	case LLSD::TypeUndefined: +	default:                        // TypeLLSDTypeEnd, TypeLLSDNumTypes, etc.  		return 0;  	}  	return 0; diff --git a/indra/test/lltut.cpp b/indra/test/lltut.cpp index da7031b52a..c43a8f0c7d 100644 --- a/indra/test/lltut.cpp +++ b/indra/test/lltut.cpp @@ -34,6 +34,7 @@  #include "llformat.h"  #include "llsd.h"  #include "lluri.h" +#include "stringize.h"  namespace tut  { @@ -144,6 +145,10 @@ namespace tut  				}  				return;  			} +			default: +				// should never get here, but compiler produces warning if we +				// don't cover this case, and at Linden warnings are fatal. +				throw failure(STRINGIZE("invalid type field " << actual.type()));  		}  	} | 
