Commits

dessie linden  committed c6175c9 Merge

merge

  • Participants
  • Parent commits d004e07, a1e6e9a
  • Tags 3.2.5-release, DRTVWR-105_3.2.5-release

Comments (0)

Files changed (312)

 80f3e30d8aa4d8f674a48bd742aaa6d8e9eae0b5 3.2.3-start
 a8c7030d6845186fac7c188be4323a0e887b4184 DRTVWR-99_3.2.1-release
 a8c7030d6845186fac7c188be4323a0e887b4184 3.2.1-release
+3fe994349fae64fc40874bb59db387131eb35a41 3.2.4-start
 3fe994349fae64fc40874bb59db387131eb35a41 DRTVWR-104_3.2.4-beta1
 3fe994349fae64fc40874bb59db387131eb35a41 3.2.4-beta1
 fe3a8e7973072ea62043c08b19b66626c1a720eb DRTVWR-62_2.7.2-release
 fe3a8e7973072ea62043c08b19b66626c1a720eb 2.7.2-release
 bd6bcde2584491fd9228f1fa51c4575f4e764e19 DRTVWR-103_3.2.4-release
 bd6bcde2584491fd9228f1fa51c4575f4e764e19 3.2.4-release
+8a44ff3d2104269ce76145c2772cf1bdff2a2abe 3.2.5-start
+3d2d5d244c6398a4214c666d5dd3965b0918709a DRTVWR-106_3.2.5-beta1
+3d2d5d244c6398a4214c666d5dd3965b0918709a 3.2.5-beta1
+65a2c1c8d855b88edfbea4e16ef2f27e7cff8b1d DRTVWR-107_3.2.5-beta2
+65a2c1c8d855b88edfbea4e16ef2f27e7cff8b1d 3.2.5-beta2

File autobuild.xml

             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>7108c2443dbcf4c032305814ce65ebb7</string>
+              <string>26aa7c367ffadd573f61a6a96f820f80</string>
               <key>url</key>
-              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llqtwebkit/rev/244065/arch/Darwin/installer/llqtwebkit-4.7.1-darwin-20111028.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llqtwebkit/rev/245988/arch/Darwin/installer/llqtwebkit-4.7.1-darwin-20111201.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>24048a31d7b852774dc3117acbd4a86a</string>
+              <string>270db8568a0c4bab266d98e1a820aec4</string>
               <key>url</key>
-              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llqtwebkit/rev/244065/arch/CYGWIN/installer/llqtwebkit-4.7.1-windows-20111028.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llqtwebkit/rev/245988/arch/CYGWIN/installer/llqtwebkit-4.7.1-windows-20111201.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>

File doc/contributions.txt

 	VWR-25480
 	VWR-26150
 	STORM-1685
+	STORM-1713
 Aralara Rajal
 Ardy Lay
 	STORM-859
 	STORM-1639
 	STORM-910
 	STORM-1642
+	STORM-591
 	STORM-1105
 	STORM-1679
 	STORM-1222
 	STORM-1659
 	STORM-1674
 	STORM-1685
+	STORM-1721
+	STORM-1719
+	STORM-1712
+	STORM-1728
 Kadah Coba
 	STORM-1060
 Jondan Lundquist
 	SNOW-599
 	SNOW-747
 	STORM-422
+	STORM-591
 	STORM-960
 	STORM-1019
 	STORM-1095

File indra/cmake/00-Common.cmake

   set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /MDd /MP -D_SCL_SECURE_NO_WARNINGS=1"
       CACHE STRING "C++ compiler debug options" FORCE)
   set(CMAKE_CXX_FLAGS_RELWITHDEBINFO 
-      "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od /Zi /MD /MP /Ob2 -D_SECURE_STL=0"
+      "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od /Zi /MD /MP /Ob0 -D_SECURE_STL=0"
       CACHE STRING "C++ compiler release-with-debug options" FORCE)
   set(CMAKE_CXX_FLAGS_RELEASE
       "${CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /O2 /Zi /MD /MP /Ob2 -D_SECURE_STL=0 -D_HAS_ITERATOR_DEBUGGING=0"
       /D_UNICODE 
       /GS
       /TP
-      /W2
+      /W3
       /c
       /Zc:forScope
       /nologo

File indra/llcommon/llmemory.cpp

 	if(update)
 	{
 		updateMemoryInfo() ;
+		LLPrivateMemoryPoolManager::getInstance()->updateStatistics() ;
 	}
 
 	llinfos << "Current allocated physical memory(KB): " << sAllocatedMemInKB << llendl ;

File indra/llcommon/llpreprocessor.h

 
 #pragma warning (disable : 4251) // member needs to have dll-interface to be used by clients of class
 #pragma warning (disable : 4275) // non dll-interface class used as base for dll-interface class
+#pragma warning (disable : 4018) // '<' : signed/unsigned mismatch	
 #endif	//	LL_MSVC
 
 #if LL_WINDOWS

File indra/llcommon/llsd.cpp

  * $/LicenseInfo$
  */
 
+// Must turn on conditional declarations in header file so definitions end up
+// with proper linkage.
+#define LLSD_DEBUG_INFO
 #include "linden_common.h"
 #include "llsd.h"
 
 #include "../llmath/llmath.h"
 #include "llformat.h"
 #include "llsdserialize.h"
+#include "stringize.h"
 
 #ifndef LL_RELEASE_FOR_DOWNLOAD
 #define NAME_UNNAMED_NAMESPACE
 using namespace LLSDUnnamedNamespace;
 #endif
 
+namespace llsd
+{
+
+// statics
+S32	sLLSDAllocationCount = 0;
+S32 sLLSDNetObjects = 0;
+
+} // namespace llsd
+
+#define	ALLOC_LLSD_OBJECT			{ llsd::sLLSDNetObjects++;	llsd::sLLSDAllocationCount++;	}
+#define	FREE_LLSD_OBJECT			{ llsd::sLLSDNetObjects--;									}
+
 class LLSD::Impl
 	/**< This class is the abstract base class of the implementation of LLSD
 		 It provides the reference counting implementation, and the default
 		
 	*/
 {
-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
 		
 	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);
 		///< safely set var to refer to the new impl (possibly shared)
 	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;
+	// Container subclasses contain LLSD objects, rather than directly
+	// containing Impl objects. This helper forwards through LLSD.
+	void calcStats(const LLSD& llsd, S32 type_counts[], S32 share_counts[]) const
+	{
+		safe(llsd.impl).calcStats(type_counts, share_counts);
+	}
+
+	static const Impl& getImpl(const LLSD& llsd)	{ return safe(llsd.impl); }
+	static Impl& getImpl(LLSD& llsd)				{ return safe(llsd.impl); }
+
 	static const LLSD& undef();
 	
 	static U32 sAllocationCount;
 		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)
 		return i->second;
 	}
 
+	void ImplMap::dumpStats() const
+	{
+		std::cout << "Map size: " << mData.size() << std::endl;
+
+		std::cout << "LLSD Net Objects: " << llsd::sLLSDNetObjects << std::endl;
+		std::cout << "LLSD allocations: " << llsd::sLLSDAllocationCount << std::endl;
+
+		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;
+			Impl::calcStats((*iter).second, type_counts, share_counts);
+			iter++;
+		}
+
+		// Add in the values for this map
+		Impl::calcStats(type_counts, share_counts);
+	}
+
+
 	class ImplArray : public LLSD::Impl
 	{
 	private:
 		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)
 	
 	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);
 		}
 		
 		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
+			Impl::calcStats((*iter), type_counts, share_counts);
+			iter++;
+		}
+
+		// Add in the values for this array
+		Impl::calcStats(type_counts, share_counts);
+	}
 }
 
 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;
 	}
 
 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;
 }
 
 	return immutableUndefined;
 }
 
+void LLSD::Impl::dumpStats() const
+{
+	S32 type_counts[LLSD::TypeLLSDNumTypes + 1];
+	memset(&type_counts, 0, sizeof(type_counts));
+
+	S32 share_counts[LLSD::TypeLLSDNumTypes + 1];
+	memset(&share_counts, 0, sizeof(share_counts));
+
+	// 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
+{
+	S32 tp = S32(type());
+	if (0 <= tp && tp < LLSD::TypeLLSDNumTypes)
+	{
+		type_counts[tp]++;	
+		if (shared())
+		{
+			share_counts[tp]++;
+		}
+	}
+}
+
+
 U32 LLSD::Impl::sAllocationCount = 0;
 U32 LLSD::Impl::sOutstandingCount = 0;
 
 }
 
 
-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); }
 
 
 
 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); }
+// Scalar Constructors
+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); }
 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));
 const LLSD& LLSD::operator[](Integer i) const
 										{ return safe(impl).ref(i); }
 
-U32 LLSD::allocationCount()				{ return Impl::sAllocationCount; }
-U32 LLSD::outstandingCount()			{ return Impl::sOutstandingCount; }
-
 static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat)
 {
 	// sStorage is used to hold the string representation of the llsd last
 	{
 		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();
 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(); }
+
+namespace llsd
+{
+
+U32 allocationCount()								{ return LLSD::Impl::sAllocationCount; }
+U32 outstandingCount()								{ return LLSD::Impl::sOutstandingCount; }
+
+// Diagnostic dump of contents in an LLSD object
+void dumpStats(const LLSD& llsd)					{ LLSD::Impl::getImpl(llsd).dumpStats(); }
+
+} // namespace llsd
+
+// 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 && type < LL_ARRAY_SIZE(sTypeNameArray))
+	{
+		return sTypeNameArray[type];
+	}
+	return STRINGIZE("** invalid type value " << type);
+}

File indra/llcommon/llsd.h

 /**
 	LLSD provides a flexible data system similar to the data facilities of
 	dynamic languages like Perl and Python.  It is created to support exchange
-	of structured data between loosly coupled systems.  (Here, "loosly coupled"
+	of structured data between loosely coupled systems.  (Here, "loosely coupled"
 	means not compiled together into the same module.)
 	
-	Data in such exchanges must be highly tollerant of changes on either side
+	Data in such exchanges must be highly tolerant of changes on either side
 	such as:
 			- recompilation
 			- implementation in a different langauge
 			- execution of older versions (with fewer parameters)
 
 	To this aim, the C++ API of LLSD strives to be very easy to use, and to
-	default to "the right thing" whereever possible.  It is extremely tollerant
+	default to "the right thing" wherever possible.  It is extremely tolerant
 	of errors and unexpected situations.
 	
-	The fundimental class is LLSD.  LLSD is a value holding object.  It holds
+	The fundamental class is LLSD.  LLSD is a value holding object.  It holds
 	one value that is either undefined, one of the scalar types, or a map or an
 	array.  LLSD objects have value semantics (copying them copies the value,
-	though it can be considered efficient, due to shareing.), and mutable.
+	though it can be considered efficient, due to sharing), and mutable.
 
 	Undefined is the singular value given to LLSD objects that are not
 	initialized with any data.  It is also used as the return value for
-	operations that return an LLSD,
+	operations that return an LLSD.
 	
-	The sclar data types are:
+	The scalar data types are:
 		- Boolean	- true or false
 		- Integer	- a 32 bit signed integer
 		- Real		- a 64 IEEE 754 floating point value
 	
 	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:
 	//@}
 	
 	/** @name Character Pointer Helpers
-		These are helper routines to make working with char* the same as easy as
+		These are helper routines to make working with char* as easy as
 		working with strings.
 	 */
 	//@{
 	/** @name Type Testing */
 	//@{
 		enum Type {
-			TypeUndefined,
+			TypeUndefined = 0,
 			TypeBoolean,
 			TypeInteger,
 			TypeReal,
 			TypeURI,
 			TypeBinary,
 			TypeMap,
-			TypeArray
+			TypeArray,
+			TypeLLSDTypeEnd,
+			TypeLLSDTypeBegin = TypeUndefined,
+			TypeLLSDNumTypes = (TypeLLSDTypeEnd - TypeLLSDTypeBegin)
 		};
 		
 		Type type() const;
 		If you get a linker error about these being missing, you have made
 		mistake in your code.  DO NOT IMPLEMENT THESE FUNCTIONS as a fix.
 		
-		All of thse problems stem from trying to support char* in LLSD or in
+		All of these problems stem from trying to support char* in LLSD or in
 		std::string.  There are too many automatic casts that will lead to
 		using an arbitrary pointer or scalar type to std::string.
 	 */
 		void assign(const void*);		///< assign from arbitrary pointers
 		LLSD& operator=(const void*);	///< assign from arbitrary pointers
 		
-		bool has(Integer) const;		///< has only works for Maps
+		bool has(Integer) const;		///< has() only works for Maps
 	//@}
 	
 	/** @name Implementation */
 		class Impl;
 private:
 		Impl* impl;
-	//@}
-	
-	/** @name Unit Testing Interface */
-	//@{
-public:
-		static U32 allocationCount();	///< how many Impls have been made
-		static U32 outstandingCount();	///< how many Impls are still alive
+		friend class LLSD::Impl;
 	//@}
 
 private:
 		/// Returns Notation version of llsd -- only to be called from debugger
 		static const char *dump(const LLSD &llsd);
 	//@}
+
+public:
+
+	static std::string		typeString(Type type);		// Return human-readable type as a string
 };
 
 struct llsd_select_bool : public std::unary_function<LLSD, LLSD::Boolean>
 
 LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLSD& llsd);
 
+namespace llsd
+{
+
+#ifdef LLSD_DEBUG_INFO
+/** @name Unit Testing Interface */
+//@{
+	LL_COMMON_API void dumpStats(const LLSD&);	///< Output information on object and usage
+
+	/// @warn THE FOLLOWING COUNTS WILL NOT BE ACCURATE IN A MULTI-THREADED
+	/// ENVIRONMENT.
+	///
+	/// These counts track LLSD::Impl (hidden) objects.
+	LL_COMMON_API U32 allocationCount();	///< how many Impls have been made
+	LL_COMMON_API U32 outstandingCount();	///< how many Impls are still alive
+
+	/// These counts track LLSD (public) objects.
+	LL_COMMON_API extern S32 sLLSDAllocationCount;	///< Number of LLSD objects ever created
+	LL_COMMON_API extern S32 sLLSDNetObjects;		///< Number of LLSD objects that exist
+#endif
+//@}
+
+} // namespace llsd
+
 /** QUESTIONS & TO DOS
-	- Would Binary be more convenient as usigned char* buffer semantics?
-	- Should Binary be convertable to/from String, and if so how?
+	- Would Binary be more convenient as unsigned char* buffer semantics?
+	- Should Binary be convertible to/from String, and if so how?
 		- as UTF8 encoded strings (making not like UUID<->String)
 		- as Base64 or Base96 encoded (making like UUID<->String)
 	- Conversions to std::string and LLUUID do not result in easy assignment

File indra/llcommon/llthread.h

 	LLMutexLock(LLMutex* mutex)
 	{
 		mMutex = mutex;
-		mMutex->lock();
+		
+		if(mMutex)
+			mMutex->lock();
 	}
 	~LLMutexLock()
 	{
-		mMutex->unlock();
+		if(mMutex)
+			mMutex->unlock();
 	}
 private:
 	LLMutex* mMutex;

File indra/llcommon/llversionviewer.h

 
 const S32 LL_VERSION_MAJOR = 3;
 const S32 LL_VERSION_MINOR = 2;
-const S32 LL_VERSION_PATCH = 4;
+const S32 LL_VERSION_PATCH = 5;
 const S32 LL_VERSION_BUILD = 0;
 
 const char * const LL_CHANNEL = "Second Life Developer";

File indra/llimage/llimage.cpp

 		mData = (U8*)ALLOCATE_MEM(sPrivatePoolp, size);
 		if (!mData)
 		{
-			llwarns << "allocate image data: " << size << llendl;
+			llwarns << "Failed to allocate image data size [" << size << "]" << llendl;
 			size = 0 ;
 			mWidth = mHeight = 0 ;
 			mBadBufferAllocation = true ;

File indra/llmessage/llcurl.cpp

 std::vector<LLMutex*> LLCurl::sSSLMutex;
 std::string LLCurl::sCAPath;
 std::string LLCurl::sCAFile;
-
-bool LLCurl::sMultiThreaded = false;
-static U32 sMainThreadID = 0;
+LLCurlThread* LLCurl::sCurlThread = NULL ;
 
 void check_curl_code(CURLcode code)
 {
 
 std::set<CURL*> LLCurl::Easy::sFreeHandles;
 std::set<CURL*> LLCurl::Easy::sActiveHandles;
-LLMutex* LLCurl::Easy::sHandleMutex = NULL;
-LLMutex* LLCurl::Easy::sMultiMutex = NULL;
+LLMutex* LLCurl::Easy::sHandleMutexp = NULL ;
 
 //static
 CURL* LLCurl::Easy::allocEasyHandle()
 {
 	CURL* ret = NULL;
-	LLMutexLock lock(sHandleMutex);
+
+	LLMutexLock lock(sHandleMutexp) ;
+
 	if (sFreeHandles.empty())
 	{
 		ret = curl_easy_init();
 		llerrs << "handle cannot be NULL!" << llendl;
 	}
 
-	LLMutexLock lock(sHandleMutex);
-	
+	LLMutexLock lock(sHandleMutexp) ;
 	if (sActiveHandles.find(handle) != sActiveHandles.end())
 	{
 		sActiveHandles.erase(handle);
 }
 
 ////////////////////////////////////////////////////////////////////////////
-
+LLMutex* LLCurl::Multi::sMultiInitMutexp = NULL ;
 LLCurl::Multi::Multi()
-	: LLThread("Curl Multi"),
-	  mQueued(0),
+	: mQueued(0),
 	  mErrorCount(0),
-	  mPerformState(PERFORM_STATE_READY)
+	  mState(STATE_READY),
+	  mDead(FALSE),
+	  mMutexp(NULL),
+	  mDeletionMutexp(NULL),
+	  mEasyMutexp(NULL)
 {
-	mQuitting = false;
-
-	mThreaded = LLCurl::sMultiThreaded && LLThread::currentID() == sMainThreadID;
-	if (mThreaded)
-	{
-		mSignal = new LLCondition(NULL);
-	}
-	else
-	{
-		mSignal = NULL;
-	}
-
-	mCurlMultiHandle = curl_multi_init();
+	mCurlMultiHandle = initMulti();
 	if (!mCurlMultiHandle)
 	{
 		llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl;
-		mCurlMultiHandle = curl_multi_init();
+		mCurlMultiHandle = initMulti();
 	}
 	
-	llassert_always(mCurlMultiHandle);
+	llassert_always(mCurlMultiHandle);	
+
+	if(LLCurl::getCurlThread()->getThreaded())
+	{
+		mMutexp = new LLMutex(NULL) ;
+		mDeletionMutexp = new LLMutex(NULL) ;
+		mEasyMutexp = new LLMutex(NULL) ;
+	}
+	LLCurl::getCurlThread()->addMulti(this) ;
+
 	++gCurlMultiCount;
 }
 
 LLCurl::Multi::~Multi()
 {
-	llassert(isStopped());
-
-	if (LLCurl::sMultiThreaded)
-	{
-		LLCurl::Easy::sMultiMutex->lock();
-	}
-
-	delete mSignal;
-	mSignal = NULL;
-
 	// Clean up active
 	for(easy_active_list_t::iterator iter = mEasyActiveList.begin();
 		iter != mEasyActiveList.end(); ++iter)
 	mEasyFreeList.clear();
 
 	check_curl_multi_code(curl_multi_cleanup(mCurlMultiHandle));
+
+	delete mMutexp ;
+	mMutexp = NULL ;
+	delete mDeletionMutexp ;
+	mDeletionMutexp = NULL ;
+	delete mEasyMutexp ;
+	mEasyMutexp = NULL ;
+
 	--gCurlMultiCount;
+}
 
-	if (LLCurl::sMultiThreaded)
+CURLM* LLCurl::Multi::initMulti()
+{
+	LLMutexLock lock(sMultiInitMutexp) ;
+
+	return curl_multi_init() ;
+}
+
+void LLCurl::Multi::lock()
+{
+	if(mMutexp)
 	{
-		LLCurl::Easy::sMultiMutex->unlock();
+		mMutexp->lock() ;
 	}
 }
 
+void LLCurl::Multi::unlock()
+{
+	if(mMutexp)
+	{
+		mMutexp->unlock() ;
+	}
+}
+
+void LLCurl::Multi::markDead()
+{
+	LLMutexLock lock(mDeletionMutexp) ;
+	
+	mDead = TRUE ;
+}
+
+void LLCurl::Multi::setState(LLCurl::Multi::ePerformState state)
+{
+	lock() ;
+	mState = state ;
+	unlock() ;
+
+	if(mState == STATE_READY)
+	{
+		LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_NORMAL) ;
+	}	
+}
+
+LLCurl::Multi::ePerformState LLCurl::Multi::getState()
+{
+	return mState;
+}
+	
+bool LLCurl::Multi::isCompleted() 
+{
+	return STATE_COMPLETED == getState() ;
+}
+
+bool LLCurl::Multi::waitToComplete()
+{
+	if(!mMutexp) //not threaded
+	{
+		doPerform() ;
+		return true ;
+	}
+
+	bool completed = (STATE_COMPLETED == mState) ;
+	if(!completed)
+	{
+		LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_URGENT) ;
+	}
+	
+	return completed;
+}
+
 CURLMsg* LLCurl::Multi::info_read(S32* msgs_in_queue)
 {
+	LLMutexLock lock(mMutexp) ;
+
 	CURLMsg* curlmsg = curl_multi_info_read(mCurlMultiHandle, msgs_in_queue);
 	return curlmsg;
 }
 
-void LLCurl::Multi::perform()
+//return true if dead
+bool LLCurl::Multi::doPerform()
 {
-	if (mThreaded)
+	LLMutexLock lock(mDeletionMutexp) ;
+	
+	bool dead = mDead ;
+
+	if(mDead)
 	{
-		if (mPerformState == PERFORM_STATE_READY)
+		setState(STATE_COMPLETED);
+		mQueued = 0 ;
+	}
+	else if(getState() != STATE_COMPLETED)
+	{		
+		setState(STATE_PERFORMING);
+
+		S32 q = 0;
+		for (S32 call_count = 0;
+				call_count < MULTI_PERFORM_CALL_REPEAT;
+				call_count++)
 		{
-			mSignal->signal();
+			LLMutexLock lock(mMutexp) ;
+			CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q);
+			if (CURLM_CALL_MULTI_PERFORM != code || q == 0)
+			{
+				check_curl_multi_code(code);
+			
+				break;
+			}
 		}
+
+		mQueued = q;	
+		setState(STATE_COMPLETED) ;
 	}
-	else
-	{
-		doPerform();
-	}
-}
 
-void LLCurl::Multi::run()
-{
-	llassert(mThreaded);
-
-	while (!mQuitting)
-	{
-		mSignal->wait();
-		mPerformState = PERFORM_STATE_PERFORMING;
-		if (!mQuitting)
-		{
-			LLMutexLock lock(LLCurl::Easy::sMultiMutex);
-			doPerform();
-		}
-	}
-}
-
-void LLCurl::Multi::doPerform()
-{
-	S32 q = 0;
-	for (S32 call_count = 0;
-			call_count < MULTI_PERFORM_CALL_REPEAT;
-			call_count += 1)
-	{
-		CURLMcode code = curl_multi_perform(mCurlMultiHandle, &q);
-		if (CURLM_CALL_MULTI_PERFORM != code || q == 0)
-		{
-			check_curl_multi_code(code);
-			break;
-		}
-	
-	}
-	mQueued = q;
-	mPerformState = PERFORM_STATE_COMPLETED;
+	return dead ;
 }
 
 S32 LLCurl::Multi::process()
 {
-	perform();
+	waitToComplete() ;
 
-	if (mPerformState != PERFORM_STATE_COMPLETED)
+	if (getState() != STATE_COMPLETED)
 	{
 		return 0;
 	}
 		if (msg->msg == CURLMSG_DONE)
 		{
 			U32 response = 0;
-			easy_active_map_t::iterator iter = mEasyActiveMap.find(msg->easy_handle);
-			if (iter != mEasyActiveMap.end())
+			Easy* easy = NULL ;
+
 			{
-				Easy* easy = iter->second;
+				LLMutexLock lock(mEasyMutexp) ;
+				easy_active_map_t::iterator iter = mEasyActiveMap.find(msg->easy_handle);
+				if (iter != mEasyActiveMap.end())
+				{
+					easy = iter->second;
+				}
+			}
+
+			if(easy)
+			{
 				response = easy->report(msg->data.result);
 				removeEasy(easy);
 			}
 		}
 	}
 
-	mPerformState = PERFORM_STATE_READY;
+	setState(STATE_READY);
+
 	return processed;
 }
 
 LLCurl::Easy* LLCurl::Multi::allocEasy()
 {
-	Easy* easy = 0;
+	Easy* easy = 0;	
 
 	if (mEasyFreeList.empty())
-	{
+	{		
 		easy = Easy::getEasy();
 	}
 	else
 	{
+		LLMutexLock lock(mEasyMutexp) ;
 		easy = *(mEasyFreeList.begin());
 		mEasyFreeList.erase(easy);
 	}
 	if (easy)
 	{
+		LLMutexLock lock(mEasyMutexp) ;
 		mEasyActiveList.insert(easy);
 		mEasyActiveMap[easy->getCurlHandle()] = easy;
 	}
 
 bool LLCurl::Multi::addEasy(Easy* easy)
 {
+	LLMutexLock lock(mMutexp) ;
 	CURLMcode mcode = curl_multi_add_handle(mCurlMultiHandle, easy->getCurlHandle());
 	check_curl_multi_code(mcode);
 	//if (mcode != CURLM_OK)
 
 void LLCurl::Multi::easyFree(Easy* easy)
 {
+	if(mEasyMutexp)
+	{
+		mEasyMutexp->lock() ;
+	}
+
 	mEasyActiveList.erase(easy);
 	mEasyActiveMap.erase(easy->getCurlHandle());
+
 	if (mEasyFreeList.size() < EASY_HANDLE_POOL_SIZE)
-	{
+	{		
+		mEasyFreeList.insert(easy);
+		
+		if(mEasyMutexp)
+		{
+			mEasyMutexp->unlock() ;
+		}
+
 		easy->resetState();
-		mEasyFreeList.insert(easy);
 	}
 	else
 	{
+		if(mEasyMutexp)
+		{
+			mEasyMutexp->unlock() ;
+		}
 		delete easy;
 	}
 }
 
 void LLCurl::Multi::removeEasy(Easy* easy)
 {
-	check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle()));
+	{
+		LLMutexLock lock(mMutexp) ;
+		check_curl_multi_code(curl_multi_remove_handle(mCurlMultiHandle, easy->getCurlHandle()));
+	}
 	easyFree(easy);
 }
 
+//------------------------------------------------------------
+//LLCurlThread
+LLCurlThread::CurlRequest::CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread) :
+	LLQueuedThread::QueuedRequest(handle, LLQueuedThread::PRIORITY_NORMAL, FLAG_AUTO_COMPLETE),
+	mMulti(multi),
+	mCurlThread(curl_thread)
+{	
+}
+
+LLCurlThread::CurlRequest::~CurlRequest()
+{	
+	if(mMulti)
+	{
+		mCurlThread->deleteMulti(mMulti) ;
+		mMulti = NULL ;
+	}
+}
+
+bool LLCurlThread::CurlRequest::processRequest()
+{
+	bool completed = true ;
+	if(mMulti)
+	{
+		completed = mCurlThread->doMultiPerform(mMulti) ;
+		setPriority(LLQueuedThread::PRIORITY_LOW) ;
+	}
+
+	return completed ;
+}
+
+void LLCurlThread::CurlRequest::finishRequest(bool completed)
+{
+	mCurlThread->deleteMulti(mMulti) ;
+	mMulti = NULL ;
+}
+	
+LLCurlThread::LLCurlThread(bool threaded) :
+	LLQueuedThread("curlthread", threaded)
+{
+	if(!LLCurl::Multi::sMultiInitMutexp)
+	{
+		LLCurl::Multi::sMultiInitMutexp = new LLMutex(NULL) ;
+	}
+}
+	
+//virtual 
+LLCurlThread::~LLCurlThread() 
+{
+	delete LLCurl::Multi::sMultiInitMutexp ;
+	LLCurl::Multi::sMultiInitMutexp = NULL ;
+}
+
+S32 LLCurlThread::update(U32 max_time_ms)
+{	
+	return LLQueuedThread::update(max_time_ms);
+}
+
+void LLCurlThread::addMulti(LLCurl::Multi* multi)
+{
+	multi->mHandle = generateHandle() ;
+
+	CurlRequest* req = new CurlRequest(multi->mHandle, multi, this) ;
+
+	if (!addRequest(req))
+	{
+		llwarns << "curl request added when the thread is quitted" << llendl;
+	}
+}
+	
+void LLCurlThread::killMulti(LLCurl::Multi* multi)
+{
+	multi->markDead() ;
+}
+
+//private
+bool LLCurlThread::doMultiPerform(LLCurl::Multi* multi) 
+{
+	return multi->doPerform() ;
+}
+
+//private
+void LLCurlThread::deleteMulti(LLCurl::Multi* multi) 
+{
+	delete multi ;
+}
+//------------------------------------------------------------
+
 //static
 std::string LLCurl::strerror(CURLcode errorcode)
 {
 	mActiveMulti(NULL),
 	mActiveRequestCount(0)
 {
-	mThreadID = LLThread::currentID();
 	mProcessing = FALSE;
 }
 
 LLCurlRequest::~LLCurlRequest()
 {
-	llassert_always(mThreadID == LLThread::currentID());
-
 	//stop all Multi handle background threads
 	for (curlmulti_set_t::iterator iter = mMultiSet.begin(); iter != mMultiSet.end(); ++iter)
 	{
-		LLCurl::Multi* multi = *iter;
-		multi->mQuitting = true;
-		if (multi->mThreaded)
-		{
-			while (!multi->isStopped())
-			{
-				multi->mSignal->signal();
-				apr_sleep(1000);
-			}
-		}
+		LLCurl::getCurlThread()->killMulti(*iter) ;
 	}
-	for_each(mMultiSet.begin(), mMultiSet.end(), DeletePointer());
+	mMultiSet.clear() ;
 }
 
 void LLCurlRequest::addMulti()
 {
-	llassert_always(mThreadID == LLThread::currentID());
 	LLCurl::Multi* multi = new LLCurl::Multi();
-	if (multi->mThreaded)
-	{
-		multi->start();
-	}
+	
 	mMultiSet.insert(multi);
 	mActiveMulti = multi;
 	mActiveRequestCount = 0;
 // Note: call once per frame
 S32 LLCurlRequest::process()
 {
-	llassert_always(mThreadID == LLThread::currentID());
 	S32 res = 0;
 
 	mProcessing = TRUE;
 		if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0)
 		{
 			mMultiSet.erase(curiter);
-			multi->mQuitting = true;
-			if (multi->mThreaded)
-			{
-				while (!multi->isStopped())
-				{
-					multi->mSignal->signal();
-					apr_sleep(1000);
-				}
-			}
-
-			delete multi;
+			LLCurl::getCurlThread()->killMulti(multi);
 		}
 	}
 	mProcessing = FALSE;
 
 S32 LLCurlRequest::getQueued()
 {
-	llassert_always(mThreadID == LLThread::currentID());
 	S32 queued = 0;
 	for (curlmulti_set_t::iterator iter = mMultiSet.begin();
 		 iter != mMultiSet.end(); )
 		curlmulti_set_t::iterator curiter = iter++;
 		LLCurl::Multi* multi = *curiter;
 		queued += multi->mQueued;
-		if (multi->mPerformState != LLCurl::Multi::PERFORM_STATE_READY)
+		if (multi->getState() != LLCurl::Multi::STATE_READY)
 		{
 			++queued;
 		}
 	  mResultReturned(false)
 {
 	mMulti = new LLCurl::Multi();
-	if (mMulti->mThreaded)
-	{
-		mMulti->start();
-	}
+	
 	mEasy = mMulti->allocEasy();
 	if (mEasy)
 	{
 
 LLCurlEasyRequest::~LLCurlEasyRequest()
 {
-	mMulti->mQuitting = true;
-	if (mMulti->mThreaded)
-	{
-		while (!mMulti->isStopped())
-		{
-			mMulti->mSignal->signal();
-			apr_sleep(1000);
-		}
-	}
-	delete mMulti;
+	LLCurl::getCurlThread()->killMulti(mMulti) ;
 }
 	
 void LLCurlEasyRequest::setopt(CURLoption option, S32 value)
 	}
 }
 
-void LLCurlEasyRequest::perform()
-{
-	mMulti->perform();
-}
-
 // Usage: Call getRestult until it returns false (no more messages)
 bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info)
 {
-	if (mMulti->mPerformState != LLCurl::Multi::PERFORM_STATE_COMPLETED)
+	if (!mMulti->isCompleted())
 	{ //we're busy, try again later
 		return false;
 	}
-	mMulti->mPerformState = LLCurl::Multi::PERFORM_STATE_READY;
+	mMulti->setState(LLCurl::Multi::STATE_READY) ;
 
 	if (!mEasy)
 	{
 
 void LLCurl::initClass(bool multi_threaded)
 {
-	sMainThreadID = LLThread::currentID();
-	sMultiThreaded = multi_threaded;
 	// Do not change this "unless you are familiar with and mean to control 
 	// internal operations of libcurl"
 	// - http://curl.haxx.se/libcurl/c/curl_global_init.html
 
 	check_curl_code(code);
 	
-	Easy::sHandleMutex = new LLMutex(NULL);
-	Easy::sMultiMutex = new LLMutex(NULL);
-
 #if SAFE_SSL
 	S32 mutex_count = CRYPTO_num_locks();
 	for (S32 i=0; i<mutex_count; i++)
 	CRYPTO_set_id_callback(&LLCurl::ssl_thread_id);
 	CRYPTO_set_locking_callback(&LLCurl::ssl_locking_callback);
 #endif
+
+	sCurlThread = new LLCurlThread(multi_threaded) ;
+	if(multi_threaded)
+	{
+		Easy::sHandleMutexp = new LLMutex(NULL) ;
+	}
 }
 
 void LLCurl::cleanupClass()
 {
+	//shut down curl thread
+	while(1)
+	{
+		if(!sCurlThread->update(1)) //finish all tasks
+		{
+			break ;
+		}
+	}
+	sCurlThread->shutdown() ;
+	delete sCurlThread ;
+	sCurlThread = NULL ;
+
 #if SAFE_SSL
 	CRYPTO_set_locking_callback(NULL);
 	for_each(sSSLMutex.begin(), sSSLMutex.end(), DeletePointer());
 #endif
 
-	delete Easy::sHandleMutex;
-	Easy::sHandleMutex = NULL;
-	delete Easy::sMultiMutex;
-	Easy::sMultiMutex = NULL;
-
 	for (std::set<CURL*>::iterator iter = Easy::sFreeHandles.begin(); iter != Easy::sFreeHandles.end(); ++iter)
 	{
 		CURL* curl = *iter;
 
 	Easy::sFreeHandles.clear();
 
+	delete Easy::sHandleMutexp ;
+	Easy::sHandleMutexp = NULL ;
+
 	llassert(Easy::sActiveHandles.empty());
 }
 

File indra/llmessage/llcurl.h

 #include "lliopipe.h"
 #include "llsd.h"
 #include "llthread.h"
+#include "llqueuedthread.h"
 
 class LLMutex;
+class LLCurlThread;
 
 // For whatever reason, this is not typedef'd in curl.h
 typedef size_t (*curl_header_callback)(void *ptr, size_t size, size_t nmemb, void *stream);
 	class Easy;
 	class Multi;
 
-	static bool sMultiThreaded;
-
 	struct TransferInfo
 	{
 		TransferInfo() : mSizeDownload(0.0), mTotalTime(0.0), mSpeedDownload(0.0) {}
 	static void ssl_locking_callback(int mode, int type, const char *file, int line);
 	static unsigned long ssl_thread_id(void);
 
+	static LLCurlThread* getCurlThread() { return sCurlThread ;}
 private:
 	static std::string sCAPath;
 	static std::string sCAFile;
 	static const unsigned int MAX_REDIRECTS;
+	static LLCurlThread* sCurlThread;
 };
 
 class LLCurl::Easy
 	U32 report(CURLcode);
 	void getTransferInfo(LLCurl::TransferInfo* info);
 
-	void prepRequest(const std::string& url, const std::vector<std::string>& headers, ResponderPtr, S32 time_out = 0, bool post = false);
+	void prepRequest(const std::string& url, const std::vector<std::string>& headers, LLCurl::ResponderPtr, S32 time_out = 0, bool post = false);
 
 	const char* getErrorBuffer();
 
 	// Note: char*'s not strings since we pass pointers to curl
 	std::vector<char*>	mStrings;
 
-	ResponderPtr		mResponder;
+	LLCurl::ResponderPtr		mResponder;
 
 	static std::set<CURL*> sFreeHandles;
 	static std::set<CURL*> sActiveHandles;
-	static LLMutex* sHandleMutex;
-	static LLMutex* sMultiMutex;
+	static LLMutex*        sHandleMutexp ;
 };
 
-class LLCurl::Multi : public LLThread
+class LLCurl::Multi
 {
 	LOG_CLASS(Multi);
+
+	friend class LLCurlThread ;
+
+private:
+	~Multi();
+
+	void markDead() ;
+	bool doPerform();
+
 public:
 
 	typedef enum
 	{
-		PERFORM_STATE_READY=0,
-		PERFORM_STATE_PERFORMING=1,
-		PERFORM_STATE_COMPLETED=2
+		STATE_READY=0,
+		STATE_PERFORMING=1,
+		STATE_COMPLETED=2
 	} ePerformState;
 
-	Multi();
-	~Multi();
+	Multi();	
 
-	Easy* allocEasy();
-	bool addEasy(Easy* easy);
+	LLCurl::Easy* allocEasy();
+	bool addEasy(LLCurl::Easy* easy);	
+	void removeEasy(LLCurl::Easy* easy);
 	
-	void removeEasy(Easy* easy);
+	void lock() ;
+	void unlock() ;
+
+	void setState(ePerformState state) ;
+	ePerformState getState() ;
+	bool isCompleted() ;
+
+	bool waitToComplete() ;
 
 	S32 process();
-	void perform();
-	void doPerform();
 	
-	virtual void run();
-
 	CURLMsg* info_read(S32* msgs_in_queue);
 
 	S32 mQueued;
 	S32 mErrorCount;
 	
-	S32 mPerformState;
-
-	LLCondition* mSignal;
-	bool mQuitting;
-	bool mThreaded;
-
+	static CURLM* initMulti() ;
 private:
-	void easyFree(Easy*);
+	void easyFree(LLCurl::Easy*);
 	
 	CURLM* mCurlMultiHandle;
 
-	typedef std::set<Easy*> easy_active_list_t;
+	typedef std::set<LLCurl::Easy*> easy_active_list_t;
 	easy_active_list_t mEasyActiveList;
-	typedef std::map<CURL*, Easy*> easy_active_map_t;
+	typedef std::map<CURL*, LLCurl::Easy*> easy_active_map_t;
 	easy_active_map_t mEasyActiveMap;
-	typedef std::set<Easy*> easy_free_list_t;
+	typedef std::set<LLCurl::Easy*> easy_free_list_t;
 	easy_free_list_t mEasyFreeList;
+
+	LLQueuedThread::handle_t mHandle ;
+	ePerformState mState;
+
+	BOOL mDead ;
+	LLMutex* mMutexp ;
+	LLMutex* mDeletionMutexp ;
+	LLMutex* mEasyMutexp ;
+
+	static LLMutex* sMultiInitMutexp ;
 };
 
+class LLCurlThread : public LLQueuedThread
+{
+public:
+
+	class CurlRequest : public LLQueuedThread::QueuedRequest
+	{
+	protected:
+		virtual ~CurlRequest(); // use deleteRequest()
+		
+	public:
+		CurlRequest(handle_t handle, LLCurl::Multi* multi, LLCurlThread* curl_thread);
+
+		/*virtual*/ bool processRequest();
+		/*virtual*/ void finishRequest(bool completed);
+
+	private:
+		// input
+		LLCurl::Multi* mMulti;
+		LLCurlThread*  mCurlThread;
+	};
+	friend class CurlRequest;
+
+public:
+	LLCurlThread(bool threaded = true) ;
+	virtual ~LLCurlThread() ;
+
+	S32 update(U32 max_time_ms);
+
+	void addMulti(LLCurl::Multi* multi) ;
+	void killMulti(LLCurl::Multi* multi) ;
+
+private:
+	bool doMultiPerform(LLCurl::Multi* multi) ;
+	void deleteMulti(LLCurl::Multi* multi) ;
+} ;
+
 namespace boost
 {
 	void intrusive_ptr_add_ref(LLCurl::Responder* p);
 	LLCurl::Multi* mActiveMulti;
 	S32 mActiveRequestCount;
 	BOOL mProcessing;
-	U32 mThreadID; // debug
 };
 
 class LLCurlEasyRequest
 	void slist_append(const char* str);
 	void sendRequest(const std::string& url);
 	void requestComplete();
-	void perform();
 	bool getResult(CURLcode* result, LLCurl::TransferInfo* info = NULL);
 	std::string getErrorString();
+	bool isCompleted() {return mMulti->isCompleted() ;}
+	bool wait() { return mMulti->waitToComplete(); }
 
 	LLCurl::Easy* getEasy() const { return mEasy; }
 

File indra/llmessage/llsdmessagereader.cpp

 	case LLSD::TypeMap:
 	case LLSD::TypeArray:
 	case LLSD::TypeUndefined:
+	default:                        // TypeLLSDTypeEnd, TypeLLSDNumTypes, etc.
 		return 0;
 	}
-	return 0;
+	//return 0;
 }
 
 //virtual 

File indra/llmessage/llurlrequest.cpp

 {
 	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
 	delete mDetail;
+	mDetail = NULL ;
 }
 
 void LLURLRequest::setURL(const std::string& url)
 		static LLFastTimer::DeclareTimer FTM_URL_PERFORM("Perform");
 		{
 			LLFastTimer t(FTM_URL_PERFORM);
<