Commits

Anonymous committed c95b998 Merge
  • Participants
  • Parent commits 0e5b335, dd5ca3d

Comments (0)

Files changed (72)

 80f3e30d8aa4d8f674a48bd742aaa6d8e9eae0b5 3.2.3-start
 a8c7030d6845186fac7c188be4323a0e887b4184 DRTVWR-99_3.2.1-release
 a8c7030d6845186fac7c188be4323a0e887b4184 3.2.1-release
+a9abb9633a266c8d2fe62411cfd1c86d32da72bf DRTVWR-60_2.7.1-release
+fe3a8e7973072ea62043c08b19b66626c1a720eb DRTVWR-60_2.7.1-release
+a9abb9633a266c8d2fe62411cfd1c86d32da72bf 2.7.1-release
+fe3a8e7973072ea62043c08b19b66626c1a720eb 2.7.1-release
 3fe994349fae64fc40874bb59db387131eb35a41 3.2.4-start
 a9abb9633a266c8d2fe62411cfd1c86d32da72bf DRTVWR-60_2.7.1-release
 fe3a8e7973072ea62043c08b19b66626c1a720eb DRTVWR-60_2.7.1-release
 3d2d5d244c6398a4214c666d5dd3965b0918709a 3.2.5-beta1
 65a2c1c8d855b88edfbea4e16ef2f27e7cff8b1d DRTVWR-107_3.2.5-beta2
 65a2c1c8d855b88edfbea4e16ef2f27e7cff8b1d 3.2.5-beta2
+c6175c955a19e9b9353d242889ec1779b5762522 DRTVWR-105_3.2.5-release
+c6175c955a19e9b9353d242889ec1779b5762522 3.2.5-release
 2174ed1c7129562428a5cfe8651ed77b8d26ae18 3.2.6-start
+286d73ff5c19f6c00e023dc1b60975ed6bbe2872 DRTVWR-109_3.2.6-beta1
 4891c46a56fed7512c783b9cbe7cb7260727bf0c 3.2.7-start
+286d73ff5c19f6c00e023dc1b60975ed6bbe2872 3.2.6-beta1
+c6175c955a19e9b9353d242889ec1779b5762522 DRTVWR-105_3.2.5-release
+c6175c955a19e9b9353d242889ec1779b5762522 3.2.5-release
+3d75c836d178c7c7e788f256afe195f6cab764a2 DRTVWR-111_3.2.7-beta1
+3d75c836d178c7c7e788f256afe195f6cab764a2 3.2.7-beta1
+89980333c99dbaf1787fe20784f1d8849e9b5d4f 3.2.8-start
+37dd400ad721e2a89ee820ffc1e7e433c68f3ca2 3.2.9-start

File doc/contributions.txt

 	STORM-1737
 	STORM-1733
 	STORM-1790
+	STORM-1788
+	STORM-1799
+	STORM-1796
 Kadah Coba
 	STORM-1060
 Jondan Lundquist

File indra/llcommon/llthread.cpp

 
 void LLMutex::lock()
 {
-#if LL_DARWIN
-	if (mLockingThread == LLThread::currentID())
-#else
-	if (mLockingThread == sThreadID)
-#endif
+	if(isSelfLocked())
 	{ //redundant lock
 		mCount++;
 		return;
 	}
 }
 
+bool LLMutex::isSelfLocked()
+{
+#if LL_DARWIN
+	return mLockingThread == LLThread::currentID();
+#else
+	return mLockingThread == sThreadID;
+#endif
+}
+
 U32 LLMutex::lockingThread() const
 {
 	return mLockingThread;

File indra/llcommon/llthread.h

 	void lock();		// blocks
 	void unlock();
 	bool isLocked(); 	// non-blocking, but does do a lock/unlock so not free
+	bool isSelfLocked(); //return true if locked in a same thread
 	U32 lockingThread() const; //get ID of locking thread
 	
 protected:

File indra/llcommon/llversionviewer.h

 #define LL_LLVERSIONVIEWER_H
 
 const S32 LL_VERSION_MAJOR = 3;
-const S32 LL_VERSION_MINOR = 2;
-const S32 LL_VERSION_PATCH = 8;
+const S32 LL_VERSION_MINOR = 3;
+const S32 LL_VERSION_PATCH = 0;
 const S32 LL_VERSION_BUILD = 0;
 
 const char * const LL_CHANNEL = "Second Life Developer";

File indra/llmath/llvolume.cpp

 	mFaceMask = 0x0;
 	mDetail = detail;
 	mSculptLevel = -2;
+	mSurfaceArea = 1.f; //only calculated for sculpts, defaults to 1 for all other prims
 	mIsMeshAssetLoaded = FALSE;
 	mLODScaleBias.setVec(1,1,1);
 	mHullPoints = NULL;
 		{
 			F32 area = sculptGetSurfaceArea();
 
+			mSurfaceArea = area;
+
 			const F32 SCULPT_MAX_AREA = 384.f;
 
 			if (area < SCULPT_MIN_AREA || area > SCULPT_MAX_AREA)

File indra/llmath/llvolume.h

 	S32	getNumFaces() const;
 	S32 getNumVolumeFaces() const							{ return mVolumeFaces.size(); }
 	F32 getDetail() const									{ return mDetail; }
+	F32 getSurfaceArea() const								{ return mSurfaceArea; }
 	const LLVolumeParams& getParams() const					{ return mParams; }
 	LLVolumeParams getCopyOfParams() const					{ return mParams; }
 	const LLProfile& getProfile() const						{ return *mProfilep; }
 	BOOL mUnique;
 	F32 mDetail;
 	S32 mSculptLevel;
+	F32 mSurfaceArea; //unscaled surface area
 	BOOL mIsMeshAssetLoaded;
 	
 	LLVolumeParams mParams;

File indra/llmessage/llbuffer.cpp

 #include "llmath.h"
 #include "llmemtype.h"
 #include "llstl.h"
+#include "llthread.h"
+
+#define ASSERT_LLBUFFERARRAY_MUTEX_LOCKED llassert(!mMutexp || mMutexp->isSelfLocked());
 
 /** 
  * LLSegment
  * LLBufferArray
  */
 LLBufferArray::LLBufferArray() :
-	mNextBaseChannel(0)
+	mNextBaseChannel(0),
+	mMutexp(NULL)
 {
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 }
 {
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 	std::for_each(mBuffers.begin(), mBuffers.end(), DeletePointer());
+
+	delete mMutexp;
 }
 
 // static
 	return rv;
 }
 
+void LLBufferArray::lock()
+{
+	if(mMutexp)
+	{
+		mMutexp->lock() ;
+	}
+}
+
+void LLBufferArray::unlock()
+{
+	if(mMutexp)
+	{
+		mMutexp->unlock() ;
+	}
+}
+
+LLMutex* LLBufferArray::getMutex()
+{
+	return mMutexp ;
+}
+
+void LLBufferArray::setThreaded(bool threaded)
+{
+	if(threaded)
+	{
+		if(!mMutexp)
+		{
+			mMutexp = new LLMutex(NULL);
+		}
+	}
+	else
+	{
+		if(mMutexp)
+		{
+			delete mMutexp ;
+			mMutexp = NULL ;
+		}
+	}
+}
+
 LLChannelDescriptors LLBufferArray::nextChannel()
 {
 	LLChannelDescriptors rv(mNextBaseChannel++);
 	return rv;
 }
 
+//mMutexp should be locked before calling this.
 S32 LLBufferArray::capacity() const
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
+
 	S32 total = 0;
 	const_buffer_iterator_t iter = mBuffers.begin();
 	const_buffer_iterator_t end = mBuffers.end();
 
 bool LLBufferArray::append(S32 channel, const U8* src, S32 len)
 {
+	LLMutexLock lock(mMutexp) ;
+
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 	std::vector<LLSegment> segments;
 	if(copyIntoBuffers(channel, src, len, segments))
 	return false;
 }
 
+//mMutexp should be locked before calling this.
 bool LLBufferArray::prepend(S32 channel, const U8* src, S32 len)
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
+
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 	std::vector<LLSegment> segments;
 	if(copyIntoBuffers(channel, src, len, segments))
 {
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 	std::vector<LLSegment> segments;
+
+	LLMutexLock lock(mMutexp) ;
 	if(mSegments.end() != segment)
 	{
 		++segment;
 	return false;
 }
 
+//mMutexp should be locked before calling this.
 LLBufferArray::segment_iterator_t LLBufferArray::splitAfter(U8* address)
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
+
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 	segment_iterator_t end = mSegments.end();
 	segment_iterator_t it = getSegment(address);
 	return rv;
 }
 							   
+//mMutexp should be locked before calling this.
 LLBufferArray::segment_iterator_t LLBufferArray::beginSegment()
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
 	return mSegments.begin();
 }
 
+//mMutexp should be locked before calling this.
 LLBufferArray::segment_iterator_t LLBufferArray::endSegment()
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
 	return mSegments.end();
 }
 
+//mMutexp should be locked before calling this.
 LLBufferArray::segment_iterator_t LLBufferArray::constructSegmentAfter(
 	U8* address,
 	LLSegment& segment)
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 	segment_iterator_t rv = mSegments.begin();
 	segment_iterator_t end = mSegments.end();
 	return rv;
 }
 
+//mMutexp should be locked before calling this.
 LLBufferArray::segment_iterator_t LLBufferArray::getSegment(U8* address)
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
 	segment_iterator_t end = mSegments.end();
 	if(!address)
 	{
 	return end;
 }
 
+//mMutexp should be locked before calling this.
 LLBufferArray::const_segment_iterator_t LLBufferArray::getSegment(
 	U8* address) const
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
 	const_segment_iterator_t end = mSegments.end();
 	if(!address)
 	{
 	S32 count = 0;
 	S32 offset = 0;
 	const_segment_iterator_t it;
+
+	LLMutexLock lock(mMutexp) ;
 	const_segment_iterator_t end = mSegments.end();
 	if(start)
 	{
 	len = 0;
 	S32 bytes_to_copy = 0;
 	const_segment_iterator_t it;
+
+	LLMutexLock lock(mMutexp) ;
 	const_segment_iterator_t end = mSegments.end();
 	if(start)
 	{
 	U8* start,
 	S32 delta) const
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 	const_segment_iterator_t it;
 	const_segment_iterator_t end = mSegments.end();
 	return rv;
 }
 
+//test use only
 bool LLBufferArray::takeContents(LLBufferArray& source)
 {
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+
+	LLMutexLock lock(mMutexp);
+	source.lock();
+
 	std::copy(
 		source.mBuffers.begin(),
 		source.mBuffers.end(),
 		std::back_insert_iterator<segment_list_t>(mSegments));
 	source.mSegments.clear();
 	source.mNextBaseChannel = 0;
+	source.unlock();
+
 	return true;
 }
 
+//mMutexp should be locked before calling this.
 LLBufferArray::segment_iterator_t LLBufferArray::makeSegment(
 	S32 channel,
 	S32 len)
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 	// start at the end of the buffers, because it is the most likely
 	// to have free space.
 	return send;
 }
 
+//mMutexp should be locked before calling this.
 bool LLBufferArray::eraseSegment(const segment_iterator_t& erase_iter)
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 
 	// Find out which buffer contains the segment, and if it is found,
 	return rv;
 }
 
-
+//mMutexp should be locked before calling this.
 bool LLBufferArray::copyIntoBuffers(
 	S32 channel,
 	const U8* src,
 	S32 len,
 	std::vector<LLSegment>& segments)
 {
+	ASSERT_LLBUFFERARRAY_MUTEX_LOCKED
 	LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
 	if(!src || !len) return false;
 	S32 copied = 0;

File indra/llmessage/llbuffer.h

 #include <list>
 #include <vector>
 
+class LLMutex;
 /** 
  * @class LLChannelDescriptors
  * @brief A way simple interface to accesss channels inside a buffer
 	 * @return Returns true on success.
 	 */
 	bool eraseSegment(const segment_iterator_t& iter);
+
+	/**
+	* @brief Lock the mutex if it exists
+	* This method locks mMutexp to make accessing LLBufferArray thread-safe
+	*/
+	void lock();
+
+	/**
+	* @brief Unlock the mutex if it exists
+	*/
+	void unlock();
+
+	/**
+	* @brief Return mMutexp
+	*/
+	LLMutex* getMutex();
+
+	/**
+	* @brief Set LLBufferArray to be shared across threads or not
+	* This method is to create mMutexp if is threaded.
+	* @param threaded Indicates this LLBufferArray instance is shared across threads if true.
+	*/
+	void setThreaded(bool threaded);
 	//@}
 
 protected:
 	S32 mNextBaseChannel;
 	buffer_list_t mBuffers;
 	segment_list_t mSegments;
+	LLMutex* mMutexp;
 };
 
 #endif // LL_LLBUFFER_H

File indra/llmessage/llbufferstream.cpp

 
 #include "llbuffer.h"
 #include "llmemtype.h"
+#include "llthread.h"
 
 static const S32 DEFAULT_OUTPUT_SEGMENT_SIZE = 1024 * 4;
 
 		return EOF;
 	}
 
+	LLMutexLock lock(mBuffer->getMutex());
 	LLBufferArray::segment_iterator_t iter;
 	LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
 	U8* last_pos = (U8*)gptr();
 	// since we got here, we have a buffer, and we have a character to
 	// put on it.
 	LLBufferArray::segment_iterator_t it;
+	LLMutexLock lock(mBuffer->getMutex());
 	it = mBuffer->makeSegment(mChannels.out(), DEFAULT_OUTPUT_SEGMENT_SIZE);
 	if(it != mBuffer->endSegment())
 	{
 
 	// *NOTE: I bet we could just --address if address is not NULL.
 	// Need to think about that.
+	LLMutexLock lock(mBuffer->getMutex());
 	address = mBuffer->seek(mChannels.out(), address, -1);
 	if(address)
 	{
 			// NULL is fine
 			break;
 		}
+
+		LLMutexLock lock(mBuffer->getMutex());
 		address = mBuffer->seek(mChannels.in(), base_addr, off);
 		if(address)
 		{
 			// NULL is fine
 			break;
 		}
+
+		LLMutexLock lock(mBuffer->getMutex());
 		address = mBuffer->seek(mChannels.out(), base_addr, off);
 		if(address)
 		{

File indra/llmessage/llcurl.cpp

 
 static const U32 EASY_HANDLE_POOL_SIZE		= 5;
 static const S32 MULTI_PERFORM_CALL_REPEAT	= 5;
-static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds
+static const S32 CURL_REQUEST_TIMEOUT = 30; // seconds per operation
 static const S32 MAX_ACTIVE_REQUEST_COUNT = 100;
 
-static 
 // DEBUG //
 S32 gCurlEasyCount = 0;
 S32 gCurlMultiCount = 0;
 std::string LLCurl::sCAPath;
 std::string LLCurl::sCAFile;
 LLCurlThread* LLCurl::sCurlThread = NULL ;
+LLMutex* LLCurl::sHandleMutexp = NULL ;
+S32      LLCurl::sTotalHandles = 0 ;
+bool     LLCurl::sNotQuitting = true;
+F32      LLCurl::sCurlRequestTimeOut = 120.f; //seonds
+S32      LLCurl::sMaxHandles = 256; //max number of handles, (multi handles and easy handles combined).
 
 void check_curl_code(CURLcode code)
 {
 //static
 CURL* LLCurl::Easy::allocEasyHandle()
 {
+	llassert(LLCurl::getCurlThread()) ;
+
 	CURL* ret = NULL;
 
 	LLMutexLock lock(sHandleMutexp) ;
 
 	if (sFreeHandles.empty())
 	{
-		ret = curl_easy_init();
+		ret = LLCurl::newEasyHandle();
 	}
 	else
 	{
 //static
 void LLCurl::Easy::releaseEasyHandle(CURL* handle)
 {
+	static const S32 MAX_NUM_FREE_HANDLES = 32 ;
+
 	if (!handle)
 	{
-		llerrs << "handle cannot be NULL!" << llendl;
+		return ; //handle allocation failed.
+		//llerrs << "handle cannot be NULL!" << llendl;
 	}
 
 	LLMutexLock lock(sHandleMutexp) ;
 	if (sActiveHandles.find(handle) != sActiveHandles.end())
 	{
 		sActiveHandles.erase(handle);
+
+		if(sFreeHandles.size() < MAX_NUM_FREE_HANDLES)
+		{
 		sFreeHandles.insert(handle);
 	}
 	else
 	{
+			LLCurl::deleteEasyHandle(handle) ;
+		}
+	}
+	else
+	{
 		llerrs << "Invalid handle." << llendl;
 	}
 }
 	--gCurlEasyCount;
 	curl_slist_free_all(mHeaders);
 	for_each(mStrings.begin(), mStrings.end(), DeletePointerArray());
+
+	if (mResponder && LLCurl::sNotQuitting) //aborted
+	{	
+		std::string reason("Request timeout, aborted.") ;
+		mResponder->completedRaw(408, //HTTP_REQUEST_TIME_OUT, timeout, abort
+			reason, mChannels, mOutput);		
+	}
+	mResponder = NULL;
 }
 
 void LLCurl::Easy::resetState()
 	LLProxy::getInstance()->applyProxySettings(this);
 
 	mOutput.reset(new LLBufferArray);
+	mOutput->setThreaded(true);
 	setopt(CURLOPT_WRITEFUNCTION, (void*)&curlWriteCallback);
 	setopt(CURLOPT_WRITEDATA, (void*)this);
 
 }
 
 ////////////////////////////////////////////////////////////////////////////
-LLMutex* LLCurl::Multi::sMultiInitMutexp = NULL ;
-LLCurl::Multi::Multi()
+LLCurl::Multi::Multi(F32 idle_time_out)
 	: mQueued(0),
 	  mErrorCount(0),
 	  mState(STATE_READY),
 	  mDeletionMutexp(NULL),
 	  mEasyMutexp(NULL)
 {
-	mCurlMultiHandle = initMulti();
+	mCurlMultiHandle = LLCurl::newMultiHandle();
 	if (!mCurlMultiHandle)
 	{
 		llwarns << "curl_multi_init() returned NULL! Easy handles: " << gCurlEasyCount << " Multi handles: " << gCurlMultiCount << llendl;
-		mCurlMultiHandle = initMulti();
+		mCurlMultiHandle = LLCurl::newMultiHandle();
 	}
 	
-	llassert_always(mCurlMultiHandle);	
+	//llassert_always(mCurlMultiHandle);	
 
+	if(mCurlMultiHandle)
+	{
 	if(LLCurl::getCurlThread()->getThreaded())
 	{
 		mMutexp = new LLMutex(NULL) ;
 	}
 	LLCurl::getCurlThread()->addMulti(this) ;
 
+		mIdleTimeOut = idle_time_out ;
+		if(mIdleTimeOut < LLCurl::sCurlRequestTimeOut)
+		{
+			mIdleTimeOut = LLCurl::sCurlRequestTimeOut ;
+		}
+
 	++gCurlMultiCount;
 }
+}
 
 LLCurl::Multi::~Multi()
 {
+	cleanup() ;	
+}
+
+void LLCurl::Multi::cleanup()
+{
+	if(!mCurlMultiHandle)
+	{
+		return ; //nothing to clean.
+	}
+
 	// Clean up active
 	for(easy_active_list_t::iterator iter = mEasyActiveList.begin();
 		iter != mEasyActiveList.end(); ++iter)
 	for_each(mEasyFreeList.begin(), mEasyFreeList.end(), DeletePointer());	
 	mEasyFreeList.clear();
 
-	check_curl_multi_code(curl_multi_cleanup(mCurlMultiHandle));
+	check_curl_multi_code(LLCurl::deleteMultiHandle(mCurlMultiHandle));
+	mCurlMultiHandle = NULL ;
 
 	delete mMutexp ;
 	mMutexp = NULL ;
 	delete mEasyMutexp ;
 	mEasyMutexp = NULL ;
 
+	mQueued = 0 ;
+	mState = STATE_COMPLETED;
+	
 	--gCurlMultiCount;
-}
 
-CURLM* LLCurl::Multi::initMulti()
-{
-	LLMutexLock lock(sMultiInitMutexp) ;
-
-	return curl_multi_init() ;
+	return ;
 }
 
 void LLCurl::Multi::lock()
 	LLMutexLock lock(mDeletionMutexp) ;
 	
 	mDead = TRUE ;
+	LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_URGENT) ; 
 }
 
 void LLCurl::Multi::setState(LLCurl::Multi::ePerformState state)
 
 bool LLCurl::Multi::waitToComplete()
 {
+	if(!isValid())
+	{
+		return true ;
+	}
+
 	if(!mMutexp) //not threaded
 	{
 		doPerform() ;
 	bool completed = (STATE_COMPLETED == mState) ;
 	if(!completed)
 	{
-		LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_URGENT) ;
+		LLCurl::getCurlThread()->setPriority(mHandle, LLQueuedThread::PRIORITY_HIGH) ;
 	}
 	
 	return completed;
 
 		mQueued = q;	
 		setState(STATE_COMPLETED) ;
+		mIdleTimer.reset() ;
+	}
+	else if(mIdleTimer.getElapsedTimeF32() > mIdleTimeOut) //idle for too long, remove it.
+	{
+		dead = true ;
 	}
 
 	return dead ;
 
 S32 LLCurl::Multi::process()
 {
+	if(!isValid())
+	{
+		return 0 ;
+	}
+
 	waitToComplete() ;
 
 	if (getState() != STATE_COMPLETED)
 	if(mMulti)
 	{
 		completed = mCurlThread->doMultiPerform(mMulti) ;
+
+		if(!completed)
+		{
 		setPriority(LLQueuedThread::PRIORITY_LOW) ;
 	}
+	}
 
 	return completed ;
 }
 
 void LLCurlThread::CurlRequest::finishRequest(bool completed)
 {
+	if(mMulti->isDead())
+	{
 	mCurlThread->deleteMulti(mMulti) ;
+	}
+	else
+	{
+		mCurlThread->cleanupMulti(mMulti) ; //being idle too long, remove the request.
+	}
+
 	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(F32 max_time_ms)
 	
 void LLCurlThread::killMulti(LLCurl::Multi* multi)
 {
+	if(!multi)
+	{
+		return ;
+	}
+
+	if(multi->isValid())
+	{
 	multi->markDead() ;
 }
+	else
+	{
+		deleteMulti(multi) ;
+	}
+}
 
 //private
 bool LLCurlThread::doMultiPerform(LLCurl::Multi* multi) 
 {
 	delete multi ;
 }
+
+//private
+void LLCurlThread::cleanupMulti(LLCurl::Multi* multi) 
+{
+	multi->cleanup() ;
+}
+
 //------------------------------------------------------------
 
 //static
 void LLCurlRequest::addMulti()
 {
 	LLCurl::Multi* multi = new LLCurl::Multi();
+	if(!multi->isValid())
+	{
+		LLCurl::getCurlThread()->killMulti(multi) ;
+		mActiveMulti = NULL ;
+		mActiveRequestCount = 0 ;
+		return;
+	}
 	
 	mMultiSet.insert(multi);
 	mActiveMulti = multi;
 	{
 		addMulti();
 	}
-	llassert_always(mActiveMulti);
+	if(!mActiveMulti)
+	{
+		return NULL ;
+	}
+
+	//llassert_always(mActiveMulti);
 	++mActiveRequestCount;
 	LLCurl::Easy* easy = mActiveMulti->allocEasy();
 	return easy;
 	{
 		curlmulti_set_t::iterator curiter = iter++;
 		LLCurl::Multi* multi = *curiter;
+
+		if(!multi->isValid())
+		{
+			if(multi == mActiveMulti)
+			{				
+				mActiveMulti = NULL ;
+				mActiveRequestCount = 0 ;
+			}
+			mMultiSet.erase(curiter) ;
+			LLCurl::getCurlThread()->killMulti(multi) ;
+			continue ;
+		}
+
 		S32 tres = multi->process();
 		res += tres;
 		if (multi != mActiveMulti && tres == 0 && multi->mQueued == 0)
 	{
 		curlmulti_set_t::iterator curiter = iter++;
 		LLCurl::Multi* multi = *curiter;
+		
+		if(!multi->isValid())
+		{
+			if(multi == mActiveMulti)
+			{				
+				mActiveMulti = NULL ;
+				mActiveRequestCount = 0 ;
+			}
+			LLCurl::getCurlThread()->killMulti(multi);
+			mMultiSet.erase(curiter) ;
+			continue ;
+		}
+
 		queued += multi->mQueued;
 		if (multi->getState() != LLCurl::Multi::STATE_READY)
 		{
 {
 	mMulti = new LLCurl::Multi();
 	
+	if(mMulti->isValid())
+	{
 	mEasy = mMulti->allocEasy();
 	if (mEasy)
 	{
 		LLProxy::getInstance()->applyProxySettings(mEasy);
 	}
 }
+	else
+	{
+		LLCurl::getCurlThread()->killMulti(mMulti) ;
+		mEasy = NULL ;
+		mMulti = NULL ;
+	}
+}
 
 LLCurlEasyRequest::~LLCurlEasyRequest()
 {
 	
 void LLCurlEasyRequest::setopt(CURLoption option, S32 value)
 {
-	if (mEasy)
+	if (isValid() && mEasy)
 	{
 		mEasy->setopt(option, value);
 	}
 
 void LLCurlEasyRequest::setoptString(CURLoption option, const std::string& value)
 {
-	if (mEasy)
+	if (isValid() && mEasy)
 	{
 		mEasy->setoptString(option, value);
 	}
 
 void LLCurlEasyRequest::setPost(char* postdata, S32 size)
 {
-	if (mEasy)
+	if (isValid() && mEasy)
 	{
 		mEasy->setopt(CURLOPT_POST, 1);
 		mEasy->setopt(CURLOPT_POSTFIELDS, postdata);
 
 void LLCurlEasyRequest::setHeaderCallback(curl_header_callback callback, void* userdata)
 {
-	if (mEasy)
+	if (isValid() && mEasy)
 	{
 		mEasy->setopt(CURLOPT_HEADERFUNCTION, (void*)callback);
 		mEasy->setopt(CURLOPT_HEADERDATA, userdata); // aka CURLOPT_WRITEHEADER
 
 void LLCurlEasyRequest::setWriteCallback(curl_write_callback callback, void* userdata)
 {
-	if (mEasy)
+	if (isValid() && mEasy)
 	{
 		mEasy->setopt(CURLOPT_WRITEFUNCTION, (void*)callback);
 		mEasy->setopt(CURLOPT_WRITEDATA, userdata);
 
 void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userdata)
 {
-	if (mEasy)
+	if (isValid() && mEasy)
 	{
 		mEasy->setopt(CURLOPT_READFUNCTION, (void*)callback);
 		mEasy->setopt(CURLOPT_READDATA, userdata);
 
 void LLCurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata)
 {
-	if (mEasy)
+	if (isValid() && mEasy)
 	{
 		mEasy->setopt(CURLOPT_SSL_CTX_FUNCTION, (void*)callback);
 		mEasy->setopt(CURLOPT_SSL_CTX_DATA, userdata);
 
 void LLCurlEasyRequest::slist_append(const char* str)
 {
-	if (mEasy)
+	if (isValid() && mEasy)
 	{
 		mEasy->slist_append(str);
 	}
 	llassert_always(!mRequestSent);
 	mRequestSent = true;
 	lldebugs << url << llendl;
-	if (mEasy)
+	if (isValid() && mEasy)
 	{
 		mEasy->setHeaders();
 		mEasy->setoptString(CURLOPT_URL, url);
 {
 	llassert_always(mRequestSent);
 	mRequestSent = false;
-	if (mEasy)
+	if (isValid() && mEasy)
 	{
 		mMulti->removeEasy(mEasy);
 	}
 // Usage: Call getRestult until it returns false (no more messages)
 bool LLCurlEasyRequest::getResult(CURLcode* result, LLCurl::TransferInfo* info)
 {
+	if(!isValid())
+	{
+		return false ;
+	}
 	if (!mMulti->isCompleted())
 	{ //we're busy, try again later
 		return false;
 
 std::string LLCurlEasyRequest::getErrorString()
 {
-	return mEasy ? std::string(mEasy->getErrorBuffer()) : std::string();
+	return isValid() &&  mEasy ? std::string(mEasy->getErrorBuffer()) : std::string();
 }
 
 ////////////////////////////////////////////////////////////////////////////
 }
 #endif
 
-void LLCurl::initClass(bool multi_threaded)
+void LLCurl::initClass(F32 curl_reuest_timeout, S32 max_number_handles, bool multi_threaded)
 {
+	sCurlRequestTimeOut = curl_reuest_timeout ; //seconds
+	sMaxHandles = max_number_handles ; //max number of handles, (multi handles and easy handles combined).
+
 	// 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
 	sCurlThread = new LLCurlThread(multi_threaded) ;
 	if(multi_threaded)
 	{
+		sHandleMutexp = new LLMutex(NULL) ;
 		Easy::sHandleMutexp = new LLMutex(NULL) ;
 	}
 }
 
 void LLCurl::cleanupClass()
 {
+	sNotQuitting = false; //set quitting
+
 	//shut down curl thread
 	while(1)
 	{
 	for (std::set<CURL*>::iterator iter = Easy::sFreeHandles.begin(); iter != Easy::sFreeHandles.end(); ++iter)
 	{
 		CURL* curl = *iter;
-		curl_easy_cleanup(curl);
+		LLCurl::deleteEasyHandle(curl);
 	}
 
 	Easy::sFreeHandles.clear();
 	delete Easy::sHandleMutexp ;
 	Easy::sHandleMutexp = NULL ;
 
+	delete sHandleMutexp ;
+	sHandleMutexp = NULL ;
+
 	llassert(Easy::sActiveHandles.empty());
 }
 
+//static 
+CURLM* LLCurl::newMultiHandle()
+{
+	LLMutexLock lock(sHandleMutexp) ;
+
+	if(sTotalHandles + 1 > sMaxHandles)
+	{
+		llwarns << "no more handles available." << llendl ;
+		return NULL ; //failed
+	}
+	sTotalHandles++;
+
+	CURLM* ret = curl_multi_init() ;
+	if(!ret)
+	{
+		llwarns << "curl_multi_init failed." << llendl ;
+	}
+
+	return ret ;
+}
+
+//static 
+CURLMcode  LLCurl::deleteMultiHandle(CURLM* handle)
+{
+	if(handle)
+	{
+		LLMutexLock lock(sHandleMutexp) ;		
+		sTotalHandles-- ;
+		return curl_multi_cleanup(handle) ;
+	}
+	return CURLM_OK ;
+}
+
+//static 
+CURL*  LLCurl::newEasyHandle()
+{
+	LLMutexLock lock(sHandleMutexp) ;
+
+	if(sTotalHandles + 1 > sMaxHandles)
+	{
+		llwarns << "no more handles available." << llendl ;
+		return NULL ; //failed
+	}
+	sTotalHandles++;
+
+	CURL* ret = curl_easy_init() ;
+	if(!ret)
+	{
+		llwarns << "curl_easy_init failed." << llendl ;
+	}
+
+	return ret ;
+}
+
+//static 
+void  LLCurl::deleteEasyHandle(CURL* handle)
+{
+	if(handle)
+	{
+		LLMutexLock lock(sHandleMutexp) ;
+		curl_easy_cleanup(handle) ;
+		sTotalHandles-- ;
+	}
+}
+
 const unsigned int LLCurl::MAX_REDIRECTS = 5;
 
 // Provide access to LLCurl free functions outside of llcurl.cpp without polluting the global namespace.

File indra/llmessage/llcurl.h

 #include "llsd.h"
 #include "llthread.h"
 #include "llqueuedthread.h"
+#include "llframetimer.h"
 
 class LLMutex;
 class LLCurlThread;
 	/**
 	 * @ brief Initialize LLCurl class
 	 */
-	static void initClass(bool multi_threaded = false);
+	static void initClass(F32 curl_reuest_timeout = 120.f, S32 max_number_handles = 256, bool multi_threaded = false);
 
 	/**
 	 * @ brief Cleanup LLCurl class
 	static unsigned long ssl_thread_id(void);
 
 	static LLCurlThread* getCurlThread() { return sCurlThread ;}
+
+	static CURLM* newMultiHandle() ;
+	static CURLMcode deleteMultiHandle(CURLM* handle) ;
+	static CURL*  newEasyHandle() ;
+	static void   deleteEasyHandle(CURL* handle) ;
+
 private:
 	static std::string sCAPath;
 	static std::string sCAFile;
 	static const unsigned int MAX_REDIRECTS;
 	static LLCurlThread* sCurlThread;
+
+	static LLMutex* sHandleMutexp ;
+	static S32      sTotalHandles ;
+	static S32      sMaxHandles;
+public:
+	static bool     sNotQuitting;
+	static F32      sCurlRequestTimeOut;	
 };
 
 class LLCurl::Easy
 		STATE_COMPLETED=2
 	} ePerformState;
 
-	Multi();	
+	Multi(F32 idle_time_out = 0.f);	
 
 	LLCurl::Easy* allocEasy();
 	bool addEasy(LLCurl::Easy* easy);	
 
 	void setState(ePerformState state) ;
 	ePerformState getState() ;
+	
 	bool isCompleted() ;
+	bool isValid() {return mCurlMultiHandle != NULL ;}
+	bool isDead() {return mDead;}
 
 	bool waitToComplete() ;
 
 	S32 mQueued;
 	S32 mErrorCount;
 	
-	static CURLM* initMulti() ;
 private:
 	void easyFree(LLCurl::Easy*);
+	void cleanup() ;
 	
 	CURLM* mCurlMultiHandle;
 
 	LLMutex* mMutexp ;
 	LLMutex* mDeletionMutexp ;
 	LLMutex* mEasyMutexp ;
-
-	static LLMutex* sMultiInitMutexp ;
+	LLFrameTimer mIdleTimer ;
+	F32 mIdleTimeOut;
 };
 
 class LLCurlThread : public LLQueuedThread
 private:
 	bool doMultiPerform(LLCurl::Multi* multi) ;
 	void deleteMulti(LLCurl::Multi* multi) ;
+	void cleanupMulti(LLCurl::Multi* multi) ;
 } ;
 
 namespace boost
 	std::string getErrorString();
 	bool isCompleted() {return mMulti->isCompleted() ;}
 	bool wait() { return mMulti->waitToComplete(); }
+	bool isValid() {return mMulti && mMulti->isValid(); }
 
 	LLCurl::Easy* getEasy() const { return mEasy; }
 

File indra/llmessage/llhttpassetstorage.cpp

 void LLHTTPAssetRequest::setupCurlHandle()
 {
 	// *NOTE: Similar code exists in mapserver/llcurlutil.cpp  JC
-	mCurlHandle = curl_easy_init();
+	mCurlHandle = LLCurl::newEasyHandle();
+	llassert_always(mCurlHandle != NULL) ;
 
 	// Apply proxy settings if configured to do so
 	LLProxy::getInstance()->applyProxySettings(mCurlHandle);
 
 void LLHTTPAssetRequest::cleanupCurlHandle()
 {
-	curl_easy_cleanup(mCurlHandle);
+	LLCurl::deleteEasyHandle(mCurlHandle);
 	if (mAssetStoragep)
 	{
 		// Terminating a request.  Thus upload or download is no longer pending.
 
 	// curl_global_init moved to LLCurl::initClass()
 	
-	mCurlMultiHandle = curl_multi_init();
+	mCurlMultiHandle = LLCurl::newMultiHandle() ;
+	llassert_always(mCurlMultiHandle != NULL) ;
 }
 
 LLHTTPAssetStorage::~LLHTTPAssetStorage()
 {
-	curl_multi_cleanup(mCurlMultiHandle);
+	LLCurl::deleteMultiHandle(mCurlMultiHandle);
 	mCurlMultiHandle = NULL;
 	
 	// curl_global_cleanup moved to LLCurl::initClass()

File indra/llmessage/llhttpclient.cpp

 	LLPumpIO::chain_t chain;
 
 	LLURLRequest* req = new LLURLRequest(method, url);
+	if(!req->isValid())//failed
+	{
+		delete req ;
+		return ;
+	}
+
 	req->setSSLVerifyCallback(LLHTTPClient::getCertVerifyCallback(), (void *)req);
 
 	
 {
 	lldebugs << "blockingRequest of " << url << llendl;
 	char curl_error_buffer[CURL_ERROR_SIZE] = "\0";
-	CURL* curlp = curl_easy_init();
+	CURL* curlp = LLCurl::newEasyHandle();
+	llassert_always(curlp != NULL) ;
+
 	LLHTTPBuffer http_buffer;
 	std::string body_str;
 	
 	}
 
 	// * Cleanup
-	curl_easy_cleanup(curlp);
+	LLCurl::deleteEasyHandle(curlp);
 	return response;
 }
 

File indra/llmessage/lliohttpserver.cpp

 
   			// Copy everything after mLast read to the out.
 			LLBufferArray::segment_iterator_t seg_iter;
+
+			buffer->lock();
 			seg_iter = buffer->splitAfter(mLastRead);
 			if(seg_iter != buffer->endSegment())
 			{
 				}
 #endif
 			}
-
+			buffer->unlock();
 			//
 			// *FIX: get rid of extra bytes off the end
 			//

File indra/llmessage/lliopipe.cpp

 	//lldebugs << "destroying LLIOPipe" << llendl;
 }
 
+//virtual 
+bool LLIOPipe::isValid() 
+{
+	return true ;
+}
+
 // static
 std::string LLIOPipe::lookupStatusString(EStatus status)
 {

File indra/llmessage/lliopipe.h

 	 */
 	virtual ~LLIOPipe();
 
+	virtual bool isValid() ;
+
 protected:
 	/**
 	 * @brief Base Constructor.

File indra/llmessage/lliosocket.cpp

 	// efficient - not only because writev() is better, but also
 	// because we won't have to do as much work to find the start
 	// address.
+	buffer->lock();
 	LLBufferArray::segment_iterator_t it;
 	LLBufferArray::segment_iterator_t end = buffer->endSegment();
 	LLSegment segment;
 		}
 
 	}
+	buffer->unlock();
+
 	PUMP_DEBUG;
 	if(done && eos)
 	{

File indra/llmessage/llpumpio.cpp

 	return ((pool == NULL) ? false : true);
 }
 
-bool LLPumpIO::addChain(const chain_t& chain, F32 timeout)
+bool LLPumpIO::addChain(const chain_t& chain, F32 timeout, bool has_curl_request)
 {
 	LLMemType m1(LLMemType::MTYPE_IO_PUMP);
 	if(chain.empty()) return false;
 	LLScopedLock lock(mChainsMutex);
 #endif
 	LLChainInfo info;
+	info.mHasCurlRequest = has_curl_request;
 	info.setTimeoutSeconds(timeout);
 	info.mData = LLIOPipe::buffer_ptr_t(new LLBufferArray);
+	info.mData->setThreaded(has_curl_request);
 	LLLinkInfo link;
 #if LL_DEBUG_PIPE_TYPE_IN_PUMP
 	lldebugs << "LLPumpIO::addChain() " << chain[0] << " '"
 
 static LLFastTimer::DeclareTimer FTM_PUMP_IO("Pump IO");
 
+LLPumpIO::current_chain_t LLPumpIO::removeRunningChain(LLPumpIO::current_chain_t& run_chain) 
+{
+	std::for_each(
+				(*run_chain).mDescriptors.begin(),
+				(*run_chain).mDescriptors.end(),
+				ll_delete_apr_pollset_fd_client_data());
+	return mRunningChains.erase(run_chain);
+}
+
 //timeout is in microseconds
 void LLPumpIO::pump(const S32& poll_timeout)
 {
 //						<< (*run_chain).mChainLinks[0].mPipe
 //						<< " because we reached the end." << llendl;
 #endif
-				run_chain = mRunningChains.erase(run_chain);
+				run_chain = removeRunningChain(run_chain);
 				continue;
 			}
 		}
+		else if(isChainExpired(*run_chain))
+		{
+			run_chain = removeRunningChain(run_chain);
+			continue;
+		}
+
 		PUMP_DEBUG;
 		if((*run_chain).mLock)
 		{
 			PUMP_DEBUG;
 			// This chain is done. Clean up any allocated memory and
 			// erase the chain info.
-			std::for_each(
-				(*run_chain).mDescriptors.begin(),
-				(*run_chain).mDescriptors.end(),
-				ll_delete_apr_pollset_fd_client_data());
-			run_chain = mRunningChains.erase(run_chain);
+			run_chain = removeRunningChain(run_chain);
 
 			// *NOTE: may not always need to rebuild the pollset.
 			mRebuildPollset = true;
 	PUMP_DEBUG;
 }
 
+bool LLPumpIO::isChainExpired(LLChainInfo& chain)
+{
+	if(!chain.mHasCurlRequest)
+	{
+		return false ;
+	}
+
+	for(links_t::iterator iter = chain.mChainLinks.begin(); iter != chain.mChainLinks.end(); ++iter)
+	{
+		if(!(*iter).mPipe->isValid())
+		{
+			return true ;
+		}
+	}
+
+	return false ;
+}
+
 bool LLPumpIO::handleChainError(
 	LLChainInfo& chain,
 	LLIOPipe::EStatus error)
 #endif
 			keep_going = false;
 			break;
+		case LLIOPipe::STATUS_EXPIRED:
+			keep_going = false;
+			break ;
 		default:
 			if(LLIOPipe::isSuccess(error))
 			{
 LLPumpIO::LLChainInfo::LLChainInfo() :
 	mInit(false),
 	mLock(0),
-	mEOS(false)
+	mEOS(false),
+	mHasCurlRequest(false)
 {
 	LLMemType m1(LLMemType::MTYPE_IO_PUMP);
 	mTimer.setTimerExpirySec(DEFAULT_CHAIN_EXPIRY_SECS);

File indra/llmessage/llpumpio.h

 	 * @param chain The pipes for the chain
 	 * @param timeout The number of seconds in the future to
 	 * expire. Pass in 0.0f to never expire.
+	 * @param has_curl_request The chain contains LLURLRequest if true.
 	 * @return Returns true if anything was added to the pump.
 	 */
-	bool addChain(const chain_t& chain, F32 timeout);
+	bool addChain(const chain_t& chain, F32 timeout, bool has_curl_request = false);
 	
 	/** 
 	 * @brief Struct to associate a pipe with it's buffer io indexes.
 
 		// basic member data
 		bool mInit;
+		bool mEOS;
+		bool mHasCurlRequest;
 		S32 mLock;
 		LLFrameTimer mTimer;
 		links_t::iterator mHead;
 		links_t mChainLinks;
-		LLIOPipe::buffer_ptr_t mData;
-		bool mEOS;
+		LLIOPipe::buffer_ptr_t mData;		
 		LLSD mContext;
 
 		// tracking inside the pump
 protected:
 	void initialize(apr_pool_t* pool);
 	void cleanup();
-
+	current_chain_t removeRunningChain(current_chain_t& chain) ;
 	/** 
 	 * @brief Given the internal state of the chains, rebuild the pollset
 	 * @see setConditional()
 	 */
 	bool handleChainError(LLChainInfo& chain, LLIOPipe::EStatus error);
 
+	//if the chain is expired, remove it
+	bool isChainExpired(LLChainInfo& chain) ;
+
 public:
 	/** 
 	 * @brief Return number of running chains.

File indra/llmessage/llsdrpcclient.h

 	virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
 	{
 		lldebugs << "LLSDRPCClientFactory::build" << llendl;
+		LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
+		if(!http->isValid())
+		{
+			llwarns << "Creating LLURLRequest failed." << llendl ;
+			delete http;
+			return false;
+		}
+
 		LLIOPipe::ptr_t service(new Client);
-		chain.push_back(service);
-		LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
+		chain.push_back(service);		
 		LLIOPipe::ptr_t http_pipe(http);
 		http->addHeader("Content-Type: text/llsd");
 		if(mURL.empty())
 	virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
 	{
 		lldebugs << "LLXMLSDRPCClientFactory::build" << llendl;
+
+		LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
+		if(!http->isValid())
+		{
+			llwarns << "Creating LLURLRequest failed." << llendl ;
+			delete http;
+			return false ;
+		}
 		LLIOPipe::ptr_t service(new Client);
-		chain.push_back(service);
-		LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
+		chain.push_back(service);		
 		LLIOPipe::ptr_t http_pipe(http);
 		http->addHeader("Content-Type: text/xml");
 		if(mURL.empty())

File indra/llmessage/llurlrequest.cpp

 {
 	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
 	mCurlRequest = new LLCurlEasyRequest();
+	
+	if(!mCurlRequest->isValid()) //failed.
+	{
+		delete mCurlRequest ;
+		mCurlRequest = NULL ;
+	}
 }
 
 LLURLRequestDetail::~LLURLRequestDetail()
 	mDetail->mCurlRequest->setoptString(CURLOPT_COOKIEFILE, "");
 }
 
+//virtual 
+bool LLURLRequest::isValid() 
+{
+	return mDetail->mCurlRequest && mDetail->mCurlRequest->isValid(); 
+}
+
 // virtual
 LLIOPipe::EStatus LLURLRequest::handleError(
 	LLIOPipe::EStatus status,
 	LLPumpIO* pump)
 {
 	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+	
+	if(!isValid())
+	{
+		return STATUS_EXPIRED ;
+	}
+
 	if(mCompletionCallback && pump)
 	{
 		LLURLRequestComplete* complete = NULL;
 	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
 	mState = STATE_INITIALIZED;
 	mDetail = new LLURLRequestDetail;
+
+	if(!isValid())
+	{
+		return ;
+	}
+
 	mDetail->mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1);
 	mDetail->mCurlRequest->setWriteCallback(&downCallback, (void*)this);
 	mDetail->mCurlRequest->setReadCallback(&upCallback, (void*)this);

File indra/llmessage/llurlrequest.h

 	 */
 	void allowCookies();
 
+	/*virtual*/ bool isValid() ;
+
 public:
 	/** 
 	 * @brief Give this pipe a chance to handle a generated error

File indra/llrender/llrender.cpp

 		const glh::matrix4f& mat = gGL.getModelviewMatrix();
 		mat.mult_matrix_dir(dir);
 
-		mSpotDirection.set(direction);
+		mSpotDirection.set(dir.v);
 	}
 }
 
 	flush();
 
 	{
+		llassert_always(mMatrixMode < NUM_MATRIX_MODES) ;
+
 		mMatrix[mMatrixMode][mMatIdx[mMatrixMode]].make_identity();
 		mMatHash[mMatrixMode]++;
 	}

File indra/llrender/llvertexbuffer.cpp

 	return r;
 }
 
-U8* LLVBOPool::allocate(U32& name, U32 size)
+volatile U8* LLVBOPool::allocate(U32& name, U32 size)
 {
 	llassert(nhpo2(size) == size);
 
 		mFreeList.resize(i+1);
 	}
 
-	U8* ret = NULL;
+	volatile U8* ret = NULL;
 
 	if (mFreeList[i].empty())
 	{
 		//make a new buffer
 		glGenBuffersARB(1, &name);
 		glBindBufferARB(mType, name);
-		glBufferDataARB(mType, size, 0, mUsage);
 		LLVertexBuffer::sAllocatedBytes += size;
 
-		if (LLVertexBuffer::sDisableVBOMapping)
+		if (LLVertexBuffer::sDisableVBOMapping || mUsage != GL_DYNAMIC_DRAW_ARB)
 		{
+			glBufferDataARB(mType, size, 0, mUsage);
 			ret = (U8*) ll_aligned_malloc_16(size);
 		}
+		else
+		{ //always use a true hint of static draw when allocating non-client-backed buffers
+			glBufferDataARB(mType, size, 0, GL_STATIC_DRAW_ARB);
+		}
+
 		glBindBufferARB(mType, 0);
 	}
 	else
 	return ret;
 }
 
-void LLVBOPool::release(U32 name, U8* buffer, U32 size)
+void LLVBOPool::release(U32 name, volatile U8* buffer, U32 size)
 {
 	llassert(nhpo2(size) == size);
 
 	rec.mClientData = buffer;
 
 	sBytesPooled += size;
-
-	mFreeList[i].push_back(rec);
+	
+	if (!LLVertexBuffer::sDisableVBOMapping && mUsage == GL_DYNAMIC_DRAW_ARB)
+	{
+		glDeleteBuffersARB(1, &rec.mGLName);
+	}
+	else
+	{
+		mFreeList[i].push_back(rec);
+	}
 }
 
 void LLVBOPool::cleanup()
 
 			if (r.mClientData)
 			{
-				ll_aligned_free_16(r.mClientData);
+				ll_aligned_free_16((void*) r.mClientData);
 			}
 
 			l.pop_front();
 void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indices_offset) const
 {
 	validateRange(start, end, count, indices_offset);
-
+	mMappable = FALSE;
 	gGL.syncMatrices();
 
 	llassert(mNumVerts >= 0);
 void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const
 {
 	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
-
+	mMappable = FALSE;
 	gGL.syncMatrices();
 
 	llassert(mNumIndices >= 0);
 void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const
 {
 	llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShaderPtr != NULL);
-	
+	mMappable = FALSE;
 	gGL.syncMatrices();
 	
 	llassert(mNumVerts >= 0);
 
 	if (mUsage && mUsage != GL_STREAM_DRAW_ARB)
 	{ //only stream_draw and dynamic_draw are supported when using VBOs, dynamic draw is the default
-		mUsage = GL_DYNAMIC_DRAW_ARB;
+		if (sDisableVBOMapping)
+		{ //always use stream draw if VBO mapping is disabled
+			mUsage = GL_STREAM_DRAW_ARB;
+		}
+		else
+		{
+			mUsage = GL_DYNAMIC_DRAW_ARB;
+		}
 	}
-		
+	
+
+	if (mUsage == GL_DYNAMIC_DRAW_ARB && !sDisableVBOMapping)
+	{
+		mMappable = TRUE;
+	}
+	else
+	{
+		mMappable = FALSE;
+	}
+
 	//zero out offsets
 	for (U32 i = 0; i < TYPE_MAX; i++)
 	{
 		}
 		else
 		{
-			FREE_MEM(sPrivatePoolp, mMappedData) ;
+			FREE_MEM(sPrivatePoolp, (void*) mMappedData) ;
 			mMappedData = NULL;
 			mEmpty = TRUE;
 		}
 		}
 		else
 		{
-			FREE_MEM(sPrivatePoolp, mMappedIndexData) ;
+			FREE_MEM(sPrivatePoolp, (void*) mMappedIndexData) ;
 			mMappedIndexData = NULL;
 			mEmpty = TRUE;
 		}
 	return true;
 }
 
+static LLFastTimer::DeclareTimer FTM_VBO_MAP_BUFFER_RANGE("VBO Map Range");
+static LLFastTimer::DeclareTimer FTM_VBO_MAP_BUFFER("VBO Map");
+
 // Map for data access
-U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 index, S32 count, bool map_range)
+volatile U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 index, S32 count, bool map_range)
 {
 	bindGLBuffer(true);
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER);
 		
 	if (useVBOs())
 	{
-		if (sDisableVBOMapping || gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
+		if (!mMappable || gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
 		{
 			if (count == -1)
 			{
 			if (!mapped)
 			{
 				//not already mapped, map new region
-				MappedRegion region(type, !sDisableVBOMapping && map_range ? -1 : index, count);
+				MappedRegion region(type, mMappable && map_range ? -1 : index, count);
 				mMappedVertexRegions.push_back(region);
 			}
 		}
 			sMappedCount++;
 			stop_glerror();	
 
-			if(sDisableVBOMapping)
+			if(!mMappable)
 			{
 				map_range = false;
 			}
 			else
 			{
-				U8* src = NULL;
+				volatile U8* src = NULL;
 				waitFence();
 				if (gGLManager.mHasMapBufferRange)
 				{
 					if (map_range)
 					{
 #ifdef GL_ARB_map_buffer_range
+						LLFastTimer t(FTM_VBO_MAP_BUFFER_RANGE);
 						S32 offset = mOffsets[type] + sTypeSize[type]*index;
 						S32 length = (sTypeSize[type]*count+0xF) & ~0xF;
 						src = (U8*) glMapBufferRange(GL_ARRAY_BUFFER_ARB, offset, length, 
 							}
 						}
 
+						LLFastTimer t(FTM_VBO_MAP_BUFFER);
 						src = (U8*) glMapBufferRange(GL_ARRAY_BUFFER_ARB, 0, mSize, 
 							GL_MAP_WRITE_BIT | 
 							GL_MAP_FLUSH_EXPLICIT_BIT);
 
 				llassert(src != NULL);
 
-				mMappedData = LL_NEXT_ALIGNED_ADDRESS<U8>(src);
+				mMappedData = LL_NEXT_ALIGNED_ADDRESS<volatile U8>(src);
 				mAlignedOffset = mMappedData - src;
 			
 				stop_glerror();
 			//check the availability of memory
 			LLMemory::logMemoryInfo(TRUE) ; 
 			
-				if(!sDisableVBOMapping)
+				if(mMappable)
 				{			
 					//--------------------
 					//print out more debug info before crash
 		map_range = false;
 	}
 	
-	if (map_range && gGLManager.mHasMapBufferRange && !sDisableVBOMapping)
+	if (map_range && gGLManager.mHasMapBufferRange && mMappable)
 	{
 		return mMappedData;
 	}
 	}
 }
 
-U8* LLVertexBuffer::mapIndexBuffer(S32 index, S32 count, bool map_range)
+
+static LLFastTimer::DeclareTimer FTM_VBO_MAP_INDEX_RANGE("IBO Map Range");
+static LLFastTimer::DeclareTimer FTM_VBO_MAP_INDEX("IBO Map");
+
+volatile U8* LLVertexBuffer::mapIndexBuffer(S32 index, S32 count, bool map_range)
 {
 	LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER);
 	bindGLIndices(true);
 
 	if (useVBOs())
 	{
-		if (sDisableVBOMapping || gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
+		if (!mMappable || gGLManager.mHasMapBufferRange || gGLManager.mHasFlushBufferRange)
 		{
 			if (count == -1)
 			{
 			if (!mapped)
 			{
 				//not already mapped, map new region
-				MappedRegion region(TYPE_INDEX, !sDisableVBOMapping && map_range ? -1 : index, count);
+				MappedRegion region(TYPE_INDEX, mMappable && map_range ? -1 : index, count);
 				mMappedIndexRegions.push_back(region);
 			}
 		}
 				}
 			}
 
-			if(sDisableVBOMapping)
+			if(!mMappable)
 			{
 				map_range = false;
 			}
 			else
 			{
-				U8* src = NULL;
+				volatile U8* src = NULL;
 				waitFence();
 				if (gGLManager.mHasMapBufferRange)
 				{
 					if (map_range)
 					{
 #ifdef GL_ARB_map_buffer_range
+						LLFastTimer t(FTM_VBO_MAP_INDEX_RANGE);
 						S32 offset = sizeof(U16)*index;
 						S32 length = sizeof(U16)*count;
 						src = (U8*) glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, offset, length, 
 					else
 					{
 #ifdef GL_ARB_map_buffer_range
+						LLFastTimer t(FTM_VBO_MAP_INDEX);
 						src = (U8*) glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, sizeof(U16)*mNumIndices, 
 							GL_MAP_WRITE_BIT | 
 							GL_MAP_FLUSH_EXPLICIT_BIT);
 				}
 				else
 				{
+					LLFastTimer t(FTM_VBO_MAP_INDEX);
 					map_range = false;
 					src = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
 				}
 			log_glerror();
 			LLMemory::logMemoryInfo(TRUE) ;
 
-			if(!sDisableVBOMapping)
+			if(mMappable)
 			{
 				GLint buff;
 				glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff);
 		map_range = false;
 	}
 
-	if (map_range && gGLManager.mHasMapBufferRange && !sDisableVBOMapping)