From 188221a90c7a054d390ddaa534d391f6370ac6bc Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Tue, 9 Apr 2013 23:44:59 +0000
Subject: SH-4088 Deadman timer switch started in llcommon.  Unit test started.
 Will be used for mesh, inventory, etc., operation markers.

---
 indra/llcommon/CMakeLists.txt                |   3 +
 indra/llcommon/lldeadmantimer.cpp            | 141 ++++++++++++++++++++++
 indra/llcommon/lldeadmantimer.h              | 171 +++++++++++++++++++++++++++
 indra/llcommon/lltimer.h                     |   7 ++
 indra/llcommon/tests/lldeadmantimer_test.cpp |  63 ++++++++++
 5 files changed, 385 insertions(+)
 create mode 100644 indra/llcommon/lldeadmantimer.cpp
 create mode 100644 indra/llcommon/lldeadmantimer.h
 create mode 100644 indra/llcommon/tests/lldeadmantimer_test.cpp

(limited to 'indra')

diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 5cce8ff2c4..7ed4137065 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -42,6 +42,7 @@ set(llcommon_SOURCE_FILES
     llcriticaldamp.cpp
     llcursortypes.cpp
     lldate.cpp
+    lldeadmantimer.cpp
     lldependencies.cpp
     lldictionary.cpp
     llerror.cpp
@@ -144,6 +145,7 @@ set(llcommon_HEADER_FILES
     lldarray.h
     lldarrayptr.h
     lldate.h
+    lldeadmantimer.h
     lldefs.h
     lldependencies.h
     lldeleteutils.h
@@ -322,6 +324,7 @@ if (LL_TESTS)
   LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}")
+  LL_ADD_INTEGRATION_TEST(lldeadmantimer "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lldependencies "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llerror "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}")
diff --git a/indra/llcommon/lldeadmantimer.cpp b/indra/llcommon/lldeadmantimer.cpp
new file mode 100644
index 0000000000..2f48d13c2d
--- /dev/null
+++ b/indra/llcommon/lldeadmantimer.cpp
@@ -0,0 +1,141 @@
+/** 
+* @file lldeadmantimer.cpp
+* @brief Simple deadman-switch timer.
+* @author monty@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+
+#include "lldeadmantimer.h"
+
+
+LLDeadmanTimer::LLDeadmanTimer(F64 horizon)
+	: mHorizon(U64(horizon * gClockFrequency)),
+	  mActive(false),			// If true, a timer is running.
+	  mDone(false),				// If true, timer has completed and can be read (once)
+	  mStarted(U64L(0)),
+	  mExpires(U64L(0)),
+	  mStopped(U64L(0)),
+	  mCount(U64L(0))
+{}
+
+
+void LLDeadmanTimer::start(U64 now)
+{
+	// *TODO:  If active, let's complete an existing timer and save
+	// the result to the side.  I think this will be useful later.
+	// For now, wipe out anything in progress, start fresh.
+	
+	if (! now)
+	{
+		now = LLTimer::getCurrentClockCount();
+	}
+	mActive = true;
+	mDone = false;
+	mStarted = now;
+	mExpires = now + mHorizon;
+	mStopped = now;
+	mCount = U64L(0);
+}
+
+
+void LLDeadmanTimer::stop(U64 now)
+{
+	if (! mActive)
+	{
+		return;
+	}
+
+	if (! now)
+	{
+		now = LLTimer::getCurrentClockCount();
+	}
+	mStopped = now;
+	mActive = false;
+	mDone = true;
+}
+
+
+bool LLDeadmanTimer::isExpired(F64 & started, F64 & stopped, U64 & count, U64 now)
+{
+	if (! mActive)
+	{
+		return false;
+	}
+	
+	if (! mDone)
+	{
+		if (! now)
+		{
+			now = LLTimer::getCurrentClockCount();
+		}
+
+		if (now > mExpires)
+		{
+			// mStopped from ringBell() is the value we want
+			mActive = false;
+			mDone = true;
+		}
+	}
+
+	if (! mDone)
+	{
+		return false;
+	}
+	
+	started = mStarted * gClockFrequencyInv;
+	stopped = mStopped * gClockFrequencyInv;
+	count = mCount;
+	mDone = false;
+
+	return true;
+}
+
+	
+void LLDeadmanTimer::ringBell(U64 now)
+{
+	if (! mActive)
+	{
+		return;
+	}
+	
+	if (! now)
+	{
+		now = LLTimer::getCurrentClockCount();
+	}
+
+	if (now > mExpires)
+	{
+		mActive = false;
+		mDone = true;
+	}
+	else
+	{
+		mStopped = now;
+		mExpires = now + mHorizon;
+		++mCount;
+	}
+	
+	return;
+}
+
diff --git a/indra/llcommon/lldeadmantimer.h b/indra/llcommon/lldeadmantimer.h
new file mode 100644
index 0000000000..84023723ab
--- /dev/null
+++ b/indra/llcommon/lldeadmantimer.h
@@ -0,0 +1,171 @@
+/** 
+* @file   lldeadmantimer.h
+* @brief  Interface to a simple event timer with a deadman's switch
+* @author monty@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#ifndef	LL_DEADMANTIMER_H
+#define	LL_DEADMANTIMER_H
+
+
+#include "linden_common.h"
+
+#include "lltimer.h"
+
+
+/// @file lldeadmantimer.h
+///
+/// There are interesting user-experienced events in the viewer that
+/// would seem to have well-defined start and stop points but which
+/// actually lack such milestones in the code.  Such events (like
+/// time to load meshes after logging in, initial inventory load,
+/// display name fetch) can be defined somewhat after-the-fact by
+/// noticing when we no longer perform operations towards their
+/// completion.  This class is intended to help in such applications.
+///
+/// What it implements is a deadman's switch (also known as a
+/// keepalive switch and a doorbell switch).  The basic operation is
+/// as follows:
+///
+/// * LLDeadmanTimer is instantiated with a horizon value in seconds,
+///   one for each event of interest.
+/// * When an event starts, @see start() is invoked to begin a
+///   timing operation.
+/// * As operations are performed in service of the event (issuing
+///   HTTP requests, receiving responses), @see ringBell() is invoked
+///   to inform the timer that the operation is still active.
+/// * If the operation is canceled or otherwise terminated, @see
+///   stop() can be called to end the timing operation.
+/// * Concurrent with the ringBell() calls, the program makes
+///   periodic (shorter than the horizon but not too short) calls
+///   to @see isExpired() to see if the event has expired due to
+///   either a stop() call or lack of activity (defined as a ringBell()
+///   call in the previous 'horizon' seconds).  If it has expired,
+///   the caller also receives start, stop and count values for the
+///   event which the application can then report in whatever manner
+///   it sees fit.
+/// * The timer becomes passive after an isExpired() call that returns
+///   true.  It can then be restarted with a new start() call.
+///
+/// Threading:  Instances are not thread-safe.  They also use
+/// timing code from lltimer.h which is also unsafe.
+///
+/// Allocation:  Not refcounted, may be stack or heap allocated.
+///
+
+class LL_COMMON_API LLDeadmanTimer
+{
+public:
+	/// Construct and initialize an LLDeadmanTimer
+	///
+	/// @param horizon	Time, in seconds, after the last @see ringBell()
+	///                 call at which point the timer will consider itself
+	///					expired.
+	///
+	LLDeadmanTimer(F64 horizon);
+
+	~LLDeadmanTimer() 
+		{}
+	
+private:
+	LLDeadmanTimer(const LLDeadmanTimer &);				// Not defined
+	void operator=(const LLDeadmanTimer &);				// Not defined
+
+public:
+	/// Begin timing.  If the timer is already active, it is reset
+	///	and timing begins now.
+	///
+	/// @param now		Current time as returned by @see
+	///					LLTimer::getCurrentClockCount().  If zero,
+	///					method will lookup current time.
+	///
+	void start(U64 now = U64L(0));
+
+
+	/// End timing.  Actively declare the end of the event independent
+	/// of the deadman's switch operation.  @see isExpired() will return
+	/// true and appropriate values will be returned.
+	///
+	/// @param now		Current time as returned by @see
+	///					LLTimer::getCurrentClockCount().  If zero,
+	///					method will lookup current time.
+	///
+	void stop(U64 now = U64L(0));
+
+
+	/// Declare that something interesting happened.  This has two
+	/// effects on an unexpired-timer.  1)  The expiration time
+	/// is extended for 'horizon' seconds after the 'now' value.
+	/// 2)  An internal counter associated with the event is incremented.
+	/// This count is returned via the @see isExpired() method.
+	///
+	/// @param now		Current time as returned by @see
+	///					LLTimer::getCurrentClockCount().  If zero,
+	///					method will lookup current time.
+	///
+	void ringBell(U64 now = U64L(0));
+	
+
+	/// Checks on the status of the timer Declare that something interesting happened.  This has two
+	/// effects on an unexpired-timer.  1)  The expiration time
+	/// is extended for 'horizon' seconds after the 'now' value.
+	/// 2)  An internal counter associated with the event is incremented.
+	/// This count is returned via the @see isExpired() method.
+	///
+	/// @param started	If expired, the starting time of the event is
+	///					returned to the caller via this reference.
+	///
+	/// @param stopped	If expired, the ending time of the event is
+	///					returned to the caller via this reference.
+	///					Ending time will be that provided in the
+	///					stop() method or the last ringBell() call
+	///					leading to expiration, whichever (stop() call
+	///					or notice of expiration) happened first.
+	///
+	/// @param count	If expired, the number of ringBell() calls
+	///					made prior to expiration.
+	///
+	/// @param now		Current time as returned by @see
+	///					LLTimer::getCurrentClockCount().  If zero,
+	///					method will lookup current time.
+	///
+	/// @return			true if the timer has expired, false otherwise.
+	///					If true, it also returns the started,
+	///					stopped and count values otherwise these are
+	///					left unchanged.
+	///
+	bool isExpired(F64 & started, F64 & stopped, U64 & count, U64 now = U64L(0));
+
+protected:
+	U64			mHorizon;
+	bool		mActive;
+	bool		mDone;
+	U64			mStarted;
+	U64			mExpires;
+	U64			mStopped;
+	U64			mCount;
+};
+	
+
+#endif	// LL_DEADMANTIMER_H
diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h
index 513de0605d..e73741217c 100644
--- a/indra/llcommon/lltimer.h
+++ b/indra/llcommon/lltimer.h
@@ -146,6 +146,13 @@ static inline time_t time_max()
 	}
 }
 
+// These are really statics but they've been global for awhile
+// and they're material to other timing classes.  If you are
+// not implementing a timer class, do not use these directly.
+extern LL_COMMON_API F64 gClockFrequency;
+extern LL_COMMON_API F64 gClockFrequencyInv;
+extern LL_COMMON_API F64 gClocksToMicroseconds;
+
 // Correction factor used by time_corrected() above.
 extern LL_COMMON_API S32 gUTCOffset;
 
diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp
new file mode 100644
index 0000000000..52a27b9c0a
--- /dev/null
+++ b/indra/llcommon/tests/lldeadmantimer_test.cpp
@@ -0,0 +1,63 @@
+/** 
+ * @file lldeadmantimer_test.cpp
+ * @brief Tests for the LLDeadmanTimer class.
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "../lldeadmantimer.h"
+#include "../llsd.h"
+
+#include "../test/lltut.h"
+
+namespace tut
+{
+
+struct deadmantimer_test
+{
+	deadmantimer_test()
+		{
+			// LLTimer internals updating
+			update_clock_frequencies();
+		}
+};
+
+typedef test_group<deadmantimer_test> deadmantimer_group_t;
+typedef deadmantimer_group_t::object deadmantimer_object_t;
+tut::deadmantimer_group_t deadmantimer_instance("LLDeadmanTimer");
+
+template<> template<>
+void deadmantimer_object_t::test<1>()
+{
+	F64 started(42.0), stopped(97.0);
+	U64 count(U64L(8));
+	LLDeadmanTimer timer(1.0);
+
+	ensure_equals("isExpired() returns false after ctor()", timer.isExpired(started, stopped, count), false);
+	ensure_approximately_equals("isExpired() does not modify started", started, F64(42.0), 2);
+	ensure_approximately_equals("isExpired() does not modify stopped", stopped, F64(97.0), 2);
+	ensure_equals("isExpired() does not modified count", count, U64L(8));
+}
+
+} // end namespace tut
-- 
cgit v1.2.3


From aabda8f3073928980b26530eac9b05eeb85a2207 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 10 Apr 2013 16:30:26 +0000
Subject: SH-4089 Unit test work for timer.  Knocked off some tests for the
 deadman's timer.  Found some bugs, dig some cleanup and documented a few
 things.  Definitely want to get rid of the U64/F64 interfaces at sometime but
 this is a good start.

---
 indra/llcommon/lldeadmantimer.cpp            |  26 ++-
 indra/llcommon/tests/lldeadmantimer_test.cpp | 259 ++++++++++++++++++++++++++-
 2 files changed, 272 insertions(+), 13 deletions(-)

(limited to 'indra')

diff --git a/indra/llcommon/lldeadmantimer.cpp b/indra/llcommon/lldeadmantimer.cpp
index 2f48d13c2d..3d3f738c06 100644
--- a/indra/llcommon/lldeadmantimer.cpp
+++ b/indra/llcommon/lldeadmantimer.cpp
@@ -29,8 +29,21 @@
 #include "lldeadmantimer.h"
 
 
+// *TODO:  Currently, this uses lltimer functions for its time
+// aspects and this leaks into the apis in the U64s/F64s.  Would
+// like to perhaps switch this over to TSC register-based timers
+// sometime and drop the overhead some more.
+
+
+//  Flag states and their meaning:
+//  mActive  mDone   Meaning
+//   false   false   Nothing running, no result available
+//    true   false   Timer running, no result available
+//   false    true   Timer finished, result can be read once
+//    true    true   Not allowed
+//
 LLDeadmanTimer::LLDeadmanTimer(F64 horizon)
-	: mHorizon(U64(horizon * gClockFrequency)),
+	: mHorizon(U64(llmax(horizon, F64(0.0)) * gClockFrequency)),
 	  mActive(false),			// If true, a timer is running.
 	  mDone(false),				// If true, timer has completed and can be read (once)
 	  mStarted(U64L(0)),
@@ -78,19 +91,14 @@ void LLDeadmanTimer::stop(U64 now)
 
 bool LLDeadmanTimer::isExpired(F64 & started, F64 & stopped, U64 & count, U64 now)
 {
-	if (! mActive)
-	{
-		return false;
-	}
-	
-	if (! mDone)
+	if (mActive && ! mDone)
 	{
 		if (! now)
 		{
 			now = LLTimer::getCurrentClockCount();
 		}
 
-		if (now > mExpires)
+		if (now >= mExpires)
 		{
 			// mStopped from ringBell() is the value we want
 			mActive = false;
@@ -124,7 +132,7 @@ void LLDeadmanTimer::ringBell(U64 now)
 		now = LLTimer::getCurrentClockCount();
 	}
 
-	if (now > mExpires)
+	if (now >= mExpires)
 	{
 		mActive = false;
 		mDone = true;
diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp
index 52a27b9c0a..571d43825f 100644
--- a/indra/llcommon/tests/lldeadmantimer_test.cpp
+++ b/indra/llcommon/tests/lldeadmantimer_test.cpp
@@ -31,6 +31,20 @@
 
 #include "../test/lltut.h"
 
+// Convert between floating point time deltas and U64 time deltas.
+// Reflects an implementation detail inside lldeadmantimer.cpp
+
+static U64 float_time_to_u64(F64 delta)
+{
+	return U64(delta * gClockFrequency);
+}
+
+static F64 u64_time_to_float(U64 delta)
+{
+	return delta * gClockFrequencyInv;
+}
+
+
 namespace tut
 {
 
@@ -47,17 +61,254 @@ typedef test_group<deadmantimer_test> deadmantimer_group_t;
 typedef deadmantimer_group_t::object deadmantimer_object_t;
 tut::deadmantimer_group_t deadmantimer_instance("LLDeadmanTimer");
 
+// Basic construction test and isExpired() call
 template<> template<>
 void deadmantimer_object_t::test<1>()
 {
 	F64 started(42.0), stopped(97.0);
 	U64 count(U64L(8));
-	LLDeadmanTimer timer(1.0);
+	LLDeadmanTimer timer(10.0);
 
 	ensure_equals("isExpired() returns false after ctor()", timer.isExpired(started, stopped, count), false);
-	ensure_approximately_equals("isExpired() does not modify started", started, F64(42.0), 2);
-	ensure_approximately_equals("isExpired() does not modify stopped", stopped, F64(97.0), 2);
-	ensure_equals("isExpired() does not modified count", count, U64L(8));
+	ensure_approximately_equals("t1 - isExpired() does not modify started", started, F64(42.0), 2);
+	ensure_approximately_equals("t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+	ensure_equals("t1 - isExpired() does not modify count", count, U64L(8));
+}
+
+
+// Construct with negative horizon - not useful generally but will be useful in testing
+template<> template<>
+void deadmantimer_object_t::test<2>()
+{
+	F64 started(42.0), stopped(97.0);
+	U64 count(U64L(8));
+	LLDeadmanTimer timer(0.0);			// Zero is pre-expired
+
+	ensure_equals("isExpired() still returns false with 0.0 time ctor()", timer.isExpired(started, stopped, count), false);
+}
+
+
+// "pre-expired" timer - starting a timer with a 0.0 horizon will result in
+// expiration on first test.
+template<> template<>
+void deadmantimer_object_t::test<3>()
+{
+	F64 started(42.0), stopped(97.0);
+	U64 count(U64L(8));
+	LLDeadmanTimer timer(0.0);
+
+	timer.start();
+	ensure_equals("isExpired() returns true with 0.0 horizon time", timer.isExpired(started, stopped, count), true);
+	ensure_approximately_equals("expired timer with no bell ringing has stopped == started", started, stopped, 8);
+}
+
+
+// "pre-expired" timer - bell rings are ignored as we're already expired.
+template<> template<>
+void deadmantimer_object_t::test<4>()
+{
+	F64 started(42.0), stopped(97.0);
+	U64 count(U64L(8));
+	LLDeadmanTimer timer(0.0);
+	
+	timer.start();
+	timer.ringBell(LLTimer::getCurrentClockCount() + float_time_to_u64(1000.0));
+	ensure_equals("isExpired() returns true with 0.0 horizon time after bell ring", timer.isExpired(started, stopped, count), true);
+	ensure_approximately_equals("ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
+}
+
+
+// start() test - unexpired timer reports unexpired
+template<> template<>
+void deadmantimer_object_t::test<5>()
+{
+	F64 started(42.0), stopped(97.0);
+	U64 count(U64L(8));
+	LLDeadmanTimer timer(10.0);
+	
+	timer.start();
+	ensure_equals("isExpired() returns false after starting with 10.0 horizon time", timer.isExpired(started, stopped, count), false);
+	ensure_approximately_equals("t5 - isExpired() does not modify started", started, F64(42.0), 2);
+	ensure_approximately_equals("t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+	ensure_equals("t5 - isExpired() does not modify count", count, U64L(8));
 }
 
+
+// start() test - start in the past but not beyond 1 horizon
+template<> template<>
+void deadmantimer_object_t::test<6>()
+{
+	F64 started(42.0), stopped(97.0);
+	U64 count(U64L(8));
+	LLDeadmanTimer timer(10.0);
+
+	U64 the_past(LLTimer::getCurrentClockCount() - float_time_to_u64(5.0));
+	timer.start(the_past);
+	ensure_equals("isExpired() returns false with 10.0 horizon time starting 5.0 in past", timer.isExpired(started, stopped, count), false);
+	ensure_approximately_equals("t6 - isExpired() does not modify started", started, F64(42.0), 2);
+	ensure_approximately_equals("t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+	ensure_equals("t6 - isExpired() does not modify count", count, U64L(8));
+}
+
+
+// start() test - start in the past but well beyond 1 horizon
+template<> template<>
+void deadmantimer_object_t::test<7>()
+{
+	F64 started(42.0), stopped(97.0);
+	U64 count(U64L(8));
+	LLDeadmanTimer timer(10.0);
+
+	U64 the_past(LLTimer::getCurrentClockCount() - float_time_to_u64(20.0));
+	timer.start(the_past);
+	ensure_equals("isExpired() returns true with 10.0 horizon time starting 20.0 in past", timer.isExpired(started, stopped, count), true);
+	ensure_approximately_equals("starting before horizon still gives equal started / stopped", started, stopped, 8);
+}
+
+
+// isExpired() test - results are read-once.  Probes after first true are false.
+template<> template<>
+void deadmantimer_object_t::test<8>()
+{
+	F64 started(42.0), stopped(97.0);
+	U64 count(U64L(8));
+	LLDeadmanTimer timer(10.0);
+
+	U64 the_past(LLTimer::getCurrentClockCount() - float_time_to_u64(20.0));
+	timer.start(the_past);
+	ensure_equals("t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", timer.isExpired(started, stopped, count), true);
+
+	started = 42.0;
+	stopped = 97.0;
+	count = U64L(8);
+	ensure_equals("t8 - second isExpired() returns false after true", timer.isExpired(started, stopped, count), false);
+	ensure_approximately_equals("t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
+	ensure_approximately_equals("t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
+	ensure_equals("t8 - 2nd isExpired() does not modify count", count, U64L(8));
+}
+
+
+// ringBell() test - see that we can keep a timer from expiring
+template<> template<>
+void deadmantimer_object_t::test<9>()
+{
+	F64 started(42.0), stopped(97.0);
+	U64 count(U64L(8));
+	LLDeadmanTimer timer(5.0);
+
+	U64 now(LLTimer::getCurrentClockCount());
+	F64 real_start(u64_time_to_float(now));
+	timer.start();
+
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	ensure_equals("t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings", timer.isExpired(started, stopped, count, now), false);
+	F64 last_good_ring(u64_time_to_float(now));
+
+	// Jump forward and expire
+	now += float_time_to_u64(10.0);
+	ensure_equals("t9 - 5.0 horizon timer expires on 10-second jump", timer.isExpired(started, stopped, count, now), true);
+	ensure_approximately_equals("t9 - started matches start() time", started, real_start, 4);
+	ensure_approximately_equals("t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+	ensure_equals("t9 - 10 good ringBell()s", count, U64L(10));
+	ensure_equals("t9 - single read only", timer.isExpired(started, stopped, count, now), false);
+}
+
+
+// restart after expiration test - verify that restarts behave well
+template<> template<>
+void deadmantimer_object_t::test<10>()
+{
+	F64 started(42.0), stopped(97.0);
+	U64 count(U64L(8));
+	LLDeadmanTimer timer(5.0);
+
+	U64 now(LLTimer::getCurrentClockCount());
+	F64 real_start(u64_time_to_float(now));
+	timer.start();
+
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	ensure_equals("t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings", timer.isExpired(started, stopped, count, now), false);
+	F64 last_good_ring(u64_time_to_float(now));
+
+	// Jump forward and expire
+	now += float_time_to_u64(10.0);
+	ensure_equals("t10 - 5.0 horizon timer expires on 10-second jump", timer.isExpired(started, stopped, count, now), true);
+	ensure_approximately_equals("t10 - started matches start() time", started, real_start, 4);
+	ensure_approximately_equals("t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+	ensure_equals("t10 - 10 good ringBell()s", count, U64L(10));
+	ensure_equals("t10 - single read only", timer.isExpired(started, stopped, count, now), false);
+
+	// Jump forward and restart
+	now += float_time_to_u64(1.0);
+	real_start = u64_time_to_float(now);
+	timer.start(now);
+
+	// Run a modified bell ring sequence
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	now += float_time_to_u64(1.0);
+	timer.ringBell(now);
+	ensure_equals("t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings", timer.isExpired(started, stopped, count, now), false);
+	last_good_ring = u64_time_to_float(now);
+
+	// Jump forward and expire
+	now += float_time_to_u64(10.0);
+	ensure_equals("t10 - 5.0 horizon timer expires on 8-second jump", timer.isExpired(started, stopped, count, now), true);
+	ensure_approximately_equals("t10 - 2nd started matches start() time", started, real_start, 4);
+	ensure_approximately_equals("t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
+	ensure_equals("t10 - 8 good ringBell()s", count, U64L(8));
+	ensure_equals("t10 - single read only - 2nd start", timer.isExpired(started, stopped, count, now), false);
+}
+
+
 } // end namespace tut
-- 
cgit v1.2.3


From 6536fe24049248a84447f1f06570f1be1dd24ef9 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 10 Apr 2013 17:50:42 +0000
Subject: SH-4089 Unit test work.  lltimer basis on Windows is zero which
 doesn't allow negative offsetting for unit tests.  Had to keep things
 positive.  Could do windows-specific test cases but I'm hoping to dump
 lltimer from the implementation later.

---
 indra/llcommon/tests/lldeadmantimer_test.cpp | 31 +++++++++++++++++++++-------
 1 file changed, 23 insertions(+), 8 deletions(-)

(limited to 'indra')

diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp
index 571d43825f..40e354115b 100644
--- a/indra/llcommon/tests/lldeadmantimer_test.cpp
+++ b/indra/llcommon/tests/lldeadmantimer_test.cpp
@@ -76,7 +76,7 @@ void deadmantimer_object_t::test<1>()
 }
 
 
-// Construct with negative horizon - not useful generally but will be useful in testing
+// Construct with zero horizon - not useful generally but will be useful in testing
 template<> template<>
 void deadmantimer_object_t::test<2>()
 {
@@ -142,9 +142,14 @@ void deadmantimer_object_t::test<6>()
 	U64 count(U64L(8));
 	LLDeadmanTimer timer(10.0);
 
-	U64 the_past(LLTimer::getCurrentClockCount() - float_time_to_u64(5.0));
+	// Would like to do subtraction on current time but can't because
+	// the implementation on Windows is zero-based.  We wrap around
+	// the backside resulting in a large U64 number.
+	
+	U64 the_past(LLTimer::getCurrentClockCount());
+	U64 now(the_past + float_time_to_u64(5.0));
 	timer.start(the_past);
-	ensure_equals("isExpired() returns false with 10.0 horizon time starting 5.0 in past", timer.isExpired(started, stopped, count), false);
+	ensure_equals("isExpired() returns false with 10.0 horizon time starting 5.0 in past", timer.isExpired(started, stopped, count, now), false);
 	ensure_approximately_equals("t6 - isExpired() does not modify started", started, F64(42.0), 2);
 	ensure_approximately_equals("t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
 	ensure_equals("t6 - isExpired() does not modify count", count, U64L(8));
@@ -159,9 +164,14 @@ void deadmantimer_object_t::test<7>()
 	U64 count(U64L(8));
 	LLDeadmanTimer timer(10.0);
 
-	U64 the_past(LLTimer::getCurrentClockCount() - float_time_to_u64(20.0));
+	// Would like to do subtraction on current time but can't because
+	// the implementation on Windows is zero-based.  We wrap around
+	// the backside resulting in a large U64 number.
+	
+	U64 the_past(LLTimer::getCurrentClockCount());
+	U64 now(the_past + float_time_to_u64(20.0));
 	timer.start(the_past);
-	ensure_equals("isExpired() returns true with 10.0 horizon time starting 20.0 in past", timer.isExpired(started, stopped, count), true);
+	ensure_equals("isExpired() returns true with 10.0 horizon time starting 20.0 in past", timer.isExpired(started, stopped, count, now), true);
 	ensure_approximately_equals("starting before horizon still gives equal started / stopped", started, stopped, 8);
 }
 
@@ -174,14 +184,19 @@ void deadmantimer_object_t::test<8>()
 	U64 count(U64L(8));
 	LLDeadmanTimer timer(10.0);
 
-	U64 the_past(LLTimer::getCurrentClockCount() - float_time_to_u64(20.0));
+	// Would like to do subtraction on current time but can't because
+	// the implementation on Windows is zero-based.  We wrap around
+	// the backside resulting in a large U64 number.
+	
+	U64 the_past(LLTimer::getCurrentClockCount());
+	U64 now(the_past + float_time_to_u64(20.0));
 	timer.start(the_past);
-	ensure_equals("t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", timer.isExpired(started, stopped, count), true);
+	ensure_equals("t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", timer.isExpired(started, stopped, count, now), true);
 
 	started = 42.0;
 	stopped = 97.0;
 	count = U64L(8);
-	ensure_equals("t8 - second isExpired() returns false after true", timer.isExpired(started, stopped, count), false);
+	ensure_equals("t8 - second isExpired() returns false after true", timer.isExpired(started, stopped, count, now), false);
 	ensure_approximately_equals("t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
 	ensure_approximately_equals("t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
 	ensure_equals("t8 - 2nd isExpired() does not modify count", count, U64L(8));
-- 
cgit v1.2.3


From 1310630ac60ba292f52761e795eaa55610818b9b Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 12 Apr 2013 20:11:17 +0000
Subject: SH-4090 [WIP] Basic deadman timer integration started on Linux. 
 Moving to windows to do real work.

---
 indra/newview/llmeshrepository.cpp | 45 ++++++++++++++++++++++++++++++++++++--
 indra/newview/llmeshrepository.h   | 11 ++++++++--
 2 files changed, 52 insertions(+), 4 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 1223615079..11c5780a30 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -37,6 +37,7 @@
 #include "llcallbacklist.h"
 #include "llcurl.h"
 #include "lldatapacker.h"
+#include "lldeadmantimer.h"
 #include "llfloatermodelpreview.h"
 #include "llfloaterperms.h"
 #include "lleconomy.h"
@@ -94,8 +95,9 @@ U32 LLMeshRepository::sLODPending = 0;
 U32 LLMeshRepository::sCacheBytesRead = 0;
 U32 LLMeshRepository::sCacheBytesWritten = 0;
 U32 LLMeshRepository::sPeakKbps = 0;
-	
+LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0);
 
+	
 const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5;
 
 static S32 dump_num = 0;
@@ -115,7 +117,6 @@ std::string header_lod[] =
 	"high_lod"
 };
 
-
 //get the number of bytes resident in memory for given volume
 U32 get_volume_memory_size(const LLVolume* volume)
 {
@@ -2678,6 +2679,9 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom
 
 void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume)
 { //called from main thread
+	// Manage time-to-load metrics for mesh download operations.
+	metricsCheck();
+	
 	S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
 
 	//get list of objects waiting to be notified this mesh is loaded
@@ -3699,3 +3703,40 @@ bool LLMeshRepository::meshRezEnabled()
 	}
 	return false;
 }
+
+void LLMeshRepository::metricsStart()
+{
+	sQuiescentTimer.start();
+}
+
+void LLMeshRepository::metricsStop()
+{
+	sQuiescentTimer.stop();
+}
+
+void LLMeshRepository::metricsCheck()
+{
+	static bool first_start(true);
+	F64 started, stopped;
+	U64 count;
+
+	if (first_start)
+	{
+		// Let the first request start the timing cycle for login.
+		metricsStart();
+		first_start = false;
+	}
+	sQuiescentTimer.ringBell();
+	if (sQuiescentTimer.isExpired(started, stopped, count))
+	{
+		LLSD metrics;
+
+		metrics["reason"] = "Mesh download quiescent";
+		metrics["scope"] = "Login";
+		metrics["start"] = started;
+		metrics["stop"] = stopped;
+		metrics["downloads"] = LLSD::Integer(count);
+		llinfos << "MetricsMarker" << metrics << llendl;
+	}
+}
+	
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 8602271f84..f08feedf81 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -32,6 +32,7 @@
 #include "lluuid.h"
 #include "llviewertexture.h"
 #include "llvolume.h"
+#include "lldeadmantimer.h"
 
 #define LLCONVEXDECOMPINTER_STATIC 1
 
@@ -455,14 +456,15 @@ public:
 	static U32 sCacheBytesRead;
 	static U32 sCacheBytesWritten;
 	static U32 sPeakKbps;
-	
+	static LLDeadmanTimer sQuiescentTimer;  // time-to-complete-mesh-downloads after significant events
+
 	static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
 
 	LLMeshRepository();
 
 	void init();
 	void shutdown();
-	S32 update() ;
+	S32 update();
 
 	//mesh management functions
 	S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1);
@@ -495,6 +497,11 @@ public:
 
 	S32 getMeshSize(const LLUUID& mesh_id, S32 lod);
 
+	// Quiescent timer management, main thread only.
+	static void metricsStart();
+	static void metricsStop();
+	static void metricsCheck();
+	
 	typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map;
 	mesh_load_map mLoadingMeshes[4];
 	
-- 
cgit v1.2.3


From 2df0a2494691ebfdd305e6a5ee280dd758a1b337 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Tue, 7 May 2013 16:18:31 +0000
Subject: SH-4139 Convert http downloaders and responders to llcorehttp
 patterns Initial work completed on linux, moving over to windows to do debug
 and refinement.  This includes 5/6 handlers based on existing responders and
 use of llcorehttp for the mesh header fetch.

---
 indra/llcorehttp/_httpinternal.h   |   6 +-
 indra/llcorehttp/httprequest.h     |  15 +-
 indra/newview/llappcorehttp.cpp    |  48 ++-
 indra/newview/llappcorehttp.h      |  16 +-
 indra/newview/llmeshrepository.cpp | 854 ++++++++++++++++++++++++++++++++++++-
 indra/newview/llmeshrepository.h   |  16 +-
 6 files changed, 929 insertions(+), 26 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h
index 14f744a9f1..30b0905c12 100644
--- a/indra/llcorehttp/_httpinternal.h
+++ b/indra/llcorehttp/_httpinternal.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -97,8 +97,8 @@ namespace LLCore
 {
 
 // Maxium number of policy classes that can be defined.
-// *TODO:  Currently limited to the default class, extend.
-const int HTTP_POLICY_CLASS_LIMIT = 1;
+// *TODO:  Currently limited to the default class + 1, extend.
+const int HTTP_POLICY_CLASS_LIMIT = 2;
 
 // Debug/informational tracing.  Used both
 // as a global option and in per-request traces.
diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h
index ab2f302d34..5000f47d0d 100644
--- a/indra/llcorehttp/httprequest.h
+++ b/indra/llcorehttp/httprequest.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -158,10 +158,17 @@ public:
 
 	/// Create a new policy class into which requests can be made.
 	///
+	/// All class creation must occur before threads are started and
+	/// transport begins.  Policy classes are limited to a small value.
+	/// Currently that limit is the default class + 1.
+	///
 	/// @return			If positive, the policy_id used to reference
-	///					the class in other methods.  If 0, an error
-	///					occurred and @see getStatus() may provide more
-	///					detail on the reason.
+	///					the class in other methods.  If 0, requests
+	///					for classes have exceeded internal limits
+	///					or caller has tried to create a class after
+	///					threads have been started.  Caller must fallback
+	///					and recover.
+	///
 	static policy_t createPolicyClass();
 
 	enum EClassPolicy
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index 0d7d41304d..4386e3283b 100644
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -38,7 +38,9 @@ LLAppCoreHttp::LLAppCoreHttp()
 	  mStopHandle(LLCORE_HTTP_HANDLE_INVALID),
 	  mStopRequested(0.0),
 	  mStopped(false),
-	  mPolicyDefault(-1)
+	  mPolicyDefault(-1),
+	  mPolicyTexture(-1),
+	  mPolicyMesh(-1)
 {}
 
 
@@ -95,6 +97,9 @@ void LLAppCoreHttp::init()
 	
 	// Setup default policy and constrain if directed to
 	mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID;
+
+	// Texture policy will use default for now.
+	mPolicyTexture = mPolicyDefault;
 	static const std::string texture_concur("TextureFetchConcurrency");
 	if (gSavedSettings.controlExists(texture_concur))
 	{
@@ -103,7 +108,7 @@ void LLAppCoreHttp::init()
 		if (concur > 0)
 		{
 			LLCore::HttpStatus status;
-			status = LLCore::HttpRequest::setPolicyClassOption(mPolicyDefault,
+			status = LLCore::HttpRequest::setPolicyClassOption(mPolicyTexture,
 															   LLCore::HttpRequest::CP_CONNECTION_LIMIT,
 															   concur);
 			if (! status)
@@ -120,6 +125,43 @@ void LLAppCoreHttp::init()
 			}
 		}
 	}
+
+	// Create the mesh class
+	mPolicyMesh = LLCore::HttpRequest::createPolicyClass();
+	if (! mPolicyMesh)
+	{
+		LL_WARNS("Init") << "Failed to create HTTP policy class for Mesh.  Using default policy."
+						 << LL_ENDL;
+		mPolicyMesh = mPolicyDefault;
+	}
+	else
+	{
+		static const std::string mesh_concur("MeshMaxConcurrentRequests");
+		if (gSavedSettings.controlExists(mesh_concur))
+		{
+			U32 setting(llmin(gSavedSettings.getU32(mesh_concur), U32(32)));
+			
+			if (setting > 0)
+			{
+				LLCore::HttpStatus status;
+				status = LLCore::HttpRequest::setPolicyClassOption(mPolicyMesh,
+																   LLCore::HttpRequest::CP_CONNECTION_LIMIT,
+																   setting);
+				if (! status)
+				{
+					LL_WARNS("Init") << "Unable to set mesh fetch concurrency.  Reason:  "
+									 << status.toString()
+								 << LL_ENDL;
+				}
+				else
+				{
+					LL_INFOS("Init") << "Application settings overriding default mesh fetch concurrency.  New value:  "
+									 << setting
+									 << LL_ENDL;
+				}
+			}
+		}
+	}
 	
 	// Kick the thread
 	status = LLCore::HttpRequest::startThread();
diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h
index 241d73ad52..d90af9e5ca 100644
--- a/indra/newview/llappcorehttp.h
+++ b/indra/newview/llappcorehttp.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -70,6 +70,18 @@ public:
 		{
 			return mPolicyDefault;
 		}
+
+	// Get the texture fetch policy class.
+	int getPolicyTexture() const
+		{
+			return mPolicyTexture;
+		}
+
+	// Get the mesh fetch policy class.
+	int getPolicyMesh() const
+		{
+			return mPolicyMesh;
+		}
 	
 private:
 	static const F64			MAX_THREAD_WAIT_TIME;
@@ -80,6 +92,8 @@ private:
 	F64							mStopRequested;
 	bool						mStopped;
 	int							mPolicyDefault;
+	int							mPolicyTexture;
+	int							mPolicyMesh;
 };
 
 
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 0f33128057..21b7b120f6 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -66,6 +66,7 @@
 #include "llfoldertype.h"
 #include "llviewerparcelmgr.h"
 #include "lluploadfloaterobservers.h"
+#include "bufferarray.h"
 
 #include "boost/lexical_cast.hpp"
 
@@ -77,6 +78,7 @@
 
 LLMeshRepository gMeshRepo;
 
+const S32 MESH_HEADER_SIZE = 4096;
 const U32 MAX_MESH_REQUESTS_PER_SECOND = 100;
 
 // Maximum mesh version to support.  Three least significant digits are reserved for the minor version, 
@@ -124,6 +126,7 @@ static bool metrics_inited(false);
 static boost::signals2::connection metrics_teleport_connection;
 static unsigned int metrics_teleport_start_count(0);
 static void metrics_teleport_started();
+static bool is_retryable(LLCore::HttpStatus status);
 
 //get the number of bytes resident in memory for given volume
 U32 get_volume_memory_size(const LLVolume* volume)
@@ -209,6 +212,160 @@ S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
 S32 LLMeshRepoThread::sActiveLODRequests = 0;
 U32	LLMeshRepoThread::sMaxConcurrentRequests = 1;
 
+class LLMeshHandlerBase : public LLCore::HttpHandler
+{
+public:
+	LLMeshHandlerBase()
+		: LLCore::HttpHandler(),
+		  mMeshParams(),
+		  mProcessed(false)
+	{}
+	virtual ~LLMeshHandlerBase();
+
+protected:
+	LLMeshHandlerBase(const LLMeshHandlerBase &);				// Not defined
+	void operator=(const LLMeshHandlerBase &);					// Not defined
+	
+public:
+	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
+	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size) = 0;
+	virtual void processFailure(LLCore::HttpStatus status) = 0;
+	
+public:
+	LLVolumeParams		mMeshParams;
+	bool				mProcessed;
+	LLCore::HttpHandle	mHttpHandle;
+};
+
+
+class LLMeshHeaderHandler : public LLMeshHandlerBase
+{
+public:
+	LLMeshHeaderHandler(const LLVolumeParams & mesh_params)
+		: LLMeshHandlerBase()
+	{
+		mMeshParams = mesh_params;
+		LLMeshRepoThread::incActiveHeaderRequests();
+	}
+	virtual ~LLMeshHeaderHandler();
+
+protected:
+	LLMeshHeaderHandler(const LLMeshHeaderHandler &);			// Not defined
+	void operator=(const LLMeshHeaderHandler &);				// Not defined
+	
+public:
+	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size);
+	virtual void processFailure(LLCore::HttpStatus status);
+};
+
+
+class LLMeshLODHandler : public LLMeshHandlerBase
+{
+public:
+	LLMeshLODHandler(const LLVolumeParams & mesh_params, S32 lod, U32 offset, U32 requested_bytes)
+		: LLMeshHandlerBase(),
+		  mLOD(lod),
+		  mRequestedBytes(requested_bytes),
+		  mOffset(offset)
+	{
+		mMeshParams = mesh_params;
+		LLMeshRepoThread::incActiveLODRequests();
+	}
+	virtual ~LLMeshLODHandler();
+	
+protected:
+	LLMeshLODHandler(const LLMeshLODHandler &);					// Not defined
+	void operator=(const LLMeshLODHandler &);					// Not defined
+	
+public:
+	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size);
+	virtual void processFailure(LLCore::HttpStatus status);
+
+public:
+	S32 mLOD;
+	U32 mRequestedBytes;
+	U32 mOffset;
+};
+
+
+class LLMeshSkinInfoHandler : public LLMeshHandlerBase
+{
+public:
+	LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 size)
+		: LLMeshHandlerBase(),
+		  mMeshID(id),
+		  mRequestedBytes(size),
+		  mOffset(offset)
+	{}
+	virtual ~LLMeshSkinInfoHandler();
+
+protected:
+	LLMeshSkinInfoHandler(const LLMeshSkinInfoHandler &);		// Not defined
+	void operator=(const LLMeshSkinInfoHandler &);				// Not defined
+	
+public:
+	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size);
+	virtual void processFailure(LLCore::HttpStatus status);
+
+public:
+	LLUUID mMeshID;
+	U32 mRequestedBytes;
+	U32 mOffset;
+};
+
+
+class LLMeshDecompositionHandler : public LLMeshHandlerBase
+{
+public:
+	LLMeshDecompositionHandler(const LLUUID& id, U32 offset, U32 size)
+		: LLMeshHandlerBase(),
+		  mMeshID(id),
+		  mRequestedBytes(size),
+		  mOffset(offset)
+	{}
+	virtual ~LLMeshDecompositionHandler();
+
+protected:
+	LLMeshDecompositionHandler(const LLMeshDecompositionHandler &);		// Not defined
+	void operator=(const LLMeshDecompositionHandler &);					// Not defined
+	
+public:
+	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size);
+	virtual void processFailure(LLCore::HttpStatus status);
+
+public:
+	LLUUID mMeshID;
+	U32 mRequestedBytes;
+	U32 mOffset;
+};
+
+
+class LLMeshPhysicsShapeHandler : public LLMeshHandlerBase
+{
+public:
+	LLMeshPhysicsShapeHandler(const LLUUID& id, U32 offset, U32 size)
+		: LLMeshHandlerBase(),
+		  mMeshID(id),
+		  mRequestedBytes(size),
+		  mOffset(offset)
+	{}
+	virtual ~LLMeshPhysicsShapeHandler();
+
+protected:
+	LLMeshPhysicsShapeHandler(const LLMeshPhysicsShapeHandler &);	// Not defined
+	void operator=(const LLMeshPhysicsShapeHandler &);				// Not defined
+	
+public:
+	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size);
+	virtual void processFailure(LLCore::HttpStatus status);
+
+public:
+	LLUUID		mMeshID;
+	U32			mRequestedBytes;
+	U32			mOffset;
+};
+
+	
 class LLMeshHeaderResponder : public LLCurl::Responder
 {
 public:
@@ -538,16 +695,45 @@ public:
 };
 
 LLMeshRepoThread::LLMeshRepoThread()
-: LLThread("mesh repo") 
+: LLThread("mesh repo"),
+  mCurlRequest(NULL),
+  mWaiting(false),
+  mHttpRequest(NULL),
+  mHttpOptions(NULL),
+  mHttpHeaders(NULL),
+  mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID)
 { 
-	mWaiting = false;
 	mMutex = new LLMutex(NULL);
 	mHeaderMutex = new LLMutex(NULL);
 	mSignal = new LLCondition(NULL);
+	mHttpRequest = new LLCore::HttpRequest;
+	mHttpOptions = new LLCore::HttpOptions;
+	mHttpHeaders = new LLCore::HttpHeaders;
+	mHttpHeaders->mHeaders.push_back("Accept: application/vnd.ll.mesh");
+	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyMesh();
 }
 
 LLMeshRepoThread::~LLMeshRepoThread()
 {
+	for (http_request_set::iterator iter(mHttpRequestSet.begin());
+		 iter != mHttpRequestSet.end();
+		 ++iter)
+	{
+		delete *iter;
+	}
+	mHttpRequestSet.clear();
+	if (mHttpHeaders)
+	{
+		mHttpHeaders->release();
+		mHttpHeaders = NULL;
+	}
+	if (mHttpOptions)
+	{
+		mHttpOptions->release();
+		mHttpOptions = NULL;
+	}
+	delete mHttpRequest;
+	mHttpRequest = NULL;
 	delete mMutex;
 	mMutex = NULL;
 	delete mHeaderMutex;
@@ -571,7 +757,7 @@ void LLMeshRepoThread::run()
 		mSignal->wait();
 		mWaiting = false;
 
-		if (!LLApp::isQuitting())
+		if (! LLApp::isQuitting() && ! mHttpRequestSet.empty())
 		{
 			static U32 count = 0;
 
@@ -660,6 +846,7 @@ void LLMeshRepoThread::run()
 			}
 
 			mCurlRequest->process();
+			mHttpRequest->update(0L);
 		}
 	}
 	
@@ -1045,13 +1232,15 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 		S32 size = file.getSize();
 
 		if (size > 0)
-		{ //NOTE -- if the header size is ever more than 4KB, this will break
-			U8 buffer[4096];
-			S32 bytes = llmin(size, 4096);
+		{
+			// *NOTE:  if the header size is ever more than 4KB, this will break
+			U8 buffer[MESH_HEADER_SIZE];
+			S32 bytes = llmin(size, MESH_HEADER_SIZE);
 			LLMeshRepository::sCacheBytesRead += bytes;	
 			file.read(buffer, bytes);
 			if (headerReceived(mesh_params, buffer, bytes))
-			{ //did not do an HTTP request, return false
+			{
+				// Found mesh in VFS cache
 				return true;
 			}
 		}
@@ -1068,7 +1257,31 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 		//grab first 4KB if we're going to bother with a fetch.  Cache will prevent future fetches if a full mesh fits
 		//within the first 4KB
 		//NOTE -- this will break of headers ever exceed 4KB		
-		retval = mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params));
+#if 0
+		retval = mCurlRequest->getByteRange(http_url, headers, 0, MESH_HEADER_SIZE, new LLMeshHeaderResponder(mesh_params));
+#else
+		LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params);
+		LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
+																	  0,				// *TODO:  Get better priority value
+																	  http_url,
+																	  0,
+																	  MESH_HEADER_SIZE,
+																	  mHttpOptions,
+																	  mHttpHeaders,
+																	  handler);
+		if (LLCORE_HTTP_HANDLE_INVALID == handle)
+		{
+			// *TODO:  Better error message
+			llwarns << "HTTP GET request failed for mesh " << mID << llendl;
+			delete handler;
+			retval = false;
+		}
+		else
+		{
+			handler->mHttpHandle = handle;
+			mHttpRequestSet.insert(handler);
+		}
+#endif		
 		if(retval)
 		{
 			LLMeshRepository::sHTTPRequestCount++;
@@ -1209,7 +1422,7 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat
 			LLMutexLock lock(mHeaderMutex);
 			mMeshHeaderSize[mesh_id] = header_size;
 			mMeshHeader[mesh_id] = header;
-			}
+		}
 
 
 		LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time.
@@ -2162,6 +2375,336 @@ void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& re
 	delete [] data;
 }
 
+	
+LLMeshHandlerBase::~LLMeshHandlerBase()
+{}
+
+
+void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)
+{
+	mProcessed = true;
+
+	LLCore::HttpStatus status(response->getStatus());
+	if (! status)
+	{
+		processFailure(status);
+	}
+	else
+	{
+		// From texture fetch code and applies here:
+		//
+		// A warning about partial (HTTP 206) data.  Some grid services
+		// do *not* return a 'Content-Range' header in the response to
+		// Range requests with a 206 status.  We're forced to assume
+		// we get what we asked for in these cases until we can fix
+		// the services.
+		static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT);
+
+		LLCore::BufferArray * body(response->getBody());
+		S32 data_size(body ? body->size() : 0);
+		U8 * data(NULL);
+
+		if (data_size > 0)
+		{
+			// *TODO: Try to get rid of data copying and add interfaces
+			// that support BufferArray directly.
+			data = new U8[data_size];
+			body->read(0, (char *) data, data_size);
+			LLMeshRepository::sBytesReceived += llmin(data_size, MESH_HEADER_SIZE);
+		}
+
+		processData(body, data, data_size);
+
+		delete [] data;
+	}
+
+	// Release handler
+	gMeshRepo.mThread->mHttpRequestSet.erase(this);
+
+	delete this;		// Must be last statement
+}
+
+
+LLMeshHeaderHandler::~LLMeshHeaderHandler()
+{
+	if (!LLApp::isQuitting())
+	{
+		if (! mProcessed)
+		{
+			// something went wrong, retry
+			llwarns << "Timeout or service unavailable, retrying." << llendl;
+			LLMeshRepository::sHTTPRetryCount++;
+			LLMeshRepoThread::HeaderRequest req(mMeshParams);
+			LLMutexLock lock(gMeshRepo.mThread->mMutex);
+			gMeshRepo.mThread->mHeaderReqQ.push(req);
+		}
+		LLMeshRepoThread::decActiveHeaderRequests();
+	}
+}
+
+
+void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
+{
+	if (is_retryable(status))
+	{
+		llwarns << "Timeout or service unavailable, retrying." << llendl;
+		LLMeshRepository::sHTTPRetryCount++;
+		LLMeshRepoThread::HeaderRequest req(mMeshParams);
+		LLMutexLock lock(gMeshRepo.mThread->mMutex);
+		gMeshRepo.mThread->mHeaderReqQ.push(req);
+	}
+	else
+	{
+		// *TODO:  better error message
+		llwarns << "Unhandled status." << llendl;
+	}
+}
+
+
+void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
+{
+	bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size);
+	llassert(success);
+	if (! success)
+	{
+		// *TODO:  Get real reason for parse failure here
+		llwarns << "Unable to parse mesh header: " << llendl;
+	}
+	else if (data && data_size > 0)
+	{
+		// header was successfully retrieved from sim, cache in vfs
+		LLUUID mesh_id = mMeshParams.getSculptID();
+		LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id];
+
+		S32 version = header["version"].asInteger();
+
+		if (version <= MAX_MESH_VERSION)
+		{
+			std::stringstream str;
+
+			S32 lod_bytes = 0;
+
+			for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i)
+			{
+				// figure out how many bytes we'll need to reserve in the file
+				std::string lod_name = header_lod[i];
+				lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger());
+			}
+		
+			// just in case skin info or decomposition is at the end of the file (which it shouldn't be)
+			lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger());
+			lod_bytes = llmax(lod_bytes, header["physics_convex"]["offset"].asInteger() + header["physics_convex"]["size"].asInteger());
+
+			S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id];
+			S32 bytes = lod_bytes + header_bytes; 
+
+		
+			// It's possible for the remote asset to have more data than is needed for the local cache
+			// only allocate as much space in the VFS as is needed for the local cache
+			data_size = llmin(data_size, bytes);
+
+			LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE);
+			if (file.getMaxSize() >= bytes || file.setMaxSize(bytes))
+			{
+				LLMeshRepository::sCacheBytesWritten += data_size;
+
+				file.write(data, data_size);
+			
+				// zero out the rest of the file 
+				U8 block[MESH_HEADER_SIZE];
+				memset(block, 0, MESH_HEADER_SIZE);
+
+				while (bytes-file.tell() > MESH_HEADER_SIZE)
+				{
+					file.write(block, MESH_HEADER_SIZE);
+				}
+
+				S32 remaining = bytes-file.tell();
+					
+				if (remaining > 0)
+				{
+					file.write(block, remaining);
+				}
+			}
+		}
+	}
+}
+
+
+LLMeshLODHandler::~LLMeshLODHandler()
+{
+	if (! LLApp::isQuitting())
+	{
+		if (! mProcessed)
+		{
+			llwarns << "Killed without being processed, retrying." << llendl;
+			LLMeshRepository::sHTTPRetryCount++;
+			gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD);
+		}
+		LLMeshRepoThread::decActiveLODRequests();
+	}
+}
+
+void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)
+{
+	if (is_retryable(status))
+	{
+		llwarns << "Timeout or service unavailable, retrying." << llendl;
+		LLMeshRepository::sHTTPRetryCount++;
+		// *FIXME:  Is this safe?  Does this need locking?
+		gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD);
+	}
+	else
+	{
+		// *TODO:  better error message
+		llwarns << "Unhandled status." << llendl;
+	}
+}
+
+void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
+{
+	if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size))
+	{
+		// good fetch from sim, write to VFS for caching
+		LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE);
+
+		S32 offset = mOffset;
+		S32 size = mRequestedBytes;
+
+		if (file.getSize() >= offset+size)
+		{
+			file.seek(offset);
+			file.write(data, size);
+			LLMeshRepository::sCacheBytesWritten += size;
+		}
+	}
+}
+
+
+LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler()
+{
+		llassert(mProcessed);
+}
+
+void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)
+{
+	if (is_retryable(status))
+	{
+		llwarns << "Timeout or service unavailable, retrying." << llendl;
+		LLMeshRepository::sHTTPRetryCount++;
+		// *FIXME:  Is this safe?  Does this need locking?
+		gMeshRepo.mThread->loadMeshSkinInfo(mMeshID);
+	}
+	else
+	{
+		// *TODO:  better error message
+		llwarns << "Unhandled status." << llendl;
+	}
+}
+
+void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
+{
+	if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
+	{
+		// good fetch from sim, write to VFS for caching
+		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
+
+		S32 offset = mOffset;
+		S32 size = mRequestedBytes;
+
+		if (file.getSize() >= offset+size)
+		{
+			LLMeshRepository::sCacheBytesWritten += size;
+			file.seek(offset);
+			file.write(data, size);
+		}
+	}
+}
+
+
+LLMeshDecompositionHandler::~LLMeshDecompositionHandler()
+{
+		llassert(mProcessed);
+}
+
+void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status)
+{
+	if (is_retryable(status))
+	{
+		llwarns << "Timeout or service unavailable, retrying." << llendl;
+		LLMeshRepository::sHTTPRetryCount++;
+		// *FIXME:  Is this safe?  Does this need locking?
+		gMeshRepo.mThread->loadMeshDecomposition(mMeshID);
+	}
+	else
+	{
+		// *TODO:  better error message
+		llwarns << "Unhandled status." << llendl;
+	}
+}
+
+void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
+{
+	if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size))
+	{
+		// good fetch from sim, write to VFS for caching
+		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
+
+		S32 offset = mOffset;
+		S32 size = mRequestedBytes;
+
+		if (file.getSize() >= offset+size)
+		{
+			LLMeshRepository::sCacheBytesWritten += size;
+			file.seek(offset);
+			file.write(data, size);
+		}
+	}
+}
+
+
+LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler()
+{
+		llassert(mProcessed);
+}
+
+void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status)
+{
+	if (is_retryable(status))
+	{
+		llwarns << "Timeout or service unavailable, retrying." << llendl;
+		LLMeshRepository::sHTTPRetryCount++;
+		// *FIXME:  Is this safe?  Does this need locking?
+		gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID);
+	}
+	else
+	{
+		// *TODO:  better error message
+		llwarns << "Unhandled status." << llendl;
+	}
+}
+
+	
+void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
+{
+	if (gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size))
+	{
+		// good fetch from sim, write to VFS for caching
+		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
+
+		S32 offset = mOffset;
+		S32 size = mRequestedBytes;
+
+		if (file.getSize() >= offset+size)
+		{
+			LLMeshRepository::sCacheBytesWritten += size;
+			file.seek(offset);
+			file.write(data, size);
+		}
+	}
+}
+
+
 void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
 							  const LLChannelDescriptors& channels,
 							  const LLIOPipe::buffer_ptr_t& buffer)
@@ -2215,7 +2758,7 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
 		buffer->readAfter(channels.in(), NULL, data, data_size);
 	}
 
-	LLMeshRepository::sBytesReceived += llmin(data_size, 4096);
+	LLMeshRepository::sBytesReceived += llmin(data_size, MESH_HEADER_SIZE);
 
 	bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size);
 	
@@ -2267,12 +2810,12 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
 				file.write((const U8*) data, data_size);
 			
 				//zero out the rest of the file 
-				U8 block[4096];
-				memset(block, 0, 4096);
+				U8 block[MESH_HEADER_SIZE];
+				memset(block, 0, MESH_HEADER_SIZE);
 
-				while (bytes-file.tell() > 4096)
+				while (bytes-file.tell() > MESH_HEADER_SIZE)
 				{
-					file.write(block, 4096);
+					file.write(block, MESH_HEADER_SIZE);
 				}
 
 				S32 remaining = bytes-file.tell();
@@ -3797,3 +4340,286 @@ void metrics_teleport_started()
 	++metrics_teleport_start_count;
 }
 
+
+// This comes from an edit in viewer-cat.  Unify this once that's
+// available everywhere.
+bool is_retryable(LLCore::HttpStatus status)
+{
+	static const LLCore::HttpStatus cant_connect(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
+	static const LLCore::HttpStatus cant_res_proxy(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY);
+	static const LLCore::HttpStatus cant_res_host(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST);
+	static const LLCore::HttpStatus send_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR);
+	static const LLCore::HttpStatus recv_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR);
+	static const LLCore::HttpStatus upload_failed(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED);
+	static const LLCore::HttpStatus op_timedout(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT);
+	static const LLCore::HttpStatus post_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR);
+	static const LLCore::HttpStatus partial_file(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_PARTIAL_FILE);
+	static const LLCore::HttpStatus inv_cont_range(LLCore::HttpStatus::LLCORE, LLCore::HE_INV_CONTENT_RANGE_HDR);
+	
+	return ((! status) &&
+			((status.isHttpStatus() && status.mType >= 499 && status.mType <= 599) ||		// Include special 499 in retryables
+			 status == cant_connect ||			// Connection reset/endpoint problems
+			 status == cant_res_proxy ||		// DNS problems
+			 status == cant_res_host ||			// DNS problems
+			 status == send_error ||			// General socket problems
+			 status == recv_error ||			// General socket problems
+			 status == upload_failed ||			// Transport problem
+			 status == op_timedout ||			// Timer expired
+			 status == post_error ||			// Transport problem
+			 status == partial_file ||			// Data inconsistency in response
+			 status == inv_cont_range));		// Short data read disagrees with content-range
+}
+
+
+
+
+// ===========
+//
+// HTTP fragments I'll be needing
+//
+//
+#if 0
+	  mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
+	mHttpRequest = new LLCore::HttpRequest;
+	mHttpOptions = new LLCore::HttpOptions;
+	mHttpHeaders = new LLCore::HttpHeaders;
+	mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c");
+	mHttpMetricsHeaders = new LLCore::HttpHeaders;
+	mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml");
+	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault();
+
+
+	LLCore::HttpHandle		mHttpHandle;				// Handle of any active request
+	LLCore::BufferArray	*	mHttpBufferArray;			// Refcounted pointer to response data 
+	int						mHttpPolicyClass;
+	bool					mHttpActive;				// Active request to http library
+	unsigned int			mHttpReplySize;				// Actual received data size
+	unsigned int			mHttpReplyOffset;			// Actual received data offset
+	bool					mHttpHasResource;			// Counts against Fetcher's mHttpSemaphore
+
+									 
+		mHttpHandle = LLCORE_HTTP_HANDLE_INVALID;
+		if (!mUrl.empty())
+		{
+			mRequestedTimer.reset();
+			mLoaded = FALSE;
+			mGetStatus = LLCore::HttpStatus();
+			mGetReason.clear();
+			LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset
+								 << " Bytes: " << mRequestedSize
+								 << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth
+								 << LL_ENDL;
+
+			// Will call callbackHttpGet when curl request completes
+			mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass,
+																	  mWorkPriority,
+																	  mUrl,
+																	  mRequestedOffset,
+																	  mRequestedSize,
+																	  mFetcher->mHttpOptions,
+																	  mFetcher->mHttpHeaders,
+																	  this);
+		}
+		if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle)
+		{
+			llwarns << "HTTP GET request failed for " << mID << llendl;
+			resetFormattedData();
+			releaseHttpSemaphore();
+			return true; // failed
+		}
+
+		mHttpActive = true;
+		mFetcher->addToHTTPQueue(mID);
+		recordTextureStart(true);
+		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+		mState = WAIT_HTTP_REQ;	
+		
+		// fall through
+	}
+
+
+
+// Threads:  Ttf
+// virtual
+void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)
+{
+	static LLCachedControl<bool> log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog");
+	static LLCachedControl<bool> log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator");
+	static LLCachedControl<bool> log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ;
+
+	LLMutexLock lock(&mWorkMutex);										// +Mw
+
+	mHttpActive = false;
+	
+	if (log_to_viewer_log || log_to_sim)
+	{
+		U64 timeNow = LLTimer::getTotalTime();
+		mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime);
+		mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
+		mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize);
+		mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset);
+		mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow);
+	}
+
+	bool success = true;
+	bool partial = false;
+	LLCore::HttpStatus status(response->getStatus());
+	
+	lldebugs << "HTTP COMPLETE: " << mID
+			 << " status: " << status.toHex()
+			 << " '" << status.toString() << "'"
+			 << llendl;
+//	unsigned int offset(0), length(0), full_length(0);
+//	response->getRange(&offset, &length, &full_length);
+// 	llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle
+// 			<< " status: " << status.toULong() << " '" << status.toString() << "'"
+// 			<< " req offset: " << mRequestedOffset << " req length: " << mRequestedSize
+// 			<< " offset: " << offset << " length: " << length
+// 			<< llendl;
+
+	if (! status)
+	{
+		success = false;
+		std::string reason(status.toString());
+		setGetStatus(status, reason);
+		llwarns << "CURL GET FAILED, status: " << status.toHex()
+				<< " reason: " << reason << llendl;
+	}
+	else
+	{
+		// A warning about partial (HTTP 206) data.  Some grid services
+		// do *not* return a 'Content-Range' header in the response to
+		// Range requests with a 206 status.  We're forced to assume
+		// we get what we asked for in these cases until we can fix
+		// the services.
+		static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT);
+
+		partial = (par_status == status);
+	}
+	
+	S32 data_size = callbackHttpGet(response, partial, success);
+			
+	if (log_texture_traffic && data_size > 0)
+	{
+		LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID);
+		if (tex)
+		{
+			gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ;
+		}
+	}
+
+	mFetcher->removeFromHTTPQueue(mID, data_size);
+	
+	recordTextureDone(true);
+}																		// -Mw
+
+
+// Threads:  Ttf
+// Locks:  Mw
+S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response,
+										  bool partial, bool success)
+{
+	S32 data_size = 0 ;
+
+	if (mState != WAIT_HTTP_REQ)
+	{
+		llwarns << "callbackHttpGet for unrequested fetch worker: " << mID
+				<< " req=" << mSentRequest << " state= " << mState << llendl;
+		return data_size;
+	}
+	if (mLoaded)
+	{
+		llwarns << "Duplicate callback for " << mID.asString() << llendl;
+		return data_size ; // ignore duplicate callback
+	}
+	if (success)
+	{
+		// get length of stream:
+		LLCore::BufferArray * body(response->getBody());
+		data_size = body ? body->size() : 0;
+
+		LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL;
+		if (data_size > 0)
+		{
+			LLViewerStatsRecorder::instance().textureFetch(data_size);
+			// *TODO: set the formatted image data here directly to avoid the copy
+
+			// Hold on to body for later copy
+			llassert_always(NULL == mHttpBufferArray);
+			body->addRef();
+			mHttpBufferArray = body;
+
+			if (partial)
+			{
+				unsigned int offset(0), length(0), full_length(0);
+				response->getRange(&offset, &length, &full_length);
+				if (! offset && ! length)
+				{
+					// This is the case where we receive a 206 status but
+					// there wasn't a useful Content-Range header in the response.
+					// This could be because it was badly formatted but is more
+					// likely due to capabilities services which scrub headers
+					// from responses.  Assume we got what we asked for...
+					mHttpReplySize = data_size;
+					mHttpReplyOffset = mRequestedOffset;
+				}
+				else
+				{
+					mHttpReplySize = length;
+					mHttpReplyOffset = offset;
+				}
+			}
+
+			if (! partial)
+			{
+				// Response indicates this is the entire asset regardless
+				// of our asking for a byte range.  Mark it so and drop
+				// any partial data we might have so that the current
+				// response body becomes the entire dataset.
+				if (data_size <= mRequestedOffset)
+				{
+					LL_WARNS("Texture") << "Fetched entire texture " << mID
+										<< " when it was expected to be marked complete.  mImageSize:  "
+										<< mFileSize << " datasize:  " << mFormattedImage->getDataSize()
+										<< LL_ENDL;
+				}
+				mHaveAllData = TRUE;
+				llassert_always(mDecodeHandle == 0);
+				mFormattedImage = NULL; // discard any previous data we had
+			}
+			else if (data_size < mRequestedSize)
+			{
+				mHaveAllData = TRUE;
+			}
+			else if (data_size > mRequestedSize)
+			{
+				// *TODO: This shouldn't be happening any more  (REALLY don't expect this anymore)
+				llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl;
+				mHaveAllData = TRUE;
+				llassert_always(mDecodeHandle == 0);
+				mFormattedImage = NULL; // discard any previous data we had
+			}
+		}
+		else
+		{
+			// We requested data but received none (and no error),
+			// so presumably we have all of it
+			mHaveAllData = TRUE;
+		}
+		mRequestedSize = data_size;
+	}
+	else
+	{
+		mRequestedSize = -1; // error
+	}
+	
+	mLoaded = TRUE;
+	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+
+	LLViewerStatsRecorder::instance().log(0.2f);
+	return data_size ;
+}
+
+#endif
+// ============
+
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 3cdc66e1f0..8ffe2d68cb 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2010-2013, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -33,6 +33,11 @@
 #include "llviewertexture.h"
 #include "llvolume.h"
 #include "lldeadmantimer.h"
+#include "httpcommon.h"
+#include "httprequest.h"
+#include "httpoptions.h"
+#include "httpheaders.h"
+#include "httphandler.h"
 
 #define LLCONVEXDECOMPINTER_STATIC 1
 
@@ -316,6 +321,15 @@ public:
 	typedef std::map<LLVolumeParams, std::vector<S32> > pending_lod_map;
 	pending_lod_map mPendingLOD;
 
+	// llcorehttp library interface objects.
+	LLCore::HttpRequest *				mHttpRequest;
+	LLCore::HttpOptions *				mHttpOptions;
+	LLCore::HttpHeaders *				mHttpHeaders;
+	LLCore::HttpRequest::policy_t		mHttpPolicyClass;
+
+	typedef std::set<LLCore::HttpHandler *> http_request_set;
+	http_request_set					mHttpRequestSet;			// Outstanding HTTP requests
+
 	static std::string constructUrl(LLUUID mesh_id);
 
 	LLMeshRepoThread();
-- 
cgit v1.2.3


From aa855b18ecde2786c758ac27d2ddb049d0929d55 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Tue, 7 May 2013 17:24:12 -0400
Subject: SH-4139 Convert http downloaders and responders to llcorehttp
 patterns First version running with all five downloaders converted.  Not
 certain all are functional yet and the whole thing is slow but it is running.

---
 indra/newview/llmeshrepository.cpp | 247 ++++++++++++++++++-------------------
 1 file changed, 123 insertions(+), 124 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 21b7b120f6..419613091a 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -753,11 +753,19 @@ void LLMeshRepoThread::run()
 
 	while (!LLApp::isQuitting())
 	{
-		mWaiting = true;
-		mSignal->wait();
-		mWaiting = false;
-
-		if (! LLApp::isQuitting() && ! mHttpRequestSet.empty())
+		if (! mHttpRequestSet.empty())
+		{
+			mHttpRequest->update(0L);
+			ms_sleep(100);
+		}
+		else
+		{
+			mWaiting = true;
+			mSignal->wait();
+			mWaiting = false;
+		}
+		
+		if (! LLApp::isQuitting())
 		{
 			static U32 count = 0;
 
@@ -846,7 +854,6 @@ void LLMeshRepoThread::run()
 			}
 
 			mCurlRequest->process();
-			mHttpRequest->update(0L);
 		}
 	}
 	
@@ -1007,10 +1014,35 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
-			{				
+			{
+#if 0
 				ret = mCurlRequest->getByteRange(http_url, headers, offset, size,
 												 new LLMeshSkinInfoResponder(mesh_id, offset, size));
-				if(ret)
+#else
+				LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size);
+				// LL_WARNS("Mesh") << "MESH:  Issuing Skin Info Request" << LL_ENDL;
+				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
+																			  0,				// *TODO:  Get better priority value
+																			  http_url,
+																			  offset,
+																			  size,
+																			  mHttpOptions,
+																			  mHttpHeaders,
+																			  handler);
+				if (LLCORE_HTTP_HANDLE_INVALID == handle)
+				{
+					// *TODO:  Better error message
+					llwarns << "HTTP GET request failed for mesh " << mID << llendl;
+					delete handler;
+					ret = false;
+				}
+				else
+				{
+					handler->mHttpHandle = handle;
+					mHttpRequestSet.insert(handler);
+				}
+#endif
+				if (ret)
 				{
 					LLMeshRepository::sHTTPRequestCount++;
 				}
@@ -1089,10 +1121,35 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
-			{				
+			{
+#if 0
 				ret = mCurlRequest->getByteRange(http_url, headers, offset, size,
 												 new LLMeshDecompositionResponder(mesh_id, offset, size));
-				if(ret)
+#else
+				LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size);
+				// LL_WARNS("Mesh") << "MESH:  Issuing Decomp Request" << LL_ENDL;
+				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
+																			  0,	// *TODO:  Get better priority value
+																			  http_url,
+																			  offset,
+																			  size,
+																			  mHttpOptions,
+																			  mHttpHeaders,
+																			  handler);
+				if (LLCORE_HTTP_HANDLE_INVALID == handle)
+				{
+					// *TODO:  Better error message
+					llwarns << "HTTP GET request failed for decomposition mesh " << mID << llendl;
+					delete handler;
+					ret = false;
+				}
+				else
+				{
+					handler->mHttpHandle = handle;
+					mHttpRequestSet.insert(handler);
+				}
+#endif
+				if (ret)
 				{
 					LLMeshRepository::sHTTPRequestCount++;
 				}
@@ -1170,11 +1227,35 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
-			{				
+			{
+#if 0
 				ret = mCurlRequest->getByteRange(http_url, headers, offset, size,
 												 new LLMeshPhysicsShapeResponder(mesh_id, offset, size));
-
-				if(ret)
+#else
+				LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size);
+				// LL_WARNS("Mesh") << "MESH:  Issuing Physics Shape Request" << LL_ENDL;
+				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
+																			  0,		// *TODO:  Get better priority value
+																			  http_url,
+																			  offset,
+																			  size,
+																			  mHttpOptions,
+																			  mHttpHeaders,
+																			  handler);
+				if (LLCORE_HTTP_HANDLE_INVALID == handle)
+				{
+					// *TODO:  Better error message
+					llwarns << "HTTP GET request failed for physics shape mesh " << mID << llendl;
+					delete handler;
+					ret = false;
+				}
+				else
+				{
+					handler->mHttpHandle = handle;
+					mHttpRequestSet.insert(handler);
+				}
+#endif
+				if (ret)
 				{
 					LLMeshRepository::sHTTPRequestCount++;
 				}
@@ -1261,6 +1342,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 		retval = mCurlRequest->getByteRange(http_url, headers, 0, MESH_HEADER_SIZE, new LLMeshHeaderResponder(mesh_params));
 #else
 		LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params);
+		// LL_WARNS("Mesh") << "MESH:  Issuing Request" << LL_ENDL;
 		LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
 																	  0,				// *TODO:  Get better priority value
 																	  http_url,
@@ -1352,10 +1434,34 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
-			{				
+			{
+#if 0
 				retval = mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
 										   new LLMeshLODResponder(mesh_params, lod, offset, size));
-
+#else
+				LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size);
+				// LL_WARNS("Mesh") << "MESH:  Issuing LOD Request" << LL_ENDL;
+				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
+																			  0,		// *TODO:  Get better priority value
+																			  http_url,
+																			  offset,
+																			  size,
+																			  mHttpOptions,
+																			  mHttpHeaders,
+																			  handler);
+				if (LLCORE_HTTP_HANDLE_INVALID == handle)
+				{
+					// *TODO:  Better error message
+					llwarns << "HTTP GET request failed for LOD mesh " << mID << llendl;
+					delete handler;
+					retval = false;
+				}
+				else
+				{
+					handler->mHttpHandle = handle;
+					mHttpRequestSet.insert(handler);
+				}
+#endif
 				if(retval)
 				{
 					LLMeshRepository::sHTTPRequestCount++;
@@ -2445,6 +2551,7 @@ LLMeshHeaderHandler::~LLMeshHeaderHandler()
 
 void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
 {
+	LL_WARNS("Mesh") << "MESH:  Processing Failure" << LL_ENDL;
 	if (is_retryable(status))
 	{
 		llwarns << "Timeout or service unavailable, retrying." << llendl;
@@ -2463,6 +2570,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
 
 void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
 {
+	// LL_WARNS("Mesh") << "MESH:  Processing Data" << LL_ENDL;
 	bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size);
 	llassert(success);
 	if (! success)
@@ -4379,103 +4487,6 @@ bool is_retryable(LLCore::HttpStatus status)
 //
 //
 #if 0
-	  mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
-	mHttpRequest = new LLCore::HttpRequest;
-	mHttpOptions = new LLCore::HttpOptions;
-	mHttpHeaders = new LLCore::HttpHeaders;
-	mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c");
-	mHttpMetricsHeaders = new LLCore::HttpHeaders;
-	mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml");
-	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault();
-
-
-	LLCore::HttpHandle		mHttpHandle;				// Handle of any active request
-	LLCore::BufferArray	*	mHttpBufferArray;			// Refcounted pointer to response data 
-	int						mHttpPolicyClass;
-	bool					mHttpActive;				// Active request to http library
-	unsigned int			mHttpReplySize;				// Actual received data size
-	unsigned int			mHttpReplyOffset;			// Actual received data offset
-	bool					mHttpHasResource;			// Counts against Fetcher's mHttpSemaphore
-
-									 
-		mHttpHandle = LLCORE_HTTP_HANDLE_INVALID;
-		if (!mUrl.empty())
-		{
-			mRequestedTimer.reset();
-			mLoaded = FALSE;
-			mGetStatus = LLCore::HttpStatus();
-			mGetReason.clear();
-			LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset
-								 << " Bytes: " << mRequestedSize
-								 << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth
-								 << LL_ENDL;
-
-			// Will call callbackHttpGet when curl request completes
-			mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass,
-																	  mWorkPriority,
-																	  mUrl,
-																	  mRequestedOffset,
-																	  mRequestedSize,
-																	  mFetcher->mHttpOptions,
-																	  mFetcher->mHttpHeaders,
-																	  this);
-		}
-		if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle)
-		{
-			llwarns << "HTTP GET request failed for " << mID << llendl;
-			resetFormattedData();
-			releaseHttpSemaphore();
-			return true; // failed
-		}
-
-		mHttpActive = true;
-		mFetcher->addToHTTPQueue(mID);
-		recordTextureStart(true);
-		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
-		mState = WAIT_HTTP_REQ;	
-		
-		// fall through
-	}
-
-
-
-// Threads:  Ttf
-// virtual
-void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)
-{
-	static LLCachedControl<bool> log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog");
-	static LLCachedControl<bool> log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator");
-	static LLCachedControl<bool> log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ;
-
-	LLMutexLock lock(&mWorkMutex);										// +Mw
-
-	mHttpActive = false;
-	
-	if (log_to_viewer_log || log_to_sim)
-	{
-		U64 timeNow = LLTimer::getTotalTime();
-		mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime);
-		mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
-		mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize);
-		mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset);
-		mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow);
-	}
-
-	bool success = true;
-	bool partial = false;
-	LLCore::HttpStatus status(response->getStatus());
-	
-	lldebugs << "HTTP COMPLETE: " << mID
-			 << " status: " << status.toHex()
-			 << " '" << status.toString() << "'"
-			 << llendl;
-//	unsigned int offset(0), length(0), full_length(0);
-//	response->getRange(&offset, &length, &full_length);
-// 	llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle
-// 			<< " status: " << status.toULong() << " '" << status.toString() << "'"
-// 			<< " req offset: " << mRequestedOffset << " req length: " << mRequestedSize
-// 			<< " offset: " << offset << " length: " << length
-// 			<< llendl;
 
 	if (! status)
 	{
@@ -4499,19 +4510,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe
 	
 	S32 data_size = callbackHttpGet(response, partial, success);
 			
-	if (log_texture_traffic && data_size > 0)
-	{
-		LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID);
-		if (tex)
-		{
-			gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ;
-		}
-	}
 
-	mFetcher->removeFromHTTPQueue(mID, data_size);
-	
-	recordTextureDone(true);
-}																		// -Mw
 
 
 // Threads:  Ttf
-- 
cgit v1.2.3


From e3db003cbff0faa44d29e35139601b9778acfbca Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 8 May 2013 13:48:14 -0400
Subject: SH-4139 Convert http downloaders and responders to llcorehttp
 patterns Conversion was mostly trivial.  Did some refactoring in the
 conversion of Responders to Handlers which eliminated 5X code replication. 
 More will be done especially as this is extended to deal with the various
 possible combinations of 200/206/416 status for ranged gets.  There are a lot
 of thread races in the existing code, that is going to need some real
 attention.  And the scheduling/liveness logic in the thread management
 bounces around from thread to thread wasting a lot of time and using
 expensive synchronization.  Much can be done here.  But the result is that
 the 8 connections in the Mesh corehttp class now perform as did the 32
 connections of the original.  And that 32 actually looks like it could bleed
 to over 64.  So, progress...

---
 indra/newview/llmeshrepository.cpp | 54 +++++---------------------------------
 1 file changed, 6 insertions(+), 48 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 2d4692be5b..7a7cc72711 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -756,25 +756,21 @@ void LLMeshRepoThread::run()
 		if (! mHttpRequestSet.empty())
 		{
 			mHttpRequest->update(0L);
-			ms_sleep(100);
-		}
-		else
-		{
-			mWaiting = true;
-			mSignal->wait();
-			mWaiting = false;
 		}
+
+		mWaiting = true;
+		mSignal->wait();
+		mWaiting = false;
 		
 		if (! LLApp::isQuitting())
 		{
 			static U32 count = 0;
-
 			static F32 last_hundred = gFrameTimeSeconds;
 
 			if (gFrameTimeSeconds - last_hundred > 1.f)
 			{ //a second has gone by, clear count
 				last_hundred = gFrameTimeSeconds;
-				count = 0;	
+				count = 0;
 			}
 
 			// NOTE: throttling intentionally favors LOD requests over header requests
@@ -1015,10 +1011,6 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
-#if 0
-				ret = mCurlRequest->getByteRange(http_url, headers, offset, size,
-												 new LLMeshSkinInfoResponder(mesh_id, offset, size));
-#else
 				LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing Skin Info Request" << LL_ENDL;
 				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
@@ -1040,10 +1032,6 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-				}
-#endif
-				if (ret)
-				{
 					LLMeshRepository::sHTTPRequestCount++;
 				}
 			}
@@ -1122,10 +1110,6 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
-#if 0
-				ret = mCurlRequest->getByteRange(http_url, headers, offset, size,
-												 new LLMeshDecompositionResponder(mesh_id, offset, size));
-#else
 				LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing Decomp Request" << LL_ENDL;
 				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
@@ -1147,10 +1131,6 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-				}
-#endif
-				if (ret)
-				{
 					LLMeshRepository::sHTTPRequestCount++;
 				}
 			}
@@ -1228,10 +1208,6 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
-#if 0
-				ret = mCurlRequest->getByteRange(http_url, headers, offset, size,
-												 new LLMeshPhysicsShapeResponder(mesh_id, offset, size));
-#else
 				LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing Physics Shape Request" << LL_ENDL;
 				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
@@ -1253,10 +1229,6 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-				}
-#endif
-				if (ret)
-				{
 					LLMeshRepository::sHTTPRequestCount++;
 				}
 			}
@@ -1338,9 +1310,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 		//grab first 4KB if we're going to bother with a fetch.  Cache will prevent future fetches if a full mesh fits
 		//within the first 4KB
 		//NOTE -- this will break of headers ever exceed 4KB		
-#if 0
-		retval = mCurlRequest->getByteRange(http_url, headers, 0, MESH_HEADER_SIZE, new LLMeshHeaderResponder(mesh_params));
-#else
+
 		LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params);
 		// LL_WARNS("Mesh") << "MESH:  Issuing Request" << LL_ENDL;
 		LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
@@ -1362,10 +1332,6 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 		{
 			handler->mHttpHandle = handle;
 			mHttpRequestSet.insert(handler);
-		}
-#endif		
-		if(retval)
-		{
 			LLMeshRepository::sHTTPRequestCount++;
 		}
 		count++;
@@ -1435,10 +1401,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
-#if 0
-				retval = mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size,
-										   new LLMeshLODResponder(mesh_params, lod, offset, size));
-#else
 				LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing LOD Request" << LL_ENDL;
 				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
@@ -1460,10 +1422,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-				}
-#endif
-				if(retval)
-				{
 					LLMeshRepository::sHTTPRequestCount++;
 				}
 				count++;
-- 
cgit v1.2.3


From f55f4bf1b25d14bcd3d956e6c170ece3880ad5f5 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 8 May 2013 18:37:08 -0400
Subject: SH-4163 Run an initial series of 'B' tests on the mesh downloader
 code Bleh, had some old initialization code in place that meant I was using
 32 connections.  (Always verify with 'netstat'...)  Logic is now to use 1/4
 of MeshMaxConncurrentRequests to head in the direction of 8 at a time.  Full
 count is used to implement a high-water level keeping llcorehttp in work.

---
 indra/newview/llappcorehttp.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'indra')

diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index 4386e3283b..142344e277 100644
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -139,7 +139,8 @@ void LLAppCoreHttp::init()
 		static const std::string mesh_concur("MeshMaxConcurrentRequests");
 		if (gSavedSettings.controlExists(mesh_concur))
 		{
-			U32 setting(llmin(gSavedSettings.getU32(mesh_concur), U32(32)));
+			U32 setting(llmin(gSavedSettings.getU32(mesh_concur), 256U) / 4U);
+			setting = llmax(setting, 2U);
 			
 			if (setting > 0)
 			{
-- 
cgit v1.2.3


From 211d1dfb770aa029d77cd231815a5848640b54a6 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Mon, 3 Jun 2013 13:54:15 -0400
Subject: SH-4184 Conversion to llcorehttp.  Remove unneeded responder classes
 (moved to Handlers).

---
 indra/newview/llmeshrepository.cpp | 559 +------------------------------------
 1 file changed, 2 insertions(+), 557 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 7a7cc72711..f1def284df 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -218,7 +218,8 @@ public:
 	LLMeshHandlerBase()
 		: LLCore::HttpHandler(),
 		  mMeshParams(),
-		  mProcessed(false)
+		  mProcessed(false),
+		  mHttpHandle(LLCORE_HTTP_HANDLE_INVALID)
 	{}
 	virtual ~LLMeshHandlerBase();
 
@@ -365,154 +366,6 @@ public:
 	U32			mOffset;
 };
 
-	
-class LLMeshHeaderResponder : public LLCurl::Responder
-{
-public:
-	LLVolumeParams mMeshParams;
-	bool mProcessed;
-
-	LLMeshHeaderResponder(const LLVolumeParams& mesh_params)
-		: mMeshParams(mesh_params)
-	{
-		LLMeshRepoThread::incActiveHeaderRequests();
-		mProcessed = false;
-	}
-
-	~LLMeshHeaderResponder()
-	{
-		if (!LLApp::isQuitting())
-		{
-			if (!mProcessed)
-			{ //something went wrong, retry
-				llwarns << "Timeout or service unavailable, retrying." << llendl;
-				LLMeshRepository::sHTTPRetryCount++;
-				LLMeshRepoThread::HeaderRequest req(mMeshParams);
-				LLMutexLock lock(gMeshRepo.mThread->mMutex);
-				gMeshRepo.mThread->mHeaderReqQ.push(req);
-			}
-
-			LLMeshRepoThread::decActiveHeaderRequests();
-		}
-	}
-
-	virtual void completedRaw(U32 status, const std::string& reason,
-							  const LLChannelDescriptors& channels,
-							  const LLIOPipe::buffer_ptr_t& buffer);
-
-};
-
-class LLMeshLODResponder : public LLCurl::Responder
-{
-public:
-	LLVolumeParams mMeshParams;
-	S32 mLOD;
-	U32 mRequestedBytes;
-	U32 mOffset;
-	bool mProcessed;
-
-	LLMeshLODResponder(const LLVolumeParams& mesh_params, S32 lod, U32 offset, U32 requested_bytes)
-		: mMeshParams(mesh_params), mLOD(lod), mOffset(offset), mRequestedBytes(requested_bytes)
-	{
-		LLMeshRepoThread::incActiveLODRequests();
-		mProcessed = false;
-	}
-
-	~LLMeshLODResponder()
-	{
-		if (!LLApp::isQuitting())
-		{
-			if (!mProcessed)
-			{
-				llwarns << "Killed without being processed, retrying." << llendl;
-				LLMeshRepository::sHTTPRetryCount++;
-				gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD);
-			}
-			LLMeshRepoThread::decActiveLODRequests();
-		}
-	}
-
-	virtual void completedRaw(U32 status, const std::string& reason,
-							  const LLChannelDescriptors& channels,
-							  const LLIOPipe::buffer_ptr_t& buffer);
-
-};
-
-class LLMeshSkinInfoResponder : public LLCurl::Responder
-{
-public:
-	LLUUID mMeshID;
-	U32 mRequestedBytes;
-	U32 mOffset;
-	bool mProcessed;
-
-	LLMeshSkinInfoResponder(const LLUUID& id, U32 offset, U32 size)
-		: mMeshID(id), mRequestedBytes(size), mOffset(offset)
-	{
-		mProcessed = false;
-	}
-
-	~LLMeshSkinInfoResponder()
-	{
-		llassert(mProcessed);
-	}
-
-	virtual void completedRaw(U32 status, const std::string& reason,
-							  const LLChannelDescriptors& channels,
-							  const LLIOPipe::buffer_ptr_t& buffer);
-
-};
-
-class LLMeshDecompositionResponder : public LLCurl::Responder
-{
-public:
-	LLUUID mMeshID;
-	U32 mRequestedBytes;
-	U32 mOffset;
-	bool mProcessed;
-
-	LLMeshDecompositionResponder(const LLUUID& id, U32 offset, U32 size)
-		: mMeshID(id), mRequestedBytes(size), mOffset(offset)
-	{
-		mProcessed = false;
-	}
-
-	~LLMeshDecompositionResponder()
-	{
-		llassert(mProcessed);
-	}
-
-	virtual void completedRaw(U32 status, const std::string& reason,
-							  const LLChannelDescriptors& channels,
-							  const LLIOPipe::buffer_ptr_t& buffer);
-
-};
-
-class LLMeshPhysicsShapeResponder : public LLCurl::Responder
-{
-public:
-	LLUUID mMeshID;
-	U32 mRequestedBytes;
-	U32 mOffset;
-	bool mProcessed;
-
-	LLMeshPhysicsShapeResponder(const LLUUID& id, U32 offset, U32 size)
-		: mMeshID(id), mRequestedBytes(size), mOffset(offset)
-	{
-		mProcessed = false;
-	}
-
-	~LLMeshPhysicsShapeResponder()
-	{
-		llassert(mProcessed);
-	}
-
-	virtual void completedRaw(U32 status, const std::string& reason,
-							  const LLChannelDescriptors& channels,
-							  const LLIOPipe::buffer_ptr_t& buffer);
-
-};
-
 void log_upload_error(S32 status, const LLSD& content, std::string stage, std::string model_name)
 {
 	// Add notification popup.
@@ -1005,9 +858,6 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			std::vector<std::string> headers;
-			headers.push_back("Accept: application/octet-stream");
-
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
@@ -1104,9 +954,6 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			std::vector<std::string> headers;
-			headers.push_back("Accept: application/octet-stream");
-
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
@@ -1202,9 +1049,6 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			std::vector<std::string> headers;
-			headers.push_back("Accept: application/octet-stream");
-
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
@@ -1301,9 +1145,6 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 
 	//either cache entry doesn't exist or is corrupt, request header from simulator	
 	bool retval = true ;
-	std::vector<std::string> headers;
-	headers.push_back("Accept: application/octet-stream");
-
 	std::string http_url = constructUrl(mesh_params.getSculptID());
 	if (!http_url.empty())
 	{
@@ -1395,9 +1236,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			std::vector<std::string> headers;
-			headers.push_back("Accept: application/octet-stream");
-
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
@@ -2184,270 +2022,12 @@ void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header)
 
 }
 
-void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason,
-							  const LLChannelDescriptors& channels,
-							  const LLIOPipe::buffer_ptr_t& buffer)
-{
-	mProcessed = true;
-	
-	// thread could have already be destroyed during logout
-	if( !gMeshRepo.mThread )
-	{
-		return;
-	}
-	
-	S32 data_size = buffer->countAfter(channels.in(), NULL);
-
-	if (status < 200 || status > 400)
-	{
-		llwarns << status << ": " << reason << llendl;
-	}
-
-	if (data_size < mRequestedBytes)
-	{
-		if (status == 499 || status == 503)
-		{ //timeout or service unavailable, try again
-			llwarns << "Timeout or service unavailable, retrying." << llendl;
-			LLMeshRepository::sHTTPRetryCount++;
-			gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD);
-		}
-		else
-		{
-			llassert(status == 499 || status == 503); //intentionally trigger a breakpoint
-			llwarns << "Unhandled status " << status << llendl;
-		}
-		return;
-	}
-
-	LLMeshRepository::sBytesReceived += mRequestedBytes;
-
-	U8* data = NULL;
-
-	if (data_size > 0)
-	{
-		data = new U8[data_size];
-		buffer->readAfter(channels.in(), NULL, data, data_size);
-	}
-
-	if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size))
-	{
-		//good fetch from sim, write to VFS for caching
-		LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE);
-
-		S32 offset = mOffset;
-		S32 size = mRequestedBytes;
-
-		if (file.getSize() >= offset+size)
-		{
-			file.seek(offset);
-			file.write(data, size);
-			LLMeshRepository::sCacheBytesWritten += size;
-		}
-	}
-
-	delete [] data;
-}
-
-void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason,
-							  const LLChannelDescriptors& channels,
-							  const LLIOPipe::buffer_ptr_t& buffer)
-{
-	mProcessed = true;
-
-	// thread could have already be destroyed during logout
-	if( !gMeshRepo.mThread )
-	{
-		return;
-	}
-
-	S32 data_size = buffer->countAfter(channels.in(), NULL);
-
-	if (status < 200 || status > 400)
-	{
-		llwarns << status << ": " << reason << llendl;
-	}
-
-	if (data_size < mRequestedBytes)
-	{
-		if (status == 499 || status == 503)
-		{ //timeout or service unavailable, try again
-			llwarns << "Timeout or service unavailable, retrying." << llendl;
-			LLMeshRepository::sHTTPRetryCount++;
-			gMeshRepo.mThread->loadMeshSkinInfo(mMeshID);
-		}
-		else
-		{
-			llassert(status == 499 || status == 503); //intentionally trigger a breakpoint
-			llwarns << "Unhandled status " << status << llendl;
-		}
-		return;
-	}
-
-	LLMeshRepository::sBytesReceived += mRequestedBytes;
-
-	U8* data = NULL;
-
-	if (data_size > 0)
-	{
-		data = new U8[data_size];
-		buffer->readAfter(channels.in(), NULL, data, data_size);
-	}
-
-	if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
-	{
-		//good fetch from sim, write to VFS for caching
-		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
-
-		S32 offset = mOffset;
-		S32 size = mRequestedBytes;
-
-		if (file.getSize() >= offset+size)
-		{
-			LLMeshRepository::sCacheBytesWritten += size;
-			file.seek(offset);
-			file.write(data, size);
-		}
-	}
-
-	delete [] data;
-}
-
-void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& reason,
-							  const LLChannelDescriptors& channels,
-							  const LLIOPipe::buffer_ptr_t& buffer)
-{
-	mProcessed = true;
-	
-	if( !gMeshRepo.mThread )
-	{
-		return;
-	}
-
-	S32 data_size = buffer->countAfter(channels.in(), NULL);
-
-	if (status < 200 || status > 400)
-	{
-		llwarns << status << ": " << reason << llendl;
-	}
-
-	if (data_size < mRequestedBytes)
-	{
-		if (status == 499 || status == 503)
-		{ //timeout or service unavailable, try again
-			llwarns << "Timeout or service unavailable, retrying." << llendl;
-			LLMeshRepository::sHTTPRetryCount++;
-			gMeshRepo.mThread->loadMeshDecomposition(mMeshID);
-		}
-		else
-		{
-			llassert(status == 499 || status == 503); //intentionally trigger a breakpoint
-			llwarns << "Unhandled status " << status << llendl;
-		}
-		return;
-	}
-
-	LLMeshRepository::sBytesReceived += mRequestedBytes;
-
-	U8* data = NULL;
-
-	if (data_size > 0)
-	{
-		data = new U8[data_size];
-		buffer->readAfter(channels.in(), NULL, data, data_size);
-	}
-
-	if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size))
-	{
-		//good fetch from sim, write to VFS for caching
-		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
-
-		S32 offset = mOffset;
-		S32 size = mRequestedBytes;
-
-		if (file.getSize() >= offset+size)
-		{
-			LLMeshRepository::sCacheBytesWritten += size;
-			file.seek(offset);
-			file.write(data, size);
-		}
-	}
-
-	delete [] data;
-}
-
-void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& reason,
-							  const LLChannelDescriptors& channels,
-							  const LLIOPipe::buffer_ptr_t& buffer)
-{
-	mProcessed = true;
-
-	// thread could have already be destroyed during logout
-	if( !gMeshRepo.mThread )
-	{
-		return;
-	}
-
-	S32 data_size = buffer->countAfter(channels.in(), NULL);
-
-	if (status < 200 || status > 400)
-	{
-		llwarns << status << ": " << reason << llendl;
-	}
-
-	if (data_size < mRequestedBytes)
-	{
-		if (status == 499 || status == 503)
-		{ //timeout or service unavailable, try again
-			llwarns << "Timeout or service unavailable, retrying." << llendl;
-			LLMeshRepository::sHTTPRetryCount++;
-			gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID);
-		}
-		else
-		{
-			llassert(status == 499 || status == 503); //intentionally trigger a breakpoint
-			llwarns << "Unhandled status " << status << llendl;
-		}
-		return;
-	}
-
-	LLMeshRepository::sBytesReceived += mRequestedBytes;
-
-	U8* data = NULL;
-
-	if (data_size > 0)
-	{
-		data = new U8[data_size];
-		buffer->readAfter(channels.in(), NULL, data, data_size);
-	}
-
-	if (gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size))
-	{
-		//good fetch from sim, write to VFS for caching
-		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
-
-		S32 offset = mOffset;
-		S32 size = mRequestedBytes;
-
-		if (file.getSize() >= offset+size)
-		{
-			LLMeshRepository::sCacheBytesWritten += size;
-			file.seek(offset);
-			file.write(data, size);
-		}
-	}
-
-	delete [] data;
-}
-
-	
 LLMeshHandlerBase::~LLMeshHandlerBase()
 {}
 
-
 void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)
 {
 	mProcessed = true;
-
 	LLCore::HttpStatus status(response->getStatus());
 	if (! status)
 	{
@@ -2506,7 +2086,6 @@ LLMeshHeaderHandler::~LLMeshHeaderHandler()
 	}
 }
 
-
 void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
 {
 	LL_WARNS("Mesh") << "MESH:  Processing Failure" << LL_ENDL;
@@ -2525,7 +2104,6 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
 	}
 }
 
-
 void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
 {
 	// LL_WARNS("Mesh") << "MESH:  Processing Data" << LL_ENDL;
@@ -2596,7 +2174,6 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32
 	}
 }
 
-
 LLMeshLODHandler::~LLMeshLODHandler()
 {
 	if (! LLApp::isQuitting())
@@ -2646,7 +2223,6 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da
 	}
 }
 
-
 LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler()
 {
 		llassert(mProcessed);
@@ -2687,7 +2263,6 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S
 	}
 }
 
-
 LLMeshDecompositionHandler::~LLMeshDecompositionHandler()
 {
 		llassert(mProcessed);
@@ -2728,7 +2303,6 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * da
 	}
 }
 
-
 LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler()
 {
 		llassert(mProcessed);
@@ -2750,7 +2324,6 @@ void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status)
 	}
 }
 
-	
 void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
 {
 	if (gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size))
@@ -2770,134 +2343,6 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat
 	}
 }
 
-
-void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
-							  const LLChannelDescriptors& channels,
-							  const LLIOPipe::buffer_ptr_t& buffer)
-{
-	mProcessed = true;
-
-	// thread could have already be destroyed during logout
-	if( !gMeshRepo.mThread )
-	{
-		return;
-	}
-
-	if (status < 200 || status > 400)
-	{
-		//llwarns
-		//	<< "Header responder failed with status: "
-		//	<< status << ": " << reason << llendl;
-
-		// 503 (service unavailable) or 499 (timeout)
-		// can be due to server load and can be retried
-
-		// TODO*: Add maximum retry logic, exponential backoff
-		// and (somewhat more optional than the others) retries
-		// again after some set period of time
-
-		llassert(status == 503 || status == 499);
-
-		if (status == 503 || status == 499)
-		{ //retry
-			llwarns << "Timeout or service unavailable, retrying." << llendl;
-			LLMeshRepository::sHTTPRetryCount++;
-			LLMeshRepoThread::HeaderRequest req(mMeshParams);
-			LLMutexLock lock(gMeshRepo.mThread->mMutex);
-			gMeshRepo.mThread->mHeaderReqQ.push(req);
-
-			return;
-		}
-		else
-		{
-			llwarns << "Unhandled status." << llendl;
-		}
-	}
-
-	S32 data_size = buffer->countAfter(channels.in(), NULL);
-
-	U8* data = NULL;
-
-	if (data_size > 0)
-	{
-		data = new U8[data_size];
-		buffer->readAfter(channels.in(), NULL, data, data_size);
-	}
-
-	LLMeshRepository::sBytesReceived += llmin(data_size, MESH_HEADER_SIZE);
-
-	bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size);
-	
-	llassert(success);
-
-	if (!success)
-	{
-		llwarns
-			<< "Unable to parse mesh header: "
-			<< status << ": " << reason << llendl;
-	}
-	else if (data && data_size > 0)
-	{
-		//header was successfully retrieved from sim, cache in vfs
-		LLUUID mesh_id = mMeshParams.getSculptID();
-		LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id];
-
-		S32 version = header["version"].asInteger();
-
-		if (version <= MAX_MESH_VERSION)
-		{
-			std::stringstream str;
-
-			S32 lod_bytes = 0;
-
-			for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i)
-			{ //figure out how many bytes we'll need to reserve in the file
-				std::string lod_name = header_lod[i];
-				lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger());
-			}
-		
-			//just in case skin info or decomposition is at the end of the file (which it shouldn't be)
-			lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger());
-			lod_bytes = llmax(lod_bytes, header["physics_convex"]["offset"].asInteger() + header["physics_convex"]["size"].asInteger());
-
-			S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id];
-			S32 bytes = lod_bytes + header_bytes; 
-
-		
-			//it's possible for the remote asset to have more data than is needed for the local cache
-			//only allocate as much space in the VFS as is needed for the local cache
-			data_size = llmin(data_size, bytes);
-
-			LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE);
-			if (file.getMaxSize() >= bytes || file.setMaxSize(bytes))
-			{
-				LLMeshRepository::sCacheBytesWritten += data_size;
-
-				file.write((const U8*) data, data_size);
-			
-				//zero out the rest of the file 
-				U8 block[MESH_HEADER_SIZE];
-				memset(block, 0, MESH_HEADER_SIZE);
-
-				while (bytes-file.tell() > MESH_HEADER_SIZE)
-				{
-					file.write(block, MESH_HEADER_SIZE);
-				}
-
-				S32 remaining = bytes-file.tell();
-
-				if (remaining > 0)
-				{
-					file.write(block, remaining);
-				}
-			}
-		}
-	}
-
-	delete [] data;
-}
-
-
 LLMeshRepository::LLMeshRepository()
 : mMeshMutex(NULL),
   mMeshThreadCount(0),
-- 
cgit v1.2.3


From 7c9d0b58aca072ff932b30f927e2876dfc6858aa Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 7 Jun 2013 20:14:52 -0400
Subject: Mostly cleanup.  A chunk of comment code nobody needs.  Dereference
 after delete, erase() on end() iterator, a few more like that. Killed a dead
 variable.

---
 indra/newview/llmeshrepository.cpp | 163 ++-----------------------------------
 indra/newview/llmeshrepository.h   |   1 -
 2 files changed, 9 insertions(+), 155 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 8f6860aec7..702e940983 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -97,7 +97,6 @@ U32 LLMeshRepository::sLODPending = 0;
 
 U32 LLMeshRepository::sCacheBytesRead = 0;
 U32 LLMeshRepository::sCacheBytesWritten = 0;
-U32 LLMeshRepository::sPeakKbps = 0;
 LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true);	// true -> gather cpu metrics
 
 	
@@ -641,6 +640,7 @@ void LLMeshRepoThread::run()
 					{
 						mMutex->lock();
 						mLODReqQ.push(req) ; 
+						++LLMeshRepository::sLODProcessing;
 						mMutex->unlock();
 					}
 				}
@@ -882,7 +882,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-					LLMeshRepository::sHTTPRequestCount++;
+					++LLMeshRepository::sHTTPRequestCount;
 				}
 			}
 		}
@@ -978,7 +978,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-					LLMeshRepository::sHTTPRequestCount++;
+					++LLMeshRepository::sHTTPRequestCount;
 				}
 			}
 		}
@@ -1073,7 +1073,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-					LLMeshRepository::sHTTPRequestCount++;
+					++LLMeshRepository::sHTTPRequestCount;
 				}
 			}
 		}
@@ -1173,7 +1173,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 		{
 			handler->mHttpHandle = handle;
 			mHttpRequestSet.insert(handler);
-			LLMeshRepository::sHTTPRequestCount++;
+			++LLMeshRepository::sHTTPRequestCount;
 		}
 		count++;
 	}
@@ -1260,7 +1260,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-					LLMeshRepository::sHTTPRequestCount++;
+					++LLMeshRepository::sHTTPRequestCount;
 				}
 				count++;
 			}
@@ -2738,9 +2738,8 @@ void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info)
 				vobj->notifyMeshLoaded();
 			}
 		}
+		mLoadingSkins.erase(info.mMeshID);
 	}
-
-	mLoadingSkins.erase(info.mMeshID);
 }
 
 void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp)
@@ -2749,14 +2748,14 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom
 	if (iter == mDecompositionMap.end())
 	{ //just insert decomp into map
 		mDecompositionMap[decomp->mMeshID] = decomp;
+		mLoadingDecompositions.erase(decomp->mMeshID);
 	}
 	else
 	{ //merge decomp with existing entry
 		iter->second->merge(decomp);
+		mLoadingDecompositions.erase(decomp->mMeshID);
 		delete decomp;
 	}
-
-	mLoadingDecompositions.erase(decomp->mMeshID);
 }
 
 void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume)
@@ -3881,147 +3880,3 @@ bool is_retryable(LLCore::HttpStatus status)
 			 status == inv_cont_range));		// Short data read disagrees with content-range
 }
 
-
-
-
-// ===========
-//
-// HTTP fragments I'll be needing
-//
-//
-#if 0
-
-	if (! status)
-	{
-		success = false;
-		std::string reason(status.toString());
-		setGetStatus(status, reason);
-		llwarns << "CURL GET FAILED, status: " << status.toHex()
-				<< " reason: " << reason << llendl;
-	}
-	else
-	{
-		// A warning about partial (HTTP 206) data.  Some grid services
-		// do *not* return a 'Content-Range' header in the response to
-		// Range requests with a 206 status.  We're forced to assume
-		// we get what we asked for in these cases until we can fix
-		// the services.
-		static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT);
-
-		partial = (par_status == status);
-	}
-	
-	S32 data_size = callbackHttpGet(response, partial, success);
-			
-
-
-
-// Threads:  Ttf
-// Locks:  Mw
-S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response,
-										  bool partial, bool success)
-{
-	S32 data_size = 0 ;
-
-	if (mState != WAIT_HTTP_REQ)
-	{
-		llwarns << "callbackHttpGet for unrequested fetch worker: " << mID
-				<< " req=" << mSentRequest << " state= " << mState << llendl;
-		return data_size;
-	}
-	if (mLoaded)
-	{
-		llwarns << "Duplicate callback for " << mID.asString() << llendl;
-		return data_size ; // ignore duplicate callback
-	}
-	if (success)
-	{
-		// get length of stream:
-		LLCore::BufferArray * body(response->getBody());
-		data_size = body ? body->size() : 0;
-
-		LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL;
-		if (data_size > 0)
-		{
-			LLViewerStatsRecorder::instance().textureFetch(data_size);
-			// *TODO: set the formatted image data here directly to avoid the copy
-
-			// Hold on to body for later copy
-			llassert_always(NULL == mHttpBufferArray);
-			body->addRef();
-			mHttpBufferArray = body;
-
-			if (partial)
-			{
-				unsigned int offset(0), length(0), full_length(0);
-				response->getRange(&offset, &length, &full_length);
-				if (! offset && ! length)
-				{
-					// This is the case where we receive a 206 status but
-					// there wasn't a useful Content-Range header in the response.
-					// This could be because it was badly formatted but is more
-					// likely due to capabilities services which scrub headers
-					// from responses.  Assume we got what we asked for...
-					mHttpReplySize = data_size;
-					mHttpReplyOffset = mRequestedOffset;
-				}
-				else
-				{
-					mHttpReplySize = length;
-					mHttpReplyOffset = offset;
-				}
-			}
-
-			if (! partial)
-			{
-				// Response indicates this is the entire asset regardless
-				// of our asking for a byte range.  Mark it so and drop
-				// any partial data we might have so that the current
-				// response body becomes the entire dataset.
-				if (data_size <= mRequestedOffset)
-				{
-					LL_WARNS("Texture") << "Fetched entire texture " << mID
-										<< " when it was expected to be marked complete.  mImageSize:  "
-										<< mFileSize << " datasize:  " << mFormattedImage->getDataSize()
-										<< LL_ENDL;
-				}
-				mHaveAllData = TRUE;
-				llassert_always(mDecodeHandle == 0);
-				mFormattedImage = NULL; // discard any previous data we had
-			}
-			else if (data_size < mRequestedSize)
-			{
-				mHaveAllData = TRUE;
-			}
-			else if (data_size > mRequestedSize)
-			{
-				// *TODO: This shouldn't be happening any more  (REALLY don't expect this anymore)
-				llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl;
-				mHaveAllData = TRUE;
-				llassert_always(mDecodeHandle == 0);
-				mFormattedImage = NULL; // discard any previous data we had
-			}
-		}
-		else
-		{
-			// We requested data but received none (and no error),
-			// so presumably we have all of it
-			mHaveAllData = TRUE;
-		}
-		mRequestedSize = data_size;
-	}
-	else
-	{
-		mRequestedSize = -1; // error
-	}
-	
-	mLoaded = TRUE;
-	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
-
-	LLViewerStatsRecorder::instance().log(0.2f);
-	return data_size ;
-}
-
-#endif
-// ============
-
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 8ffe2d68cb..62f81ce9e2 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -469,7 +469,6 @@ public:
 	static U32 sLODProcessing;
 	static U32 sCacheBytesRead;
 	static U32 sCacheBytesWritten;
-	static U32 sPeakKbps;
 	static LLDeadmanTimer sQuiescentTimer;  // time-to-complete-mesh-downloads after significant events
 
 	static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
-- 
cgit v1.2.3


From 626752beab7c12a355ab707d70aba6f4fe096c10 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 19 Jun 2013 13:55:54 -0400
Subject: SH-4252 Add second policy class for large mesh asset downloads Added
 second mesh class as well as an asset upload class. Refactored initialization
 to use less code and more data to cleanly get http started.  Modified mesh to
 use the new http class for large requests (>2MB for now).  Added additional
 timeout setting to llcorehttp to distinguish connection timeout from
 transport timeout and are now using transport timeout values for large asset
 downloads that may need more time.

---
 indra/llcorehttp/_httpinternal.h    |   3 +-
 indra/llcorehttp/_httpoprequest.cpp |  15 ++-
 indra/llcorehttp/_httpoprequest.h   |   3 +-
 indra/llcorehttp/_httppolicy.cpp    |   6 ++
 indra/llcorehttp/httpoptions.cpp    |   9 +-
 indra/llcorehttp/httpoptions.h      |   9 +-
 indra/llcorehttp/httpresponse.cpp   |   6 +-
 indra/llcorehttp/httpresponse.h     |  17 +++-
 indra/newview/llappcorehttp.cpp     | 156 ++++++++++++++++------------
 indra/newview/llappcorehttp.h       |  36 +++----
 indra/newview/llmeshrepository.cpp  | 197 +++++++++++++++++++++---------------
 indra/newview/llmeshrepository.h    |  21 +++-
 indra/newview/lltexturefetch.cpp    |   4 +-
 13 files changed, 301 insertions(+), 181 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h
index 30b0905c12..d60996756f 100755
--- a/indra/llcorehttp/_httpinternal.h
+++ b/indra/llcorehttp/_httpinternal.h
@@ -98,7 +98,7 @@ namespace LLCore
 
 // Maxium number of policy classes that can be defined.
 // *TODO:  Currently limited to the default class + 1, extend.
-const int HTTP_POLICY_CLASS_LIMIT = 2;
+const int HTTP_POLICY_CLASS_LIMIT = 4;
 
 // Debug/informational tracing.  Used both
 // as a global option and in per-request traces.
@@ -129,6 +129,7 @@ const int HTTP_REDIRECTS_DEFAULT = 10;
 // Retries and time-on-queue are not included and aren't
 // accounted for.
 const long HTTP_REQUEST_TIMEOUT_DEFAULT = 30L;
+const long HTTP_REQUEST_XFER_TIMEOUT_DEFAULT = 0L;
 const long HTTP_REQUEST_TIMEOUT_MIN = 0L;
 const long HTTP_REQUEST_TIMEOUT_MAX = 3600L;
 
diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index 51a8eaf998..d403b2d249 100755
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -110,6 +110,7 @@ HttpOpRequest::HttpOpRequest()
 	  mReplyFullLength(0),
 	  mReplyHeaders(NULL),
 	  mPolicyRetries(0),
+	  mPolicy503Retries(0),
 	  mPolicyRetryAt(HttpTime(0)),
 	  mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT)
 {
@@ -224,6 +225,7 @@ void HttpOpRequest::visitNotifier(HttpRequest * request)
 			response->setRange(mReplyOffset, mReplyLength, mReplyFullLength);
 		}
 		response->setContentType(mReplyConType);
+		response->setRetries(mPolicyRetries, mPolicy503Retries);
 		
 		mUserHandler->onCompleted(static_cast<HttpHandle>(this), response);
 
@@ -524,12 +526,19 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 
 	// Request options
 	long timeout(HTTP_REQUEST_TIMEOUT_DEFAULT);
+	long xfer_timeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT);
 	if (mReqOptions)
-	{
+ 	{
 		timeout = mReqOptions->getTimeout();
 		timeout = llclamp(timeout, HTTP_REQUEST_TIMEOUT_MIN, HTTP_REQUEST_TIMEOUT_MAX);
+		xfer_timeout = mReqOptions->getTransferTimeout();
+		xfer_timeout = llclamp(xfer_timeout, HTTP_REQUEST_TIMEOUT_MIN, HTTP_REQUEST_TIMEOUT_MAX);
+	}
+	if (xfer_timeout == 0L)
+	{
+		xfer_timeout = timeout;
 	}
-	curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout);
+	curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, xfer_timeout);
 	curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout);
 
 	// Request headers
diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h
index 7b65d17783..831e5bebf7 100755
--- a/indra/llcorehttp/_httpoprequest.h
+++ b/indra/llcorehttp/_httpoprequest.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -187,6 +187,7 @@ public:
 
 	// Policy data
 	int					mPolicyRetries;
+	int					mPolicy503Retries;
 	HttpTime			mPolicyRetryAt;
 	int					mPolicyRetryLimit;
 };  // end class HttpOpRequest
diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp
index 76c1e22431..54c9c6bb1b 100755
--- a/indra/llcorehttp/_httppolicy.cpp
+++ b/indra/llcorehttp/_httppolicy.cpp
@@ -140,6 +140,7 @@ void HttpPolicy::addOp(HttpOpRequest * op)
 	const int policy_class(op->mReqPolicy);
 	
 	op->mPolicyRetries = 0;
+	op->mPolicy503Retries = 0;
 	mState[policy_class].mReadyQueue.push(op);
 }
 
@@ -155,6 +156,7 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
 			5000000				// ... to every 5.0 S.
 		};
 	static const int delta_max(int(LL_ARRAY_SIZE(retry_deltas)) - 1);
+	static const HttpStatus error_503(503);
 	
 	const HttpTime now(totalTime());
 	const int policy_class(op->mReqPolicy);
@@ -162,6 +164,10 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
 	const HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]);
 	op->mPolicyRetryAt = now + delta;
 	++op->mPolicyRetries;
+	if (error_503 == op->mStatus)
+	{
+		++op->mPolicy503Retries;
+	}
 	LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
 						 << " retry " << op->mPolicyRetries
 						 << " scheduled for +" << (delta / HttpTime(1000))
diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp
index 1699d19f8d..4dcd862ca4 100755
--- a/indra/llcorehttp/httpoptions.cpp
+++ b/indra/llcorehttp/httpoptions.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -38,6 +38,7 @@ HttpOptions::HttpOptions()
 	  mWantHeaders(false),
 	  mTracing(HTTP_TRACE_OFF),
 	  mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT),
+	  mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT),
 	  mRetries(HTTP_RETRY_COUNT_DEFAULT)
 {}
 
@@ -64,6 +65,12 @@ void HttpOptions::setTimeout(unsigned int timeout)
 }
 
 
+void HttpOptions::setTransferTimeout(unsigned int timeout)
+{
+	mTransferTimeout = timeout;
+}
+
+
 void HttpOptions::setRetries(unsigned int retries)
 {
 	mRetries = retries;
diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h
index 97e46a8cd3..623d71d3e6 100755
--- a/indra/llcorehttp/httpoptions.h
+++ b/indra/llcorehttp/httpoptions.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -86,6 +86,12 @@ public:
 			return mTimeout;
 		}
 
+	void				setTransferTimeout(unsigned int timeout);
+	unsigned int		getTransferTimeout() const
+		{
+			return mTransferTimeout;
+		}
+
 	void				setRetries(unsigned int retries);
 	unsigned int		getRetries() const
 		{
@@ -96,6 +102,7 @@ protected:
 	bool				mWantHeaders;
 	int					mTracing;
 	unsigned int		mTimeout;
+	unsigned int		mTransferTimeout;
 	unsigned int		mRetries;
 	
 }; // end class HttpOptions
diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp
index a552e48a1b..c974395b0a 100755
--- a/indra/llcorehttp/httpresponse.cpp
+++ b/indra/llcorehttp/httpresponse.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -39,7 +39,9 @@ HttpResponse::HttpResponse()
 	  mReplyLength(0U),
 	  mReplyFullLength(0U),
 	  mBufferArray(NULL),
-	  mHeaders(NULL)
+	  mHeaders(NULL),
+	  mRetries(0U),
+	  m503Retries(0U)
 {}
 
 
diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h
index 4a481db6ac..a7f296e03f 100755
--- a/indra/llcorehttp/httpresponse.h
+++ b/indra/llcorehttp/httpresponse.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -144,6 +144,19 @@ public:
 			mContentType = con_type;
 		}
 
+	/// Get and set retry attempt information on the request.
+	void getRetries(unsigned int * retries, unsigned int * retries_503) const
+		{
+			*retries = mRetries;
+			*retries_503 = m503Retries;
+		}
+
+	void setRetries(unsigned int retries, unsigned int retries_503)
+		{
+			mRetries = retries;
+			m503Retries = retries_503;
+		}
+
 protected:
 	// Response data here
 	HttpStatus			mStatus;
@@ -153,6 +166,8 @@ protected:
 	BufferArray *		mBufferArray;
 	HttpHeaders *		mHeaders;
 	std::string			mContentType;
+	unsigned int		mRetries;
+	unsigned int		m503Retries;
 };
 
 
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index 142344e277..b601b31d21 100755
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -37,11 +37,13 @@ LLAppCoreHttp::LLAppCoreHttp()
 	: mRequest(NULL),
 	  mStopHandle(LLCORE_HTTP_HANDLE_INVALID),
 	  mStopRequested(0.0),
-	  mStopped(false),
-	  mPolicyDefault(-1),
-	  mPolicyTexture(-1),
-	  mPolicyMesh(-1)
-{}
+	  mStopped(false)
+{
+	for (int i(0); i < LL_ARRAY_SIZE(mPolicies); ++i)
+	{
+		mPolicies[i] = LLCore::HttpRequest::DEFAULT_POLICY_ID;
+	}
+}
 
 
 LLAppCoreHttp::~LLAppCoreHttp()
@@ -53,11 +55,43 @@ LLAppCoreHttp::~LLAppCoreHttp()
 
 void LLAppCoreHttp::init()
 {
+	static const struct
+	{
+		EAppPolicy		mPolicy;
+		U32				mDefault;
+		U32				mMin;
+		U32				mMax;
+		U32				mDivisor;
+		std::string		mKey;
+		const char *	mUsage;
+	} init_data[] =					//  Default and dynamic values for classes
+		  {
+			  {
+				  AP_TEXTURE,			8,		1,		12,		1,
+				  "TextureFetchConcurrency",
+				  "texture fetch"
+			  },
+			  {
+				  AP_MESH,				8,		1,		32,		4,
+				  "MeshMaxConcurrentRequests",
+				  "mesh fetch"
+			  },
+			  {
+				  AP_LARGE_MESH,		2,		1,		8,		1,
+				  "",
+				  "large mesh fetch"
+			  },
+			  {
+				  AP_UPLOADS,			2,		1,		8,		1,
+				  "",
+				  "asset upload"
+			  }
+		  };
+		
 	LLCore::HttpStatus status = LLCore::HttpRequest::createService();
 	if (! status)
 	{
-		LL_ERRS("Init") << "Failed to initialize HTTP services.  Reason:  "
-						<< status.toString()
+		LL_ERRS("Init") << "Failed to initialize HTTP services.  Reason:  " << status.toString()
 						<< LL_ENDL;
 	}
 
@@ -66,8 +100,7 @@ void LLAppCoreHttp::init()
 														gDirUtilp->getCAFile());
 	if (! status)
 	{
-		LL_ERRS("Init") << "Failed to set CA File for HTTP services.  Reason:  "
-						<< status.toString()
+		LL_ERRS("Init") << "Failed to set CA File for HTTP services.  Reason:  " << status.toString()
 						<< LL_ENDL;
 	}
 
@@ -77,8 +110,7 @@ void LLAppCoreHttp::init()
 	status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1);
 	if (! status)
 	{
-		LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services.  Reason:  "
-						<< status.toString()
+		LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services.  Reason:  " << status.toString()
 						<< LL_ENDL;
 	}
 
@@ -96,80 +128,72 @@ void LLAppCoreHttp::init()
 	}
 	
 	// Setup default policy and constrain if directed to
-	mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID;
+	mPolicies[AP_DEFAULT] = LLCore::HttpRequest::DEFAULT_POLICY_ID;
 
-	// Texture policy will use default for now.
-	mPolicyTexture = mPolicyDefault;
-	static const std::string texture_concur("TextureFetchConcurrency");
-	if (gSavedSettings.controlExists(texture_concur))
+	// Setup additional policies based on table and some special rules
+	// *TODO:  Make these configurations dynamic later
+	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
 	{
-		U32 concur(llmin(gSavedSettings.getU32(texture_concur), U32(12)));
+		const EAppPolicy policy(init_data[i].mPolicy);
 
-		if (concur > 0)
+		// Create a policy class but use default for texture for now.
+		// This also has the side-effect of initializing the default
+		// class to desired values.
+		if (AP_TEXTURE == policy)
 		{
-			LLCore::HttpStatus status;
-			status = LLCore::HttpRequest::setPolicyClassOption(mPolicyTexture,
-															   LLCore::HttpRequest::CP_CONNECTION_LIMIT,
-															   concur);
-			if (! status)
-			{
-				LL_WARNS("Init") << "Unable to set texture fetch concurrency.  Reason:  "
-								 << status.toString()
-								 << LL_ENDL;
-			}
-			else
+			mPolicies[policy] = mPolicies[AP_DEFAULT];
+		}
+		else
+		{
+			mPolicies[policy] = LLCore::HttpRequest::createPolicyClass();
+			if (! mPolicies[policy])
 			{
-				LL_INFOS("Init") << "Application settings overriding default texture fetch concurrency.  New value:  "
-								 << concur
+				// Use default policy (but don't accidentally modify default)
+				LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage
+								 << ".  Using default policy."
 								 << LL_ENDL;
+				mPolicies[policy] = mPolicies[AP_DEFAULT];
+				continue;
 			}
 		}
-	}
 
-	// Create the mesh class
-	mPolicyMesh = LLCore::HttpRequest::createPolicyClass();
-	if (! mPolicyMesh)
-	{
-		LL_WARNS("Init") << "Failed to create HTTP policy class for Mesh.  Using default policy."
-						 << LL_ENDL;
-		mPolicyMesh = mPolicyDefault;
-	}
-	else
-	{
-		static const std::string mesh_concur("MeshMaxConcurrentRequests");
-		if (gSavedSettings.controlExists(mesh_concur))
+		// Get target connection concurrency value
+		U32 setting(init_data[i].mDefault);
+		if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))
 		{
-			U32 setting(llmin(gSavedSettings.getU32(mesh_concur), 256U) / 4U);
-			setting = llmax(setting, 2U);
-			
-			if (setting > 0)
+			U32 new_setting(gSavedSettings.getU32(init_data[i].mKey));
+			if (new_setting)
 			{
-				LLCore::HttpStatus status;
-				status = LLCore::HttpRequest::setPolicyClassOption(mPolicyMesh,
-																   LLCore::HttpRequest::CP_CONNECTION_LIMIT,
-																   setting);
-				if (! status)
-				{
-					LL_WARNS("Init") << "Unable to set mesh fetch concurrency.  Reason:  "
-									 << status.toString()
-								 << LL_ENDL;
-				}
-				else
-				{
-					LL_INFOS("Init") << "Application settings overriding default mesh fetch concurrency.  New value:  "
-									 << setting
-									 << LL_ENDL;
-				}
+				// Treat zero settings as an ask for default
+				setting = new_setting / init_data[i].mDivisor;
+				setting = llclamp(setting, init_data[i].mMin, init_data[i].mMax);
 			}
 		}
+
+		// Set it and report
+		LLCore::HttpStatus status;
+		status = LLCore::HttpRequest::setPolicyClassOption(mPolicies[policy],
+														   LLCore::HttpRequest::CP_CONNECTION_LIMIT,
+														   setting);
+		if (! status)
+		{
+			LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
+							 << " concurrency.  Reason:  " << status.toString()
+							 << LL_ENDL;
+		}
+		else if (setting != init_data[i].mDefault)
+		{
+			LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage
+							 << " concurrency.  New value:  " << setting
+							 << LL_ENDL;
+		}
 	}
 	
 	// Kick the thread
 	status = LLCore::HttpRequest::startThread();
 	if (! status)
 	{
-		LL_ERRS("Init") << "Failed to start HTTP servicing thread.  Reason:  "
-						<< status.toString()
+		LL_ERRS("Init") << "Failed to start HTTP servicing thread.  Reason:  " << status.toString()
 						<< LL_ENDL;
 	}
 
diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h
index d90af9e5ca..532e1f5cb0 100755
--- a/indra/newview/llappcorehttp.h
+++ b/indra/newview/llappcorehttp.h
@@ -40,6 +40,19 @@
 // as a singleton and static construction is fine.
 class LLAppCoreHttp : public LLCore::HttpHandler
 {
+public:
+	typedef LLCore::HttpRequest::policy_t policy_t;
+
+	enum EAppPolicy
+	{
+		AP_DEFAULT,
+		AP_TEXTURE,
+		AP_MESH,
+		AP_LARGE_MESH,
+		AP_UPLOADS,
+		AP_COUNT						// Must be last
+	};
+	
 public:
 	LLAppCoreHttp();
 	~LLAppCoreHttp();
@@ -65,22 +78,11 @@ public:
 	// Notification when the stop request is complete.
 	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
 
-	// Retrieve the policy class for default operations.
-	int getPolicyDefault() const
-		{
-			return mPolicyDefault;
-		}
-
-	// Get the texture fetch policy class.
-	int getPolicyTexture() const
-		{
-			return mPolicyTexture;
-		}
-
-	// Get the mesh fetch policy class.
-	int getPolicyMesh() const
+	// Retrieve a policy class identifier for desired
+	// application function.
+	policy_t getPolicy(EAppPolicy policy) const
 		{
-			return mPolicyMesh;
+			return mPolicies[policy];
 		}
 	
 private:
@@ -91,9 +93,7 @@ private:
 	LLCore::HttpHandle			mStopHandle;
 	F64							mStopRequested;
 	bool						mStopped;
-	int							mPolicyDefault;
-	int							mPolicyTexture;
-	int							mPolicyMesh;
+	policy_t					mPolicies[AP_COUNT];
 };
 
 
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 702e940983..f0ec97a34d 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -80,6 +80,10 @@ LLMeshRepository gMeshRepo;
 
 const S32 MESH_HEADER_SIZE = 4096;
 const U32 MAX_MESH_REQUESTS_PER_SECOND = 100;
+const S32 REQUEST_HIGH_WATER_MIN = 32;
+const S32 REQUEST_LOW_WATER_MIN = 16;
+const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;		// Size at which requests goes to narrow/slow queue
+const long LARGE_MESH_XFER_TIMEOUT = 240L;				// Seconds to complete xfer
 
 // Maximum mesh version to support.  Three least significant digits are reserved for the minor version, 
 // with major version changes indicating a format change that is not backwards compatible and should not
@@ -210,6 +214,8 @@ void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res,
 S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
 S32 LLMeshRepoThread::sActiveLODRequests = 0;
 U32	LLMeshRepoThread::sMaxConcurrentRequests = 1;
+S32 LLMeshRepoThread::sRequestLowWater = REQUEST_LOW_WATER_MIN;
+S32 LLMeshRepoThread::sRequestHighWater = REQUEST_HIGH_WATER_MIN;
 
 class LLMeshHandlerBase : public LLCore::HttpHandler
 {
@@ -548,25 +554,37 @@ public:
 
 LLMeshRepoThread::LLMeshRepoThread()
 : LLThread("mesh repo"),
-  mCurlRequest(NULL),
   mWaiting(false),
   mHttpRequest(NULL),
   mHttpOptions(NULL),
+  mHttpLargeOptions(NULL),
   mHttpHeaders(NULL),
-  mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID)
+  mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
+  mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
+  mHttpPriority(0),
+  mHttpGetCount(0U),
+  mHttpLargeGetCount(0U)
 { 
 	mMutex = new LLMutex(NULL);
 	mHeaderMutex = new LLMutex(NULL);
 	mSignal = new LLCondition(NULL);
 	mHttpRequest = new LLCore::HttpRequest;
 	mHttpOptions = new LLCore::HttpOptions;
+	mHttpLargeOptions = new LLCore::HttpOptions;
+	mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT);
 	mHttpHeaders = new LLCore::HttpHeaders;
 	mHttpHeaders->mHeaders.push_back("Accept: application/vnd.ll.mesh");
-	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyMesh();
+	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH);
+	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_LARGE_MESH);
 }
 
+
 LLMeshRepoThread::~LLMeshRepoThread()
 {
+	LL_INFOS("Mesh") << "Small GETs issued:  "
+					 << mHttpGetCount << ", Large GETs issued:  "
+					 << mHttpLargeGetCount << LL_ENDL;
+
 	for (http_request_set::iterator iter(mHttpRequestSet.begin());
 		 iter != mHttpRequestSet.end();
 		 ++iter)
@@ -584,6 +602,11 @@ LLMeshRepoThread::~LLMeshRepoThread()
 		mHttpOptions->release();
 		mHttpOptions = NULL;
 	}
+	if (mHttpLargeOptions)
+	{
+		mHttpLargeOptions->release();
+		mHttpLargeOptions = NULL;
+	}
 	delete mHttpRequest;
 	mHttpRequest = NULL;
 	delete mMutex;
@@ -596,7 +619,6 @@ LLMeshRepoThread::~LLMeshRepoThread()
 
 void LLMeshRepoThread::run()
 {
-	mCurlRequest = new LLCurlRequest();
 	LLCDResult res = LLConvexDecomposition::initThread();
 	if (res != LLCD_OK)
 	{
@@ -627,7 +649,7 @@ void LLMeshRepoThread::run()
 
 			// NOTE: throttling intentionally favors LOD requests over header requests
 			
-			while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < sMaxConcurrentRequests)
+			while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND)
 			{
 				if (mMutex)
 				{
@@ -646,7 +668,7 @@ void LLMeshRepoThread::run()
 				}
 			}
 
-			while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < sMaxConcurrentRequests)
+			while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND)
 			{
 				if (mMutex)
 				{
@@ -701,8 +723,6 @@ void LLMeshRepoThread::run()
 				}
 				mPhysicsShapeRequests = incomplete;
 			}
-
-			mCurlRequest->process();
 		}
 	}
 	
@@ -716,9 +736,6 @@ void LLMeshRepoThread::run()
 	{
 		llwarns << "convex decomposition unable to be quit" << llendl;
 	}
-
-	delete mCurlRequest;
-	mCurlRequest = NULL;
 }
 
 void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id)
@@ -800,6 +817,42 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
 	return http_url;
 }
 
+// May only be called by repo thread
+LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url,
+												  size_t offset,
+												  size_t len,
+												  LLCore::HttpHandler * handler)
+{
+	LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
+	
+	if (len < LARGE_MESH_FETCH_THRESHOLD)
+	{
+		handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
+												   mHttpPriority,
+												   url,
+												   offset,
+												   len,
+												   mHttpOptions,
+												   mHttpHeaders,
+												   handler);
+		++mHttpGetCount;
+	}
+	else
+	{
+		handle = mHttpRequest->requestGetByteRange(mHttpLargePolicyClass,
+												   mHttpPriority,
+												   url,
+												   offset,
+												   len,
+												   mHttpLargeOptions,
+												   mHttpHeaders,
+												   handler);
+		++mHttpLargeGetCount;
+	}
+	return handle;
+}
+
+
 bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 { //protected by mMutex
 	
@@ -863,14 +916,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 			{
 				LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing Skin Info Request" << LL_ENDL;
-				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
-																			  0,				// *TODO:  Get better priority value
-																			  http_url,
-																			  offset,
-																			  size,
-																			  mHttpOptions,
-																			  mHttpHeaders,
-																			  handler);
+				LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
 					// *TODO:  Better error message
@@ -959,14 +1005,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 			{
 				LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing Decomp Request" << LL_ENDL;
-				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
-																			  0,	// *TODO:  Get better priority value
-																			  http_url,
-																			  offset,
-																			  size,
-																			  mHttpOptions,
-																			  mHttpHeaders,
-																			  handler);
+				LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
 					// *TODO:  Better error message
@@ -1054,14 +1093,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 			{
 				LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing Physics Shape Request" << LL_ENDL;
-				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
-																			  0,		// *TODO:  Get better priority value
-																			  http_url,
-																			  offset,
-																			  size,
-																			  mHttpOptions,
-																			  mHttpHeaders,
-																			  handler);
+				LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
 					// *TODO:  Better error message
@@ -1154,14 +1186,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 
 		LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params);
 		// LL_WARNS("Mesh") << "MESH:  Issuing Request" << LL_ENDL;
-		LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
-																	  0,				// *TODO:  Get better priority value
-																	  http_url,
-																	  0,
-																	  MESH_HEADER_SIZE,
-																	  mHttpOptions,
-																	  mHttpHeaders,
-																	  handler);
+		LLCore::HttpHandle handle = getByteRange(http_url, 0, MESH_HEADER_SIZE, handler);
 		if (LLCORE_HTTP_HANDLE_INVALID == handle)
 		{
 			// *TODO:  Better error message
@@ -1241,14 +1266,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 			{
 				LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing LOD Request" << LL_ENDL;
-				LLCore::HttpHandle handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
-																			  0,		// *TODO:  Get better priority value
-																			  http_url,
-																			  offset,
-																			  size,
-																			  mHttpOptions,
-																			  mHttpHeaders,
-																			  handler);
+				LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
 					// *TODO:  Better error message
@@ -2537,9 +2555,14 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
 
 void LLMeshRepository::notifyLoadedMeshes()
 { //called from main thread
-
-	LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests");
-
+	// *FIXME:  Scaling down the setting by a factor of 4 for now to reflect
+	// target goal.  May want to rename the setting before release.
+	LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests") / 4;
+	LLMeshRepoThread::sRequestHighWater = llmax(50 * S32(LLMeshRepoThread::sMaxConcurrentRequests),
+												REQUEST_HIGH_WATER_MIN);
+	LLMeshRepoThread::sRequestLowWater = llmax(LLMeshRepoThread::sRequestLowWater / 2,
+											   REQUEST_LOW_WATER_MIN);
+	
 	//clean up completed upload threads
 	for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); )
 	{
@@ -2617,7 +2640,7 @@ void LLMeshRepository::notifyLoadedMeshes()
 	//call completed callbacks on finished decompositions
 	mDecompThread->notifyCompleted();
 	
-	if (!mThread->mWaiting)
+	if (!mThread->mWaiting && mPendingRequests.empty())
 	{ //curl thread is churning, wait for it to go idle
 		return;
 	}
@@ -2644,47 +2667,55 @@ void LLMeshRepository::notifyLoadedMeshes()
 			mUploadErrorQ.pop();
 		}
 
-		S32 push_count = LLMeshRepoThread::sMaxConcurrentRequests-(LLMeshRepoThread::sActiveHeaderRequests+LLMeshRepoThread::sActiveLODRequests);
-
-		if (push_count > 0)
+		S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests;
+		if (active_count < LLMeshRepoThread::sRequestLowWater)
 		{
-			//calculate "score" for pending requests
-
-			//create score map
-			std::map<LLUUID, F32> score_map;
+			S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count;
 
-			for (U32 i = 0; i < 4; ++i)
+			if (mPendingRequests.size() > push_count)
 			{
-				for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin();  iter != mLoadingMeshes[i].end(); ++iter)
+				// More requests than the high-water limit allows so
+				// sort and forward the most important.
+
+				//calculate "score" for pending requests
+
+				//create score map
+				std::map<LLUUID, F32> score_map;
+
+				for (U32 i = 0; i < 4; ++i)
 				{
-					F32 max_score = 0.f;
-					for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
+					for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin();  iter != mLoadingMeshes[i].end(); ++iter)
 					{
-						LLViewerObject* object = gObjectList.findObject(*obj_iter);
-
-						if (object)
+						F32 max_score = 0.f;
+						for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
 						{
-							LLDrawable* drawable = object->mDrawable;
-							if (drawable)
+							LLViewerObject* object = gObjectList.findObject(*obj_iter);
+
+							if (object)
 							{
-								F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f);
-								max_score = llmax(max_score, cur_score);
+								LLDrawable* drawable = object->mDrawable;
+								if (drawable)
+								{
+									F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f);
+									max_score = llmax(max_score, cur_score);
+								}
 							}
 						}
-					}
 				
-					score_map[iter->first.getSculptID()] = max_score;
+						score_map[iter->first.getSculptID()] = max_score;
+					}
 				}
-			}
 
-			//set "score" for pending requests
-			for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter)
-			{
-				iter->mScore = score_map[iter->mMeshParams.getSculptID()];
-			}
+				//set "score" for pending requests
+				for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter)
+				{
+					iter->mScore = score_map[iter->mMeshParams.getSculptID()];
+				}
 
-			//sort by "score"
-			std::sort(mPendingRequests.begin(), mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater());
+				//sort by "score"
+				std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count,
+								  mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater());
+			}
 
 			while (!mPendingRequests.empty() && push_count > 0)
 			{
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 62f81ce9e2..0dca29e7d4 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -224,13 +224,14 @@ public:
 	static S32 sActiveHeaderRequests;
 	static S32 sActiveLODRequests;
 	static U32 sMaxConcurrentRequests;
+	static S32 sRequestLowWater;
+	static S32 sRequestHighWater;
 
-	LLCurlRequest* mCurlRequest;
 	LLMutex*	mMutex;
 	LLMutex*	mHeaderMutex;
 	LLCondition* mSignal;
 
-	bool mWaiting;
+	volatile bool mWaiting;
 
 	//map of known mesh headers
 	typedef std::map<LLUUID, LLSD> mesh_header_map;
@@ -324,8 +325,11 @@ public:
 	// llcorehttp library interface objects.
 	LLCore::HttpRequest *				mHttpRequest;
 	LLCore::HttpOptions *				mHttpOptions;
+	LLCore::HttpOptions *				mHttpLargeOptions;
 	LLCore::HttpHeaders *				mHttpHeaders;
 	LLCore::HttpRequest::policy_t		mHttpPolicyClass;
+	LLCore::HttpRequest::policy_t		mHttpLargePolicyClass;
+	LLCore::HttpRequest::priority_t		mHttpPriority;
 
 	typedef std::set<LLCore::HttpHandler *> http_request_set;
 	http_request_set					mHttpRequestSet;			// Outstanding HTTP requests
@@ -373,6 +377,19 @@ public:
 	static void incActiveHeaderRequests();
 	static void decActiveHeaderRequests();
 
+private:
+	// Issue a GET request to a URL with 'Range' header using
+	// the correct policy class and other attributes.  If an invalid
+	// handle is returned, the request failed and caller must retry
+	// or dispose of handler.
+	//
+	// Threads:  Repo thread only
+	LLCore::HttpHandle getByteRange(const std::string & url, size_t offset, size_t len, 
+									LLCore::HttpHandler * handler);
+
+private:
+	U32 mHttpGetCount;
+	U32 mHttpLargeGetCount;
 };
 
 class LLMeshUploadThread : public LLThread 
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index be5fde9e2b..d934ef9dc4 100755
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2000&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -2410,7 +2410,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
 	mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c");
 	mHttpMetricsHeaders = new LLCore::HttpHeaders;
 	mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml");
-	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault();
+	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_TEXTURE);
 }
 
 LLTextureFetch::~LLTextureFetch()
-- 
cgit v1.2.3


From 4eef1c8a2e2a8abcc463b9df38b66f025da57589 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Mon, 15 Apr 2013 16:55:35 +0000
Subject: SH-4106 Significantly upgrade the HttpHeaders interface for SSB.
 Header container moves from a vector of raw lines to a vector of string pairs
 representing name/value pairs in headers.  For incoming headers, we normalize
 the name to lowercase and trim it.  Values are only left-trimmed.  Outgoing
 headers are left as-is.  Simple find() method for the common case, forward
 and reverse iterators for those few who need to do it themselves. The HTTP
 status line (e.g. 'HTTP/1.1 200 Ok') is no longer treated as a header to be
 returned to caller.  Unit tests, as usual, were a bear but they absolutely
 ensured outgoing HTTP header conformance after the change.  Grunt work paid
 off.

LLTextureFetch was also given a second options structure
for texture fetches.  Same as the original but with header return
to caller requested.  Baked textures should use this, the other
20,000 texture fetch requests should continue to use the original.
---
 indra/llcorehttp/_httplibcurl.cpp               |  17 +-
 indra/llcorehttp/_httpoprequest.cpp             |  12 +-
 indra/llcorehttp/examples/http_texture_load.cpp |   4 +-
 indra/llcorehttp/httpheaders.cpp                | 141 +++++-
 indra/llcorehttp/httpheaders.h                  | 112 ++++-
 indra/llcorehttp/tests/test_httpheaders.hpp     | 345 ++++++++++++++-
 indra/llcorehttp/tests/test_httprequest.hpp     | 563 +++++++++++++++++++-----
 indra/newview/lltexturefetch.cpp                |  15 +-
 indra/newview/lltexturefetch.h                  |   3 +-
 9 files changed, 1054 insertions(+), 158 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp
index 6fe0bfc7d1..d187697e7b 100755
--- a/indra/llcorehttp/_httplibcurl.cpp
+++ b/indra/llcorehttp/_httplibcurl.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -359,12 +359,17 @@ int HttpLibcurl::getActiveCountInClass(int policy_class) const
 
 struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist)
 {
-	for (HttpHeaders::container_t::const_iterator it(headers->mHeaders.begin());
-
-		headers->mHeaders.end() != it;
-		 ++it)
+	const HttpHeaders::const_iterator end(headers->end());
+	for (HttpHeaders::const_iterator it(headers->begin()); end != it; ++it)
 	{
-		slist = curl_slist_append(slist, (*it).c_str());
+		static const char sep[] = ": ";
+		std::string header;
+		header.reserve((*it).first.size() + (*it).second.size() + sizeof(sep));
+		header.append((*it).first);
+		header.append(sep);
+		header.append((*it).second);
+		
+		slist = curl_slist_append(slist, header.c_str());
 	}
 	return slist;
 }
diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index d403b2d249..a4c0a12fdc 100755
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -618,7 +618,8 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
 
 	const size_t hdr_size(size * nmemb);
 	const char * hdr_data(static_cast<const char *>(data));		// Not null terminated
-
+	bool is_header(true);
+	
 	if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len))
 	{
 		// One of possibly several status lines.  Reset what we know and start over
@@ -629,8 +630,9 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
 		op->mStatus = HttpStatus();
 		if (op->mReplyHeaders)
 		{
-			op->mReplyHeaders->mHeaders.clear();
+			op->mReplyHeaders->clear();
 		}
+		is_header = false;
 	}
 
 	// Nothing in here wants a final CR/LF combination.  Remove
@@ -645,18 +647,18 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
 	}
 	
 	// Save header if caller wants them in the response
-	if (op->mProcFlags & PF_SAVE_HEADERS)
+	if (is_header && op->mProcFlags & PF_SAVE_HEADERS)
 	{
 		// Save headers in response
 		if (! op->mReplyHeaders)
 		{
 			op->mReplyHeaders = new HttpHeaders;
 		}
-		op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_hdr_size));
+		op->mReplyHeaders->appendNormal(hdr_data, wanted_hdr_size);
 	}
 
 	// Detect and parse 'Content-Range' headers
-	if (op->mProcFlags & PF_SCAN_RANGE_HEADER)
+	if (is_header && op->mProcFlags & PF_SCAN_RANGE_HEADER)
 	{
 		char hdr_buffer[128];			// Enough for a reasonable header
 		size_t frag_size((std::min)(wanted_hdr_size, sizeof(hdr_buffer) - 1));
diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp
index 40ad4f047d..909dc5b0cb 100755
--- a/indra/llcorehttp/examples/http_texture_load.cpp
+++ b/indra/llcorehttp/examples/http_texture_load.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -328,7 +328,7 @@ WorkingSet::WorkingSet()
 	mTextures.reserve(30000);
 
 	mHeaders = new LLCore::HttpHeaders;
-	mHeaders->mHeaders.push_back("Accept: image/x-j2c");
+	mHeaders->append("Accept", "image/x-j2c");
 }
 
 
diff --git a/indra/llcorehttp/httpheaders.cpp b/indra/llcorehttp/httpheaders.cpp
index 2832696271..23ebea361c 100755
--- a/indra/llcorehttp/httpheaders.cpp
+++ b/indra/llcorehttp/httpheaders.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -26,6 +26,8 @@
 
 #include "httpheaders.h"
 
+#include "llstring.h"
+
 
 namespace LLCore
 {
@@ -40,5 +42,142 @@ HttpHeaders::~HttpHeaders()
 {}
 
 
+void
+HttpHeaders::clear()
+{
+	mHeaders.clear();
+}
+
+
+void HttpHeaders::append(const std::string & name, const std::string & value)
+{
+	mHeaders.push_back(value_type(name, value));
+}
+
+
+void HttpHeaders::append(const char * name, const char * value)
+{
+	mHeaders.push_back(value_type(name, value));
+}
+
+
+void HttpHeaders::appendNormal(const char * header, size_t size)
+{
+	std::string name;
+	std::string value;
+
+	int col_pos(0);
+	for (; col_pos < size; ++col_pos)
+	{
+		if (':' == header[col_pos])
+			break;
+	}
+	
+	if (col_pos < size)
+	{
+		// Looks like a header, split it and normalize.
+		// Name is everything before the colon, may be zero-length.
+		name.assign(header, col_pos);
+
+		// Value is everything after the colon, may also be zero-length.
+		const size_t val_len(size - col_pos - 1);
+		if (val_len)
+		{
+			value.assign(header + col_pos + 1, val_len);
+		}
+
+		// Clean the strings
+		LLStringUtil::toLower(name);
+		LLStringUtil::trim(name);
+		LLStringUtil::trimHead(value);
+	}
+	else
+	{
+		// Uncertain what this is, we'll pack it as
+		// a name without a value.  Won't clean as we don't
+		// know what it is...
+		name.assign(header, size);
+	}
+
+	mHeaders.push_back(value_type(name, value));
+}
+
+
+// Find from end to simulate a tradition of using single-valued
+// std::map for this in the past.
+const std::string * HttpHeaders::find(const char * name) const
+{
+	const_reverse_iterator iend(rend());
+	for (const_reverse_iterator iter(rbegin()); iend != iter; ++iter)
+	{
+		if ((*iter).first == name)
+		{
+			return &(*iter).second;
+		}
+	}
+	return NULL;
+}
+
+
+// Standard Iterators
+HttpHeaders::iterator HttpHeaders::begin()
+{
+	return mHeaders.begin();
+}
+
+
+HttpHeaders::const_iterator HttpHeaders::begin() const
+{
+	return mHeaders.begin();
+}
+
+
+HttpHeaders::iterator HttpHeaders::end()
+{
+	return mHeaders.end();
+}
+
+
+HttpHeaders::const_iterator HttpHeaders::end() const
+{
+	return mHeaders.end();
+}
+
+
+// Standard Reverse Iterators
+HttpHeaders::reverse_iterator HttpHeaders::rbegin()
+{
+	return mHeaders.rbegin();
+}
+
+
+HttpHeaders::const_reverse_iterator HttpHeaders::rbegin() const
+{
+	return mHeaders.rbegin();
+}
+
+
+HttpHeaders::reverse_iterator HttpHeaders::rend()
+{
+	return mHeaders.rend();
+}
+
+
+HttpHeaders::const_reverse_iterator HttpHeaders::rend() const
+{
+	return mHeaders.rend();
+}
+
+
+// Return the raw container to the caller.
+//
+// To be used FOR UNIT TESTS ONLY.
+//
+HttpHeaders::container_t & HttpHeaders::getContainerTESTONLY()
+{
+	return mHeaders;
+}
+
+
 }   // end namespace LLCore
 
diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h
index 3449daa3a1..f70cd898f3 100755
--- a/indra/llcorehttp/httpheaders.h
+++ b/indra/llcorehttp/httpheaders.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -43,13 +43,26 @@ namespace LLCore
 /// caller has asked that headers be returned (not the default
 /// option).
 ///
-/// @note
-/// This is a minimally-functional placeholder at the moment
-/// to fill out the class hierarchy.  The final class will be
-/// something else, probably more pair-oriented.  It's also
-/// an area where shared values are desirable so refcounting is
-/// already specced and a copy-on-write scheme imagined.
-/// Expect changes here.
+/// Class is mostly a thin wrapper around a vector of pairs
+/// of strings.  Methods provided are few and intended to
+/// reflect actual use patterns.  These include:
+/// - Clearing the list
+/// - Appending a name/value pair to the vector
+/// - Processing a raw byte string into a normalized name/value
+///   pair and appending the result.
+/// - Simple case-sensitive find-last-by-name search
+/// - Forward and reverse iterators over all pairs
+///
+/// Container is ordered and multi-valued.  Headers are
+/// written in the order in which they are appended and
+/// are stored in the order in which they're received from
+/// the wire.  The same header may appear two or more times
+/// in any container.  Searches using the simple find()
+/// interface will find only the last occurrence (somewhat
+/// simulates the use of std::map).  Fuller searches require
+/// the use of an iterator.  Headers received from the wire
+/// are only returned from the last request when redirections
+/// are involved.
 ///
 /// Threading:  Not intrinsically thread-safe.  It *is* expected
 /// that callers will build these objects and then share them
@@ -63,6 +76,16 @@ namespace LLCore
 
 class HttpHeaders : public LLCoreInt::RefCounted
 {
+public:
+	typedef std::pair<std::string, std::string> header_t;
+	typedef std::vector<header_t> container_t;
+	typedef container_t::iterator iterator;
+	typedef container_t::const_iterator const_iterator;
+	typedef container_t::reverse_iterator reverse_iterator;
+	typedef container_t::const_reverse_iterator const_reverse_iterator;
+	typedef container_t::value_type value_type;
+	typedef container_t::size_type size_type;
+
 public:
 	/// @post In addition to the instance, caller has a refcount
 	/// to the instance.  A call to @see release() will destroy
@@ -76,7 +99,78 @@ protected:
 	void operator=(const HttpHeaders &);		// Not defined
 
 public:
-	typedef std::vector<std::string> container_t;
+	// Empty the list of headers.
+	void clear();
+
+	// Append a name/value pair supplied as either std::strings
+	// or NUL-terminated char * to the header list.  No normalization
+	// is performed on the strings.  No conformance test is
+	// performed (names may contain spaces, colons, etc.).
+	//
+	void append(const std::string & name, const std::string & value);
+	void append(const char * name, const char * value);
+
+	// Extract a name/value pair from a raw byte array using
+	// the first colon character as a separator.  Input string
+	// does not need to be NUL-terminated.  Resulting name/value
+	// pair is appended to the header list.
+	//
+	// Normalization is performed on the name/value pair as
+	// follows:
+	// - name is lower-cased according to mostly ASCII rules
+	// - name is left- and right-trimmed of spaces and tabs
+	// - value is left-trimmed of spaces and tabs
+	// - either or both of name and value may be zero-length
+	//
+	// By convention, headers read from the wire will be normalized
+	// in this fashion prior to delivery to any HttpHandler code.
+	// Headers to be written to the wire are left as appended to
+	// the list.
+	void appendNormal(const char * header, size_t size);
+
+	// Perform a simple, case-sensitive search of the header list
+	// returning a pointer to the value of the last matching header
+	// in the header list.  If none is found, a NULL pointer is returned.
+	//
+	// Any pointer returned references objects in the container itself
+	// and will have the same lifetime as this class.  If you want
+	// the value beyond the lifetime of this instance, make a copy.
+	//
+	// @arg		name	C-style string giving the name of a header
+	//					to search.  The comparison is case-sensitive
+	//					though list entries may have been normalized
+	//					to lower-case.
+	//
+	// @return			NULL if the header wasn't found otherwise
+	//					a pointer to a std::string in the container.
+	//					Pointer is valid only for the lifetime of
+	//					the container or until container is modifed.
+	//
+	const std::string * find(const char * name) const;
+
+	// Count of headers currently in the list.
+	size_type size() const
+		{
+			return mHeaders.size();
+		}
+
+	// Standard std::vector-based forward iterators.
+	iterator begin();
+	const_iterator begin() const;
+	iterator end();
+	const_iterator end() const;
+
+	// Standard std::vector-based reverse iterators.
+	reverse_iterator rbegin();
+	const_reverse_iterator rbegin() const;
+	reverse_iterator rend();
+	const_reverse_iterator rend() const;
+
+public:
+	// For unit tests only - not a public API
+	container_t &		getContainerTESTONLY();
+	
+protected:
 	container_t			mHeaders;
 	
 }; // end class HttpHeaders
diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp
index ce0d19b058..668c36dc66 100755
--- a/indra/llcorehttp/tests/test_httpheaders.hpp
+++ b/indra/llcorehttp/tests/test_httpheaders.hpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -36,7 +36,6 @@
 using namespace LLCoreInt;
 
 
-
 namespace tut
 {
 
@@ -63,7 +62,7 @@ void HttpHeadersTestObjectType::test<1>()
 	HttpHeaders * headers = new HttpHeaders();
 	ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1);
 	ensure("Memory being used", mMemTotal < GetMemTotal());
-	ensure("Nothing in headers", 0 == headers->mHeaders.size());
+	ensure("Nothing in headers", 0 == headers->size());
 
 	// release the implicit reference, causing the object to be released
 	headers->release();
@@ -85,14 +84,340 @@ void HttpHeadersTestObjectType::test<2>()
 	
 	{
 		// Append a few strings
-		std::string str1("Pragma:");
-		headers->mHeaders.push_back(str1);
-		std::string str2("Accept: application/json");
-		headers->mHeaders.push_back(str2);
+		std::string str1n("Pragma");
+		std::string str1v("");
+		headers->append(str1n, str1v);
+		std::string str2n("Accept");
+		std::string str2v("application/json");
+		headers->append(str2n, str2v);
+	
+		ensure("Headers retained", 2 == headers->size());
+		HttpHeaders::container_t & c(headers->getContainerTESTONLY());
+		
+		ensure("First name is first name", c[0].first == str1n);
+		ensure("First value is first value", c[0].second == str1v);
+		ensure("Second name is second name", c[1].first == str2n);
+		ensure("Second value is second value", c[1].second == str2v);
+	}
+	
+	// release the implicit reference, causing the object to be released
+	headers->release();
+
+	// make sure we didn't leak any memory
+	ensure(mMemTotal == GetMemTotal());
+}
+
+template <> template <>
+void HttpHeadersTestObjectType::test<3>()
+{
+	set_test_name("HttpHeaders basic find");
+
+	// record the total amount of dynamically allocated memory
+	mMemTotal = GetMemTotal();
+
+	// create a new ref counted object with an implicit reference
+	HttpHeaders * headers = new HttpHeaders();
+	
+	{
+		// Append a few strings
+		std::string str1n("Uno");
+		std::string str1v("1");
+		headers->append(str1n, str1v);
+		std::string str2n("doS");
+		std::string str2v("2-2-2-2");
+		headers->append(str2n, str2v);
+		std::string str3n("TRES");
+		std::string str3v("trois gymnopedie");
+		headers->append(str3n, str3v);
+	
+		ensure("Headers retained", 3 == headers->size());
+
+		const std::string * result(NULL);
+
+		// Find a header
+		result = headers->find("TRES");
+		ensure("Found the last item", result != NULL);
+		ensure("Last item is a nice", result != NULL && str3v == *result);
+
+		// appends above are raw and find is case sensitive
+		result = headers->find("TReS");
+		ensure("Last item not found due to case", result == NULL);
+
+		result = headers->find("TRE");
+		ensure("Last item not found due to prefixing (1)", result == NULL);
+
+		result = headers->find("TRESS");
+		ensure("Last item not found due to prefixing (2)", result == NULL);
+	}
+	
+	// release the implicit reference, causing the object to be released
+	headers->release();
+
+	// make sure we didn't leak any memory
+	ensure(mMemTotal == GetMemTotal());
+}
+
+template <> template <>
+void HttpHeadersTestObjectType::test<4>()
+{
+	set_test_name("HttpHeaders normalized header entry");
+
+	// record the total amount of dynamically allocated memory
+	mMemTotal = GetMemTotal();
+
+	// create a new ref counted object with an implicit reference
+	HttpHeaders * headers = new HttpHeaders();
+	
+	{
+		static char line1[] = " AcCePT : image/yourfacehere";
+		static char line1v[] = "image/yourfacehere";
+		headers->appendNormal(line1, sizeof(line1) - 1);
+		
+		ensure("First append worked in some fashion", 1 == headers->size());
+
+		const std::string * result(NULL);
+
+		// Find a header
+		result = headers->find("accept");
+		ensure("Found 'accept'", result != NULL);
+		ensure("accept value has face", result != NULL && *result == line1v);
+
+		// Left-clean on value
+		static char line2[] = " next : \t\tlinejunk \t";
+		headers->appendNormal(line2, sizeof(line2) - 1);
+		ensure("Second append worked", 2 == headers->size());
+		result = headers->find("next");
+		ensure("Found 'next'", result != NULL);
+		ensure("next value is left-clean", result != NULL &&
+			   *result == "linejunk \t");
+
+		// First value unmolested
+		result = headers->find("accept");
+		ensure("Found 'accept' again", result != NULL);
+		ensure("accept value has face", result != NULL && *result == line1v);
+
+		// Colons in value are okay
+		static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
+		static char line3v[] = ":plop:-neuf-=vleem=";
+		headers->appendNormal(line3, sizeof(line3) - 1);
+		ensure("Third append worked", 3 == headers->size());
+		result = headers->find("fancy-pants");
+		ensure("Found 'fancy-pants'", result != NULL);
+		ensure("fancy-pants value has colons", result != NULL && *result == line3v);
+
+		// Zero-length value
+		static char line4[] = "all-talk-no-walk:";
+		headers->appendNormal(line4, sizeof(line4) - 1);
+		ensure("Fourth append worked", 4 == headers->size());
+		result = headers->find("all-talk-no-walk");
+		ensure("Found 'all-talk'", result != NULL);
+		ensure("al-talk value is zero-length", result != NULL && result->size() == 0);
+
+		// Zero-length name
+		static char line5[] = ":all-talk-no-walk";
+		static char line5v[] = "all-talk-no-walk";
+		headers->appendNormal(line5, sizeof(line5) - 1);
+		ensure("Fifth append worked", 5 == headers->size());
+		result = headers->find("");
+		ensure("Found no-name", result != NULL);
+		ensure("no-name value is something", result != NULL && *result == line5v);
+
+		// Lone colon is still something
+		headers->clear();
+		static char line6[] = "  :";
+		headers->appendNormal(line6, sizeof(line6) - 1);
+		ensure("Sixth append worked", 1 == headers->size());
+		result = headers->find("");
+		ensure("Found 2nd no-name", result != NULL);
+		ensure("2nd no-name value is nothing", result != NULL && result->size() == 0);
+
+		// Line without colons is taken as-is and unstripped in name
+		static char line7[] = " \toskdgioasdghaosdghoowg28342908tg8902hg0hwedfhqew890v7qh0wdebv78q0wdevbhq>?M>BNM<ZV>?NZ? \t";
+		headers->appendNormal(line7, sizeof(line7) - 1);
+		ensure("Seventh append worked", 2 == headers->size());
+		result = headers->find(line7);
+		ensure("Found whatsit line", result != NULL);
+		ensure("Whatsit line has no value", result != NULL && result->size() == 0);
+
+		// Normaling interface heeds the byte count, doesn't look for NUL-terminator
+		static char line8[] = "binary:ignorestuffontheendofthis";
+		headers->appendNormal(line8, 13);
+		ensure("Eighth append worked", 3 == headers->size());
+		result = headers->find("binary");
+		ensure("Found 'binary'", result != NULL);
+		ensure("binary value was limited to 'ignore'", result != NULL &&
+			   *result == "ignore");
+
+	}
 	
-		ensure("Headers retained", 2 == headers->mHeaders.size());
-		ensure("First is first", headers->mHeaders[0] == str1);
-		ensure("Second is second", headers->mHeaders[1] == str2);
+	// release the implicit reference, causing the object to be released
+	headers->release();
+
+	// make sure we didn't leak any memory
+	ensure(mMemTotal == GetMemTotal());
+}
+
+// Verify forward iterator finds everything as expected
+template <> template <>
+void HttpHeadersTestObjectType::test<5>()
+{
+	set_test_name("HttpHeaders iterator tests");
+
+	// record the total amount of dynamically allocated memory
+	mMemTotal = GetMemTotal();
+
+	// create a new ref counted object with an implicit reference
+	HttpHeaders * headers = new HttpHeaders();
+
+	HttpHeaders::iterator end(headers->end()), begin(headers->begin());
+	ensure("Empty container has equal begin/end const iterators", end == begin);
+	HttpHeaders::const_iterator cend(headers->end()), cbegin(headers->begin());
+	ensure("Empty container has equal rbegin/rend const iterators", cend == cbegin);
+
+	ensure("Empty container has equal begin/end iterators", headers->end() == headers->begin());
+	
+	{
+		static char line1[] = " AcCePT : image/yourfacehere";
+		static char line1v[] = "image/yourfacehere";
+		headers->appendNormal(line1, sizeof(line1) - 1);
+
+		static char line2[] = " next : \t\tlinejunk \t";
+		static char line2v[] = "linejunk \t";
+		headers->appendNormal(line2, sizeof(line2) - 1);
+
+		static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
+		static char line3v[] = ":plop:-neuf-=vleem=";
+		headers->appendNormal(line3, sizeof(line3) - 1);
+
+		static char line4[] = "all-talk-no-walk:";
+		static char line4v[] = "";
+		headers->appendNormal(line4, sizeof(line4) - 1);
+
+		static char line5[] = ":all-talk-no-walk";
+		static char line5v[] = "all-talk-no-walk";
+		headers->appendNormal(line5, sizeof(line5) - 1);
+
+		static char line6[] = "  :";
+		static char line6v[] = "";
+		headers->appendNormal(line6, sizeof(line6) - 1);
+
+		ensure("All entries accounted for", 6 == headers->size());
+
+		static char * values[] = {
+			line1v,
+			line2v,
+			line3v,
+			line4v,
+			line5v,
+			line6v
+		};
+			
+		int i(0);
+		HttpHeaders::const_iterator cend(headers->end());
+		for (HttpHeaders::const_iterator it(headers->begin());
+			 cend != it;
+			 ++it, ++i)
+		{
+			std::ostringstream str;
+			str << "Const Iterator value # " << i << " was " << values[i];
+			ensure(str.str(), (*it).second == values[i]);
+		}
+
+		// Rewind, do non-consts
+		i = 0;
+		HttpHeaders::iterator end(headers->end());
+		for (HttpHeaders::iterator it(headers->begin());
+			 end != it;
+			 ++it, ++i)
+		{
+			std::ostringstream str;
+			str << "Const Iterator value # " << i << " was " << values[i];
+			ensure(str.str(), (*it).second == values[i]);
+		}
+	}
+	
+	// release the implicit reference, causing the object to be released
+	headers->release();
+
+	// make sure we didn't leak any memory
+	ensure(mMemTotal == GetMemTotal());
+}
+
+// Reverse iterators find everything as expected
+template <> template <>
+void HttpHeadersTestObjectType::test<6>()
+{
+	set_test_name("HttpHeaders reverse iterator tests");
+
+	// record the total amount of dynamically allocated memory
+	mMemTotal = GetMemTotal();
+
+	// create a new ref counted object with an implicit reference
+	HttpHeaders * headers = new HttpHeaders();
+
+	HttpHeaders::reverse_iterator rend(headers->rend()), rbegin(headers->rbegin());
+	ensure("Empty container has equal rbegin/rend const iterators", rend == rbegin);
+	HttpHeaders::const_reverse_iterator crend(headers->rend()), crbegin(headers->rbegin());
+	ensure("Empty container has equal rbegin/rend const iterators", crend == crbegin);
+	
+	{
+		static char line1[] = " AcCePT : image/yourfacehere";
+		static char line1v[] = "image/yourfacehere";
+		headers->appendNormal(line1, sizeof(line1) - 1);
+
+		static char line2[] = " next : \t\tlinejunk \t";
+		static char line2v[] = "linejunk \t";
+		headers->appendNormal(line2, sizeof(line2) - 1);
+
+		static char line3[] = "FancY-PANTs::plop:-neuf-=vleem=";
+		static char line3v[] = ":plop:-neuf-=vleem=";
+		headers->appendNormal(line3, sizeof(line3) - 1);
+
+		static char line4[] = "all-talk-no-walk:";
+		static char line4v[] = "";
+		headers->appendNormal(line4, sizeof(line4) - 1);
+
+		static char line5[] = ":all-talk-no-walk";
+		static char line5v[] = "all-talk-no-walk";
+		headers->appendNormal(line5, sizeof(line5) - 1);
+
+		static char line6[] = "  :";
+		static char line6v[] = "";
+		headers->appendNormal(line6, sizeof(line6) - 1);
+
+		ensure("All entries accounted for", 6 == headers->size());
+
+		static char * values[] = {
+			line6v,
+			line5v,
+			line4v,
+			line3v,
+			line2v,
+			line1v
+		};
+			
+		int i(0);
+		HttpHeaders::const_reverse_iterator cend(headers->rend());
+		for (HttpHeaders::const_reverse_iterator it(headers->rbegin());
+			 cend != it;
+			 ++it, ++i)
+		{
+			std::ostringstream str;
+			str << "Const Iterator value # " << i << " was " << values[i];
+			ensure(str.str(), (*it).second == values[i]);
+		}
+
+		// Rewind, do non-consts
+		i = 0;
+		HttpHeaders::reverse_iterator end(headers->rend());
+		for (HttpHeaders::reverse_iterator it(headers->rbegin());
+			 end != it;
+			 ++it, ++i)
+		{
+			std::ostringstream str;
+			str << "Iterator value # " << i << " was " << values[i];
+			ensure(str.str(), (*it).second == values[i]);
+		}
 	}
 	
 	// release the implicit reference, causing the object to be released
diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp
index e5488cf941..27d65f171e 100755
--- a/indra/llcorehttp/tests/test_httprequest.hpp
+++ b/indra/llcorehttp/tests/test_httprequest.hpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -60,6 +60,8 @@ void usleep(unsigned long usec);
 namespace tut
 {
 
+typedef std::vector<std::pair<boost::regex, boost::regex> > regex_container_t;
+
 struct HttpRequestTestData
 {
 	// the test objects inherit from this so the member functions and variables
@@ -109,11 +111,17 @@ public:
 					for (int i(0); i < mHeadersRequired.size(); ++i)
 					{
 						bool found = false;
-						for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin());
-							 header->mHeaders.end() != iter;
+						for (HttpHeaders::const_iterator iter(header->begin());
+							 header->end() != iter;
 							 ++iter)
 						{
-							if (boost::regex_match(*iter, mHeadersRequired[i]))
+							// std::cerr << "Header: " << (*iter).first
+							//		  << ": " << (*iter).second << std::endl;
+							
+							if (boost::regex_match((*iter).first,
+												   mHeadersRequired[i].first) &&
+								boost::regex_match((*iter).second,
+												   mHeadersRequired[i].second))
 							{
 								found = true;
 								break;
@@ -129,11 +137,14 @@ public:
 				{
 					for (int i(0); i < mHeadersDisallowed.size(); ++i)
 					{
-						for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin());
-							 header->mHeaders.end() != iter;
+						for (HttpHeaders::const_iterator iter(header->begin());
+							 header->end() != iter;
 							 ++iter)
 						{
-							if (boost::regex_match(*iter, mHeadersDisallowed[i]))
+							if (boost::regex_match((*iter).first,
+												   mHeadersDisallowed[i].first) &&
+								boost::regex_match((*iter).second,
+												   mHeadersDisallowed[i].second))
 							{
 								std::ostringstream str;
 								str << "Disallowed header # " << i << " not found in response";
@@ -159,8 +170,8 @@ public:
 	std::string mName;
 	HttpHandle mExpectHandle;
 	std::string mCheckContentType;
-	std::vector<boost::regex> mHeadersRequired;
-	std::vector<boost::regex> mHeadersDisallowed;
+	regex_container_t mHeadersRequired;
+	regex_container_t mHeadersDisallowed;
 };
 
 typedef test_group<HttpRequestTestData> HttpRequestTestGroupType;
@@ -1335,7 +1346,9 @@ void HttpRequestTestObjectType::test<13>()
 		
 		// Issue a GET that succeeds
 		mStatus = HttpStatus(200);
-		handler.mHeadersRequired.push_back(boost::regex("\\W*X-LL-Special:.*", boost::regex::icase));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(boost::regex("X-LL-Special", boost::regex::icase),
+										  boost::regex(".*", boost::regex::icase)));
 		HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
 													 0U,
 													 url_base,
@@ -1702,18 +1715,54 @@ void HttpRequestTestObjectType::test<16>()
 		
 		// Issue a GET that *can* connect
 		mStatus = HttpStatus(200);
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-connection", boost::regex::icase),
+				boost::regex("keep-alive", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept", boost::regex::icase),
+				boost::regex("\\*/\\*", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+				boost::regex("\\d+", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-host", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-cache-control", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-pragma", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-range", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-referer", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-type", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
 		HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
 											0U,
 											url_base + "reflect/",
@@ -1735,23 +1784,60 @@ void HttpRequestTestObjectType::test<16>()
 
 		// Do a texture-style fetch
 		headers = new HttpHeaders;
-		headers->mHeaders.push_back("Accept: image/x-j2c");
+		headers->append("Accept", "image/x-j2c");
 		
 		mStatus = HttpStatus(200);
 		handler.mHeadersRequired.clear();
 		handler.mHeadersDisallowed.clear();
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*image/x-j2c", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-connection", boost::regex::icase),
+				boost::regex("keep-alive", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept", boost::regex::icase),
+				boost::regex("image/x-j2c", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+				boost::regex("\\d+", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-host", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("\\W*X-Reflect-range", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-cache-control", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-pragma", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-referer", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-type", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
 		handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
 										  0U,
 										  url_base + "reflect/",
@@ -1892,20 +1978,63 @@ void HttpRequestTestObjectType::test<17>()
 			
 		// Issue a default POST
 		mStatus = HttpStatus(200);
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-connection", boost::regex::icase),
+				boost::regex("keep-alive", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept", boost::regex::icase),
+				boost::regex("\\*/\\*", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+				boost::regex("\\d+", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-host", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-length", boost::regex::icase),
+				boost::regex("\\d+", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-type", boost::regex::icase),
+				boost::regex("application/x-www-form-urlencoded", boost::regex::icase)));
+
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-cache-control", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-pragma", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-range", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-referer", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-expect", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-transfer_encoding", boost::regex::icase),
+				boost::regex(".*chunked.*", boost::regex::icase)));
 		HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
 											 0U,
 											 url_base + "reflect/",
@@ -2052,20 +2181,64 @@ void HttpRequestTestObjectType::test<18>()
 			
 		// Issue a default PUT
 		mStatus = HttpStatus(200);
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:.*", boost::regex::icase));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-connection", boost::regex::icase),
+				boost::regex("keep-alive", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept", boost::regex::icase),
+				boost::regex("\\*/\\*", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+				boost::regex("\\d+", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-host", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-length", boost::regex::icase),
+				boost::regex("\\d+", boost::regex::icase)));
+
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-cache-control", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-pragma", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-range", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-referer", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-expect", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+				boost::regex(".*chunked.*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-type", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+
 		HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
 											0U,
 											url_base + "reflect/",
@@ -2206,27 +2379,73 @@ void HttpRequestTestObjectType::test<19>()
 
 		// headers
 		headers = new HttpHeaders;
-		headers->mHeaders.push_back("Keep-Alive: 120");
-		headers->mHeaders.push_back("Accept-encoding: deflate");
-		headers->mHeaders.push_back("Accept: text/plain");
+		headers->append("Keep-Alive", "120");
+		headers->append("Accept-encoding", "deflate");
+		headers->append("Accept", "text/plain");
 
 		// Issue a GET with modified headers
 		mStatus = HttpStatus(200);
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/plain", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*deflate", boost::regex::icase)); // close enough
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
-		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-connection", boost::regex::icase),
+				boost::regex("keep-alive", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept", boost::regex::icase),
+				boost::regex("text/plain", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+				boost::regex("deflate", boost::regex::icase))); // close enough
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+				boost::regex("120", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-host", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+				boost::regex("300", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept", boost::regex::icase),
+				boost::regex("\\*/\\*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-cache-control", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-pragma", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-range", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-referer", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-type", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
 		HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID,
 											0U,
 											url_base + "reflect/",
@@ -2359,10 +2578,10 @@ void HttpRequestTestObjectType::test<20>()
 
 		// headers
 		headers = new HttpHeaders();
-		headers->mHeaders.push_back("keep-Alive: 120");
-		headers->mHeaders.push_back("Accept:  text/html");
-		headers->mHeaders.push_back("content-type:  application/llsd+xml");
-		headers->mHeaders.push_back("cache-control: no-store");
+		headers->append("keep-Alive", "120");
+		headers->append("Accept", "text/html");
+		headers->append("content-type", "application/llsd+xml");
+		headers->append("cache-control", "no-store");
 		
 		// And a buffer array
 		const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>");
@@ -2371,23 +2590,76 @@ void HttpRequestTestObjectType::test<20>()
 			
 		// Issue a default POST
 		mStatus = HttpStatus(200);
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/html", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("\\s*X-Reflect-cache-control:\\s*no-store", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-connection", boost::regex::icase),
+				boost::regex("keep-alive", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept", boost::regex::icase),
+				boost::regex("text/html", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+				boost::regex("120", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-host", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-length", boost::regex::icase),
+				boost::regex("\\d+", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-type", boost::regex::icase),
+				boost::regex("application/llsd\\+xml", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-cache-control", boost::regex::icase),
+				boost::regex("no-store", boost::regex::icase)));
+
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-type", boost::regex::icase),
+				boost::regex("application/x-www-form-urlencoded", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept", boost::regex::icase),
+				boost::regex("\\*/\\*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+				boost::regex("300", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-pragma", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-range", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-referer", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-expect", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+
 		HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID,
 											 0U,
 											 url_base + "reflect/",
@@ -2529,9 +2801,9 @@ void HttpRequestTestObjectType::test<21>()
 
 		// headers
 		headers = new HttpHeaders;
-		headers->mHeaders.push_back("content-type:  text/plain");
-		headers->mHeaders.push_back("content-type:  text/html");
-		headers->mHeaders.push_back("content-type:  application/llsd+xml");
+		headers->append("content-type", "text/plain");
+		headers->append("content-type", "text/html");
+		headers->append("content-type", "application/llsd+xml");
 		
 		// And a buffer array
 		const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>");
@@ -2540,22 +2812,71 @@ void HttpRequestTestObjectType::test<21>()
 			
 		// Issue a default PUT
 		mStatus = HttpStatus(200);
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase));
-		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/plain", boost::regex::icase));
-		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/html", boost::regex::icase));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-connection", boost::regex::icase),
+				boost::regex("keep-alive", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept", boost::regex::icase),
+				boost::regex("\\*/\\*", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-accept-encoding", boost::regex::icase),
+				boost::regex("((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase))); // close enough
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-keep-alive", boost::regex::icase),
+				boost::regex("\\d+", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-host", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-length", boost::regex::icase),
+				boost::regex("\\d+", boost::regex::icase)));
+		handler.mHeadersRequired.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-type", boost::regex::icase),
+				boost::regex("application/llsd\\+xml", boost::regex::icase)));
+
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-cache-control", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-pragma", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-range", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-referer", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-expect", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-transfer-encoding", boost::regex::icase),
+				boost::regex(".*", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-type", boost::regex::icase),
+				boost::regex("text/plain", boost::regex::icase)));
+		handler.mHeadersDisallowed.push_back(
+			regex_container_t::value_type(
+				boost::regex("X-Reflect-content-type", boost::regex::icase),
+				boost::regex("text/html", boost::regex::icase)));
 		HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID,
 											0U,
 											url_base + "reflect/",
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index d934ef9dc4..a8c8445ee1 100755
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -2376,6 +2376,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
 	  mQAMode(qa_mode),
 	  mHttpRequest(NULL),
 	  mHttpOptions(NULL),
+	  mHttpOptionsWithHeaders(NULL),
 	  mHttpHeaders(NULL),
 	  mHttpMetricsHeaders(NULL),
 	  mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
@@ -2406,10 +2407,12 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
 	
 	mHttpRequest = new LLCore::HttpRequest;
 	mHttpOptions = new LLCore::HttpOptions;
+	mHttpOptionsWithHeaders = new LLCore::HttpOptions;
+	mHttpOptionsWithHeaders->setWantHeaders(true);
 	mHttpHeaders = new LLCore::HttpHeaders;
-	mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c");
+	mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C);
 	mHttpMetricsHeaders = new LLCore::HttpHeaders;
-	mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml");
+	mHttpMetricsHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML);
 	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_TEXTURE);
 }
 
@@ -2430,6 +2433,12 @@ LLTextureFetch::~LLTextureFetch()
 		mHttpOptions = NULL;
 	}
 
+	if (mHttpOptionsWithHeaders)
+	{
+		mHttpOptionsWithHeaders->release();
+		mHttpOptionsWithHeaders = NULL;
+	}
+
 	if (mHttpHeaders)
 	{
 		mHttpHeaders->release();
@@ -4041,7 +4050,7 @@ void LLTextureFetchDebugger::init()
 	if (! mHttpHeaders)
 	{
 		mHttpHeaders = new LLCore::HttpHeaders;
-		mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c");
+		mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C);
 	}
 }
 
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index 902a3d7a25..3c79a5a24d 100755
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2000&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -351,6 +351,7 @@ private:
 	// LLCurl interfaces used in the past.
 	LLCore::HttpRequest *				mHttpRequest;					// Ttf
 	LLCore::HttpOptions *				mHttpOptions;					// Ttf
+	LLCore::HttpOptions *				mHttpOptionsWithHeaders;		// Ttf
 	LLCore::HttpHeaders *				mHttpHeaders;					// Ttf
 	LLCore::HttpHeaders *				mHttpMetricsHeaders;			// Ttf
 	LLCore::HttpRequest::policy_t		mHttpPolicyClass;				// T*
-- 
cgit v1.2.3


From e59d59878200d25dc6e94753026d986d2704ab8d Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 17 Apr 2013 18:30:46 -0400
Subject: SH-4090 Integrating deadman timer with mesh repo downloads. Timer
 interface violated my design rules and I paid for it with clumsiness and
 silent errors.  Cleaned it up mainly removing the evil default values.  Found
 better integration points in the mesh downloader and it's producing fairly
 consistent numbers on the MeshTest2 test region (about 5500 downloads, ~90
 seconds, +/- 10 seconds).  Will review with davep and do an early timer stop
 on teleport which invalidates a timing sequence.

---
 indra/llcommon/lldeadmantimer.cpp            |  25 +++--
 indra/llcommon/lldeadmantimer.h              |  59 +++++++----
 indra/llcommon/tests/lldeadmantimer_test.cpp | 143 +++++++++++++++------------
 indra/newview/llmeshrepository.cpp           |  48 ++++++---
 indra/newview/llmeshrepository.h             |   3 +-
 5 files changed, 174 insertions(+), 104 deletions(-)

(limited to 'indra')

diff --git a/indra/llcommon/lldeadmantimer.cpp b/indra/llcommon/lldeadmantimer.cpp
index 3d3f738c06..2a356d857a 100644
--- a/indra/llcommon/lldeadmantimer.cpp
+++ b/indra/llcommon/lldeadmantimer.cpp
@@ -43,7 +43,7 @@
 //    true    true   Not allowed
 //
 LLDeadmanTimer::LLDeadmanTimer(F64 horizon)
-	: mHorizon(U64(llmax(horizon, F64(0.0)) * gClockFrequency)),
+	: mHorizon(time_type(llmax(horizon, F64(0.0)) * gClockFrequency)),
 	  mActive(false),			// If true, a timer is running.
 	  mDone(false),				// If true, timer has completed and can be read (once)
 	  mStarted(U64L(0)),
@@ -53,7 +53,14 @@ LLDeadmanTimer::LLDeadmanTimer(F64 horizon)
 {}
 
 
-void LLDeadmanTimer::start(U64 now)
+// static
+LLDeadmanTimer::time_type LLDeadmanTimer::getNow()
+{
+	return LLTimer::getCurrentClockCount();
+}
+
+
+void LLDeadmanTimer::start(time_type now)
 {
 	// *TODO:  If active, let's complete an existing timer and save
 	// the result to the side.  I think this will be useful later.
@@ -72,7 +79,7 @@ void LLDeadmanTimer::start(U64 now)
 }
 
 
-void LLDeadmanTimer::stop(U64 now)
+void LLDeadmanTimer::stop(time_type now)
 {
 	if (! mActive)
 	{
@@ -81,7 +88,7 @@ void LLDeadmanTimer::stop(U64 now)
 
 	if (! now)
 	{
-		now = LLTimer::getCurrentClockCount();
+		now = getNow();
 	}
 	mStopped = now;
 	mActive = false;
@@ -89,13 +96,13 @@ void LLDeadmanTimer::stop(U64 now)
 }
 
 
-bool LLDeadmanTimer::isExpired(F64 & started, F64 & stopped, U64 & count, U64 now)
+bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count)
 {
 	if (mActive && ! mDone)
 	{
 		if (! now)
 		{
-			now = LLTimer::getCurrentClockCount();
+			now = getNow();
 		}
 
 		if (now >= mExpires)
@@ -120,7 +127,7 @@ bool LLDeadmanTimer::isExpired(F64 & started, F64 & stopped, U64 & count, U64 no
 }
 
 	
-void LLDeadmanTimer::ringBell(U64 now)
+void LLDeadmanTimer::ringBell(time_type now, unsigned int count)
 {
 	if (! mActive)
 	{
@@ -129,7 +136,7 @@ void LLDeadmanTimer::ringBell(U64 now)
 	
 	if (! now)
 	{
-		now = LLTimer::getCurrentClockCount();
+		now = getNow();
 	}
 
 	if (now >= mExpires)
@@ -141,7 +148,7 @@ void LLDeadmanTimer::ringBell(U64 now)
 	{
 		mStopped = now;
 		mExpires = now + mHorizon;
-		++mCount;
+		mCount += count;
 	}
 	
 	return;
diff --git a/indra/llcommon/lldeadmantimer.h b/indra/llcommon/lldeadmantimer.h
index 84023723ab..8643b8cad8 100644
--- a/indra/llcommon/lldeadmantimer.h
+++ b/indra/llcommon/lldeadmantimer.h
@@ -76,6 +76,16 @@
 
 class LL_COMMON_API LLDeadmanTimer
 {
+public:
+	/// Public types
+
+	/// Low-level time type chosen for compatibility with
+	/// LLTimer::getCurrentClockCount() which is the basis
+	/// of time operations in this class.  This is likely
+	/// to change in a future version in a move to TSC-based
+	/// timing.
+	typedef U64 time_type;
+	
 public:
 	/// Construct and initialize an LLDeadmanTimer
 	///
@@ -93,6 +103,18 @@ private:
 	void operator=(const LLDeadmanTimer &);				// Not defined
 
 public:
+	/// Get the current time.  Zero-basis for this time
+	/// representation is not defined and is different on
+	/// different platforms.  Do not attempt to compute
+	/// negative times relative to the first value returned,
+	/// there may not be enough 'front porch' on the range
+	/// to prevent wraparound.
+	///
+	/// Note:  Implementation is expected to change in a
+	/// future release as well.
+	///
+	static time_type getNow();
+
 	/// Begin timing.  If the timer is already active, it is reset
 	///	and timing begins now.
 	///
@@ -100,8 +122,7 @@ public:
 	///					LLTimer::getCurrentClockCount().  If zero,
 	///					method will lookup current time.
 	///
-	void start(U64 now = U64L(0));
-
+	void start(time_type now);
 
 	/// End timing.  Actively declare the end of the event independent
 	/// of the deadman's switch operation.  @see isExpired() will return
@@ -111,28 +132,34 @@ public:
 	///					LLTimer::getCurrentClockCount().  If zero,
 	///					method will lookup current time.
 	///
-	void stop(U64 now = U64L(0));
-
+	void stop(time_type now);
 
 	/// Declare that something interesting happened.  This has two
 	/// effects on an unexpired-timer.  1)  The expiration time
 	/// is extended for 'horizon' seconds after the 'now' value.
-	/// 2)  An internal counter associated with the event is incremented.
-	/// This count is returned via the @see isExpired() method.
+	/// 2)  An internal counter associated with the event is incremented
+	/// by the @ref count parameter.  This count is returned via the
+	/// @see isExpired() method.
 	///
 	/// @param now		Current time as returned by @see
 	///					LLTimer::getCurrentClockCount().  If zero,
 	///					method will lookup current time.
 	///
-	void ringBell(U64 now = U64L(0));
+	/// @param count	Count of events to be associated with
+	///					this bell ringing.
+	///
+	void ringBell(time_type now, unsigned int count);
 	
-
 	/// Checks on the status of the timer Declare that something interesting happened.  This has two
 	/// effects on an unexpired-timer.  1)  The expiration time
 	/// is extended for 'horizon' seconds after the 'now' value.
 	/// 2)  An internal counter associated with the event is incremented.
 	/// This count is returned via the @see isExpired() method.
 	///
+	/// @param now		Current time as returned by @see
+	///					LLTimer::getCurrentClockCount().  If zero,
+	///					method will lookup current time.
+	///
 	/// @param started	If expired, the starting time of the event is
 	///					returned to the caller via this reference.
 	///
@@ -146,25 +173,21 @@ public:
 	/// @param count	If expired, the number of ringBell() calls
 	///					made prior to expiration.
 	///
-	/// @param now		Current time as returned by @see
-	///					LLTimer::getCurrentClockCount().  If zero,
-	///					method will lookup current time.
-	///
 	/// @return			true if the timer has expired, false otherwise.
 	///					If true, it also returns the started,
 	///					stopped and count values otherwise these are
 	///					left unchanged.
 	///
-	bool isExpired(F64 & started, F64 & stopped, U64 & count, U64 now = U64L(0));
+	bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count);
 
 protected:
-	U64			mHorizon;
+	time_type	mHorizon;
 	bool		mActive;
 	bool		mDone;
-	U64			mStarted;
-	U64			mExpires;
-	U64			mStopped;
-	U64			mCount;
+	time_type	mStarted;
+	time_type	mExpires;
+	time_type	mStopped;
+	time_type	mCount;
 };
 	
 
diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp
index 40e354115b..63cab29e04 100644
--- a/indra/llcommon/tests/lldeadmantimer_test.cpp
+++ b/indra/llcommon/tests/lldeadmantimer_test.cpp
@@ -34,12 +34,12 @@
 // Convert between floating point time deltas and U64 time deltas.
 // Reflects an implementation detail inside lldeadmantimer.cpp
 
-static U64 float_time_to_u64(F64 delta)
+static LLDeadmanTimer::time_type float_time_to_u64(F64 delta)
 {
-	return U64(delta * gClockFrequency);
+	return LLDeadmanTimer::time_type(delta * gClockFrequency);
 }
 
-static F64 u64_time_to_float(U64 delta)
+static F64 u64_time_to_float(LLDeadmanTimer::time_type delta)
 {
 	return delta * gClockFrequencyInv;
 }
@@ -69,7 +69,7 @@ void deadmantimer_object_t::test<1>()
 	U64 count(U64L(8));
 	LLDeadmanTimer timer(10.0);
 
-	ensure_equals("isExpired() returns false after ctor()", timer.isExpired(started, stopped, count), false);
+	ensure_equals("isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false);
 	ensure_approximately_equals("t1 - isExpired() does not modify started", started, F64(42.0), 2);
 	ensure_approximately_equals("t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
 	ensure_equals("t1 - isExpired() does not modify count", count, U64L(8));
@@ -84,7 +84,8 @@ void deadmantimer_object_t::test<2>()
 	U64 count(U64L(8));
 	LLDeadmanTimer timer(0.0);			// Zero is pre-expired
 
-	ensure_equals("isExpired() still returns false with 0.0 time ctor()", timer.isExpired(started, stopped, count), false);
+	ensure_equals("isExpired() still returns false with 0.0 time ctor()",
+				  timer.isExpired(0, started, stopped, count), false);
 }
 
 
@@ -97,8 +98,9 @@ void deadmantimer_object_t::test<3>()
 	U64 count(U64L(8));
 	LLDeadmanTimer timer(0.0);
 
-	timer.start();
-	ensure_equals("isExpired() returns true with 0.0 horizon time", timer.isExpired(started, stopped, count), true);
+	timer.start(0);
+	ensure_equals("isExpired() returns true with 0.0 horizon time",
+				  timer.isExpired(0, started, stopped, count), true);
 	ensure_approximately_equals("expired timer with no bell ringing has stopped == started", started, stopped, 8);
 }
 
@@ -111,14 +113,15 @@ void deadmantimer_object_t::test<4>()
 	U64 count(U64L(8));
 	LLDeadmanTimer timer(0.0);
 	
-	timer.start();
-	timer.ringBell(LLTimer::getCurrentClockCount() + float_time_to_u64(1000.0));
-	ensure_equals("isExpired() returns true with 0.0 horizon time after bell ring", timer.isExpired(started, stopped, count), true);
+	timer.start(0);
+	timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
+	ensure_equals("isExpired() returns true with 0.0 horizon time after bell ring",
+				  timer.isExpired(0, started, stopped, count), true);
 	ensure_approximately_equals("ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
 }
 
 
-// start() test - unexpired timer reports unexpired
+// start(0) test - unexpired timer reports unexpired
 template<> template<>
 void deadmantimer_object_t::test<5>()
 {
@@ -126,8 +129,9 @@ void deadmantimer_object_t::test<5>()
 	U64 count(U64L(8));
 	LLDeadmanTimer timer(10.0);
 	
-	timer.start();
-	ensure_equals("isExpired() returns false after starting with 10.0 horizon time", timer.isExpired(started, stopped, count), false);
+	timer.start(0);
+	ensure_equals("isExpired() returns false after starting with 10.0 horizon time",
+				  timer.isExpired(0, started, stopped, count), false);
 	ensure_approximately_equals("t5 - isExpired() does not modify started", started, F64(42.0), 2);
 	ensure_approximately_equals("t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
 	ensure_equals("t5 - isExpired() does not modify count", count, U64L(8));
@@ -146,10 +150,11 @@ void deadmantimer_object_t::test<6>()
 	// the implementation on Windows is zero-based.  We wrap around
 	// the backside resulting in a large U64 number.
 	
-	U64 the_past(LLTimer::getCurrentClockCount());
-	U64 now(the_past + float_time_to_u64(5.0));
+	LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+	LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
 	timer.start(the_past);
-	ensure_equals("isExpired() returns false with 10.0 horizon time starting 5.0 in past", timer.isExpired(started, stopped, count, now), false);
+	ensure_equals("isExpired() returns false with 10.0 horizon time starting 5.0 in past",
+				  timer.isExpired(now, started, stopped, count), false);
 	ensure_approximately_equals("t6 - isExpired() does not modify started", started, F64(42.0), 2);
 	ensure_approximately_equals("t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
 	ensure_equals("t6 - isExpired() does not modify count", count, U64L(8));
@@ -168,10 +173,11 @@ void deadmantimer_object_t::test<7>()
 	// the implementation on Windows is zero-based.  We wrap around
 	// the backside resulting in a large U64 number.
 	
-	U64 the_past(LLTimer::getCurrentClockCount());
-	U64 now(the_past + float_time_to_u64(20.0));
+	LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+	LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
 	timer.start(the_past);
-	ensure_equals("isExpired() returns true with 10.0 horizon time starting 20.0 in past", timer.isExpired(started, stopped, count, now), true);
+	ensure_equals("isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+				  timer.isExpired(now,started, stopped, count), true);
 	ensure_approximately_equals("starting before horizon still gives equal started / stopped", started, stopped, 8);
 }
 
@@ -188,15 +194,17 @@ void deadmantimer_object_t::test<8>()
 	// the implementation on Windows is zero-based.  We wrap around
 	// the backside resulting in a large U64 number.
 	
-	U64 the_past(LLTimer::getCurrentClockCount());
-	U64 now(the_past + float_time_to_u64(20.0));
+	LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+	LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
 	timer.start(the_past);
-	ensure_equals("t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", timer.isExpired(started, stopped, count, now), true);
+	ensure_equals("t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+				  timer.isExpired(now, started, stopped, count), true);
 
 	started = 42.0;
 	stopped = 97.0;
 	count = U64L(8);
-	ensure_equals("t8 - second isExpired() returns false after true", timer.isExpired(started, stopped, count, now), false);
+	ensure_equals("t8 - second isExpired() returns false after true",
+				  timer.isExpired(now, started, stopped, count), false);
 	ensure_approximately_equals("t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
 	ensure_approximately_equals("t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
 	ensure_equals("t8 - 2nd isExpired() does not modify count", count, U64L(8));
@@ -211,40 +219,42 @@ void deadmantimer_object_t::test<9>()
 	U64 count(U64L(8));
 	LLDeadmanTimer timer(5.0);
 
-	U64 now(LLTimer::getCurrentClockCount());
+	LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
 	F64 real_start(u64_time_to_float(now));
-	timer.start();
+	timer.start(0);
 
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
-	ensure_equals("t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings", timer.isExpired(started, stopped, count, now), false);
+	timer.ringBell(now, 1);
+	ensure_equals("t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+				  timer.isExpired(now, started, stopped, count), false);
 	F64 last_good_ring(u64_time_to_float(now));
 
 	// Jump forward and expire
 	now += float_time_to_u64(10.0);
-	ensure_equals("t9 - 5.0 horizon timer expires on 10-second jump", timer.isExpired(started, stopped, count, now), true);
+	ensure_equals("t9 - 5.0 horizon timer expires on 10-second jump",
+				  timer.isExpired(now, started, stopped, count), true);
 	ensure_approximately_equals("t9 - started matches start() time", started, real_start, 4);
 	ensure_approximately_equals("t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
 	ensure_equals("t9 - 10 good ringBell()s", count, U64L(10));
-	ensure_equals("t9 - single read only", timer.isExpired(started, stopped, count, now), false);
+	ensure_equals("t9 - single read only", timer.isExpired(now, started, stopped, count), false);
 }
 
 
@@ -256,40 +266,42 @@ void deadmantimer_object_t::test<10>()
 	U64 count(U64L(8));
 	LLDeadmanTimer timer(5.0);
 
-	U64 now(LLTimer::getCurrentClockCount());
+	LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
 	F64 real_start(u64_time_to_float(now));
-	timer.start();
+	timer.start(0);
 
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
-	ensure_equals("t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings", timer.isExpired(started, stopped, count, now), false);
+	timer.ringBell(now, 1);
+	ensure_equals("t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+				  timer.isExpired(now, started, stopped, count), false);
 	F64 last_good_ring(u64_time_to_float(now));
 
 	// Jump forward and expire
 	now += float_time_to_u64(10.0);
-	ensure_equals("t10 - 5.0 horizon timer expires on 10-second jump", timer.isExpired(started, stopped, count, now), true);
+	ensure_equals("t10 - 5.0 horizon timer expires on 10-second jump",
+				  timer.isExpired(now, started, stopped, count), true);
 	ensure_approximately_equals("t10 - started matches start() time", started, real_start, 4);
 	ensure_approximately_equals("t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
 	ensure_equals("t10 - 10 good ringBell()s", count, U64L(10));
-	ensure_equals("t10 - single read only", timer.isExpired(started, stopped, count, now), false);
+	ensure_equals("t10 - single read only", timer.isExpired(now, started, stopped, count), false);
 
 	// Jump forward and restart
 	now += float_time_to_u64(1.0);
@@ -298,31 +310,34 @@ void deadmantimer_object_t::test<10>()
 
 	// Run a modified bell ring sequence
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
+	timer.ringBell(now, 1);
 	now += float_time_to_u64(1.0);
-	timer.ringBell(now);
-	ensure_equals("t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings", timer.isExpired(started, stopped, count, now), false);
+	timer.ringBell(now, 1);
+	ensure_equals("t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
+				  timer.isExpired(now, started, stopped, count), false);
 	last_good_ring = u64_time_to_float(now);
 
 	// Jump forward and expire
 	now += float_time_to_u64(10.0);
-	ensure_equals("t10 - 5.0 horizon timer expires on 8-second jump", timer.isExpired(started, stopped, count, now), true);
+	ensure_equals("t10 - 5.0 horizon timer expires on 8-second jump",
+				  timer.isExpired(now, started, stopped, count), true);
 	ensure_approximately_equals("t10 - 2nd started matches start() time", started, real_start, 4);
 	ensure_approximately_equals("t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
 	ensure_equals("t10 - 8 good ringBell()s", count, U64L(8));
-	ensure_equals("t10 - single read only - 2nd start", timer.isExpired(started, stopped, count, now), false);
+	ensure_equals("t10 - single read only - 2nd start",
+				  timer.isExpired(now, started, stopped, count), false);
 }
 
 
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 11c5780a30..8f97f3e71a 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2168,6 +2168,9 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
 
 	if (status < 200 || status > 400)
 	{
+		// Manage time-to-load metrics for mesh download operations.
+		LLMeshRepository::metricsProgress(0);
+
 		//llwarns
 		//	<< "Header responder failed with status: "
 		//	<< status << ": " << reason << llendl;
@@ -2358,6 +2361,9 @@ void LLMeshRepository::shutdown()
 //called in the main thread.
 S32 LLMeshRepository::update()
 {
+	// Conditionally log a mesh metrics event
+	metricsUpdate();
+	
 	if(mUploadWaitList.empty())
 	{
 		return 0 ;
@@ -2377,6 +2383,9 @@ S32 LLMeshRepository::update()
 
 S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)
 {
+	// Manage time-to-load metrics for mesh download operations.
+	metricsProgress(1);
+
 	if (detail < 0 || detail > 4)
 	{
 		return detail;
@@ -2680,7 +2689,7 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom
 void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume)
 { //called from main thread
 	// Manage time-to-load metrics for mesh download operations.
-	metricsCheck();
+	metricsProgress(0);
 	
 	S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
 
@@ -2725,6 +2734,9 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
 
 void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod)
 { //called from main thread
+	// Manage time-to-load metrics for mesh download operations.
+	metricsProgress(0);
+	
 	//get list of objects waiting to be notified this mesh is loaded
 	mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params);
 
@@ -3704,39 +3716,51 @@ bool LLMeshRepository::meshRezEnabled()
 	return false;
 }
 
+// Threading:  main thread only
+// static
 void LLMeshRepository::metricsStart()
 {
-	sQuiescentTimer.start();
+	sQuiescentTimer.start(0);
 }
 
+// Threading:  main thread only
+// static
 void LLMeshRepository::metricsStop()
 {
-	sQuiescentTimer.stop();
+	sQuiescentTimer.stop(0);
 }
 
-void LLMeshRepository::metricsCheck()
+// Threading:  main thread only
+// static
+void LLMeshRepository::metricsProgress(unsigned int this_count)
 {
 	static bool first_start(true);
-	F64 started, stopped;
-	U64 count;
-
 	if (first_start)
 	{
 		// Let the first request start the timing cycle for login.
 		metricsStart();
 		first_start = false;
 	}
-	sQuiescentTimer.ringBell();
-	if (sQuiescentTimer.isExpired(started, stopped, count))
+	sQuiescentTimer.ringBell(0, this_count);
+}
+
+// Threading:  main thread only
+// static
+void LLMeshRepository::metricsUpdate()
+{
+	F64 started, stopped;
+	U64 total_count;
+
+	if (sQuiescentTimer.isExpired(0, started, stopped, total_count))
 	{
 		LLSD metrics;
 
-		metrics["reason"] = "Mesh download quiescent";
+		metrics["reason"] = "Mesh Download Quiescent";
 		metrics["scope"] = "Login";
 		metrics["start"] = started;
 		metrics["stop"] = stopped;
-		metrics["downloads"] = LLSD::Integer(count);
-		llinfos << "MetricsMarker" << metrics << llendl;
+		metrics["downloads"] = LLSD::Integer(total_count);
+		llinfos << "EventMarker " << metrics << llendl;
 	}
 }
 	
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index f08feedf81..3cdc66e1f0 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -500,7 +500,8 @@ public:
 	// Quiescent timer management, main thread only.
 	static void metricsStart();
 	static void metricsStop();
-	static void metricsCheck();
+	static void metricsProgress(unsigned int count);
+	static void metricsUpdate();
 	
 	typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map;
 	mesh_load_map mLoadingMeshes[4];
-- 
cgit v1.2.3


From 7911f065cde252d3f1eda4a1577e5d0b3eb94e19 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 18 Apr 2013 16:23:15 -0400
Subject: SH-4090 Metrics for mesh load time - cleanup after davep review One
 of the metrics calls was running in an LLCurl-owned thread doing responder
 invocation.  Deleted that invocation and will do with the other safe ones. 
 Added a boost signal on the TeleportStarted message which is now used to
 restart the metrics timer.  I think I'd like to move the metric blob into a
 free- standing entity later...

---
 indra/newview/llmeshrepository.cpp | 47 ++++++++++++++++++++++++++++++++------
 1 file changed, 40 insertions(+), 7 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 8f97f3e71a..0f33128057 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2010-2013, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -52,6 +52,7 @@
 #include "llviewercontrol.h"
 #include "llviewerinventory.h"
 #include "llviewermenufile.h"
+#include "llviewermessage.h"
 #include "llviewerobjectlist.h"
 #include "llviewerregion.h"
 #include "llviewertexturelist.h"
@@ -109,7 +110,7 @@ std::string make_dump_name(std::string prefix, S32 num)
 void dump_llsd_to_file(const LLSD& content, std::string filename);
 LLSD llsd_from_file(std::string filename);
 
-std::string header_lod[] = 
+const std::string header_lod[] = 
 {
 	"lowest_lod",
 	"low_lod",
@@ -117,6 +118,13 @@ std::string header_lod[] =
 	"high_lod"
 };
 
+// Static data and functions to measure mesh load
+// time metrics for a new region scene.
+static bool metrics_inited(false);
+static boost::signals2::connection metrics_teleport_connection;
+static unsigned int metrics_teleport_start_count(0);
+static void metrics_teleport_started();
+
 //get the number of bytes resident in memory for given volume
 U32 get_volume_memory_size(const LLVolume* volume)
 {
@@ -2168,9 +2176,6 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason,
 
 	if (status < 200 || status > 400)
 	{
-		// Manage time-to-load metrics for mesh download operations.
-		LLMeshRepository::metricsProgress(0);
-
 		//llwarns
 		//	<< "Header responder failed with status: "
 		//	<< status << ": " << reason << llendl;
@@ -2310,12 +2315,26 @@ void LLMeshRepository::init()
 	
 	mThread = new LLMeshRepoThread();
 	mThread->start();
+
+	if (! metrics_inited)
+	{
+		// Get teleport started signals to restart timings.
+		metrics_teleport_connection = LLViewerMessage::getInstance()->
+			setTeleportStartedCallback(metrics_teleport_started);
+		metrics_inited = true;
+	}
 }
 
 void LLMeshRepository::shutdown()
 {
 	llinfos << "Shutting down mesh repository." << llendl;
 
+	if (metrics_inited)
+	{
+		metrics_teleport_connection.disconnect();
+		metrics_inited = false;
+	}
+
 	for (U32 i = 0; i < mUploads.size(); ++i)
 	{
 		llinfos << "Discard the pending mesh uploads " << llendl;
@@ -3735,9 +3754,10 @@ void LLMeshRepository::metricsStop()
 void LLMeshRepository::metricsProgress(unsigned int this_count)
 {
 	static bool first_start(true);
+
 	if (first_start)
 	{
-		// Let the first request start the timing cycle for login.
+		++metrics_teleport_start_count;
 		metricsStart();
 		first_start = false;
 	}
@@ -3760,7 +3780,20 @@ void LLMeshRepository::metricsUpdate()
 		metrics["start"] = started;
 		metrics["stop"] = stopped;
 		metrics["downloads"] = LLSD::Integer(total_count);
+		metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count);
 		llinfos << "EventMarker " << metrics << llendl;
 	}
 }
-	
+
+// Will use a request to start a teleport as a signal to
+// restart a timing sequence.  We don't get one of these
+// for login so initial start is done above.
+//
+// Threading:  main thread only
+// static
+void metrics_teleport_started()
+{
+	LLMeshRepository::metricsStart();
+	++metrics_teleport_start_count;
+}
+
-- 
cgit v1.2.3


From 7992564e06ed4977c0f4d13fee4f858f6275542a Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 1 May 2013 23:19:34 +0000
Subject: SH-4153  Port user and system cpu accounting from example program.
 The http_texture_load example program has some cpu usage gathering tools that
 should be generally useful and specifically for the deadman switch.  Port
 these into llcommon into new all-static class LLProcInfo.  Add unit test,
 etc.

---
 indra/llcommon/CMakeLists.txt            |  3 +
 indra/llcommon/llprocinfo.cpp            | 94 ++++++++++++++++++++++++++++++++
 indra/llcommon/llprocinfo.h              | 68 +++++++++++++++++++++++
 indra/llcommon/tests/llprocinfo_test.cpp | 91 +++++++++++++++++++++++++++++++
 4 files changed, 256 insertions(+)
 create mode 100644 indra/llcommon/llprocinfo.cpp
 create mode 100644 indra/llcommon/llprocinfo.h
 create mode 100644 indra/llcommon/tests/llprocinfo_test.cpp

(limited to 'indra')

diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 7ed4137065..86f18a20d6 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -79,6 +79,7 @@ set(llcommon_SOURCE_FILES
     llptrto.cpp 
     llprocess.cpp
     llprocessor.cpp
+    llprocinfo.cpp
     llqueuedthread.cpp
     llrand.cpp
     llrefcount.cpp
@@ -207,6 +208,7 @@ set(llcommon_HEADER_FILES
     llpriqueuemap.h
     llprocess.h
     llprocessor.h
+    llprocinfo.h
     llptrskiplist.h
     llptrskipmap.h
     llptrto.h
@@ -331,6 +333,7 @@ if (LL_TESTS)
   LL_ADD_INTEGRATION_TEST(llinstancetracker "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lllazy "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")
+  LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")                          
diff --git a/indra/llcommon/llprocinfo.cpp b/indra/llcommon/llprocinfo.cpp
new file mode 100644
index 0000000000..c00f979b0b
--- /dev/null
+++ b/indra/llcommon/llprocinfo.cpp
@@ -0,0 +1,94 @@
+/** 
+* @file llprocinfo.cpp
+* @brief Process, cpu and resource usage information APIs.
+* @author monty@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+
+#include "llprocinfo.h"
+
+#if LL_WINDOWS
+
+#define	PSAPI_VERSION	1
+#include "windows.h"
+#include "psapi.h"
+
+#elif LL_DARWIN
+
+#include <sys/resource.h>
+#include <mach/mach.h>
+	
+#else
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#endif // LL_WINDOWS/LL_DARWIN
+
+
+// static
+void LLProcInfo::getCPUUsage(time_type & user_time, time_type & system_time)
+{
+#if LL_WINDOWS
+
+	HANDLE self(GetCurrentProcess());			// Does not have to be closed
+	FILETIME ft_dummy, ft_system, ft_user;
+
+	GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user);
+	ULARGE_INTEGER uli;
+	uli.u.LowPart = ft_system.dwLowDateTime;
+	uli.u.HighPart = ft_system.dwHighDateTime;
+	system_time = uli.QuadPart / U64L(10);		// Convert to uS
+	uli.u.LowPart = ft_user.dwLowDateTime;
+	uli.u.HighPart = ft_user.dwHighDateTime;
+	user_time = uli.QuadPart / U64L(10);
+	
+#elif LL_DARWIN
+
+	struct rusage usage;
+
+	if (getrusage(RUSAGE_SELF, &usage))
+	{
+		user_time = system_time = time_type(0U);
+		return;
+	}
+	user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec;
+	system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec;
+
+#else // Linux
+
+	struct rusage usage;
+
+	if (getrusage(RUSAGE_SELF, &usage))
+	{
+		user_time = system_time = time_type(0U);
+		return;
+	}
+	user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec;
+	system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec;
+	
+#endif // LL_WINDOWS/LL_DARWIN/Linux
+}
+
+
diff --git a/indra/llcommon/llprocinfo.h b/indra/llcommon/llprocinfo.h
new file mode 100644
index 0000000000..e78bcf490a
--- /dev/null
+++ b/indra/llcommon/llprocinfo.h
@@ -0,0 +1,68 @@
+/** 
+* @file   llprocinfo.h
+* @brief  Interface to process/cpu/resource information services.
+* @author monty@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+* $/LicenseInfo$
+*/
+
+#ifndef	LL_PROCINFO_H
+#define	LL_PROCINFO_H
+
+
+#include "linden_common.h"
+
+/// @file llprocinfo.h
+///
+/// Right now, this is really a namespace disguised as a class.
+/// It wraps some types and functions to return information about
+/// process resource consumption in a non-OS-specific manner.
+///
+/// Threading:  No instances so that's thread-safe.  Implementations
+/// of static functions should be thread-safe, they mostly involve
+/// direct syscall invocations.
+///
+/// Allocation:  Not instantiatable.
+
+class LL_COMMON_API LLProcInfo
+{
+public:
+	/// Public types
+
+	typedef U64 time_type;								/// Relative microseconds
+	
+private:
+	LLProcInfo();										// Not defined
+	~LLProcInfo();										// Not defined
+	LLProcInfo(const LLProcInfo &);						// Not defined
+	void operator=(const LLProcInfo &);					// Not defined
+
+public:
+	/// Get accumulated system and user CPU time in
+	/// microseconds.  Syscalls involved in every invocation.
+	///
+	/// Threading:  expected to be safe.
+	static void getCPUUsage(time_type & user_time, time_type & system_time);
+};
+	
+
+#endif	// LL_PROCINFO_H
diff --git a/indra/llcommon/tests/llprocinfo_test.cpp b/indra/llcommon/tests/llprocinfo_test.cpp
new file mode 100644
index 0000000000..0a4210dc07
--- /dev/null
+++ b/indra/llcommon/tests/llprocinfo_test.cpp
@@ -0,0 +1,91 @@
+/** 
+ * @file llprocinfo_test.cpp
+ * @brief Tests for the LLProcInfo class.
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "../llprocinfo.h"
+
+#include "../test/lltut.h"
+#include "../lltimer.h"
+
+
+static const LLProcInfo::time_type bad_user(289375U), bad_system(275U);
+
+
+namespace tut
+{
+
+struct procinfo_test
+{
+	procinfo_test()
+		{
+		}
+};
+
+typedef test_group<procinfo_test> procinfo_group_t;
+typedef procinfo_group_t::object procinfo_object_t;
+tut::procinfo_group_t procinfo_instance("LLProcInfo");
+
+
+// Basic invocation works
+template<> template<>
+void procinfo_object_t::test<1>()
+{
+	LLProcInfo::time_type user(bad_user), system(bad_system);
+
+	set_test_name("getCPUUsage() basic function");
+
+	LLProcInfo::getCPUUsage(user, system);
+	
+	ensure_not_equals("getCPUUsage() writes to its user argument", user, bad_user);
+	ensure_not_equals("getCPUUsage() writes to its system argument", system, bad_system);
+}
+
+
+// Time increases
+template<> template<>
+void procinfo_object_t::test<2>()
+{
+	LLProcInfo::time_type user(bad_user), system(bad_system);
+	LLProcInfo::time_type user2(bad_user), system2(bad_system);
+
+	set_test_name("getCPUUsage() increases over time");
+
+	LLProcInfo::getCPUUsage(user, system);
+	
+	for (int i(0); i < 100000; ++i)
+	{
+		ms_sleep(0);
+	}
+	
+	LLProcInfo::getCPUUsage(user2, system2);
+
+	ensure_equals("getCPUUsage() user value increases over time", user2 > user, true);
+	ensure_equals("getCPUUsage() system value increases over time", system2 > system, true);
+}
+
+
+} // end namespace tut
-- 
cgit v1.2.3


From b833f574bc20d4371b78f9c6b02bafe460a2e359 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 2 May 2013 15:43:58 +0000
Subject: SH-4153  Port user and system cpu accounting from example program.
 Windows resolution isn't good enough for a strictly increasing time test in
 the unit tests.

---
 indra/llcommon/tests/llprocinfo_test.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'indra')

diff --git a/indra/llcommon/tests/llprocinfo_test.cpp b/indra/llcommon/tests/llprocinfo_test.cpp
index 0a4210dc07..12d5a695ee 100644
--- a/indra/llcommon/tests/llprocinfo_test.cpp
+++ b/indra/llcommon/tests/llprocinfo_test.cpp
@@ -83,8 +83,8 @@ void procinfo_object_t::test<2>()
 	
 	LLProcInfo::getCPUUsage(user2, system2);
 
-	ensure_equals("getCPUUsage() user value increases over time", user2 > user, true);
-	ensure_equals("getCPUUsage() system value increases over time", system2 > system, true);
+	ensure_equals("getCPUUsage() user value doesn't decrease over time", user2 >= user, true);
+	ensure_equals("getCPUUsage() system value doesn't decrease over time", system2 >= system, true);
 }
 
 
-- 
cgit v1.2.3


From 11cca95187f01f594172ca950315dcd8d99dc2c3 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 2 May 2013 21:59:53 +0000
Subject: SH-4161 Integrate cpu metrics into LLDeadmanTimer and then metrics
 viewer Integrated as a ctor-time option to LLDeadmanTimer and have mesh use
 this mode for the stats I'm gathering.

---
 indra/llcommon/lldeadmantimer.cpp            |  36 +-
 indra/llcommon/lldeadmantimer.h              |  38 +-
 indra/llcommon/tests/lldeadmantimer_test.cpp | 701 +++++++++++++++++++--------
 indra/newview/llmeshrepository.cpp           |   2 +-
 4 files changed, 562 insertions(+), 215 deletions(-)

(limited to 'indra')

diff --git a/indra/llcommon/lldeadmantimer.cpp b/indra/llcommon/lldeadmantimer.cpp
index 2a356d857a..2ba757d2af 100644
--- a/indra/llcommon/lldeadmantimer.cpp
+++ b/indra/llcommon/lldeadmantimer.cpp
@@ -42,14 +42,19 @@
 //   false    true   Timer finished, result can be read once
 //    true    true   Not allowed
 //
-LLDeadmanTimer::LLDeadmanTimer(F64 horizon)
+LLDeadmanTimer::LLDeadmanTimer(F64 horizon, bool inc_cpu)
 	: mHorizon(time_type(llmax(horizon, F64(0.0)) * gClockFrequency)),
 	  mActive(false),			// If true, a timer is running.
 	  mDone(false),				// If true, timer has completed and can be read (once)
 	  mStarted(U64L(0)),
 	  mExpires(U64L(0)),
 	  mStopped(U64L(0)),
-	  mCount(U64L(0))
+	  mCount(U64L(0)),
+	  mIncCPU(inc_cpu),
+	  mUStartCPU(LLProcInfo::time_type(U64L(0))),
+	  mUEndCPU(LLProcInfo::time_type(U64L(0))),
+	  mSStartCPU(LLProcInfo::time_type(U64L(0))),
+	  mSEndCPU(LLProcInfo::time_type(U64L(0)))
 {}
 
 
@@ -76,6 +81,10 @@ void LLDeadmanTimer::start(time_type now)
 	mExpires = now + mHorizon;
 	mStopped = now;
 	mCount = U64L(0);
+	if (mIncCPU)
+	{
+		LLProcInfo::getCPUUsage(mUStartCPU, mSStartCPU);
+	}
 }
 
 
@@ -93,9 +102,26 @@ void LLDeadmanTimer::stop(time_type now)
 	mStopped = now;
 	mActive = false;
 	mDone = true;
+	if (mIncCPU)
+	{
+		LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU);
+	}
 }
 
 
+bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count,
+							   LLProcInfo::time_type & user_cpu, LLProcInfo::time_type & sys_cpu)
+{
+	const bool status(isExpired(now, started, stopped, count));
+	if (status)
+	{
+		user_cpu = mUEndCPU - mUStartCPU;
+		sys_cpu = mSEndCPU - mSStartCPU;
+	}
+	return status;
+}
+
+		
 bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count)
 {
 	if (mActive && ! mDone)
@@ -141,14 +167,20 @@ void LLDeadmanTimer::ringBell(time_type now, unsigned int count)
 
 	if (now >= mExpires)
 	{
+		// Timer has expired, this event will be dropped
 		mActive = false;
 		mDone = true;
 	}
 	else
 	{
+		// Timer renewed, keep going
 		mStopped = now;
 		mExpires = now + mHorizon;
 		mCount += count;
+		if (mIncCPU)
+		{
+			LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU);
+		}
 	}
 	
 	return;
diff --git a/indra/llcommon/lldeadmantimer.h b/indra/llcommon/lldeadmantimer.h
index 8643b8cad8..a6022852b9 100644
--- a/indra/llcommon/lldeadmantimer.h
+++ b/indra/llcommon/lldeadmantimer.h
@@ -32,6 +32,7 @@
 #include "linden_common.h"
 
 #include "lltimer.h"
+#include "llprocinfo.h"
 
 
 /// @file lldeadmantimer.h
@@ -93,7 +94,11 @@ public:
 	///                 call at which point the timer will consider itself
 	///					expired.
 	///
-	LLDeadmanTimer(F64 horizon);
+	/// @param inc_cpu	If true, gather system and user cpu stats while
+	///					running the timer.  This does require more syscalls
+	///					during updates.  If false, cpu usage data isn't
+	///					collected and will be zero if queried.
+	LLDeadmanTimer(F64 horizon, bool inc_cpu);
 
 	~LLDeadmanTimer() 
 		{}
@@ -173,21 +178,38 @@ public:
 	/// @param count	If expired, the number of ringBell() calls
 	///					made prior to expiration.
 	///
+	/// @param user_cpu	Amount of CPU spent in user mode by the process
+	///					during the event.  Value in microseconds and will
+	///					read zero if not enabled by the constructor.
+	///
+	/// @param sys_cpu	Amount of CPU spent in system mode by the process.
+	///
 	/// @return			true if the timer has expired, false otherwise.
 	///					If true, it also returns the started,
 	///					stopped and count values otherwise these are
 	///					left unchanged.
 	///
+	bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count,
+				   LLProcInfo::time_type & user_cpu, LLProcInfo::time_type & sys_cpu);
+
+	/// Identical to the six-arugment form except is does without the
+	/// CPU time return if the caller isn't interested in it.
 	bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count);
 
 protected:
-	time_type	mHorizon;
-	bool		mActive;
-	bool		mDone;
-	time_type	mStarted;
-	time_type	mExpires;
-	time_type	mStopped;
-	time_type	mCount;
+	time_type					mHorizon;
+	bool						mActive;
+	bool						mDone;
+	time_type					mStarted;
+	time_type					mExpires;
+	time_type					mStopped;
+	time_type					mCount;
+
+	const bool					mIncCPU;		// Include CPU metrics in timer
+	LLProcInfo::time_type		mUStartCPU;
+	LLProcInfo::time_type		mUEndCPU;
+	LLProcInfo::time_type		mSStartCPU;
+	LLProcInfo::time_type		mSEndCPU;
 };
 	
 
diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp
index 63cab29e04..a4ec76a016 100644
--- a/indra/llcommon/tests/lldeadmantimer_test.cpp
+++ b/indra/llcommon/tests/lldeadmantimer_test.cpp
@@ -28,6 +28,7 @@
 
 #include "../lldeadmantimer.h"
 #include "../llsd.h"
+#include "../lltimer.h"
 
 #include "../test/lltut.h"
 
@@ -65,14 +66,32 @@ tut::deadmantimer_group_t deadmantimer_instance("LLDeadmanTimer");
 template<> template<>
 void deadmantimer_object_t::test<1>()
 {
-	F64 started(42.0), stopped(97.0);
-	U64 count(U64L(8));
-	LLDeadmanTimer timer(10.0);
-
-	ensure_equals("isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false);
-	ensure_approximately_equals("t1 - isExpired() does not modify started", started, F64(42.0), 2);
-	ensure_approximately_equals("t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
-	ensure_equals("t1 - isExpired() does not modify count", count, U64L(8));
+	{
+		// Without cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(10.0, false);
+
+		ensure_equals("WOCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false);
+		ensure_approximately_equals("WOCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
+		ensure_approximately_equals("WOCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+		ensure_equals("WOCM t1 - isExpired() does not modify count", count, U64L(8));
+	}
+
+	{
+		// With cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		LLDeadmanTimer timer(10.0, true);
+
+		ensure_equals("WCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
+		ensure_approximately_equals("WCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
+		ensure_approximately_equals("WCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+		ensure_equals("WCM t1 - isExpired() does not modify count", count, U64L(8));
+		ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, LLProcInfo::time_type(29000));
+		ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, LLProcInfo::time_type(57000));
+	}
 }
 
 
@@ -80,12 +99,26 @@ void deadmantimer_object_t::test<1>()
 template<> template<>
 void deadmantimer_object_t::test<2>()
 {
-	F64 started(42.0), stopped(97.0);
-	U64 count(U64L(8));
-	LLDeadmanTimer timer(0.0);			// Zero is pre-expired
-
-	ensure_equals("isExpired() still returns false with 0.0 time ctor()",
-				  timer.isExpired(0, started, stopped, count), false);
+	{
+		// Without cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(0.0, false);			// Zero is pre-expired
+		
+		ensure_equals("WOCM isExpired() still returns false with 0.0 time ctor()",
+					  timer.isExpired(0, started, stopped, count), false);
+	}
+
+	{
+		// With cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		LLDeadmanTimer timer(0.0, true);			// Zero is pre-expired
+		
+		ensure_equals("WCM isExpired() still returns false with 0.0 time ctor()",
+					  timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
+	}
 }
 
 
@@ -94,14 +127,29 @@ void deadmantimer_object_t::test<2>()
 template<> template<>
 void deadmantimer_object_t::test<3>()
 {
-	F64 started(42.0), stopped(97.0);
-	U64 count(U64L(8));
-	LLDeadmanTimer timer(0.0);
-
-	timer.start(0);
-	ensure_equals("isExpired() returns true with 0.0 horizon time",
-				  timer.isExpired(0, started, stopped, count), true);
-	ensure_approximately_equals("expired timer with no bell ringing has stopped == started", started, stopped, 8);
+	{
+		// Without cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(0.0, false);
+
+		timer.start(0);
+		ensure_equals("WOCM isExpired() returns true with 0.0 horizon time",
+					  timer.isExpired(0, started, stopped, count), true);
+ 		ensure_approximately_equals("WOCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
+	}
+	{
+		// With cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		LLDeadmanTimer timer(0.0, true);
+
+		timer.start(0);
+		ensure_equals("WCM isExpired() returns true with 0.0 horizon time",
+					  timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
+		ensure_approximately_equals("WCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
+	}
 }
 
 
@@ -109,15 +157,31 @@ void deadmantimer_object_t::test<3>()
 template<> template<>
 void deadmantimer_object_t::test<4>()
 {
-	F64 started(42.0), stopped(97.0);
-	U64 count(U64L(8));
-	LLDeadmanTimer timer(0.0);
+	{
+		// Without cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(0.0, false);
+	
+		timer.start(0);
+		timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
+		ensure_equals("WOCM isExpired() returns true with 0.0 horizon time after bell ring",
+					  timer.isExpired(0, started, stopped, count), true);
+		ensure_approximately_equals("WOCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
+	}
+	{
+		// With cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		LLDeadmanTimer timer(0.0, true);
 	
-	timer.start(0);
-	timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
-	ensure_equals("isExpired() returns true with 0.0 horizon time after bell ring",
-				  timer.isExpired(0, started, stopped, count), true);
-	ensure_approximately_equals("ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
+		timer.start(0);
+		timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
+		ensure_equals("WCM isExpired() returns true with 0.0 horizon time after bell ring",
+					  timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
+		ensure_approximately_equals("WCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
+	}
 }
 
 
@@ -125,16 +189,35 @@ void deadmantimer_object_t::test<4>()
 template<> template<>
 void deadmantimer_object_t::test<5>()
 {
-	F64 started(42.0), stopped(97.0);
-	U64 count(U64L(8));
-	LLDeadmanTimer timer(10.0);
+	{
+		// Without cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(10.0, false);
+	
+		timer.start(0);
+		ensure_equals("WOCM isExpired() returns false after starting with 10.0 horizon time",
+					  timer.isExpired(0, started, stopped, count), false);
+		ensure_approximately_equals("WOCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
+		ensure_approximately_equals("WOCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+		ensure_equals("WOCM t5 - isExpired() does not modify count", count, U64L(8));
+	}
+	{
+		// With cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		LLDeadmanTimer timer(10.0, true);
 	
-	timer.start(0);
-	ensure_equals("isExpired() returns false after starting with 10.0 horizon time",
-				  timer.isExpired(0, started, stopped, count), false);
-	ensure_approximately_equals("t5 - isExpired() does not modify started", started, F64(42.0), 2);
-	ensure_approximately_equals("t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
-	ensure_equals("t5 - isExpired() does not modify count", count, U64L(8));
+		timer.start(0);
+		ensure_equals("WCM isExpired() returns false after starting with 10.0 horizon time",
+					  timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
+		ensure_approximately_equals("WCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
+		ensure_approximately_equals("WCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+		ensure_equals("WCM t5 - isExpired() does not modify count", count, U64L(8));
+		ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, LLProcInfo::time_type(29000));
+		ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, LLProcInfo::time_type(57000));
+	}
 }
 
 
@@ -142,22 +225,47 @@ void deadmantimer_object_t::test<5>()
 template<> template<>
 void deadmantimer_object_t::test<6>()
 {
-	F64 started(42.0), stopped(97.0);
-	U64 count(U64L(8));
-	LLDeadmanTimer timer(10.0);
-
-	// Would like to do subtraction on current time but can't because
-	// the implementation on Windows is zero-based.  We wrap around
-	// the backside resulting in a large U64 number.
+	{
+		// Without cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(10.0, false);
+
+		// Would like to do subtraction on current time but can't because
+		// the implementation on Windows is zero-based.  We wrap around
+		// the backside resulting in a large U64 number.
+	
+		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
+		timer.start(the_past);
+		ensure_equals("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
+					  timer.isExpired(now, started, stopped, count), false);
+		ensure_approximately_equals("WOCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
+		ensure_approximately_equals("WOCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+		ensure_equals("WOCM t6 - isExpired() does not modify count", count, U64L(8));
+	}
+	{
+		// With cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		LLDeadmanTimer timer(10.0, true);
+
+		// Would like to do subtraction on current time but can't because
+		// the implementation on Windows is zero-based.  We wrap around
+		// the backside resulting in a large U64 number.
 	
-	LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
-	LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
-	timer.start(the_past);
-	ensure_equals("isExpired() returns false with 10.0 horizon time starting 5.0 in past",
-				  timer.isExpired(now, started, stopped, count), false);
-	ensure_approximately_equals("t6 - isExpired() does not modify started", started, F64(42.0), 2);
-	ensure_approximately_equals("t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
-	ensure_equals("t6 - isExpired() does not modify count", count, U64L(8));
+		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
+		timer.start(the_past);
+		ensure_equals("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
+					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+		ensure_approximately_equals("WCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
+		ensure_approximately_equals("WCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+		ensure_equals("t6 - isExpired() does not modify count", count, U64L(8));
+		ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, LLProcInfo::time_type(29000));
+		ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, LLProcInfo::time_type(57000));
+	}
 }
 
 
@@ -165,20 +273,41 @@ void deadmantimer_object_t::test<6>()
 template<> template<>
 void deadmantimer_object_t::test<7>()
 {
-	F64 started(42.0), stopped(97.0);
-	U64 count(U64L(8));
-	LLDeadmanTimer timer(10.0);
-
-	// Would like to do subtraction on current time but can't because
-	// the implementation on Windows is zero-based.  We wrap around
-	// the backside resulting in a large U64 number.
+	{
+		// Without cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(10.0, false);
+		
+		// Would like to do subtraction on current time but can't because
+		// the implementation on Windows is zero-based.  We wrap around
+		// the backside resulting in a large U64 number.
+	
+		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+		timer.start(the_past);
+		ensure_equals("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+					  timer.isExpired(now,started, stopped, count), true);
+		ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
+	}
+	{
+		// With cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(10.0, true);
+		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		
+		// Would like to do subtraction on current time but can't because
+		// the implementation on Windows is zero-based.  We wrap around
+		// the backside resulting in a large U64 number.
 	
-	LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
-	LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
-	timer.start(the_past);
-	ensure_equals("isExpired() returns true with 10.0 horizon time starting 20.0 in past",
-				  timer.isExpired(now,started, stopped, count), true);
-	ensure_approximately_equals("starting before horizon still gives equal started / stopped", started, stopped, 8);
+		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+		timer.start(the_past);
+		ensure_equals("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+					  timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu), true);
+		ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
+	}
 }
 
 
@@ -186,28 +315,61 @@ void deadmantimer_object_t::test<7>()
 template<> template<>
 void deadmantimer_object_t::test<8>()
 {
-	F64 started(42.0), stopped(97.0);
-	U64 count(U64L(8));
-	LLDeadmanTimer timer(10.0);
-
-	// Would like to do subtraction on current time but can't because
-	// the implementation on Windows is zero-based.  We wrap around
-	// the backside resulting in a large U64 number.
+	{
+		// Without cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(10.0, false);
+
+		// Would like to do subtraction on current time but can't because
+		// the implementation on Windows is zero-based.  We wrap around
+		// the backside resulting in a large U64 number.
 	
-	LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
-	LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
-	timer.start(the_past);
-	ensure_equals("t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
-				  timer.isExpired(now, started, stopped, count), true);
-
-	started = 42.0;
-	stopped = 97.0;
-	count = U64L(8);
-	ensure_equals("t8 - second isExpired() returns false after true",
-				  timer.isExpired(now, started, stopped, count), false);
-	ensure_approximately_equals("t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
-	ensure_approximately_equals("t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
-	ensure_equals("t8 - 2nd isExpired() does not modify count", count, U64L(8));
+		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+		timer.start(the_past);
+		ensure_equals("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+					  timer.isExpired(now, started, stopped, count), true);
+		
+		started = 42.0;
+		stopped = 97.0;
+		count = U64L(8);
+		ensure_equals("WOCM t8 - second isExpired() returns false after true",
+					  timer.isExpired(now, started, stopped, count), false);
+		ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
+		ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
+		ensure_equals("WOCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
+	}
+	{
+		// With cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(10.0, true);
+		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+
+		// Would like to do subtraction on current time but can't because
+		// the implementation on Windows is zero-based.  We wrap around
+		// the backside resulting in a large U64 number.
+	
+		LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+		LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+		timer.start(the_past);
+		ensure_equals("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+		
+		started = 42.0;
+		stopped = 97.0;
+		count = U64L(8);
+		user_cpu = 29000;
+		sys_cpu = 57000;
+		ensure_equals("WCM t8 - second isExpired() returns false after true",
+					  timer.isExpired(now, started, stopped, count), false);
+		ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
+		ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
+		ensure_equals("WCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
+		ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, LLProcInfo::time_type(29000));
+		ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, LLProcInfo::time_type(57000));
+	}
 }
 
 
@@ -215,46 +377,93 @@ void deadmantimer_object_t::test<8>()
 template<> template<>
 void deadmantimer_object_t::test<9>()
 {
-	F64 started(42.0), stopped(97.0);
-	U64 count(U64L(8));
-	LLDeadmanTimer timer(5.0);
-
-	LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
-	F64 real_start(u64_time_to_float(now));
-	timer.start(0);
-
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	ensure_equals("t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
-				  timer.isExpired(now, started, stopped, count), false);
-	F64 last_good_ring(u64_time_to_float(now));
-
-	// Jump forward and expire
-	now += float_time_to_u64(10.0);
-	ensure_equals("t9 - 5.0 horizon timer expires on 10-second jump",
-				  timer.isExpired(now, started, stopped, count), true);
-	ensure_approximately_equals("t9 - started matches start() time", started, real_start, 4);
-	ensure_approximately_equals("t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
-	ensure_equals("t9 - 10 good ringBell()s", count, U64L(10));
-	ensure_equals("t9 - single read only", timer.isExpired(now, started, stopped, count), false);
+	{
+		// Without cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(5.0, false);
+
+		LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+		F64 real_start(u64_time_to_float(now));
+		timer.start(0);
+
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		ensure_equals("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+					  timer.isExpired(now, started, stopped, count), false);
+		F64 last_good_ring(u64_time_to_float(now));
+
+		// Jump forward and expire
+		now += float_time_to_u64(10.0);
+		ensure_equals("WOCM t9 - 5.0 horizon timer expires on 10-second jump",
+					  timer.isExpired(now, started, stopped, count), true);
+		ensure_approximately_equals("WOCM t9 - started matches start() time", started, real_start, 4);
+		ensure_approximately_equals("WOCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+		ensure_equals("WOCM t9 - 10 good ringBell()s", count, U64L(10));
+		ensure_equals("WOCM t9 - single read only", timer.isExpired(now, started, stopped, count), false);
+	}
+	{
+		// With cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(5.0, true);
+		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+
+		LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+		F64 real_start(u64_time_to_float(now));
+		timer.start(0);
+
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		ensure_equals("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+		F64 last_good_ring(u64_time_to_float(now));
+
+		// Jump forward and expire
+		now += float_time_to_u64(10.0);
+		ensure_equals("WCM t9 - 5.0 horizon timer expires on 10-second jump",
+					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+		ensure_approximately_equals("WCM t9 - started matches start() time", started, real_start, 4);
+		ensure_approximately_equals("WCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+		ensure_equals("WCM t9 - 10 good ringBell()s", count, U64L(10));
+		ensure_equals("WCM t9 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+	}
 }
 
 
@@ -262,83 +471,167 @@ void deadmantimer_object_t::test<9>()
 template<> template<>
 void deadmantimer_object_t::test<10>()
 {
-	F64 started(42.0), stopped(97.0);
-	U64 count(U64L(8));
-	LLDeadmanTimer timer(5.0);
-
-	LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
-	F64 real_start(u64_time_to_float(now));
-	timer.start(0);
-
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	ensure_equals("t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
-				  timer.isExpired(now, started, stopped, count), false);
-	F64 last_good_ring(u64_time_to_float(now));
-
-	// Jump forward and expire
-	now += float_time_to_u64(10.0);
-	ensure_equals("t10 - 5.0 horizon timer expires on 10-second jump",
-				  timer.isExpired(now, started, stopped, count), true);
-	ensure_approximately_equals("t10 - started matches start() time", started, real_start, 4);
-	ensure_approximately_equals("t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
-	ensure_equals("t10 - 10 good ringBell()s", count, U64L(10));
-	ensure_equals("t10 - single read only", timer.isExpired(now, started, stopped, count), false);
-
-	// Jump forward and restart
-	now += float_time_to_u64(1.0);
-	real_start = u64_time_to_float(now);
-	timer.start(now);
-
-	// Run a modified bell ring sequence
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	now += float_time_to_u64(1.0);
-	timer.ringBell(now, 1);
-	ensure_equals("t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
-				  timer.isExpired(now, started, stopped, count), false);
-	last_good_ring = u64_time_to_float(now);
-
-	// Jump forward and expire
-	now += float_time_to_u64(10.0);
-	ensure_equals("t10 - 5.0 horizon timer expires on 8-second jump",
-				  timer.isExpired(now, started, stopped, count), true);
-	ensure_approximately_equals("t10 - 2nd started matches start() time", started, real_start, 4);
-	ensure_approximately_equals("t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
-	ensure_equals("t10 - 8 good ringBell()s", count, U64L(8));
-	ensure_equals("t10 - single read only - 2nd start",
-				  timer.isExpired(now, started, stopped, count), false);
+	{
+		// Without cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(5.0, false);
+
+		LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+		F64 real_start(u64_time_to_float(now));
+		timer.start(0);
+
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+					  timer.isExpired(now, started, stopped, count), false);
+		F64 last_good_ring(u64_time_to_float(now));
+
+		// Jump forward and expire
+		now += float_time_to_u64(10.0);
+		ensure_equals("WOCM t10 - 5.0 horizon timer expires on 10-second jump",
+					  timer.isExpired(now, started, stopped, count), true);
+		ensure_approximately_equals("WOCM t10 - started matches start() time", started, real_start, 4);
+		ensure_approximately_equals("WOCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+		ensure_equals("WOCM t10 - 10 good ringBell()s", count, U64L(10));
+		ensure_equals("WOCM t10 - single read only", timer.isExpired(now, started, stopped, count), false);
+		
+		// Jump forward and restart
+		now += float_time_to_u64(1.0);
+		real_start = u64_time_to_float(now);
+		timer.start(now);
+		
+		// Run a modified bell ring sequence
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
+					  timer.isExpired(now, started, stopped, count), false);
+		last_good_ring = u64_time_to_float(now);
+
+		// Jump forward and expire
+		now += float_time_to_u64(10.0);
+		ensure_equals("WOCM t10 - 5.0 horizon timer expires on 8-second jump",
+					  timer.isExpired(now, started, stopped, count), true);
+		ensure_approximately_equals("WOCM t10 - 2nd started matches start() time", started, real_start, 4);
+		ensure_approximately_equals("WOCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
+		ensure_equals("WOCM t10 - 8 good ringBell()s", count, U64L(8));
+		ensure_equals("WOCM t10 - single read only - 2nd start",
+					  timer.isExpired(now, started, stopped, count), false);
+	}
+	{
+		// With cpu metrics
+		F64 started(42.0), stopped(97.0);
+		U64 count(U64L(8));
+		LLDeadmanTimer timer(5.0, true);
+		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+
+		LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+		F64 real_start(u64_time_to_float(now));
+		timer.start(0);
+
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+		F64 last_good_ring(u64_time_to_float(now));
+
+		// Jump forward and expire
+		now += float_time_to_u64(10.0);
+		ensure_equals("WCM t10 - 5.0 horizon timer expires on 10-second jump",
+					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+		ensure_approximately_equals("WCM t10 - started matches start() time", started, real_start, 4);
+		ensure_approximately_equals("WCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+		ensure_equals("WCM t10 - 10 good ringBell()s", count, U64L(10));
+		ensure_equals("WCM t10 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+		
+		// Jump forward and restart
+		now += float_time_to_u64(1.0);
+		real_start = u64_time_to_float(now);
+		timer.start(now);
+		
+		// Run a modified bell ring sequence
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		now += float_time_to_u64(1.0);
+		timer.ringBell(now, 1);
+		ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
+					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+		last_good_ring = u64_time_to_float(now);
+
+		// Jump forward and expire
+		now += float_time_to_u64(10.0);
+		ensure_equals("WCM t10 - 5.0 horizon timer expires on 8-second jump",
+					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+		ensure_approximately_equals("WCM t10 - 2nd started matches start() time", started, real_start, 4);
+		ensure_approximately_equals("WCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
+		ensure_equals("WCM t10 - 8 good ringBell()s", count, U64L(8));
+		ensure_equals("WCM t10 - single read only - 2nd start",
+					  timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+	}
 }
 
 
+
 } // end namespace tut
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 0f33128057..2a863a3103 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -96,7 +96,7 @@ U32 LLMeshRepository::sLODPending = 0;
 U32 LLMeshRepository::sCacheBytesRead = 0;
 U32 LLMeshRepository::sCacheBytesWritten = 0;
 U32 LLMeshRepository::sPeakKbps = 0;
-LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0);
+LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true);
 
 	
 const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5;
-- 
cgit v1.2.3


From 960139aa6f02f90c6102d3c5d5c38b5ebe689f9c Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 2 May 2013 19:12:59 -0400
Subject: SH-4161 Integrate cpu metrics into LLDeadmanTimer and then metrics
 viewer Normalize deadman timer's args on U64/F64.  Internals remain the same.
  Modify mesh to collect and output enhanced CPU metrics.

---
 indra/llcommon/lldeadmantimer.cpp            |  6 ++--
 indra/llcommon/lldeadmantimer.h              |  2 +-
 indra/llcommon/tests/lldeadmantimer_test.cpp | 47 +++++++++++-----------------
 indra/newview/llmeshrepository.cpp           | 10 +++---
 4 files changed, 29 insertions(+), 36 deletions(-)

(limited to 'indra')

diff --git a/indra/llcommon/lldeadmantimer.cpp b/indra/llcommon/lldeadmantimer.cpp
index 2ba757d2af..7d9097e344 100644
--- a/indra/llcommon/lldeadmantimer.cpp
+++ b/indra/llcommon/lldeadmantimer.cpp
@@ -110,13 +110,13 @@ void LLDeadmanTimer::stop(time_type now)
 
 
 bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count,
-							   LLProcInfo::time_type & user_cpu, LLProcInfo::time_type & sys_cpu)
+							   U64 & user_cpu, U64 & sys_cpu)
 {
 	const bool status(isExpired(now, started, stopped, count));
 	if (status)
 	{
-		user_cpu = mUEndCPU - mUStartCPU;
-		sys_cpu = mSEndCPU - mSStartCPU;
+		user_cpu = U64(mUEndCPU - mUStartCPU);
+		sys_cpu = U64(mSEndCPU - mSStartCPU);
 	}
 	return status;
 }
diff --git a/indra/llcommon/lldeadmantimer.h b/indra/llcommon/lldeadmantimer.h
index a6022852b9..0dde16b717 100644
--- a/indra/llcommon/lldeadmantimer.h
+++ b/indra/llcommon/lldeadmantimer.h
@@ -190,7 +190,7 @@ public:
 	///					left unchanged.
 	///
 	bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count,
-				   LLProcInfo::time_type & user_cpu, LLProcInfo::time_type & sys_cpu);
+				   U64 & user_cpu, U64 & sys_cpu);
 
 	/// Identical to the six-arugment form except is does without the
 	/// CPU time return if the caller isn't interested in it.
diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp
index a4ec76a016..7fd2dde6e0 100644
--- a/indra/llcommon/tests/lldeadmantimer_test.cpp
+++ b/indra/llcommon/tests/lldeadmantimer_test.cpp
@@ -81,16 +81,15 @@ void deadmantimer_object_t::test<1>()
 	{
 		// With cpu metrics
 		F64 started(42.0), stopped(97.0);
-		U64 count(U64L(8));
-		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
 		LLDeadmanTimer timer(10.0, true);
 
 		ensure_equals("WCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
 		ensure_approximately_equals("WCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
 		ensure_approximately_equals("WCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
 		ensure_equals("WCM t1 - isExpired() does not modify count", count, U64L(8));
-		ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, LLProcInfo::time_type(29000));
-		ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, LLProcInfo::time_type(57000));
+		ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+		ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
 	}
 }
 
@@ -112,8 +111,7 @@ void deadmantimer_object_t::test<2>()
 	{
 		// With cpu metrics
 		F64 started(42.0), stopped(97.0);
-		U64 count(U64L(8));
-		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
 		LLDeadmanTimer timer(0.0, true);			// Zero is pre-expired
 		
 		ensure_equals("WCM isExpired() still returns false with 0.0 time ctor()",
@@ -141,8 +139,7 @@ void deadmantimer_object_t::test<3>()
 	{
 		// With cpu metrics
 		F64 started(42.0), stopped(97.0);
-		U64 count(U64L(8));
-		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
 		LLDeadmanTimer timer(0.0, true);
 
 		timer.start(0);
@@ -172,8 +169,7 @@ void deadmantimer_object_t::test<4>()
 	{
 		// With cpu metrics
 		F64 started(42.0), stopped(97.0);
-		U64 count(U64L(8));
-		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
 		LLDeadmanTimer timer(0.0, true);
 	
 		timer.start(0);
@@ -205,8 +201,7 @@ void deadmantimer_object_t::test<5>()
 	{
 		// With cpu metrics
 		F64 started(42.0), stopped(97.0);
-		U64 count(U64L(8));
-		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
 		LLDeadmanTimer timer(10.0, true);
 	
 		timer.start(0);
@@ -215,8 +210,8 @@ void deadmantimer_object_t::test<5>()
 		ensure_approximately_equals("WCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
 		ensure_approximately_equals("WCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
 		ensure_equals("WCM t5 - isExpired() does not modify count", count, U64L(8));
-		ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, LLProcInfo::time_type(29000));
-		ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, LLProcInfo::time_type(57000));
+		ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+		ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
 	}
 }
 
@@ -247,8 +242,7 @@ void deadmantimer_object_t::test<6>()
 	{
 		// With cpu metrics
 		F64 started(42.0), stopped(97.0);
-		U64 count(U64L(8));
-		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
+		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
 		LLDeadmanTimer timer(10.0, true);
 
 		// Would like to do subtraction on current time but can't because
@@ -263,8 +257,8 @@ void deadmantimer_object_t::test<6>()
 		ensure_approximately_equals("WCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
 		ensure_approximately_equals("WCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
 		ensure_equals("t6 - isExpired() does not modify count", count, U64L(8));
-		ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, LLProcInfo::time_type(29000));
-		ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, LLProcInfo::time_type(57000));
+		ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+		ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
 	}
 }
 
@@ -293,9 +287,8 @@ void deadmantimer_object_t::test<7>()
 	{
 		// With cpu metrics
 		F64 started(42.0), stopped(97.0);
-		U64 count(U64L(8));
+		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
 		LLDeadmanTimer timer(10.0, true);
-		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
 		
 		// Would like to do subtraction on current time but can't because
 		// the implementation on Windows is zero-based.  We wrap around
@@ -343,9 +336,8 @@ void deadmantimer_object_t::test<8>()
 	{
 		// With cpu metrics
 		F64 started(42.0), stopped(97.0);
-		U64 count(U64L(8));
+		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
 		LLDeadmanTimer timer(10.0, true);
-		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
 
 		// Would like to do subtraction on current time but can't because
 		// the implementation on Windows is zero-based.  We wrap around
@@ -367,8 +359,8 @@ void deadmantimer_object_t::test<8>()
 		ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
 		ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
 		ensure_equals("WCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
-		ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, LLProcInfo::time_type(29000));
-		ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, LLProcInfo::time_type(57000));
+		ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+		ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
 	}
 }
 
@@ -423,9 +415,8 @@ void deadmantimer_object_t::test<9>()
 	{
 		// With cpu metrics
 		F64 started(42.0), stopped(97.0);
-		U64 count(U64L(8));
+		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
 		LLDeadmanTimer timer(5.0, true);
-		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
 
 		LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
 		F64 real_start(u64_time_to_float(now));
@@ -553,9 +544,9 @@ void deadmantimer_object_t::test<10>()
 	{
 		// With cpu metrics
 		F64 started(42.0), stopped(97.0);
-		U64 count(U64L(8));
+		U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+
 		LLDeadmanTimer timer(5.0, true);
-		LLProcInfo::time_type user_cpu(29000), sys_cpu(57000);
 
 		LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
 		F64 real_start(u64_time_to_float(now));
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 2a863a3103..4d3937ded1 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -96,7 +96,7 @@ U32 LLMeshRepository::sLODPending = 0;
 U32 LLMeshRepository::sCacheBytesRead = 0;
 U32 LLMeshRepository::sCacheBytesWritten = 0;
 U32 LLMeshRepository::sPeakKbps = 0;
-LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true);
+LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true);	// true -> gather cpu metrics
 
 	
 const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5;
@@ -3769,9 +3769,9 @@ void LLMeshRepository::metricsProgress(unsigned int this_count)
 void LLMeshRepository::metricsUpdate()
 {
 	F64 started, stopped;
-	U64 total_count;
-
-	if (sQuiescentTimer.isExpired(0, started, stopped, total_count))
+	U64 total_count(U64L(0)), user_cpu(U64L(0)), sys_cpu(U64L(0));
+	
+	if (sQuiescentTimer.isExpired(0, started, stopped, total_count, user_cpu, sys_cpu))
 	{
 		LLSD metrics;
 
@@ -3781,6 +3781,8 @@ void LLMeshRepository::metricsUpdate()
 		metrics["stop"] = stopped;
 		metrics["downloads"] = LLSD::Integer(total_count);
 		metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count);
+		metrics["user_cpu"] = double(user_cpu) / 1.0e6;
+		metrics["sys_cpu"] = double(sys_cpu) / 1.0e6;
 		llinfos << "EventMarker " << metrics << llendl;
 	}
 }
-- 
cgit v1.2.3


From d6741a4fc088632c179f767df240953fc4f7474f Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 19 Jun 2013 19:58:09 +0000
Subject: Fixups for the transplant of the HttpHeader changes from sunshine.

---
 indra/newview/llmeshrepository.cpp | 2 +-
 indra/newview/lltexturefetch.cpp   | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index f0ec97a34d..221a797fc7 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -573,7 +573,7 @@ LLMeshRepoThread::LLMeshRepoThread()
 	mHttpLargeOptions = new LLCore::HttpOptions;
 	mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT);
 	mHttpHeaders = new LLCore::HttpHeaders;
-	mHttpHeaders->mHeaders.push_back("Accept: application/vnd.ll.mesh");
+	mHttpHeaders->append("Accept", "application/vnd.ll.mesh");
 	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH);
 	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_LARGE_MESH);
 }
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index a8c8445ee1..bf9d803098 100755
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -2410,9 +2410,9 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
 	mHttpOptionsWithHeaders = new LLCore::HttpOptions;
 	mHttpOptionsWithHeaders->setWantHeaders(true);
 	mHttpHeaders = new LLCore::HttpHeaders;
-	mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C);
+	mHttpHeaders->append("Accept", "image/x-j2c");
 	mHttpMetricsHeaders = new LLCore::HttpHeaders;
-	mHttpMetricsHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML);
+	mHttpMetricsHeaders->append("Content-Type", "application/llsd+xml");
 	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_TEXTURE);
 }
 
@@ -4050,7 +4050,7 @@ void LLTextureFetchDebugger::init()
 	if (! mHttpHeaders)
 	{
 		mHttpHeaders = new LLCore::HttpHeaders;
-		mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C);
+		mHttpHeaders->append("Accept", "image/x-j2c");
 	}
 }
 
-- 
cgit v1.2.3


From d6cbcd591aea32357d50b266efe8a95754302cbf Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 20 Jun 2013 19:18:39 -0400
Subject: SH-4257 Preparation for a new cap grant:  GetMesh2 Mesh repo is using
 three policy classes now:  one for large objects, one for GetMesh2 regions,
 one for GetMesh regions.  It's also detecting the presence of the cap and
 using the correct class.  Class initialization cleaned up significantly in
 llappcorehttp using data-directed code.  Pulled in the changes to HttpHeader
 done for sunshine-internal then did a refactoring pass on the header callback
 which now uses a unified approach to clean up and deliver header information
 to all interested parties.  Added support for using Retry-After header
 information on 503 retries.

---
 indra/llcorehttp/_httpinternal.h      |   6 +-
 indra/llcorehttp/_httpoperation.cpp   |   4 +-
 indra/llcorehttp/_httpoprequest.cpp   | 244 ++++++++++++++++++++++++++--------
 indra/llcorehttp/_httpoprequest.h     |   4 +
 indra/llcorehttp/_httppolicy.cpp      |  12 +-
 indra/llcorehttp/_httppolicyclass.cpp |   4 +-
 indra/llcorehttp/_httpservice.cpp     |   3 -
 indra/llcorehttp/httpoptions.cpp      |   8 +-
 indra/llcorehttp/httpoptions.h        |   8 +-
 indra/newview/llappcorehttp.cpp       |  10 +-
 indra/newview/llappcorehttp.h         |   3 +-
 indra/newview/llmeshrepository.cpp    |  55 +++++---
 indra/newview/llmeshrepository.h      |   8 +-
 13 files changed, 275 insertions(+), 94 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h
index d60996756f..f085ca3b91 100755
--- a/indra/llcorehttp/_httpinternal.h
+++ b/indra/llcorehttp/_httpinternal.h
@@ -98,7 +98,7 @@ namespace LLCore
 
 // Maxium number of policy classes that can be defined.
 // *TODO:  Currently limited to the default class + 1, extend.
-const int HTTP_POLICY_CLASS_LIMIT = 4;
+const int HTTP_POLICY_CLASS_LIMIT = 8;
 
 // Debug/informational tracing.  Used both
 // as a global option and in per-request traces.
@@ -138,6 +138,10 @@ const int HTTP_CONNECTION_LIMIT_DEFAULT = 8;
 const int HTTP_CONNECTION_LIMIT_MIN = 1;
 const int HTTP_CONNECTION_LIMIT_MAX = 256;
 
+// Miscellaneous defaults
+const long HTTP_PIPELINING_DEFAULT = 0L;
+const bool HTTP_USE_RETRY_AFTER_DEFAULT = true;
+
 // Tuning parameters
 
 // Time worker thread sleeps after a pass through the
diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp
index 5cf5bc5930..7acd728bbd 100755
--- a/indra/llcorehttp/_httpoperation.cpp
+++ b/indra/llcorehttp/_httpoperation.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -53,7 +53,7 @@ HttpOperation::HttpOperation()
 	  mUserHandler(NULL),
 	  mReqPolicy(HttpRequest::DEFAULT_POLICY_ID),
 	  mReqPriority(0U),
-	  mTracing(0)
+	  mTracing(HTTP_TRACE_OFF)
 {
 	mMetricCreated = totalTime();
 }
diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index a4c0a12fdc..8cb7fee701 100755
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -64,6 +64,15 @@ int parse_content_range_header(char * buffer,
 							   unsigned int * last,
 							   unsigned int * length);
 
+// Similar for Retry-After headers.  Only parses the delta form
+// of the header, HTTP time formats aren't interesting for client
+// purposes.
+//
+// @return		0 if successfully parsed and seconds time delta
+//				returned in time argument.
+//
+int parse_retry_after_header(char * buffer, int * time);
+
 
 // Take data from libcurl's CURLOPT_DEBUGFUNCTION callback and
 // escape and format it for a tracing line in logging.  Absolutely
@@ -74,14 +83,12 @@ void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub,
 							   std::string & safe_line);
 
 
-// OS-neutral string comparisons of various types
-int os_strncasecmp(const char *s1, const char *s2, size_t n);
-int os_strcasecmp(const char *s1, const char *s2);
-char * os_strtok_r(char *str, const char *delim, char **saveptr);
-
-
-static const char * const hdr_whitespace(" \t");
-static const char * const hdr_separator(": \t");
+// OS-neutral string comparisons of various types.
+int os_strcasecmp(const char * s1, const char * s2);
+char * os_strtok_r(char * str, const char * delim, char ** saveptr);
+char * os_strtrim(char * str);
+char * os_strltrim(char * str);
+void os_strlower(char * str);
 
 } // end anonymous namespace
 
@@ -104,6 +111,8 @@ HttpOpRequest::HttpOpRequest()
 	  mCurlService(NULL),
 	  mCurlHeaders(NULL),
 	  mCurlBodyPos(0),
+	  mCurlTemp(NULL),
+	  mCurlTempLen(0),
 	  mReplyBody(NULL),
 	  mReplyOffset(0),
 	  mReplyLength(0),
@@ -154,6 +163,10 @@ HttpOpRequest::~HttpOpRequest()
 		mCurlHeaders = NULL;
 	}
 
+	delete [] mCurlTemp;
+	mCurlTemp = NULL;
+	mCurlTempLen = 0;
+	
 	if (mReplyBody)
 	{
 		mReplyBody->release();
@@ -207,6 +220,11 @@ void HttpOpRequest::stageFromActive(HttpService * service)
 		mCurlHeaders = NULL;
 	}
 
+	// Also not needed on the other side
+	delete [] mCurlTemp;
+	mCurlTemp = NULL;
+	mCurlTempLen = 0;
+	
 	addAsReply();
 }
 
@@ -335,6 +353,10 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,
 		{
 			mProcFlags |= PF_SAVE_HEADERS;
 		}
+		if (options->getUseRetryAfter())
+		{
+			mProcFlags |= PF_USE_RETRY_AFTER;
+		}
 		mPolicyRetryLimit = options->getRetries();
 		mPolicyRetryLimit = llclamp(mPolicyRetryLimit, HTTP_RETRY_COUNT_MIN, HTTP_RETRY_COUNT_MAX);
 		mTracing = (std::max)(mTracing, llclamp(options->getTrace(), HTTP_TRACE_MIN, HTTP_TRACE_MAX));
@@ -549,7 +571,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 	}
 	curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders);
 
-	if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS))
+	if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS | PF_USE_RETRY_AFTER))
 	{
 		curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback);
 		curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this);
@@ -610,10 +632,9 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
 {
 	static const char status_line[] = "HTTP/";
 	static const size_t status_line_len = sizeof(status_line) - 1;
-
-	static const char con_ran_line[] = "content-range:";
-	static const size_t con_ran_line_len = sizeof(con_ran_line) - 1;
-
+	static const char con_ran_line[] = "content-range";
+	static const char con_retry_line[] = "retry-after";
+	
 	HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata));
 
 	const size_t hdr_size(size * nmemb);
@@ -627,6 +648,7 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
 		op->mReplyOffset = 0;
 		op->mReplyLength = 0;
 		op->mReplyFullLength = 0;
+		op->mReplyRetryAfter = 0;
 		op->mStatus = HttpStatus();
 		if (op->mReplyHeaders)
 		{
@@ -645,6 +667,53 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
 			--wanted_hdr_size;
 		}
 	}
+
+	// Copy and normalize header fragments for the following
+	// stages.  Would like to modify the data in-place but that
+	// may not be allowed and we need one byte extra for NUL.
+	// At the end of this we will have:
+	//
+	// If ':' present in header:
+	//   1.  name points to text to left of colon which
+	//       will be ascii lower-cased and left and right
+	//       trimmed of whitespace.
+	//   2.  value points to text to right of colon which
+	//       will be left trimmed of whitespace.
+	// Otherwise:
+	//   1.  name points to header which will be left
+	//       trimmed of whitespace.
+	//   2.  value is NULL
+	// Any non-NULL pointer may point to a zero-length string.
+	//
+	if (wanted_hdr_size >= op->mCurlTempLen)
+	{
+		delete [] op->mCurlTemp;
+		op->mCurlTempLen = 2 * wanted_hdr_size + 1;
+		op->mCurlTemp = new char [op->mCurlTempLen];
+	}
+	memcpy(op->mCurlTemp, hdr_data, wanted_hdr_size);
+	op->mCurlTemp[wanted_hdr_size] = '\0';
+	char * name(op->mCurlTemp);
+	char * value(strchr(name, ':'));
+	if (value)
+	{
+		*value++ = '\0';
+		os_strlower(name);
+		name = os_strtrim(name);
+		value = os_strltrim(value);
+	}
+	else
+	{
+		// Doesn't look well-formed, do minimal normalization on it
+		name = os_strltrim(name);
+	}
+
+	// Normalized, now reject headers with empty names.
+	if (! *name)
+	{
+		// No use continuing
+		return hdr_size;
+	}
 	
 	// Save header if caller wants them in the response
 	if (is_header && op->mProcFlags & PF_SAVE_HEADERS)
@@ -654,43 +723,53 @@ size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, voi
 		{
 			op->mReplyHeaders = new HttpHeaders;
 		}
-		op->mReplyHeaders->appendNormal(hdr_data, wanted_hdr_size);
+		op->mReplyHeaders->append(name, value ? value : "");
 	}
 
+	// From this point, header-specific processors are free to
+	// modify the header value.
+	
 	// Detect and parse 'Content-Range' headers
-	if (is_header && op->mProcFlags & PF_SCAN_RANGE_HEADER)
+	if (is_header
+		&& op->mProcFlags & PF_SCAN_RANGE_HEADER
+		&& value && *value
+		&& ! strcmp(name, con_ran_line))
 	{
-		char hdr_buffer[128];			// Enough for a reasonable header
-		size_t frag_size((std::min)(wanted_hdr_size, sizeof(hdr_buffer) - 1));
-		
-		memcpy(hdr_buffer, hdr_data, frag_size);
-		hdr_buffer[frag_size] = '\0';
-		if (frag_size > con_ran_line_len &&
-			! os_strncasecmp(hdr_buffer, con_ran_line, con_ran_line_len))
+		unsigned int first(0), last(0), length(0);
+		int status;
+
+		if (! (status = parse_content_range_header(value, &first, &last, &length)))
+		{
+			// Success, record the fragment position
+			op->mReplyOffset = first;
+			op->mReplyLength = last - first + 1;
+			op->mReplyFullLength = length;
+		}
+		else if (-1 == status)
 		{
-			unsigned int first(0), last(0), length(0);
-			int status;
+			// Response is badly formed and shouldn't be accepted
+			op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR);
+		}
+		else
+		{
+			// Ignore the unparsable.
+			LL_INFOS_ONCE("CoreHttp") << "Problem parsing odd Content-Range header:  '"
+									  << std::string(hdr_data, wanted_hdr_size)
+									  << "'.  Ignoring."
+									  << LL_ENDL;
+		}
+	}
 
-			if (! (status = parse_content_range_header(hdr_buffer, &first, &last, &length)))
-			{
-				// Success, record the fragment position
-				op->mReplyOffset = first;
-				op->mReplyLength = last - first + 1;
-				op->mReplyFullLength = length;
-			}
-			else if (-1 == status)
-			{
-				// Response is badly formed and shouldn't be accepted
-				op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR);
-			}
-			else
-			{
-				// Ignore the unparsable.
-				LL_INFOS_ONCE("CoreHttp") << "Problem parsing odd Content-Range header:  '"
-										  << std::string(hdr_data, frag_size)
-										  << "'.  Ignoring."
-										  << LL_ENDL;
-			}
+	// Detect and parse 'Retry-After' headers
+	if (is_header
+		&& op->mProcFlags & PF_USE_RETRY_AFTER
+		&& value && *value
+		&& ! strcmp(name, con_retry_line))
+	{
+		int time(0);
+		if (! parse_retry_after_header(value, &time))
+		{
+			op->mReplyRetryAfter = time;
 		}
 	}
 
@@ -805,14 +884,16 @@ int parse_content_range_header(char * buffer,
 							   unsigned int * last,
 							   unsigned int * length)
 {
+	static const char * const hdr_whitespace(" \t");
+
 	char * tok_state(NULL), * tok(NULL);
 	bool match(true);
 			
-	if (! os_strtok_r(buffer, hdr_separator, &tok_state))
+	if (! (tok = os_strtok_r(buffer, hdr_whitespace, &tok_state)))
 		match = false;
-	if (match && (tok = os_strtok_r(NULL, hdr_whitespace, &tok_state)))
-		match = 0 == os_strcasecmp("bytes", tok);
-	if (match && ! (tok = os_strtok_r(NULL, " \t", &tok_state)))
+	else
+		match = (0 == os_strcasecmp("bytes", tok));
+	if (match && ! (tok = os_strtok_r(NULL, hdr_whitespace, &tok_state)))
 		match = false;
 	if (match)
 	{
@@ -851,6 +932,25 @@ int parse_content_range_header(char * buffer,
 }
 
 
+int parse_retry_after_header(char * buffer, int * time)
+{
+	char * endptr(buffer);
+	long lcl_time(strtol(buffer, &endptr, 10));
+	if (*endptr == '\0' && endptr != buffer && lcl_time > 0)
+	{
+		*time = lcl_time;
+		return 0;
+	}
+
+	// Could attempt to parse HTTP time here but we're not really
+	// interested in it.  Scheduling based on wallclock time on
+	// user hardware will lead to tears.
+	
+	// Header is there but badly/unexpectedly formed, try to ignore it.
+	return 1;
+}
+
+
 void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line)
 {
 	std::string out;
@@ -887,15 +987,6 @@ void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::strin
 }
 
 
-int os_strncasecmp(const char *s1, const char *s2, size_t n)
-{
-#if LL_WINDOWS
-	return _strnicmp(s1, s2, n);
-#else
-	return strncasecmp(s1, s2, n);
-#endif	// LL_WINDOWS
-}
-
 
 int os_strcasecmp(const char *s1, const char *s2)
 {
@@ -917,6 +1008,45 @@ char * os_strtok_r(char *str, const char *delim, char ** savestate)
 }
 
 
+void os_strlower(char * str)
+{
+	for (char c(0); (c = *str); ++str)
+	{
+		*str = tolower(c);
+	}
+}
+
+
+char * os_strtrim(char * lstr)
+{
+	while (' ' == *lstr || '\t' == *lstr)
+	{
+		++lstr;
+	}
+	if (*lstr)
+	{
+		for (char * rstr(lstr + strlen(lstr)); *--rstr;)
+		{
+			if (' ' == *rstr || '\t' == *rstr)
+			{
+				*rstr = '\0';
+			}
+		}
+	}
+	return lstr;
+}
+
+
+char * os_strltrim(char * lstr)
+{
+	while (' ' == *lstr || '\t' == *lstr)
+	{
+		++lstr;
+	}
+	return lstr;
+}
+
+
 }  // end anonymous namespace
 
 		
diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h
index 831e5bebf7..2e737cf1cc 100755
--- a/indra/llcorehttp/_httpoprequest.h
+++ b/indra/llcorehttp/_httpoprequest.h
@@ -158,6 +158,7 @@ protected:
 	unsigned int		mProcFlags;
 	static const unsigned int	PF_SCAN_RANGE_HEADER = 0x00000001U;
 	static const unsigned int	PF_SAVE_HEADERS = 0x00000002U;
+	static const unsigned int	PF_USE_RETRY_AFTER = 0x00000004U;
 
 public:
 	// Request data
@@ -175,6 +176,8 @@ public:
 	HttpService *		mCurlService;
 	curl_slist *		mCurlHeaders;
 	size_t				mCurlBodyPos;
+	char *				mCurlTemp;				// Scratch buffer for header processing
+	size_t				mCurlTempLen;
 	
 	// Result data
 	HttpStatus			mStatus;
@@ -184,6 +187,7 @@ public:
 	size_t				mReplyFullLength;
 	HttpHeaders *		mReplyHeaders;
 	std::string			mReplyConType;
+	int					mReplyRetryAfter;
 
 	// Policy data
 	int					mPolicyRetries;
diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp
index 54c9c6bb1b..5f303dd0fe 100755
--- a/indra/llcorehttp/_httppolicy.cpp
+++ b/indra/llcorehttp/_httppolicy.cpp
@@ -160,8 +160,12 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
 	
 	const HttpTime now(totalTime());
 	const int policy_class(op->mReqPolicy);
-	
-	const HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]);
+	HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]);
+
+	if (op->mReplyRetryAfter > 0 && op->mReplyRetryAfter < 30)
+	{
+		delta = op->mReplyRetryAfter * U64L(1000000);
+	}
 	op->mPolicyRetryAt = now + delta;
 	++op->mPolicyRetries;
 	if (error_503 == op->mStatus)
@@ -170,10 +174,10 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
 	}
 	LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
 						 << " retry " << op->mPolicyRetries
-						 << " scheduled for +" << (delta / HttpTime(1000))
+						 << " scheduled in " << (delta / HttpTime(1000))
 						 << " mS.  Status:  " << op->mStatus.toHex()
 						 << LL_ENDL;
-	if (op->mTracing > 0)
+	if (op->mTracing > HTTP_TRACE_OFF)
 	{
 		LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle:  "
 							 << static_cast<HttpHandle>(op)
diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp
index a23b81322c..1a55ab1ac6 100755
--- a/indra/llcorehttp/_httppolicyclass.cpp
+++ b/indra/llcorehttp/_httppolicyclass.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -37,7 +37,7 @@ HttpPolicyClass::HttpPolicyClass()
 	: mSetMask(0UL),
 	  mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
 	  mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
-	  mPipelining(0)
+	  mPipelining(HTTP_PIPELINING_DEFAULT)
 {}
 
 
diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp
index 0825888d0f..0821401289 100755
--- a/indra/llcorehttp/_httpservice.cpp
+++ b/indra/llcorehttp/_httpservice.cpp
@@ -55,9 +55,6 @@ HttpService::HttpService()
 {
 	// Create the default policy class
 	HttpPolicyClass pol_class;
-	pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT);
-	pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT);
-	pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L);
 	mPolicyClasses.push_back(pol_class);
 }
 
diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp
index 4dcd862ca4..5bf1ecb4a5 100755
--- a/indra/llcorehttp/httpoptions.cpp
+++ b/indra/llcorehttp/httpoptions.cpp
@@ -39,7 +39,8 @@ HttpOptions::HttpOptions()
 	  mTracing(HTTP_TRACE_OFF),
 	  mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT),
 	  mTransferTimeout(HTTP_REQUEST_XFER_TIMEOUT_DEFAULT),
-	  mRetries(HTTP_RETRY_COUNT_DEFAULT)
+	  mRetries(HTTP_RETRY_COUNT_DEFAULT),
+	  mUseRetryAfter(HTTP_USE_RETRY_AFTER_DEFAULT)
 {}
 
 
@@ -76,5 +77,10 @@ void HttpOptions::setRetries(unsigned int retries)
 	mRetries = retries;
 }
 
+void HttpOptions::setUseRetryAfter(bool use_retry)
+{
+	mUseRetryAfter = use_retry;
+}
+
 
 }   // end namespace LLCore
diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h
index 623d71d3e6..04531425d8 100755
--- a/indra/llcorehttp/httpoptions.h
+++ b/indra/llcorehttp/httpoptions.h
@@ -98,13 +98,19 @@ public:
 			return mRetries;
 		}
 	
+	void				setUseRetryAfter(bool use_retry);
+	bool				getUseRetryAfter() const
+		{
+			return mUseRetryAfter;
+		}
+	
 protected:
 	bool				mWantHeaders;
 	int					mTracing;
 	unsigned int		mTimeout;
 	unsigned int		mTransferTimeout;
 	unsigned int		mRetries;
-	
+	bool				mUseRetryAfter;
 }; // end class HttpOptions
 
 
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index b601b31d21..2467c02d4d 100755
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -72,10 +72,16 @@ void LLAppCoreHttp::init()
 				  "texture fetch"
 			  },
 			  {
-				  AP_MESH,				8,		1,		32,		4,
+				  // *FIXME:  Should become 32, 1, 32, 1 before release
+				  AP_MESH1,				8,		1,		32,		4,
 				  "MeshMaxConcurrentRequests",
 				  "mesh fetch"
 			  },
+			  {
+				  AP_MESH2,				8,		1,		32,		4,
+				  "MeshMaxConcurrentRequests",
+				  "mesh2 fetch"
+			  },
 			  {
 				  AP_LARGE_MESH,		2,		1,		8,		1,
 				  "",
@@ -171,6 +177,8 @@ void LLAppCoreHttp::init()
 		}
 
 		// Set it and report
+		// *TODO:  These are intended to be per-host limits when we can
+		// support that in llcorehttp/libcurl.
 		LLCore::HttpStatus status;
 		status = LLCore::HttpRequest::setPolicyClassOption(mPolicies[policy],
 														   LLCore::HttpRequest::CP_CONNECTION_LIMIT,
diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h
index 532e1f5cb0..4a14c35966 100755
--- a/indra/newview/llappcorehttp.h
+++ b/indra/newview/llappcorehttp.h
@@ -47,7 +47,8 @@ public:
 	{
 		AP_DEFAULT,
 		AP_TEXTURE,
-		AP_MESH,
+		AP_MESH1,
+		AP_MESH2,
 		AP_LARGE_MESH,
 		AP_UPLOADS,
 		AP_COUNT						// Must be last
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 221a797fc7..1cda0b6a71 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -560,6 +560,7 @@ LLMeshRepoThread::LLMeshRepoThread()
   mHttpLargeOptions(NULL),
   mHttpHeaders(NULL),
   mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
+  mHttpLegacyPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
   mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
   mHttpPriority(0),
   mHttpGetCount(0U),
@@ -574,8 +575,9 @@ LLMeshRepoThread::LLMeshRepoThread()
 	mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT);
 	mHttpHeaders = new LLCore::HttpHeaders;
 	mHttpHeaders->append("Accept", "application/vnd.ll.mesh");
-	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH);
-	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_LARGE_MESH);
+	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH2);
+	mHttpLegacyPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH1);
+	mHttpLargePolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_LARGE_MESH);
 }
 
 
@@ -795,13 +797,22 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 }
 
 //static 
-std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
+std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id, int * cap_version)
 {
+	int version(1);
 	std::string http_url;
 	
 	if (gAgent.getRegion())
 	{
-		http_url = gMeshRepo.mGetMeshCapability; 
+		if (! gMeshRepo.mGetMesh2Capability.empty())
+		{
+			version = 2;
+			http_url = gMeshRepo.mGetMesh2Capability;
+		}
+		else
+		{
+			http_url = gMeshRepo.mGetMeshCapability;
+		}
 	}
 
 	if (!http_url.empty())
@@ -814,20 +825,22 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
 		llwarns << "Current region does not have GetMesh capability!  Cannot load " << mesh_id << ".mesh" << llendl;
 	}
 
+	*cap_version = version;
 	return http_url;
 }
 
 // May only be called by repo thread
-LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url,
-												  size_t offset,
-												  size_t len,
+LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int cap_version,
+												  size_t offset, size_t len,
 												  LLCore::HttpHandler * handler)
 {
 	LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
 	
 	if (len < LARGE_MESH_FETCH_THRESHOLD)
 	{
-		handle = mHttpRequest->requestGetByteRange(mHttpPolicyClass,
+		handle = mHttpRequest->requestGetByteRange((2 == cap_version
+													? mHttpPolicyClass
+													: mHttpLegacyPolicyClass),
 												   mHttpPriority,
 												   url,
 												   offset,
@@ -911,12 +924,13 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			std::string http_url = constructUrl(mesh_id);
+			int cap_version(1);
+			std::string http_url = constructUrl(mesh_id, &cap_version);
 			if (!http_url.empty())
 			{
 				LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing Skin Info Request" << LL_ENDL;
-				LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
+				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
 					// *TODO:  Better error message
@@ -1000,12 +1014,13 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			std::string http_url = constructUrl(mesh_id);
+			int cap_version(1);
+			std::string http_url = constructUrl(mesh_id, &cap_version);
 			if (!http_url.empty())
 			{
 				LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing Decomp Request" << LL_ENDL;
-				LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
+				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
 					// *TODO:  Better error message
@@ -1088,12 +1103,13 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			std::string http_url = constructUrl(mesh_id);
+			int cap_version(1);
+			std::string http_url = constructUrl(mesh_id, &cap_version);
 			if (!http_url.empty())
 			{
 				LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing Physics Shape Request" << LL_ENDL;
-				LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
+				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
 					// *TODO:  Better error message
@@ -1177,7 +1193,8 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 
 	//either cache entry doesn't exist or is corrupt, request header from simulator	
 	bool retval = true ;
-	std::string http_url = constructUrl(mesh_params.getSculptID());
+	int cap_version(1);
+	std::string http_url = constructUrl(mesh_params.getSculptID(), &cap_version);
 	if (!http_url.empty())
 	{
 		//grab first 4KB if we're going to bother with a fetch.  Cache will prevent future fetches if a full mesh fits
@@ -1186,7 +1203,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 
 		LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params);
 		// LL_WARNS("Mesh") << "MESH:  Issuing Request" << LL_ENDL;
-		LLCore::HttpHandle handle = getByteRange(http_url, 0, MESH_HEADER_SIZE, handler);
+		LLCore::HttpHandle handle = getByteRange(http_url, cap_version, 0, MESH_HEADER_SIZE, handler);
 		if (LLCORE_HTTP_HANDLE_INVALID == handle)
 		{
 			// *TODO:  Better error message
@@ -1261,12 +1278,13 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			std::string http_url = constructUrl(mesh_id);
+			int cap_version(1);
+			std::string http_url = constructUrl(mesh_id, &cap_version);
 			if (!http_url.empty())
 			{
 				LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size);
 				// LL_WARNS("Mesh") << "MESH:  Issuing LOD Request" << LL_ENDL;
-				LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler);
+				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
 					// *TODO:  Better error message
@@ -2653,6 +2671,7 @@ void LLMeshRepository::notifyLoadedMeshes()
 		{
 			region_name = gAgent.getRegion()->getName();
 			mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh");
+			mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2");
 		}
 	}
 
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 0dca29e7d4..74690e5a2a 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -328,13 +328,14 @@ public:
 	LLCore::HttpOptions *				mHttpLargeOptions;
 	LLCore::HttpHeaders *				mHttpHeaders;
 	LLCore::HttpRequest::policy_t		mHttpPolicyClass;
+	LLCore::HttpRequest::policy_t		mHttpLegacyPolicyClass;
 	LLCore::HttpRequest::policy_t		mHttpLargePolicyClass;
 	LLCore::HttpRequest::priority_t		mHttpPriority;
 
 	typedef std::set<LLCore::HttpHandler *> http_request_set;
 	http_request_set					mHttpRequestSet;			// Outstanding HTTP requests
 
-	static std::string constructUrl(LLUUID mesh_id);
+	static std::string constructUrl(LLUUID mesh_id, int * cap_version);
 
 	LLMeshRepoThread();
 	~LLMeshRepoThread();
@@ -384,7 +385,8 @@ private:
 	// or dispose of handler.
 	//
 	// Threads:  Repo thread only
-	LLCore::HttpHandle getByteRange(const std::string & url, size_t offset, size_t len, 
+	LLCore::HttpHandle getByteRange(const std::string & url, int cap_version,
+									size_t offset, size_t len, 
 									LLCore::HttpHandler * handler);
 
 private:
@@ -595,7 +597,7 @@ public:
 	void updateInventory(inventory_data data);
 
 	std::string mGetMeshCapability;
-
+	std::string mGetMesh2Capability;
 };
 
 extern LLMeshRepository gMeshRepo;
-- 
cgit v1.2.3


From 2cdddab384ce0bf4f6786a3fee08bea3f467e7c9 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 27 Jun 2013 13:55:05 -0400
Subject: SH-4310/BUG-2810/MAINT-2794  Better status checking and error logging
 in Mesh code. Pay correct attention to status codes coming back from
 services.  Generate better and consistent error messages when problems arise.
  There's more to do in error handling, need a way to cleanly fail all request
 types, only have that for LOD at this point.  Do better keeping the HTTP
 pipeline between the low and high water marks.  This was made challenging
 because the outer most code couldn't really see what's going on internally
 (whose actions are delayed in a worker thread).  More to do here, the
 debug-like requests don't honor limits, that will come later.  Made retry
 counts available from llcorehttp which can be used by the
 throttle-anticipating logic to advance the count. It helps but it reinforces
 the coupling between viewer and server which I do not like.

---
 indra/llcorehttp/httpresponse.h    |  10 +-
 indra/newview/llmeshrepository.cpp | 354 ++++++++++++++++++++++++-------------
 indra/newview/llmeshrepository.h   |   6 +-
 3 files changed, 244 insertions(+), 126 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h
index a7f296e03f..bb4b59c16e 100755
--- a/indra/llcorehttp/httpresponse.h
+++ b/indra/llcorehttp/httpresponse.h
@@ -147,8 +147,14 @@ public:
 	/// Get and set retry attempt information on the request.
 	void getRetries(unsigned int * retries, unsigned int * retries_503) const
 		{
-			*retries = mRetries;
-			*retries_503 = m503Retries;
+			if (retries)
+			{
+				*retries = mRetries;
+			}
+			if (retries_503)
+			{
+				*retries_503 = m503Retries;
+			}
 		}
 
 	void setRetries(unsigned int retries, unsigned int retries_503)
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 1cda0b6a71..a86f0e86f1 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -78,10 +78,12 @@
 
 LLMeshRepository gMeshRepo;
 
-const S32 MESH_HEADER_SIZE = 4096;
+const S32 MESH_HEADER_SIZE = 4096;						// Important:  assumption is that headers fit in this space
 const U32 MAX_MESH_REQUESTS_PER_SECOND = 100;
 const S32 REQUEST_HIGH_WATER_MIN = 32;
+const S32 REQUEST_HIGH_WATER_MAX = 80;
 const S32 REQUEST_LOW_WATER_MIN = 16;
+const S32 REQUEST_LOW_WATER_MAX = 40;
 const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;		// Size at which requests goes to narrow/slow queue
 const long LARGE_MESH_XFER_TIMEOUT = 240L;				// Seconds to complete xfer
 
@@ -122,6 +124,7 @@ const std::string header_lod[] =
 	"medium_lod",
 	"high_lod"
 };
+const char * const LOG_MESH = "Mesh";
 
 // Static data and functions to measure mesh load
 // time metrics for a new region scene.
@@ -211,12 +214,21 @@ void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res,
 	}
 }
 
-S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
-S32 LLMeshRepoThread::sActiveLODRequests = 0;
+volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
+volatile S32 LLMeshRepoThread::sActiveLODRequests = 0;
 U32	LLMeshRepoThread::sMaxConcurrentRequests = 1;
 S32 LLMeshRepoThread::sRequestLowWater = REQUEST_LOW_WATER_MIN;
 S32 LLMeshRepoThread::sRequestHighWater = REQUEST_HIGH_WATER_MIN;
 
+// Base handler class for all mesh users of llcorehttp.
+// This is roughly equivalent to a Responder class in
+// traditional LL code.  The base is going to perform
+// common response/data handling in the inherited
+// onCompleted() method.  Derived classes, one for each
+// type of HTTP action, define processData() and
+// processFailure() methods to customize handling and
+// error messages.
+//
 class LLMeshHandlerBase : public LLCore::HttpHandler
 {
 public:
@@ -225,8 +237,10 @@ public:
 		  mMeshParams(),
 		  mProcessed(false),
 		  mHttpHandle(LLCORE_HTTP_HANDLE_INVALID)
-	{}
-	virtual ~LLMeshHandlerBase();
+		{}
+
+	virtual ~LLMeshHandlerBase()
+		{}
 
 protected:
 	LLMeshHandlerBase(const LLMeshHandlerBase &);				// Not defined
@@ -244,6 +258,9 @@ public:
 };
 
 
+// Subclass for header fetches.
+//
+// Thread:  repo
 class LLMeshHeaderHandler : public LLMeshHandlerBase
 {
 public:
@@ -265,6 +282,9 @@ public:
 };
 
 
+// Subclass for LOD fetches.
+//
+// Thread:  repo
 class LLMeshLODHandler : public LLMeshHandlerBase
 {
 public:
@@ -294,6 +314,9 @@ public:
 };
 
 
+// Subclass for skin info fetches.
+//
+// Thread:  repo
 class LLMeshSkinInfoHandler : public LLMeshHandlerBase
 {
 public:
@@ -320,6 +343,9 @@ public:
 };
 
 
+// Subclass for decomposition fetches.
+//
+// Thread:  repo
 class LLMeshDecompositionHandler : public LLMeshHandlerBase
 {
 public:
@@ -346,6 +372,9 @@ public:
 };
 
 
+// Subclass for physics shape fetches.
+//
+// Thread:  repo
 class LLMeshPhysicsShapeHandler : public LLMeshHandlerBase
 {
 public:
@@ -371,6 +400,7 @@ public:
 	U32			mOffset;
 };
 
+
 void log_upload_error(S32 status, const LLSD& content, std::string stage, std::string model_name)
 {
 	// Add notification popup.
@@ -555,6 +585,7 @@ public:
 LLMeshRepoThread::LLMeshRepoThread()
 : LLThread("mesh repo"),
   mWaiting(false),
+  mHttpRetries(0U),
   mHttpRequest(NULL),
   mHttpOptions(NULL),
   mHttpLargeOptions(NULL),
@@ -583,9 +614,9 @@ LLMeshRepoThread::LLMeshRepoThread()
 
 LLMeshRepoThread::~LLMeshRepoThread()
 {
-	LL_INFOS("Mesh") << "Small GETs issued:  "
-					 << mHttpGetCount << ", Large GETs issued:  "
-					 << mHttpLargeGetCount << LL_ENDL;
+	LL_INFOS(LOG_MESH) << "Small GETs issued:  " << mHttpGetCount
+					   << ", Large GETs issued:  " << mHttpLargeGetCount
+					   << LL_ENDL;
 
 	for (http_request_set::iterator iter(mHttpRequestSet.begin());
 		 iter != mHttpRequestSet.end();
@@ -631,10 +662,12 @@ void LLMeshRepoThread::run()
 	{
 		if (! mHttpRequestSet.empty())
 		{
+			// Dispatch all HttpHandler notifications
 			mHttpRequest->update(0L);
 		}
 
 		mWaiting = true;
+		ms_sleep(5);
 		mSignal->wait();
 		mWaiting = false;
 		
@@ -648,42 +681,49 @@ void LLMeshRepoThread::run()
 				last_hundred = gFrameTimeSeconds;
 				count = 0;
 			}
-
+			else
+			{
+				count += mHttpRetries;
+			}
+			mHttpRetries = 0U;
+			
 			// NOTE: throttling intentionally favors LOD requests over header requests
 			
-			while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND)
+			while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater)
 			{
-				if (mMutex)
+				if (! mMutex)
+				{
+					break;
+				}
+				mMutex->lock();
+				LODRequest req = mLODReqQ.front();
+				mLODReqQ.pop();
+				LLMeshRepository::sLODProcessing--;
+				mMutex->unlock();
+				if (!fetchMeshLOD(req.mMeshParams, req.mLOD, count))//failed, resubmit
 				{
 					mMutex->lock();
-					LODRequest req = mLODReqQ.front();
-					mLODReqQ.pop();
-					LLMeshRepository::sLODProcessing--;
+					mLODReqQ.push(req) ; 
+					++LLMeshRepository::sLODProcessing;
 					mMutex->unlock();
-					if (!fetchMeshLOD(req.mMeshParams, req.mLOD, count))//failed, resubmit
-					{
-						mMutex->lock();
-						mLODReqQ.push(req) ; 
-						++LLMeshRepository::sLODProcessing;
-						mMutex->unlock();
-					}
 				}
 			}
 
-			while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND)
+			while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater)
 			{
-				if (mMutex)
+				if (! mMutex)
+				{
+					break;
+				}
+				mMutex->lock();
+				HeaderRequest req = mHeaderReqQ.front();
+				mHeaderReqQ.pop();
+				mMutex->unlock();
+				if (!fetchMeshHeader(req.mMeshParams, count))//failed, resubmit
 				{
 					mMutex->lock();
-					HeaderRequest req = mHeaderReqQ.front();
-					mHeaderReqQ.pop();
+					mHeaderReqQ.push(req) ;
 					mMutex->unlock();
-					if (!fetchMeshHeader(req.mMeshParams, count))//failed, resubmit
-					{
-						mMutex->lock();
-						mHeaderReqQ.push(req) ;
-						mMutex->unlock();
-					}
 				}
 			}
 
@@ -697,7 +737,7 @@ void LLMeshRepoThread::run()
 						incomplete.insert(mesh_id);
 					}
 				}
-				mSkinRequests = incomplete;
+				mSkinRequests.swap(incomplete);
 			}
 
 			{ //mDecompositionRequests is protected by mSignal
@@ -710,7 +750,7 @@ void LLMeshRepoThread::run()
 						incomplete.insert(mesh_id);
 					}
 				}
-				mDecompositionRequests = incomplete;
+				mDecompositionRequests.swap(incomplete);
 			}
 
 			{ //mPhysicsShapeRequests is protected by mSignal
@@ -723,7 +763,7 @@ void LLMeshRepoThread::run()
 						incomplete.insert(mesh_id);
 					}
 				}
-				mPhysicsShapeRequests = incomplete;
+				mPhysicsShapeRequests.swap(incomplete);
 			}
 		}
 	}
@@ -796,6 +836,10 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 	}
 }
 
+// Constructs a Cap URL for the mesh.  Prefers a GetMesh2 cap
+// over a GetMesh cap and returns what it finds to the caller
+// as an int ([1..2]).
+//
 //static 
 std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id, int * cap_version)
 {
@@ -822,14 +866,26 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id, int * cap_version)
 	}
 	else
 	{
-		llwarns << "Current region does not have GetMesh capability!  Cannot load " << mesh_id << ".mesh" << llendl;
+		LL_WARNS_ONCE(LOG_MESH) << "Current region does not have GetMesh capability!  Cannot load "
+								<< mesh_id << ".mesh" << LL_ENDL;
 	}
 
 	*cap_version = version;
 	return http_url;
 }
 
-// May only be called by repo thread
+// Issue an HTTP GET request with byte range using the right
+// policy class.  Large requests go to the large request class.
+// If the current region supports GetMesh2, we prefer that for
+// smaller requests otherwise we try to use the traditional
+// GetMesh capability and connection concurrency.
+//
+// @return		Valid handle or LLCORE_HTTP_HANDLE_INVALID.
+//				If the latter, actual status is found in
+//				mHttpStatus member which is valid until the
+//				next call to this method.
+//
+// Thread:  repo
 LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int cap_version,
 												  size_t offset, size_t len,
 												  LLCore::HttpHandler * handler)
@@ -862,6 +918,11 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c
 												   handler);
 		++mHttpLargeGetCount;
 	}
+	if (LLCORE_HTTP_HANDLE_INVALID == handle)
+	{
+		// Something went wrong, capture the error code for caller.
+		mHttpStatus = mHttpRequest->getStatus();
+	}
 	return handle;
 }
 
@@ -929,12 +990,13 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 			if (!http_url.empty())
 			{
 				LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size);
-				// LL_WARNS("Mesh") << "MESH:  Issuing Skin Info Request" << LL_ENDL;
 				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
-					// *TODO:  Better error message
-					llwarns << "HTTP GET request failed for mesh " << mID << llendl;
+					LL_WARNS(LOG_MESH) << "HTTP GET request failed for skin info on mesh " << mID
+									   << ".  Reason:  " << mHttpStatus.toString()
+									   << " (" << mHttpStatus.toHex() << ")"
+									   << LL_ENDL;
 					delete handler;
 					ret = false;
 				}
@@ -1019,12 +1081,13 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 			if (!http_url.empty())
 			{
 				LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size);
-				// LL_WARNS("Mesh") << "MESH:  Issuing Decomp Request" << LL_ENDL;
 				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
-					// *TODO:  Better error message
-					llwarns << "HTTP GET request failed for decomposition mesh " << mID << llendl;
+					LL_WARNS(LOG_MESH) << "HTTP GET request failed for decomposition mesh " << mID
+									   << ".  Reason:  " << mHttpStatus.toString()
+									   << " (" << mHttpStatus.toHex() << ")"
+									   << LL_ENDL;
 					delete handler;
 					ret = false;
 				}
@@ -1108,12 +1171,13 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 			if (!http_url.empty())
 			{
 				LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size);
-				// LL_WARNS("Mesh") << "MESH:  Issuing Physics Shape Request" << LL_ENDL;
 				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
-					// *TODO:  Better error message
-					llwarns << "HTTP GET request failed for physics shape mesh " << mID << llendl;
+					LL_WARNS(LOG_MESH) << "HTTP GET request failed for physics shape on mesh " << mID
+									   << ".  Reason:  " << mHttpStatus.toString()
+									   << " (" << mHttpStatus.toHex() << ")"
+									   << LL_ENDL;
 					delete handler;
 					ret = false;
 				}
@@ -1192,7 +1256,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 	}
 
 	//either cache entry doesn't exist or is corrupt, request header from simulator	
-	bool retval = true ;
+	bool retval = true;
 	int cap_version(1);
 	std::string http_url = constructUrl(mesh_params.getSculptID(), &cap_version);
 	if (!http_url.empty())
@@ -1202,12 +1266,13 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 		//NOTE -- this will break of headers ever exceed 4KB		
 
 		LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params);
-		// LL_WARNS("Mesh") << "MESH:  Issuing Request" << LL_ENDL;
 		LLCore::HttpHandle handle = getByteRange(http_url, cap_version, 0, MESH_HEADER_SIZE, handler);
 		if (LLCORE_HTTP_HANDLE_INVALID == handle)
 		{
-			// *TODO:  Better error message
-			llwarns << "HTTP GET request failed for mesh " << mID << llendl;
+			LL_WARNS(LOG_MESH) << "HTTP GET request failed for mesh header " << mID
+							   << ".  Reason:  " << mHttpStatus.toString()
+							   << " (" << mHttpStatus.toHex() << ")"
+							   << LL_ENDL;
 			delete handler;
 			retval = false;
 		}
@@ -1216,8 +1281,8 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 			handler->mHttpHandle = handle;
 			mHttpRequestSet.insert(handler);
 			++LLMeshRepository::sHTTPRequestCount;
+			++count;
 		}
-		count++;
 	}
 
 	return retval;
@@ -1283,12 +1348,13 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 			if (!http_url.empty())
 			{
 				LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size);
-				// LL_WARNS("Mesh") << "MESH:  Issuing LOD Request" << LL_ENDL;
 				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler);
 				if (LLCORE_HTTP_HANDLE_INVALID == handle)
 				{
-					// *TODO:  Better error message
-					llwarns << "HTTP GET request failed for LOD mesh " << mID << llendl;
+					LL_WARNS(LOG_MESH) << "HTTP GET request failed for LOD on mesh " << mID
+									   << ".  Reason:  " << mHttpStatus.toString()
+									   << " (" << mHttpStatus.toHex() << ")"
+									   << LL_ENDL;
 					delete handler;
 					retval = false;
 				}
@@ -1297,8 +1363,8 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
 					++LLMeshRepository::sHTTPRequestCount;
+					++count;
 				}
-				count++;
 			}
 			else
 			{
@@ -1320,6 +1386,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 
 bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size)
 {
+	const LLUUID mesh_id = mesh_params.getSculptID();
 	LLSD header;
 	
 	U32 header_size = 0;
@@ -1340,7 +1407,8 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat
 
 		if (!LLSDSerialize::fromBinary(header, stream, data_size))
 		{
-			llwarns << "Mesh header parse error.  Not a valid mesh asset!" << llendl;
+			LL_WARNS(LOG_MESH) << "Mesh header parse error.  Not a valid mesh asset!  ID:  " << mesh_id
+							   << LL_ENDL;
 			return false;
 		}
 
@@ -1348,13 +1416,12 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat
 	}
 	else
 	{
-		llinfos
-			<< "Marking header as non-existent, will not retry." << llendl;
+		LL_INFOS(LOG_MESH) << "Non-positive data size.  Marking header as non-existent, will not retry.  ID:  " << mesh_id
+						   << LL_ENDL;
 		header["404"] = 1;
 	}
 
 	{
-		LLUUID mesh_id = mesh_params.getSculptID();
 		
 		{
 			LLMutexLock lock(mHeaderMutex);
@@ -1416,7 +1483,8 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
 
 		if (!unzip_llsd(skin, stream, data_size))
 		{
-			llwarns << "Mesh skin info parse error.  Not a valid mesh asset!" << llendl;
+			LL_WARNS(LOG_MESH) << "Mesh skin info parse error.  Not a valid mesh asset!  ID:  " << mesh_id
+							   << LL_ENDL;
 			return false;
 		}
 	}
@@ -1444,7 +1512,8 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
 
 		if (!unzip_llsd(decomp, stream, data_size))
 		{
-			llwarns << "Mesh decomposition parse error.  Not a valid mesh asset!" << llendl;
+			LL_WARNS(LOG_MESH) << "Mesh decomposition parse error.  Not a valid mesh asset!  ID:  " << mesh_id
+							   << LL_ENDL;
 			return false;
 		}
 	}
@@ -1860,7 +1929,8 @@ void LLMeshUploadThread::doWholeModelUpload()
 
 	if (mWholeModelUploadURL.empty())
 	{
-		llinfos << "unable to upload, fee request failed" << llendl;
+		LL_WARNS(LOG_MESH) << "Missing mesh upload capability, unable to upload, fee request failed."
+						   << LL_ENDL;
 	}
 	else
 	{
@@ -1941,6 +2011,12 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 		return;
 	}
 
+	if (!mLoadedQ.empty() || !mUnavailableQ.empty())
+	{
+		// Ping time-to-load metrics for mesh download operations.
+		LLMeshRepository::metricsProgress(0);
+	}
+	
 	while (!mLoadedQ.empty())
 	{
 		mMutex->lock();
@@ -2058,12 +2134,17 @@ void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header)
 
 }
 
-LLMeshHandlerBase::~LLMeshHandlerBase()
-{}
-
 void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)
 {
 	mProcessed = true;
+
+	// Accumulate retries, we'll use these to offset the HTTP
+	// count and maybe hold to a throttle better.
+	unsigned int retries(0U);
+	response->getRetries(NULL, &retries);
+	gMeshRepo.mThread->mHttpRetries += retries;
+	LLMeshRepository::sHTTPRetryCount += retries;
+
 	LLCore::HttpStatus status(response->getStatus());
 	if (! status)
 	{
@@ -2090,7 +2171,7 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
 			// that support BufferArray directly.
 			data = new U8[data_size];
 			body->read(0, (char *) data, data_size);
-			LLMeshRepository::sBytesReceived += llmin(data_size, MESH_HEADER_SIZE);
+			LLMeshRepository::sBytesReceived += data_size;
 		}
 
 		processData(body, data, data_size);
@@ -2100,7 +2181,6 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
 
 	// Release handler
 	gMeshRepo.mThread->mHttpRequestSet.erase(this);
-
 	delete this;		// Must be last statement
 }
 
@@ -2112,8 +2192,7 @@ LLMeshHeaderHandler::~LLMeshHeaderHandler()
 		if (! mProcessed)
 		{
 			// something went wrong, retry
-			llwarns << "Timeout or service unavailable, retrying." << llendl;
-			LLMeshRepository::sHTTPRetryCount++;
+			LL_WARNS(LOG_MESH) << "Mesh header fetch canceled unexpectedly, retrying." << LL_ENDL;
 			LLMeshRepoThread::HeaderRequest req(mMeshParams);
 			LLMutexLock lock(gMeshRepo.mThread->mMutex);
 			gMeshRepo.mThread->mHeaderReqQ.push(req);
@@ -2124,36 +2203,39 @@ LLMeshHeaderHandler::~LLMeshHeaderHandler()
 
 void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
 {
-	LL_WARNS("Mesh") << "MESH:  Processing Failure" << LL_ENDL;
 	if (is_retryable(status))
 	{
-		llwarns << "Timeout or service unavailable, retrying." << llendl;
-		LLMeshRepository::sHTTPRetryCount++;
+		LL_WARNS(LOG_MESH) << "Error during mesh header handling.  Reason:  " << status.toString()
+						   << " (" << status.toHex() << ").  Retrying."
+						   << LL_ENDL;
 		LLMeshRepoThread::HeaderRequest req(mMeshParams);
 		LLMutexLock lock(gMeshRepo.mThread->mMutex);
 		gMeshRepo.mThread->mHeaderReqQ.push(req);
 	}
 	else
 	{
-		// *TODO:  better error message
-		llwarns << "Unhandled status." << llendl;
+		// *TODO:  Mark mesh unavailable
+		LL_WARNS(LOG_MESH) << "Error during mesh header handling.  Reason:  " << status.toString()
+						   << " (" << status.toHex() << ").  Not retrying."
+						   << LL_ENDL;
 	}
 }
 
 void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
 {
-	// LL_WARNS("Mesh") << "MESH:  Processing Data" << LL_ENDL;
+	LLUUID mesh_id = mMeshParams.getSculptID();
 	bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size);
 	llassert(success);
 	if (! success)
 	{
+		// *TODO:  Mark mesh unavailable
 		// *TODO:  Get real reason for parse failure here
-		llwarns << "Unable to parse mesh header: " << llendl;
+		LL_WARNS(LOG_MESH) << "Unable to parse mesh header.  ID:  " << mesh_id
+						   << LL_ENDL;
 	}
 	else if (data && data_size > 0)
 	{
 		// header was successfully retrieved from sim, cache in vfs
-		LLUUID mesh_id = mMeshParams.getSculptID();
 		LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id];
 
 		S32 version = header["version"].asInteger();
@@ -2192,15 +2274,14 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32
 			
 				// zero out the rest of the file 
 				U8 block[MESH_HEADER_SIZE];
-				memset(block, 0, MESH_HEADER_SIZE);
+				memset(block, 0, sizeof(block));
 
-				while (bytes-file.tell() > MESH_HEADER_SIZE)
+				while (bytes-file.tell() > sizeof(block))
 				{
-					file.write(block, MESH_HEADER_SIZE);
+					file.write(block, sizeof(block));
 				}
 
 				S32 remaining = bytes-file.tell();
-					
 				if (remaining > 0)
 				{
 					file.write(block, remaining);
@@ -2216,8 +2297,7 @@ LLMeshLODHandler::~LLMeshLODHandler()
 	{
 		if (! mProcessed)
 		{
-			llwarns << "Killed without being processed, retrying." << llendl;
-			LLMeshRepository::sHTTPRetryCount++;
+			LL_WARNS(LOG_MESH) << "Mesh LOD fetch canceled unexpectedly, retrying." << LL_ENDL;
 			gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD);
 		}
 		LLMeshRepoThread::decActiveLODRequests();
@@ -2228,15 +2308,21 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)
 {
 	if (is_retryable(status))
 	{
-		llwarns << "Timeout or service unavailable, retrying." << llendl;
-		LLMeshRepository::sHTTPRetryCount++;
-		// *FIXME:  Is this safe?  Does this need locking?
-		gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD);
+		LL_WARNS(LOG_MESH) << "Error during mesh header handling.  Reason:  " << status.toString()
+						   << " (" << status.toHex() << ").  Retrying."
+						   << LL_ENDL;
+		{
+			LLMutexLock lock(gMeshRepo.mThread->mMutex);
+
+			gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD);
+		}
 	}
 	else
 	{
-		// *TODO:  better error message
-		llwarns << "Unhandled status." << llendl;
+		// *TODO:  Mark mesh unavailable
+		LL_WARNS(LOG_MESH) << "Error during mesh LOD handling.  Reason:  " << status.toString()
+						   << " (" << status.toHex() << ").  Not retrying."
+						   << LL_ENDL;
 	}
 }
 
@@ -2257,6 +2343,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da
 			LLMeshRepository::sCacheBytesWritten += size;
 		}
 	}
+	// *TODO:  Mark mesh unavailable on error
 }
 
 LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler()
@@ -2268,15 +2355,21 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)
 {
 	if (is_retryable(status))
 	{
-		llwarns << "Timeout or service unavailable, retrying." << llendl;
-		LLMeshRepository::sHTTPRetryCount++;
-		// *FIXME:  Is this safe?  Does this need locking?
-		gMeshRepo.mThread->loadMeshSkinInfo(mMeshID);
+		LL_WARNS(LOG_MESH) << "Error during mesh skin info handling.  Reason:  " << status.toString()
+						   << " (" << status.toHex() << ").  Retrying."
+						   << LL_ENDL;
+		{
+			LLMutexLock lock(gMeshRepo.mThread->mMutex);
+
+			gMeshRepo.mThread->loadMeshSkinInfo(mMeshID);
+		}
 	}
 	else
 	{
-		// *TODO:  better error message
-		llwarns << "Unhandled status." << llendl;
+		// *TODO:  Mark mesh unavailable on error
+		LL_WARNS(LOG_MESH) << "Error during mesh skin info handling.  Reason:  " << status.toString()
+						   << " (" << status.toHex() << ").  Not retrying."
+						   << LL_ENDL;
 	}
 }
 
@@ -2297,6 +2390,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S
 			file.write(data, size);
 		}
 	}
+	// *TODO:  Mark mesh unavailable on error
 }
 
 LLMeshDecompositionHandler::~LLMeshDecompositionHandler()
@@ -2308,15 +2402,21 @@ void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status)
 {
 	if (is_retryable(status))
 	{
-		llwarns << "Timeout or service unavailable, retrying." << llendl;
-		LLMeshRepository::sHTTPRetryCount++;
-		// *FIXME:  Is this safe?  Does this need locking?
-		gMeshRepo.mThread->loadMeshDecomposition(mMeshID);
+		LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling.  Reason:  " << status.toString()
+						   << " (" << status.toHex() << ").  Retrying."
+						   << LL_ENDL;
+		{
+			LLMutexLock lock(gMeshRepo.mThread->mMutex);
+
+			gMeshRepo.mThread->loadMeshDecomposition(mMeshID);
+		}
 	}
 	else
 	{
-		// *TODO:  better error message
-		llwarns << "Unhandled status." << llendl;
+		// *TODO:  Mark mesh unavailable on error
+		LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling.  Reason:  " << status.toString()
+						   << " (" << status.toHex() << ").  Not retrying."
+						   << LL_ENDL;
 	}
 }
 
@@ -2337,6 +2437,7 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * da
 			file.write(data, size);
 		}
 	}
+	// *TODO:  Mark mesh unavailable on error
 }
 
 LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler()
@@ -2348,15 +2449,21 @@ void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status)
 {
 	if (is_retryable(status))
 	{
-		llwarns << "Timeout or service unavailable, retrying." << llendl;
-		LLMeshRepository::sHTTPRetryCount++;
-		// *FIXME:  Is this safe?  Does this need locking?
-		gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID);
+		LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling.  Reason:  " << status.toString()
+						   << " (" << status.toHex() << ").  Retrying."
+						   << LL_ENDL;
+		{
+			LLMutexLock lock(gMeshRepo.mThread->mMutex);
+
+			gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID);
+		}
 	}
 	else
 	{
-		// *TODO:  better error message
-		llwarns << "Unhandled status." << llendl;
+		// *TODO:  Mark mesh unavailable on error
+		LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling.  Reason:  " << status.toString()
+						   << " (" << status.toHex() << ").  Not retrying."
+						   << LL_ENDL;
 	}
 }
 
@@ -2377,6 +2484,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat
 			file.write(data, size);
 		}
 	}
+	// *TODO:  Mark mesh unavailable on error
 }
 
 LLMeshRepository::LLMeshRepository()
@@ -2574,12 +2682,15 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
 void LLMeshRepository::notifyLoadedMeshes()
 { //called from main thread
 	// *FIXME:  Scaling down the setting by a factor of 4 for now to reflect
-	// target goal.  May want to rename the setting before release.
+	// target goal.  May want to rename the setting before release.  Also
+	// want/need to get these in a coordinated fashion from llappcorehttp.
 	LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests") / 4;
-	LLMeshRepoThread::sRequestHighWater = llmax(50 * S32(LLMeshRepoThread::sMaxConcurrentRequests),
-												REQUEST_HIGH_WATER_MIN);
-	LLMeshRepoThread::sRequestLowWater = llmax(LLMeshRepoThread::sRequestLowWater / 2,
-											   REQUEST_LOW_WATER_MIN);
+	LLMeshRepoThread::sRequestHighWater = llclamp(10 * S32(LLMeshRepoThread::sMaxConcurrentRequests),
+												  REQUEST_HIGH_WATER_MIN,
+												  REQUEST_HIGH_WATER_MAX);
+	LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2,
+												 REQUEST_LOW_WATER_MIN,
+												 REQUEST_LOW_WATER_MAX);
 	
 	//clean up completed upload threads
 	for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); )
@@ -2672,6 +2783,10 @@ void LLMeshRepository::notifyLoadedMeshes()
 			region_name = gAgent.getRegion()->getName();
 			mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh");
 			mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2");
+			LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name
+								<< "', GetMesh2:  " << mGetMesh2Capability
+								<< ", GetMesh:  " << mGetMeshCapability
+								<< LL_ENDL;
 		}
 	}
 
@@ -2810,9 +2925,6 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom
 
 void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume)
 { //called from main thread
-	// Manage time-to-load metrics for mesh download operations.
-	metricsProgress(0);
-	
 	S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
 
 	//get list of objects waiting to be notified this mesh is loaded
@@ -2823,7 +2935,8 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
 		//make sure target volume is still valid
 		if (volume->getNumVolumeFaces() <= 0)
 		{
-			llwarns << "Mesh loading returned empty volume." << llendl;
+			LL_WARNS(LOG_MESH) << "Mesh loading returned empty volume.  ID:  " << mesh_params.getSculptID()
+							   << LL_ENDL;
 		}
 		
 		{ //update system volume
@@ -2836,7 +2949,8 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
 			}
 			else
 			{
-				llwarns << "Couldn't find system volume for given mesh." << llendl;
+				LL_WARNS(LOG_MESH) << "Couldn't find system volume for mesh " << mesh_params.getSculptID()
+								   << LL_ENDL;
 			}
 		}
 
@@ -2856,9 +2970,6 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
 
 void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod)
 { //called from main thread
-	// Manage time-to-load metrics for mesh download operations.
-	metricsProgress(0);
-	
 	//get list of objects waiting to be notified this mesh is loaded
 	mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params);
 
@@ -3029,7 +3140,7 @@ LLSD& LLMeshRepoThread::getMeshHeader(const LLUUID& mesh_id)
 
 
 void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures,
-									bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload,
+								   bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload,
 								   LLHandle<LLWholeModelFeeObserver> fee_observer, LLHandle<LLWholeModelUploadObserver> upload_observer)
 {
 	LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints, upload_url, 
@@ -3058,7 +3169,6 @@ S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod)
 	}
 
 	return -1;
-
 }
 
 void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation,
@@ -3880,7 +3990,7 @@ void LLMeshRepository::metricsUpdate()
 		metrics["scope"] = "Login";
 		metrics["start"] = started;
 		metrics["stop"] = stopped;
-		metrics["downloads"] = LLSD::Integer(total_count);
+		metrics["fetches"] = LLSD::Integer(total_count);
 		metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count);
 		metrics["user_cpu"] = double(user_cpu) / 1.0e6;
 		metrics["sys_cpu"] = double(sys_cpu) / 1.0e6;
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 74690e5a2a..e90ab4dd23 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -221,8 +221,8 @@ class LLMeshRepoThread : public LLThread
 {
 public:
 
-	static S32 sActiveHeaderRequests;
-	static S32 sActiveLODRequests;
+	volatile static S32 sActiveHeaderRequests;
+	volatile static S32 sActiveLODRequests;
 	static U32 sMaxConcurrentRequests;
 	static S32 sRequestLowWater;
 	static S32 sRequestHighWater;
@@ -323,6 +323,8 @@ public:
 	pending_lod_map mPendingLOD;
 
 	// llcorehttp library interface objects.
+	LLCore::HttpStatus					mHttpStatus;
+	unsigned int						mHttpRetries;
 	LLCore::HttpRequest *				mHttpRequest;
 	LLCore::HttpOptions *				mHttpOptions;
 	LLCore::HttpOptions *				mHttpLargeOptions;
-- 
cgit v1.2.3


From 350e658348431e4b1f0e5038cf999d9722a699aa Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 27 Jun 2013 20:38:18 -0400
Subject: SH-4311 Get highwater limiting of requests into llhttpcore working.
 Fixed the logic and have it covering all five types of requests now with
 validation via an assert (when enabled).  Should keep things working smoothly
 and avoid floods of 503s when in debug modes.  Also started a round of
 file-level documentation detailing thread usage and mutex coverage.  More to
 do, more to describe.  But the high- water stuff is functioning correctly.

---
 indra/newview/llmeshrepository.cpp | 189 ++++++++++++++++++++++++++++++++++---
 indra/newview/llmeshrepository.h   |   6 +-
 2 files changed, 177 insertions(+), 18 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index a86f0e86f1..aa10e64af8 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -76,9 +76,140 @@
 
 #include <queue>
 
+
+// [ Disclaimer:  this documentation isn't by one of the original authors
+//   but by someone coming through later and extracting intent and function.
+//   Some of this will be wrong so use judgement. ]
+//
+// Purpose
+//
+//   The purpose of this module is to provide access between the viewer
+//   and the asset system as regards to mesh objects.
+//
+//   * High-throughput download of mesh assets from servers while
+//     following best industry practices for network profile.
+//   * Reliable expensing and upload of new mesh assets.
+//   * Recovery and retry from errors when appropriate.
+//   * Decomposition of mesh assets for preview and uploads.
+//   * And most important:  all of the above without exposing the
+//     main thread to stalls due to deep processing or thread
+//     locking actions.  In particular, the following operations
+//     on LLMeshRepository are very averse to any stalls:
+//     * loadMesh
+//     * getMeshHeader (For structural details, see:
+//       http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format)
+//     * notifyLoadedMeshes
+//
+// Threads
+//
+//   main     Main rendering thread, very sensitive to locking and other stalls
+//   repo     Overseeing worker thread associated with the LLMeshRepoThread class
+//   decom    Worker thread for mesh decomposition requests
+//   core     HTTP worker thread:  does the work but doesn't intrude here
+//   uploadN  0-N temporary mesh upload threads
+//
+// Mutexes
+//
+//   LLMeshRepository::mMeshMutex
+//   LLMeshRepoThread::mMutex
+//   LLMeshRepoThread::mHeaderMutex
+//   LLMeshRepoThread::mSignal (LLCondition)
+//   LLPhysicsDecomp::mSignal (LLCondition)
+//   LLPhysicsDecomp::mMutex
+//   LLMeshUploadThread::mMutex
+//
+// Mutex Order Rules
+//
+//   1.  LLMeshRepoThread::mMutex before LLMeshRepoThread::mHeaderMutex
+//   2.  LLMeshRepository::mMeshMutex before LLMeshRepoThread::mMutex
+//   (There are more rules, haven't been extracted.)
+//
+// Data Member Access/Locking
+//
+//   Description of how shared access to static and instance data
+//   members is performed.  Each member is followed by the name of
+//   the mutex, if any, covering the data and then a list of data
+//   access models each of which is a triplet of the following form:
+//
+//     {ro, wo, rw}.{main, repo, any}.{mutex, none}
+//     Type of access:  read-only, write-only, read-write.
+//     Accessing thread or 'any'
+//     Relevant mutex held during access (several may be held) or 'none'
+//
+//   A careful eye will notice some unsafe operations.  Many of these
+//   have an alibi of some form.  Several types of alibi are identified
+//   and listed here:
+//
+//     [0]  No alibi.  Probably unsafe.
+//     [1]  Single-writer, self-consistent readers.  Old data must
+//          be tolerated by any reader but data will come true eventually.
+//     [2]  Like [1] but provides a hint about thread state.  These
+//          may be unsafe.
+//     [3]  empty() check outside of lock.  Can me made safish when
+//          done in double-check lock style.  But this depends on
+//          std:: implementation and memory model.
+//     [4]  Appears to be covered by a mutex but doesn't need one.
+//     [5]  Read of a double-checked lock.
+//
+//   So, in addition to documentation, take this as a to-do/review
+//   list and see if you can improve things.
+//
+//   LLMeshRepository:
+//
+//     sBytesReceived
+//     sHTTPRequestCount
+//     sHTTPRetryCount
+//     sLODPending
+//     sLODProcessing
+//     sCacheBytesRead
+//     sCacheBytesWritten
+//     mLoadingMeshes                  none            rw.main.none, rw.main.mMeshMutex [4]
+//     mSkinMap                        none            rw.main.none
+//     mDecompositionMap               none            rw.main.none
+//     mPendingRequests                mMeshMutex [4]  rw.main.mMeshMutex
+//     mLoadingSkins                   mMeshMutex [4]  rw.main.mMeshMutex
+//     mPendingSkinRequests            mMeshMutex [4]  rw.main.mMeshMutex
+//     mLoadingDecompositions          mMeshMutex [4]  rw.main.mMeshMutex
+//     mPendingDecompositionRequests   mMeshMutex [4]  rw.main.mMeshMutex
+//     mLoadingPhysicsShapes           mMeshMutex [4]  rw.main.mMeshMutex
+//     mPendingPhysicsShapeRequests    mMeshMutex [4]  rw.main.mMeshMutex
+//     mUploads                        none            rw.main.none (upload thread accessing objects)
+//     mUploadWaitList                 none            rw.main.none (upload thread accessing objects)
+//     mInventoryQ                     mMeshMutex [4]  rw.main.mMeshMutex, ro.main.none [5]
+//     mUploadErrorQ                   mMeshMutex      rw.main.mMeshMutex, rw.any.mMeshMutex
+//     mGetMeshCapability              none            rw.main.none [0], ro.any.none
+//     mGetMesh2Capability             none            rw.main.none [0], ro.any.none
+//
+//   LLMeshRepoThread:
+//
+//     sActiveHeaderRequests    mMutex        rw.any.mMutex, ro.repo.none [1]
+//     sActiveLODRequests       mMutex        rw.any.mMutex, ro.repo.none [1]
+//     sMaxConcurrentRequests   mMutex        wo.main.none, ro.repo.none, ro.main.mMutex
+//     mWaiting                 mMutex        rw.repo.none, ro.main.none [2] (race - hint)
+//     mMeshHeader              mHeaderMutex  rw.repo.mHeaderMutex, ro.main.mHeaderMutex, ro.main.none [0]
+//     mMeshHeaderSize          mHeaderMutex  rw.repo.mHeaderMutex
+//     mSkinRequests            none          rw.repo.none, rw.main.none [0]
+//     mSkinInfoQ               none          rw.repo.none, rw.main.none [0]
+//     mDecompositionRequests   none          rw.repo.none, rw.main.none [0]
+//     mPhysicsShapeRequests    none          rw.repo.none, rw.main.none [0]
+//     mDecompositionQ          none          rw.repo.none, rw.main.none [0]
+//     mHeaderReqQ              mMutex        ro.repo.none [3], rw.repo.mMutex, rw.any.mMutex
+//     mLODReqQ                 mMutex        ro.repo.none [3], rw.repo.mMutex, rw.any.mMutex
+//     mUnavailableQ            mMutex        rw.repo.none [0], ro.main.none [3], rw.main.mMutex
+//     mLoadedQ                 mMutex        rw.repo.mMutex, ro.main.none [3], rw.main.mMutex
+//     mPendingLOD              mMutex        rw.repo.mMutex, rw.any.mMutex
+//
+//   LLPhysicsDecomp:
+//    
+//     mRequestQ
+//     mCurRequest
+//     mCompletedQ
+//
+
+
 LLMeshRepository gMeshRepo;
 
-const S32 MESH_HEADER_SIZE = 4096;						// Important:  assumption is that headers fit in this space
+const S32 MESH_HEADER_SIZE = 4096;                      // Important:  assumption is that headers fit in this space
 const U32 MAX_MESH_REQUESTS_PER_SECOND = 100;
 const S32 REQUEST_HIGH_WATER_MIN = 32;
 const S32 REQUEST_HIGH_WATER_MAX = 80;
@@ -727,12 +858,21 @@ void LLMeshRepoThread::run()
 				}
 			}
 
-			{ //mSkinRequests is protected by mSignal
+			// For the final three request lists, if we scan any part of one
+			// list, we scan the entire thing.  This gets us through any requests
+			// which can be resolved in the cache.  It also keeps the request
+			// set somewhat fresher otherwise items at the end of the set
+			// order will lose.  Keep to the throttle enforcement and pay
+			// attention to the highwater level (enforced in each fetchXXX()
+			// method).
+			if (! mSkinRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater)
+			{
+				// *FIXME:  this really does need a lock as do the following ones
 				std::set<LLUUID> incomplete;
 				for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter)
 				{
 					LLUUID mesh_id = *iter;
-					if (!fetchMeshSkinInfo(mesh_id))
+					if (!fetchMeshSkinInfo(mesh_id, count))
 					{
 						incomplete.insert(mesh_id);
 					}
@@ -740,12 +880,13 @@ void LLMeshRepoThread::run()
 				mSkinRequests.swap(incomplete);
 			}
 
-			{ //mDecompositionRequests is protected by mSignal
+			if (! mDecompositionRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater)
+			{
 				std::set<LLUUID> incomplete;
 				for (std::set<LLUUID>::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter)
 				{
 					LLUUID mesh_id = *iter;
-					if (!fetchMeshDecomposition(mesh_id))
+					if (!fetchMeshDecomposition(mesh_id, count))
 					{
 						incomplete.insert(mesh_id);
 					}
@@ -753,18 +894,23 @@ void LLMeshRepoThread::run()
 				mDecompositionRequests.swap(incomplete);
 			}
 
-			{ //mPhysicsShapeRequests is protected by mSignal
+			if (! mPhysicsShapeRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater)
+			{
 				std::set<LLUUID> incomplete;
 				for (std::set<LLUUID>::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter)
 				{
 					LLUUID mesh_id = *iter;
-					if (!fetchMeshPhysicsShape(mesh_id))
+					if (!fetchMeshPhysicsShape(mesh_id, count))
 					{
 						incomplete.insert(mesh_id);
 					}
 				}
 				mPhysicsShapeRequests.swap(incomplete);
 			}
+
+			// For dev purposes, a dynamic change could make this false
+			// and that shouldn't assert.
+			// llassert_always(mHttpRequestSet.size() <= sRequestHighWater);
 		}
 	}
 	
@@ -927,8 +1073,8 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c
 }
 
 
-bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
-{ //protected by mMutex
+bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count)
+{
 	
 	if (!mHeaderMutex)
 	{
@@ -985,6 +1131,10 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
+			if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater)
+			{
+				return false;
+			}
 			int cap_version(1);
 			std::string http_url = constructUrl(mesh_id, &cap_version);
 			if (!http_url.empty())
@@ -1018,8 +1168,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 	return ret;
 }
 
-bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
-{ //protected by mMutex
+bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id, U32& count)
+{
 	if (!mHeaderMutex)
 	{
 		return false;
@@ -1076,6 +1226,10 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
+			if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater)
+			{
+				return false;
+			}
 			int cap_version(1);
 			std::string http_url = constructUrl(mesh_id, &cap_version);
 			if (!http_url.empty())
@@ -1109,8 +1263,8 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 	return ret;
 }
 
-bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
-{ //protected by mMutex
+bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count)
+{
 	if (!mHeaderMutex)
 	{
 		return false;
@@ -1166,6 +1320,10 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
+			if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater)
+			{
+				return false;
+			}
 			int cap_version(1);
 			std::string http_url = constructUrl(mesh_id, &cap_version);
 			if (!http_url.empty())
@@ -1290,7 +1448,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 
 //return false if failed to get mesh lod.
 bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count)
-{ //protected by mMutex
+{
 	if (!mHeaderMutex)
 	{
 		return false;
@@ -2249,7 +2407,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32
 			for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i)
 			{
 				// figure out how many bytes we'll need to reserve in the file
-				std::string lod_name = header_lod[i];
+				const std::string & lod_name = header_lod[i];
 				lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger());
 			}
 		
@@ -3047,6 +3205,7 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
 			std::set<LLUUID>::iterator iter = mLoadingPhysicsShapes.find(mesh_id);
 			if (iter == mLoadingPhysicsShapes.end())
 			{ //no request pending for this skin info
+				// *FIXME:  Nothing ever deletes entries, can't be right
 				mLoadingPhysicsShapes.insert(mesh_id);
 				mPendingPhysicsShapeRequests.push(mesh_id);
 			}
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index e90ab4dd23..9bf14a3715 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -365,15 +365,15 @@ public:
 
 	//send request for skin info, returns true if header info exists 
 	//  (should hold onto mesh_id and try again later if header info does not exist)
-	bool fetchMeshSkinInfo(const LLUUID& mesh_id);
+	bool fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count);
 
 	//send request for decomposition, returns true if header info exists 
 	//  (should hold onto mesh_id and try again later if header info does not exist)
-	bool fetchMeshDecomposition(const LLUUID& mesh_id);
+	bool fetchMeshDecomposition(const LLUUID& mesh_id, U32& count);
 
 	//send request for PhysicsShape, returns true if header info exists 
 	//  (should hold onto mesh_id and try again later if header info does not exist)
-	bool fetchMeshPhysicsShape(const LLUUID& mesh_id);
+	bool fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count);
 
 	static void incActiveLODRequests();
 	static void decActiveLODRequests();
-- 
cgit v1.2.3


From 8c5518f275029094ed42979a0f30d8314afadc3d Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 28 Jun 2013 17:57:57 -0400
Subject: SH-4257  Prepare for new 'GetMesh2' capability. Needed to move the
 cap from response to region instance.

---
 indra/newview/llviewerregion.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

(limited to 'indra')

diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index b8b53aa6e4..41f63c7ef6 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2000&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2010-2013, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -1597,6 +1597,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
 
 	capabilityNames.append("GetDisplayNames");
 	capabilityNames.append("GetMesh");
+	capabilityNames.append("GetMesh2");
 	capabilityNames.append("GetObjectCost");
 	capabilityNames.append("GetObjectPhysicsData");
 	capabilityNames.append("GetTexture");
-- 
cgit v1.2.3


From a12d5d7c6d8d816512ec9fc1355b37ce38a89cce Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 28 Jun 2013 20:08:29 -0400
Subject: SH-4312  Clumsy configuration coordination between mesh and corehttp
 Taught llappcorehttp to register signals on the settings values that chagne
 behavior.  Have initialization and settings changes sweep through settings
 and change them.  Dynamic changes are tried but have no effect (produce a
 warning message) as dynamic settings still aren't supported but the plumbing
 is now connected.  Just need to change llcorehttp.  Bounced the 'teleport
 started' signal around and it ended up back where it started with some
 cleanup. This is making me less angry...

---
 indra/newview/llappcorehttp.cpp    | 210 +++++++++++++++++++++++--------------
 indra/newview/llappcorehttp.h      |   9 +-
 indra/newview/llmeshrepository.cpp |  30 ++----
 3 files changed, 145 insertions(+), 104 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index 2467c02d4d..70e349e33d 100755
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -28,10 +28,52 @@
 
 #include "llappcorehttp.h"
 
+#include "llappviewer.h"
 #include "llviewercontrol.h"
 
 
 const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0);
+static const struct
+{
+	LLAppCoreHttp::EAppPolicy	mPolicy;
+	U32							mDefault;
+	U32							mMin;
+	U32							mMax;
+	U32							mDivisor;
+	std::string					mKey;
+	const char *				mUsage;
+} init_data[] =					//  Default and dynamic values for classes
+{
+	{
+		LLAppCoreHttp::AP_TEXTURE,			8,		1,		12,		1,
+		"TextureFetchConcurrency",
+		"texture fetch"
+	},
+	{
+		LLAppCoreHttp::AP_MESH1,			32,		1,		128,	1,
+		"MeshMaxConcurrentRequests",
+		"mesh fetch"
+	},
+	{
+		LLAppCoreHttp::AP_MESH2,			8,		1,		32,		4,
+		"MeshMaxConcurrentRequests",
+		"mesh2 fetch"
+	},
+	{
+		LLAppCoreHttp::AP_LARGE_MESH,		2,		1,		8,		1,
+		"",
+		"large mesh fetch"
+	},
+	{
+		LLAppCoreHttp::AP_UPLOADS,			2,		1,		8,		1,
+		"",
+		"asset upload"
+	}
+};
+
+static void teleport_started();
+static void setting_changed();
+
 
 LLAppCoreHttp::LLAppCoreHttp()
 	: mRequest(NULL),
@@ -42,6 +84,7 @@ LLAppCoreHttp::LLAppCoreHttp()
 	for (int i(0); i < LL_ARRAY_SIZE(mPolicies); ++i)
 	{
 		mPolicies[i] = LLCore::HttpRequest::DEFAULT_POLICY_ID;
+		mSettings[i] = 0U;
 	}
 }
 
@@ -55,45 +98,6 @@ LLAppCoreHttp::~LLAppCoreHttp()
 
 void LLAppCoreHttp::init()
 {
-	static const struct
-	{
-		EAppPolicy		mPolicy;
-		U32				mDefault;
-		U32				mMin;
-		U32				mMax;
-		U32				mDivisor;
-		std::string		mKey;
-		const char *	mUsage;
-	} init_data[] =					//  Default and dynamic values for classes
-		  {
-			  {
-				  AP_TEXTURE,			8,		1,		12,		1,
-				  "TextureFetchConcurrency",
-				  "texture fetch"
-			  },
-			  {
-				  // *FIXME:  Should become 32, 1, 32, 1 before release
-				  AP_MESH1,				8,		1,		32,		4,
-				  "MeshMaxConcurrentRequests",
-				  "mesh fetch"
-			  },
-			  {
-				  AP_MESH2,				8,		1,		32,		4,
-				  "MeshMaxConcurrentRequests",
-				  "mesh2 fetch"
-			  },
-			  {
-				  AP_LARGE_MESH,		2,		1,		8,		1,
-				  "",
-				  "large mesh fetch"
-			  },
-			  {
-				  AP_UPLOADS,			2,		1,		8,		1,
-				  "",
-				  "asset upload"
-			  }
-		  };
-		
 	LLCore::HttpStatus status = LLCore::HttpRequest::createService();
 	if (! status)
 	{
@@ -110,14 +114,12 @@ void LLAppCoreHttp::init()
 						<< LL_ENDL;
 	}
 
-	// Establish HTTP Proxy.  "LLProxy" is a special string which directs
-	// the code to use LLProxy::applyProxySettings() to establish any
-	// HTTP or SOCKS proxy for http operations.
+	// Establish HTTP Proxy, if desired.
 	status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1);
 	if (! status)
 	{
-		LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services.  Reason:  " << status.toString()
-						<< LL_ENDL;
+		LL_WARNS("Init") << "Failed to set HTTP proxy for HTTP services.  Reason:  " << status.toString()
+						 << LL_ENDL;
 	}
 
 	// Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy):
@@ -137,7 +139,6 @@ void LLAppCoreHttp::init()
 	mPolicies[AP_DEFAULT] = LLCore::HttpRequest::DEFAULT_POLICY_ID;
 
 	// Setup additional policies based on table and some special rules
-	// *TODO:  Make these configurations dynamic later
 	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
 	{
 		const EAppPolicy policy(init_data[i].mPolicy);
@@ -162,40 +163,10 @@ void LLAppCoreHttp::init()
 				continue;
 			}
 		}
-
-		// Get target connection concurrency value
-		U32 setting(init_data[i].mDefault);
-		if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))
-		{
-			U32 new_setting(gSavedSettings.getU32(init_data[i].mKey));
-			if (new_setting)
-			{
-				// Treat zero settings as an ask for default
-				setting = new_setting / init_data[i].mDivisor;
-				setting = llclamp(setting, init_data[i].mMin, init_data[i].mMax);
-			}
-		}
-
-		// Set it and report
-		// *TODO:  These are intended to be per-host limits when we can
-		// support that in llcorehttp/libcurl.
-		LLCore::HttpStatus status;
-		status = LLCore::HttpRequest::setPolicyClassOption(mPolicies[policy],
-														   LLCore::HttpRequest::CP_CONNECTION_LIMIT,
-														   setting);
-		if (! status)
-		{
-			LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
-							 << " concurrency.  Reason:  " << status.toString()
-							 << LL_ENDL;
-		}
-		else if (setting != init_data[i].mDefault)
-		{
-			LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage
-							 << " concurrency.  New value:  " << setting
-							 << LL_ENDL;
-		}
 	}
+
+	// Apply initial settings
+	refreshSettings(true);
 	
 	// Kick the thread
 	status = LLCore::HttpRequest::startThread();
@@ -206,6 +177,30 @@ void LLAppCoreHttp::init()
 	}
 
 	mRequest = new LLCore::HttpRequest;
+
+	// Register signals for settings and state changes
+	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
+	{
+		if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))
+		{
+			LLPointer<LLControlVariable> cntrl_ptr = gSavedSettings.getControl(init_data[i].mKey);
+			if (cntrl_ptr.isNull())
+			{
+				LL_WARNS("Init") << "Unable to set signal on global setting '" << init_data[i].mKey
+								 << "'" << LL_ENDL;
+			}
+			else
+			{
+				mSettingsSignal[i] = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed));
+			}
+		}
+	}
+}
+
+
+void setting_changed()
+{
+	LLAppViewer::instance()->getAppCoreHttp().refreshSettings(false);
 }
 
 
@@ -248,6 +243,11 @@ void LLAppCoreHttp::cleanup()
 		}
 	}
 
+	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
+	{
+		mSettingsSignal[i].disconnect();
+	}
+	
 	delete mRequest;
 	mRequest = NULL;
 
@@ -260,6 +260,60 @@ void LLAppCoreHttp::cleanup()
 	}
 }
 
+void LLAppCoreHttp::refreshSettings(bool initial)
+{
+	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
+	{
+		const EAppPolicy policy(init_data[i].mPolicy);
+
+		// Get target connection concurrency value
+		U32 setting(init_data[i].mDefault);
+		if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))
+		{
+			U32 new_setting(gSavedSettings.getU32(init_data[i].mKey));
+			if (new_setting)
+			{
+				// Treat zero settings as an ask for default
+				setting = new_setting / init_data[i].mDivisor;
+				setting = llclamp(setting, init_data[i].mMin, init_data[i].mMax);
+			}
+		}
+
+		if (! initial && setting == mSettings[policy])
+		{
+			// Unchanged, try next setting
+			continue;
+		}
+		
+		// Set it and report
+		// *TODO:  These are intended to be per-host limits when we can
+		// support that in llcorehttp/libcurl.
+		LLCore::HttpStatus status;
+		status = LLCore::HttpRequest::setPolicyClassOption(mPolicies[policy],
+														   LLCore::HttpRequest::CP_CONNECTION_LIMIT,
+														   setting);
+		if (! status)
+		{
+			LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
+							 << " concurrency.  Reason:  " << status.toString()
+							 << LL_ENDL;
+		}
+		else
+		{
+			LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage
+							  << " concurrency.  New value:  " << setting
+							  << LL_ENDL;
+			mSettings[policy] = setting;
+			if (initial && setting != init_data[i].mDefault)
+			{
+				LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage
+								 << " concurrency.  New value:  " << setting
+								 << LL_ENDL;
+			}
+		}
+	}
+}
+
 
 void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *)
 {
diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h
index 4a14c35966..6dc3bb2130 100755
--- a/indra/newview/llappcorehttp.h
+++ b/indra/newview/llappcorehttp.h
@@ -85,16 +85,21 @@ public:
 		{
 			return mPolicies[policy];
 		}
+
+	// Apply initial or new settings from the environment.
+	void refreshSettings(bool initial);
 	
 private:
 	static const F64			MAX_THREAD_WAIT_TIME;
 	
 private:
-	LLCore::HttpRequest *		mRequest;
+	LLCore::HttpRequest *		mRequest;						// Request queue to issue shutdowns
 	LLCore::HttpHandle			mStopHandle;
 	F64							mStopRequested;
 	bool						mStopped;
-	policy_t					mPolicies[AP_COUNT];
+	policy_t					mPolicies[AP_COUNT];			// Policy class id for each connection set
+	U32							mSettings[AP_COUNT];
+	boost::signals2::connection mSettingsSignal[AP_COUNT];		// Signals to global settings that affect us
 };
 
 
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index aa10e64af8..3af9ce7342 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -259,10 +259,9 @@ const char * const LOG_MESH = "Mesh";
 
 // Static data and functions to measure mesh load
 // time metrics for a new region scene.
-static bool metrics_inited(false);
-static boost::signals2::connection metrics_teleport_connection;
 static unsigned int metrics_teleport_start_count(0);
-static void metrics_teleport_started();
+boost::signals2::connection metrics_teleport_started_signal;
+static void teleport_started();
 static bool is_retryable(LLCore::HttpStatus status);
 
 //get the number of bytes resident in memory for given volume
@@ -2667,29 +2666,18 @@ void LLMeshRepository::init()
 		apr_sleep(100);
 	}
 
-	
+	metrics_teleport_started_signal = LLViewerMessage::getInstance()->setTeleportStartedCallback(teleport_started);
 	
 	mThread = new LLMeshRepoThread();
 	mThread->start();
 
-	if (! metrics_inited)
-	{
-		// Get teleport started signals to restart timings.
-		metrics_teleport_connection = LLViewerMessage::getInstance()->
-			setTeleportStartedCallback(metrics_teleport_started);
-		metrics_inited = true;
-	}
 }
 
 void LLMeshRepository::shutdown()
 {
 	llinfos << "Shutting down mesh repository." << llendl;
 
-	if (metrics_inited)
-	{
-		metrics_teleport_connection.disconnect();
-		metrics_inited = false;
-	}
+	metrics_teleport_started_signal.disconnect();
 
 	for (U32 i = 0; i < mUploads.size(); ++i)
 	{
@@ -4109,6 +4097,7 @@ bool LLMeshRepository::meshRezEnabled()
 // static
 void LLMeshRepository::metricsStart()
 {
+	++metrics_teleport_start_count;
 	sQuiescentTimer.start(0);
 }
 
@@ -4127,7 +4116,6 @@ void LLMeshRepository::metricsProgress(unsigned int this_count)
 
 	if (first_start)
 	{
-		++metrics_teleport_start_count;
 		metricsStart();
 		first_start = false;
 	}
@@ -4157,19 +4145,13 @@ void LLMeshRepository::metricsUpdate()
 	}
 }
 
-// Will use a request to start a teleport as a signal to
-// restart a timing sequence.  We don't get one of these
-// for login so initial start is done above.
-//
 // Threading:  main thread only
 // static
-void metrics_teleport_started()
+void teleport_started()
 {
 	LLMeshRepository::metricsStart();
-	++metrics_teleport_start_count;
 }
 
-
 // This comes from an edit in viewer-cat.  Unify this once that's
 // available everywhere.
 bool is_retryable(LLCore::HttpStatus status)
-- 
cgit v1.2.3


From f42d17f6c1d206a3396d9b4629246c4778ae1ca8 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Sat, 29 Jun 2013 03:08:32 +0000
Subject: Orphaned declaration preventing compilation.

---
 indra/newview/llappcorehttp.cpp | 1 -
 1 file changed, 1 deletion(-)

(limited to 'indra')

diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index 70e349e33d..0c242e57db 100755
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -71,7 +71,6 @@ static const struct
 	}
 };
 
-static void teleport_started();
 static void setting_changed();
 
 
-- 
cgit v1.2.3


From d8e32c58ce020f4d26af417d927a266209a40483 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 3 Jul 2013 15:17:26 -0400
Subject: Update the example program so it handles meshes as well.  Fix the
 request feed logic to use high/low-water level logic as is done in viewer
 code.

---
 indra/llcorehttp/examples/http_texture_load.cpp | 72 +++++++++++++++----------
 1 file changed, 44 insertions(+), 28 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp
index 909dc5b0cb..119e34f7ec 100755
--- a/indra/llcorehttp/examples/http_texture_load.cpp
+++ b/indra/llcorehttp/examples/http_texture_load.cpp
@@ -39,6 +39,7 @@
 #include "httprequest.h"
 #include "httphandler.h"
 #include "httpresponse.h"
+#include "httpoptions.h"
 #include "httpheaders.h"
 #include "bufferarray.h"
 #include "_mutex.h"
@@ -79,11 +80,11 @@ public:
 	WorkingSet();
 	~WorkingSet();
 
-	bool reload(LLCore::HttpRequest *);
+	bool reload(LLCore::HttpRequest *, LLCore::HttpOptions *);
 	
 	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
 
-	void loadTextureUuids(FILE * in);
+	void loadAssetUuids(FILE * in);
 	
 public:
 	struct Spec
@@ -93,18 +94,19 @@ public:
 		int				mLength;
 	};
 	typedef std::set<LLCore::HttpHandle> handle_set_t;
-	typedef std::vector<Spec> texture_list_t;
+	typedef std::vector<Spec> asset_list_t;
 	
 public:
 	bool						mVerbose;
 	bool						mRandomRange;
-	int							mMaxConcurrency;
+	int							mRequestLowWater;
+	int							mRequestHighWater;
 	handle_set_t				mHandles;
 	int							mRemaining;
 	int							mLimit;
 	int							mAt;
 	std::string					mUrl;
-	texture_list_t				mTextures;
+	asset_list_t				mAssets;
 	int							mErrorsApi;
 	int							mErrorsHttp;
 	int							mErrorsHttp404;
@@ -224,17 +226,23 @@ int main(int argc, char** argv)
 	// Get service point
 	LLCore::HttpRequest * hr = new LLCore::HttpRequest();
 
+	// Get request options
+	LLCore::HttpOptions * opt = new LLCore::HttpOptions();
+	opt->setRetries(12);
+	opt->setUseRetryAfter(true);
+	
 	// Get a handler/working set
 	WorkingSet ws;
 
 	// Fill the working set with work
 	ws.mUrl = url_format;
-	ws.loadTextureUuids(uuids);
+	ws.loadAssetUuids(uuids);
 	ws.mRandomRange = do_random;
 	ws.mVerbose = do_verbose;
-	ws.mMaxConcurrency = 100;
+	ws.mRequestHighWater = 100;
+	ws.mRequestLowWater = ws.mRequestHighWater / 2;
 	
-	if (! ws.mTextures.size())
+	if (! ws.mAssets.size())
 	{
 		std::cerr << "No UUIDs found in file '" << argv[optind] << "'." << std::endl;
 		return 1;
@@ -246,9 +254,9 @@ int main(int argc, char** argv)
 	
 	// Run it
 	int passes(0);
-	while (! ws.reload(hr))
+	while (! ws.reload(hr, opt))
 	{
-		hr->update(5000000);
+		hr->update(0);
 		ms_sleep(2);
 		if (0 == (++passes % 200))
 		{
@@ -275,6 +283,8 @@ int main(int argc, char** argv)
 	// Clean up
 	hr->requestStopThread(NULL);
 	ms_sleep(1000);
+	opt->release();
+	opt = NULL;
 	delete hr;
 	LLCore::HttpRequest::destroyService();
 	term_curl();
@@ -325,7 +335,7 @@ WorkingSet::WorkingSet()
 	  mSuccesses(0),
 	  mByteCount(0L)
 {
-	mTextures.reserve(30000);
+	mAssets.reserve(30000);
 
 	mHeaders = new LLCore::HttpHeaders;
 	mHeaders->append("Accept", "image/x-j2c");
@@ -342,29 +352,35 @@ WorkingSet::~WorkingSet()
 }
 
 
-bool WorkingSet::reload(LLCore::HttpRequest * hr)
+bool WorkingSet::reload(LLCore::HttpRequest * hr, LLCore::HttpOptions * opt)
 {
-	int to_do((std::min)(mRemaining, mMaxConcurrency - int(mHandles.size())));
+	if (mRequestLowWater <= mHandles.size())
+	{
+		// Haven't fallen below low-water level yet.
+		return false;
+	}
+	
+	int to_do((std::min)(mRemaining, mRequestHighWater - int(mHandles.size())));
 
 	for (int i(0); i < to_do; ++i)
 	{
 		char buffer[1024];
 #if	defined(WIN32)
-		_snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mTextures[mAt].mUuid.c_str());
+		_snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mAssets[mAt].mUuid.c_str());
 #else
-		snprintf(buffer, sizeof(buffer), mUrl.c_str(), mTextures[mAt].mUuid.c_str());
+		snprintf(buffer, sizeof(buffer), mUrl.c_str(), mAssets[mAt].mUuid.c_str());
 #endif
-		int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mOffset);
-		int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mLength);
+		int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mAssets[mAt].mOffset);
+		int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mAssets[mAt].mLength);
 
 		LLCore::HttpHandle handle;
 		if (offset || length)
 		{
-			handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, mHeaders, this);
+			handle = hr->requestGetByteRange(0, 0, buffer, offset, length, opt, mHeaders, this);
 		}
 		else
 		{
-			handle = hr->requestGet(0, 0, buffer, NULL, mHeaders, this);
+			handle = hr->requestGet(0, 0, buffer, opt, mHeaders, this);
 		}
 		if (! handle)
 		{
@@ -459,21 +475,21 @@ void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * r
 }
 
 
-void WorkingSet::loadTextureUuids(FILE * in)
+void WorkingSet::loadAssetUuids(FILE * in)
 {
 	char buffer[1024];
 
 	while (fgets(buffer, sizeof(buffer), in))
 	{
-		WorkingSet::Spec texture;
+		WorkingSet::Spec asset;
 		char * state(NULL);
 		char * token = strtok_r(buffer, " \t\n,", &state);
 		if (token && 36 == strlen(token))
 		{
 			// Close enough for this function
-			texture.mUuid = token;
-			texture.mOffset = 0;
-			texture.mLength = 0;
+			asset.mUuid = token;
+			asset.mOffset = 0;
+			asset.mLength = 0;
 			token = strtok_r(buffer, " \t\n,", &state);
 			if (token)
 			{
@@ -482,14 +498,14 @@ void WorkingSet::loadTextureUuids(FILE * in)
 				if (token)
 				{
 					int length(atoi(token));
-					texture.mOffset = offset;
-					texture.mLength = length;
+					asset.mOffset = offset;
+					asset.mLength = length;
 				}
 			}
-			mTextures.push_back(texture);
+			mAssets.push_back(asset);
 		}
 	}
-	mRemaining = mLimit = mTextures.size();
+	mRemaining = mLimit = mAssets.size();
 }
 
 
-- 
cgit v1.2.3


From fb734d621e6fa2004d191849783e81da75992d06 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Mon, 8 Jul 2013 20:31:09 +0000
Subject: Found the memory corruptor.  String trimmer didn't have a valid
 termination test.  Sheesh.  Also get some more numbers out of the
 example/load test program which drives traffic.  This should give some useful
 insights into how the current http throttle works (or doesn't) with varying
 client demands.

---
 indra/llcorehttp/_httpoprequest.cpp             |  4 ++-
 indra/llcorehttp/examples/http_texture_load.cpp | 36 ++++++++++++++++++++++---
 2 files changed, 35 insertions(+), 5 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index 8cb7fee701..ce0fc605ab 100755
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -1025,13 +1025,15 @@ char * os_strtrim(char * lstr)
 	}
 	if (*lstr)
 	{
-		for (char * rstr(lstr + strlen(lstr)); *--rstr;)
+		char * rstr(lstr + strlen(lstr));
+		while (lstr < rstr && *--rstr)
 		{
 			if (' ' == *rstr || '\t' == *rstr)
 			{
 				*rstr = '\0';
 			}
 		}
+		llassert(lstr <= rstr);
 	}
 	return lstr;
 }
diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp
index 119e34f7ec..88c8102b27 100755
--- a/indra/llcorehttp/examples/http_texture_load.cpp
+++ b/indra/llcorehttp/examples/http_texture_load.cpp
@@ -58,6 +58,7 @@ void usage(std::ostream & out);
 
 // Default command line settings
 static int concurrency_limit(40);
+static int highwater(100);
 static char url_format[1024] = "http://example.com/some/path?texture_id=%s.texture";
 
 #if defined(WIN32)
@@ -113,6 +114,8 @@ public:
 	int							mErrorsHttp416;
 	int							mErrorsHttp500;
 	int							mErrorsHttp503;
+	int							mRetries;
+	int							mRetriesHttp503;
 	int							mSuccesses;
 	long						mByteCount;
 	LLCore::HttpHeaders *		mHeaders;
@@ -160,7 +163,7 @@ int main(int argc, char** argv)
 	bool do_verbose(false);
 	
 	int option(-1);
-	while (-1 != (option = getopt(argc, argv, "u:c:h?Rv")))
+	while (-1 != (option = getopt(argc, argv, "u:c:h?RvH:")))
 	{
 		switch (option)
 		{
@@ -184,6 +187,21 @@ int main(int argc, char** argv)
 			}
 			break;
 
+		case 'H':
+		    {
+				unsigned long value;
+				char * end;
+
+				value = strtoul(optarg, &end, 10);
+				if (value < 1 || value > 100 || *end != '\0')
+				{
+					usage(std::cerr);
+					return 1;
+				}
+				highwater = value;
+			}
+			break;
+
 		case 'R':
 			do_random = true;
 			break;
@@ -239,7 +257,7 @@ int main(int argc, char** argv)
 	ws.loadAssetUuids(uuids);
 	ws.mRandomRange = do_random;
 	ws.mVerbose = do_verbose;
-	ws.mRequestHighWater = 100;
+	ws.mRequestHighWater = highwater;
 	ws.mRequestLowWater = ws.mRequestHighWater / 2;
 	
 	if (! ws.mAssets.size())
@@ -273,6 +291,8 @@ int main(int argc, char** argv)
 	std::cout << "HTTP 404 errors: " << ws.mErrorsHttp404 << "  HTTP 416 errors: " << ws.mErrorsHttp416
 			  << "  HTTP 500 errors:  " << ws.mErrorsHttp500 << "  HTTP 503 errors: " << ws.mErrorsHttp503 
 			  << std::endl;
+	std::cout << "Retries: " << ws.mRetries << "  Retries on 503: " << ws.mRetriesHttp503
+			  << std::endl;
 	std::cout << "User CPU: " << (metrics.mEndUTime - metrics.mStartUTime)
 			  << " uS  System CPU: " << (metrics.mEndSTime - metrics.mStartSTime)
 			  << " uS  Wall Time: "  << (metrics.mEndWallTime - metrics.mStartWallTime)
@@ -310,8 +330,10 @@ void usage(std::ostream & out)
 		" -u <url_format>       printf-style format string for URL generation\n"
 		"                       Default:  " << url_format << "\n"
 		" -R                    Issue GETs with random Range: headers\n"
-		" -c <limit>            Maximum request concurrency.  Range:  [1..100]\n"
+		" -c <limit>            Maximum connection concurrency.  Range:  [1..100]\n"
 		"                       Default:  " << concurrency_limit << "\n"
+		" -H <limit>            HTTP request highwater (requests fed to llcorehttp).\n"
+		"                       Range:  [1..100]  Default:  " << highwater << "\n"
 		" -v                    Verbose mode.  Issue some chatter while running\n"
 		" -h                    print this help\n"
 		"\n"
@@ -332,6 +354,8 @@ WorkingSet::WorkingSet()
 	  mErrorsHttp416(0),
 	  mErrorsHttp500(0),
 	  mErrorsHttp503(0),
+	  mRetries(0),
+	  mRetriesHttp503(0),
 	  mSuccesses(0),
 	  mByteCount(0L)
 {
@@ -426,7 +450,7 @@ void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * r
 		{
 			// More success
 			LLCore::BufferArray * data(response->getBody());
-			mByteCount += data->size();
+			mByteCount += data ? data->size() : 0;
 			++mSuccesses;
 		}
 		else
@@ -462,6 +486,10 @@ void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * r
 				++mErrorsApi;
 			}
 		}
+		unsigned int retry(0U), retry_503(0U);
+		response->getRetries(&retry, &retry_503);
+		mRetries += int(retry);
+		mRetriesHttp503 += int(retry_503);
 		mHandles.erase(it);
 	}
 
-- 
cgit v1.2.3


From eff651cffca60f2b69f6c596a8e9aa9e1ab44d3c Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 12 Jul 2013 15:00:24 -0400
Subject: SH-4312  Configuration data between viewer and llcorehttp is clumsy.
 Much improved.  Unified the global and class options into a single option
 list.  Implemented static and dynamic setting paths as much as possible. 
 Dynamic path does require packet/RPC but otherwise there's near unification. 
 Dynamic modes can't get values back yet due to the response/notifier scheme
 but this doesn't bother me. Flatten global and class options into simpler
 struct-like entities. Setter/getter available on these when needed (external
 APIs) but code can otherwise fiddle directly when it knows what to do.  Much
 duplicated options/state removed from HttpPolicy.  Comments cleaned up. 
 Threads better described and consistently mentioned in API docs.  Integration
 test extended for 503 responses with Reply-After headers.

---
 indra/llcorehttp/_httpinternal.h                |   6 +-
 indra/llcorehttp/_httplibcurl.h                 |  15 ++-
 indra/llcorehttp/_httpoperation.h               |   8 +-
 indra/llcorehttp/_httpoprequest.cpp             |  19 ++-
 indra/llcorehttp/_httpopsetget.cpp              |  93 ++++++++++---
 indra/llcorehttp/_httpopsetget.h                |  27 ++--
 indra/llcorehttp/_httppolicy.cpp                | 110 ++++++++-------
 indra/llcorehttp/_httppolicy.h                  |  39 ++++--
 indra/llcorehttp/_httppolicyclass.cpp           |  37 ++----
 indra/llcorehttp/_httppolicyclass.h             |  19 ++-
 indra/llcorehttp/_httppolicyglobal.cpp          |  66 ++++-----
 indra/llcorehttp/_httppolicyglobal.h            |  23 +++-
 indra/llcorehttp/_httpservice.cpp               | 170 +++++++++++++++++++++---
 indra/llcorehttp/_httpservice.h                 |  52 +++++---
 indra/llcorehttp/examples/http_texture_load.cpp |   7 +-
 indra/llcorehttp/httpcommon.h                   | 109 +++++++++++++--
 indra/llcorehttp/httprequest.cpp                | 107 ++++++++-------
 indra/llcorehttp/httprequest.h                  | 128 +++++++++---------
 indra/llcorehttp/tests/test_httprequest.hpp     | 140 ++++++++++++++++++-
 indra/llcorehttp/tests/test_llcorehttp_peer.py  |  49 ++++++-
 indra/newview/llappcorehttp.cpp                 |  29 ++--
 21 files changed, 881 insertions(+), 372 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h
index f085ca3b91..80f4f34942 100755
--- a/indra/llcorehttp/_httpinternal.h
+++ b/indra/llcorehttp/_httpinternal.h
@@ -36,7 +36,8 @@
 // General library to-do list
 //
 // - Implement policy classes.  Structure is mostly there just didn't
-//   need it for the first consumer.
+//   need it for the first consumer.  [Classes are there.  More
+//   advanced features, like borrowing, aren't there yet.]
 // - Consider Removing 'priority' from the request interface.  Its use
 //   in an always active class can lead to starvation of low-priority
 //   requests.  Requires coodination of priority values across all
@@ -46,6 +47,7 @@
 //   may not really need it.
 // - Set/get for global policy and policy classes is clumsy.  Rework
 //   it heading in a direction that allows for more dynamic behavior.
+//   [Mostly fixed]
 // - Move HttpOpRequest::prepareRequest() to HttpLibcurl for the
 //   pedantic.
 // - Update downloader and other long-duration services are going to
@@ -73,7 +75,7 @@
 //   the main source file.
 // - Expand areas of usage eventually leading to the removal of LLCurl.
 //   Rough order of expansion:
-//   .  Mesh fetch
+//   .  Mesh fetch [Underway]
 //   .  Avatar names
 //   .  Group membership lists
 //   .  Caps access in general
diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h
index 611f029ef5..0ec90437bb 100755
--- a/indra/llcorehttp/_httplibcurl.h
+++ b/indra/llcorehttp/_httplibcurl.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -71,16 +71,22 @@ public:
 	///
 	/// @return			Indication of how long this method is
 	///					willing to wait for next service call.
+	///
+	/// Threading:  called by worker thread.
 	HttpService::ELoopSpeed processTransport();
 
 	/// Add request to the active list.  Caller is expected to have
 	/// provided us with a reference count on the op to hold the
 	/// request.  (No additional references will be added.)
+	///
+	/// Threading:  called by worker thread.
 	void addOp(HttpOpRequest * op);
 
 	/// One-time call to set the number of policy classes to be
 	/// serviced and to create the resources for each.  Value
 	/// must agree with HttpPolicy::setPolicies() call.
+	///
+	/// Threading:  called by init thread.
 	void start(int policy_count);
 
 	/// Synchronously stop libcurl operations.  All active requests
@@ -91,9 +97,13 @@ public:
 	/// respective reply queues.
 	///
 	/// Can be restarted with a start() call.
+	///
+	/// Threading:  called by worker thread.
 	void shutdown();
 
 	/// Return global and per-class counts of active requests.
+	///
+	/// Threading:  called by worker thread.
 	int getActiveCount() const;
 	int getActiveCountInClass(int policy_class) const;
 
@@ -103,6 +113,7 @@ public:
 	///
 	/// @return			True if handle was found and operation canceled.
 	///
+	/// Threading:  called by worker thread.
 	bool cancel(HttpHandle handle);
 
 protected:
@@ -121,7 +132,7 @@ protected:
 	HttpService *		mService;				// Simple reference, not owner
 	active_set_t		mActiveOps;
 	int					mPolicyCount;
-	CURLM **			mMultiHandles;
+	CURLM **			mMultiHandles;			// One handle per policy class
 }; // end class HttpLibcurl
 
 }  // end namespace LLCore
diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h
index 914627fad0..937a61187d 100755
--- a/indra/llcorehttp/_httpoperation.h
+++ b/indra/llcorehttp/_httpoperation.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -72,7 +72,7 @@ class HttpService;
 class HttpOperation : public LLCoreInt::RefCounted
 {
 public:
-	/// Threading:  called by a consumer/application thread.
+	/// Threading:  called by consumer thread.
 	HttpOperation();
 
 protected:
@@ -108,7 +108,7 @@ public:
 	///							by the worker thread.  This is passible data
 	///							until notification is performed.
 	///
-	/// Threading:  called by application thread.
+	/// Threading:  called by consumer thread.
 	///
 	void setReplyPath(HttpReplyQueue * reply_queue,
 					  HttpHandler * handler);
@@ -141,7 +141,7 @@ public:
 	/// call to HttpRequest::update().  This method does the necessary
 	/// dispatching.
 	///
-	/// Threading:  called by application thread.
+	/// Threading:  called by consumer thread.
 	///
 	virtual void visitNotifier(HttpRequest *);
 
diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index ce0fc605ab..d72f8f6119 100755
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -402,7 +402,7 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 	// *FIXME:  better error handling later
 	HttpStatus status;
 
-	// Get policy options
+	// Get global policy options
 	HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions());
 	
 	mCurlHandle = curl_easy_init();
@@ -441,30 +441,27 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 	curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1);
 	curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);
 
-	const std::string * opt_value(NULL);
-	long opt_long(0L);
-	policy.get(HttpRequest::GP_LLPROXY, &opt_long);
-	if (opt_long)
+	if (policy.mUseLLProxy)
 	{
 		// Use the viewer-based thread-safe API which has a
 		// fast/safe check for proxy enable.  Would like to
 		// encapsulate this someway...
 		LLProxy::getInstance()->applyProxySettings(mCurlHandle);
 	}
-	else if (policy.get(HttpRequest::GP_HTTP_PROXY, &opt_value))
+	else if (policy.mHttpProxy.size())
 	{
 		// *TODO:  This is fine for now but get fuller socks5/
 		// authentication thing going later....
-		curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str());
+		curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, policy.mHttpProxy.c_str());
 		curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
 	}
-	if (policy.get(HttpRequest::GP_CA_PATH, &opt_value))
+	if (policy.mCAPath.size())
 	{
-		curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str());
+		curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, policy.mCAPath.c_str());
 	}
-	if (policy.get(HttpRequest::GP_CA_FILE, &opt_value))
+	if (policy.mCAFile.size())
 	{
-		curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str());
+		curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, policy.mCAFile.c_str());
 	}
 	
 	switch (mReqMethod)
diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp
index 8198528a9b..a5363f9170 100755
--- a/indra/llcorehttp/_httpopsetget.cpp
+++ b/indra/llcorehttp/_httpopsetget.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -27,6 +27,7 @@
 #include "_httpopsetget.h"
 
 #include "httpcommon.h"
+#include "httprequest.h"
 
 #include "_httpservice.h"
 #include "_httppolicy.h"
@@ -43,10 +44,11 @@ namespace LLCore
 
 HttpOpSetGet::HttpOpSetGet()
 	: HttpOperation(),
-	  mIsGlobal(false),
-	  mDoSet(false),
-	  mSetting(-1),				// Nothing requested
-	  mLongValue(0L)
+	  mReqOption(HttpRequest::PO_CONNECTION_LIMIT),
+	  mReqClass(HttpRequest::INVALID_POLICY_ID),
+	  mReqDoSet(false),
+	  mReqLongValue(0L),
+	  mReplyLongValue(0L)
 {}
 
 
@@ -54,37 +56,84 @@ HttpOpSetGet::~HttpOpSetGet()
 {}
 
 
-void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting)
+HttpStatus HttpOpSetGet::setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass)
 {
-	mIsGlobal = true;
-	mSetting = setting;
+	HttpStatus status;
+	
+	mReqOption = opt;
+	mReqClass = pclass;
+	return status;
 }
 
 
-void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value)
+HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value)
 {
-	mIsGlobal = true;
-	mDoSet = true;
-	mSetting = setting;
-	mStrValue = value;
+	HttpStatus status;
+
+	if (! HttpService::sOptionDesc[opt].mIsLong)
+	{
+		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
+	}
+	if (! HttpService::sOptionDesc[opt].mIsDynamic)
+	{
+		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
+	}
+	
+	mReqOption = opt;
+	mReqClass = pclass;
+	mReqDoSet = true;
+	mReqLongValue = value;
+	
+	return status;
 }
 
 
-void HttpOpSetGet::stageFromRequest(HttpService * service)
+HttpStatus HttpOpSetGet::setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value)
 {
-	HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions());
-	HttpRequest::EGlobalPolicy setting(static_cast<HttpRequest::EGlobalPolicy>(mSetting));
+	HttpStatus status;
+
+	if (HttpService::sOptionDesc[opt].mIsLong)
+	{
+		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
+	}
+	if (! HttpService::sOptionDesc[opt].mIsDynamic)
+	{
+		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
+	}
+
+	mReqOption = opt;
+	mReqClass = pclass;
+	mReqDoSet = true;
+	mReqStrValue = value;
 	
-	if (mDoSet)
+	return status;
+}
+
+
+void HttpOpSetGet::stageFromRequest(HttpService * service)
+{
+	if (mReqDoSet)
 	{
-		mStatus = pol_opt.set(setting, mStrValue);
+		if (HttpService::sOptionDesc[mReqOption].mIsLong)
+		{
+			mStatus = service->setPolicyOption(mReqOption, mReqClass,
+											   mReqLongValue, &mReplyLongValue);
+		}
+		else
+		{
+			mStatus = service->setPolicyOption(mReqOption, mReqClass,
+											   mReqStrValue, &mReplyStrValue);
+		}
 	}
-	if (mStatus)
+	else
 	{
-		const std::string * value(NULL);
-		if ((mStatus = pol_opt.get(setting, &value)))
+		if (HttpService::sOptionDesc[mReqOption].mIsLong)
+		{
+			mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyLongValue);
+		}
+		else
 		{
-			mStrValue = *value;
+			mStatus = service->getPolicyOption(mReqOption, mReqClass, &mReplyStrValue);
 		}
 	}
 	
diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h
index 6966b9d94e..a1e76dd429 100755
--- a/indra/llcorehttp/_httpopsetget.h
+++ b/indra/llcorehttp/_httpopsetget.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -46,7 +46,10 @@ namespace LLCore
 /// configuration settings.
 ///
 /// *NOTE:  Expect this to change.  Don't really like it yet.
-
+///
+/// *TODO:  Can't return values to caller yet.  Need to do
+/// something better with HttpResponse and visitNotifier().
+///
 class HttpOpSetGet : public HttpOperation
 {
 public:
@@ -61,19 +64,23 @@ private:
 
 public:
 	/// Threading:  called by application thread
-	void setupGet(HttpRequest::EGlobalPolicy setting);
-	void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value);
+	HttpStatus setupGet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass);
+	HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, long value);
+	HttpStatus setupSet(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass, const std::string & value);
 
 	virtual void stageFromRequest(HttpService *);
 
 public:
 	// Request data
-	bool				mIsGlobal;
-	bool				mDoSet;
-	int					mSetting;
-	long				mLongValue;
-	std::string			mStrValue;
-
+	HttpRequest::EPolicyOption	mReqOption;
+	HttpRequest::policy_t		mReqClass;
+	bool						mReqDoSet;
+	long						mReqLongValue;
+	std::string					mReqStrValue;
+
+	// Reply Data
+	long						mReplyLongValue;
+	std::string					mReplyStrValue;
 };  // end class HttpOpSetGet
 
 
diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp
index 5f303dd0fe..2754e8ef07 100755
--- a/indra/llcorehttp/_httppolicy.cpp
+++ b/indra/llcorehttp/_httppolicy.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -41,57 +41,64 @@ namespace LLCore
 
 
 // Per-policy-class data for a running system.
-// Collection of queues, parameters, history, metrics, etc.
+// Collection of queues, options and other data
 // for a single policy class.
 //
 // Threading:  accessed only by worker thread
-struct HttpPolicy::State
+struct HttpPolicy::ClassState
 {
 public:
-	State()
-		: mConnMax(HTTP_CONNECTION_LIMIT_DEFAULT),
-		  mConnAt(HTTP_CONNECTION_LIMIT_DEFAULT),
-		  mConnMin(1),
-		  mNextSample(0),
-		  mErrorCount(0),
-		  mErrorFactor(0)
+	ClassState()
 		{}
 	
 	HttpReadyQueue		mReadyQueue;
 	HttpRetryQueue		mRetryQueue;
 
 	HttpPolicyClass		mOptions;
-
-	long				mConnMax;
-	long				mConnAt;
-	long				mConnMin;
-
-	HttpTime			mNextSample;
-	unsigned long		mErrorCount;
-	unsigned long		mErrorFactor;
 };
 
 
 HttpPolicy::HttpPolicy(HttpService * service)
-	: mActiveClasses(0),
-	  mState(NULL),
-	  mService(service)
-{}
+	: mService(service)
+{
+	// Create default class
+	mClasses.push_back(new ClassState());
+}
 
 
 HttpPolicy::~HttpPolicy()
 {
 	shutdown();
+
+	for (class_list_t::iterator it(mClasses.begin()); it != mClasses.end(); ++it)
+	{
+		delete (*it);
+	}
+	mClasses.clear();
 	
 	mService = NULL;
 }
 
 
+HttpRequest::policy_t HttpPolicy::createPolicyClass()
+{
+	const HttpRequest::policy_t policy_class(mClasses.size());
+	if (policy_class >= HTTP_POLICY_CLASS_LIMIT)
+	{
+		return HttpRequest::INVALID_POLICY_ID;
+	}
+	mClasses.push_back(new ClassState());
+	return policy_class;
+}
+
+
 void HttpPolicy::shutdown()
 {
-	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
+	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
 	{
-		HttpRetryQueue & retryq(mState[policy_class].mRetryQueue);
+		ClassState & state(*mClasses[policy_class]);
+		
+		HttpRetryQueue & retryq(state.mRetryQueue);
 		while (! retryq.empty())
 		{
 			HttpOpRequest * op(retryq.top());
@@ -101,7 +108,7 @@ void HttpPolicy::shutdown()
 			op->release();
 		}
 
-		HttpReadyQueue & readyq(mState[policy_class].mReadyQueue);
+		HttpReadyQueue & readyq(state.mReadyQueue);
 		while (! readyq.empty())
 		{
 			HttpOpRequest * op(readyq.top());
@@ -111,28 +118,11 @@ void HttpPolicy::shutdown()
 			op->release();
 		}
 	}
-	delete [] mState;
-	mState = NULL;
-	mActiveClasses = 0;
 }
 
 
-void HttpPolicy::start(const HttpPolicyGlobal & global,
-					   const std::vector<HttpPolicyClass> & classes)
-{
-	llassert_always(! mState);
-
-	mGlobalOptions = global;
-	mActiveClasses = classes.size();
-	mState = new State [mActiveClasses];
-	for (int i(0); i < mActiveClasses; ++i)
-	{
-		mState[i].mOptions = classes[i];
-		mState[i].mConnMax = classes[i].mConnectionLimit;
-		mState[i].mConnAt = mState[i].mConnMax;
-		mState[i].mConnMin = 2;
-	}
-}
+void HttpPolicy::start()
+{}
 
 
 void HttpPolicy::addOp(HttpOpRequest * op)
@@ -141,7 +131,7 @@ void HttpPolicy::addOp(HttpOpRequest * op)
 	
 	op->mPolicyRetries = 0;
 	op->mPolicy503Retries = 0;
-	mState[policy_class].mReadyQueue.push(op);
+	mClasses[policy_class]->mReadyQueue.push(op);
 }
 
 
@@ -183,7 +173,7 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
 							 << static_cast<HttpHandle>(op)
 							 << LL_ENDL;
 	}
-	mState[policy_class].mRetryQueue.push(op);
+	mClasses[policy_class]->mRetryQueue.push(op);
 }
 
 
@@ -204,11 +194,11 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
 	HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP);
 	HttpLibcurl & transport(mService->getTransport());
 	
-	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
+	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
 	{
-		State & state(mState[policy_class]);
+		ClassState & state(*mClasses[policy_class]);
 		int active(transport.getActiveCountInClass(policy_class));
-		int needed(state.mConnAt - active);		// Expect negatives here
+		int needed(state.mOptions.mConnectionLimit - active);		// Expect negatives here
 
 		HttpRetryQueue & retryq(state.mRetryQueue);
 		HttpReadyQueue & readyq(state.mReadyQueue);
@@ -256,9 +246,9 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
 
 bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority)
 {
-	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
+	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
 	{
-		State & state(mState[policy_class]);
+		ClassState & state(*mClasses[policy_class]);
 		// We don't scan retry queue because a priority change there
 		// is meaningless.  The request will be issued based on retry
 		// intervals not priority value, which is now moot.
@@ -286,9 +276,9 @@ bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t prior
 
 bool HttpPolicy::cancel(HttpHandle handle)
 {
-	for (int policy_class(0); policy_class < mActiveClasses; ++policy_class)
+	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
 	{
-		State & state(mState[policy_class]);
+		ClassState & state(*mClasses[policy_class]);
 
 		// Scan retry queue
 		HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container());
@@ -382,13 +372,21 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
 	return false;						// not active
 }
 
+	
+HttpPolicyClass & HttpPolicy::getClassOptions(HttpRequest::policy_t pclass)
+{
+	llassert_always(pclass >= 0 && pclass < mClasses.size());
+	
+	return mClasses[pclass]->mOptions;
+}
+
 
 int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const
 {
-	if (policy_class < mActiveClasses)
+	if (policy_class < mClasses.size())
 	{
-		return (mState[policy_class].mReadyQueue.size()
-				+ mState[policy_class].mRetryQueue.size());
+		return (mClasses[policy_class]->mReadyQueue.size()
+				+ mClasses[policy_class]->mRetryQueue.size());
 	}
 	return 0;
 }
diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h
index 03d92c0b8e..bf1aa74267 100755
--- a/indra/llcorehttp/_httppolicy.h
+++ b/indra/llcorehttp/_httppolicy.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -60,6 +60,9 @@ private:
 	void operator=(const HttpPolicy &);			// Not defined
 
 public:
+	/// Threading:  called by init thread.
+	HttpRequest::policy_t createPolicyClass();
+	
 	/// Cancel all ready and retry requests sending them to
 	/// their notification queues.  Release state resources
 	/// making further request handling impossible.
@@ -71,9 +74,8 @@ public:
 	/// requests.  One-time call invoked before starting
 	/// the worker thread.
 	///
-	/// Threading:  called by application thread
-	void start(const HttpPolicyGlobal & global,
-			   const std::vector<HttpPolicyClass> & classes);
+	/// Threading:  called by init thread
+	void start();
 
 	/// Give the policy layer some cycles to scan the ready
 	/// queue promoting higher-priority requests to active
@@ -93,7 +95,7 @@ public:
 	/// and should not be modified by anyone until retrieved
 	/// from queue.
 	///
-	/// Threading:  called by any thread
+	/// Threading:  called by worker thread
 	void addOp(HttpOpRequest *);
 
 	/// Similar to addOp, used when a caller wants to retry a
@@ -130,30 +132,39 @@ public:
 	/// Threading:  called by worker thread
 	bool stageAfterCompletion(HttpOpRequest * op);
 	
-	// Get pointer to global policy options.  Caller is expected
-	// to do context checks like no setting once running.
+	/// Get a reference to global policy options.  Caller is expected
+	/// to do context checks like no setting once running.  These
+	/// are done, for example, in @see HttpService interfaces.
 	///
 	/// Threading:  called by any thread *but* the object may
 	/// only be modified by the worker thread once running.
-	///
 	HttpPolicyGlobal & getGlobalOptions()
 		{
 			return mGlobalOptions;
 		}
 
+	/// Get a reference to class policy options.  Caller is expected
+	/// to do context checks like no setting once running.  These
+	/// are done, for example, in @see HttpService interfaces.
+	///
+	/// Threading:  called by any thread *but* the object may
+	/// only be modified by the worker thread once running and
+	/// read accesses by other threads are exposed to races at
+	/// that point.
+	HttpPolicyClass & getClassOptions(HttpRequest::policy_t pclass);
+	
 	/// Get ready counts for a particular policy class
 	///
 	/// Threading:  called by worker thread
 	int getReadyCount(HttpRequest::policy_t policy_class) const;
 	
 protected:
-	struct State;
-
-	int									mActiveClasses;
-	State *								mState;
-	HttpService *						mService;				// Naked pointer, not refcounted, not owner
-	HttpPolicyGlobal					mGlobalOptions;
+	struct ClassState;
+	typedef std::vector<ClassState *>	class_list_t;
 	
+	HttpPolicyGlobal					mGlobalOptions;
+	class_list_t						mClasses;
+	HttpService *						mService;				// Naked pointer, not refcounted, not owner
 };  // end class HttpPolicy
 
 }  // end namespace LLCore
diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp
index 1a55ab1ac6..fe4359081a 100755
--- a/indra/llcorehttp/_httppolicyclass.cpp
+++ b/indra/llcorehttp/_httppolicyclass.cpp
@@ -34,8 +34,7 @@ namespace LLCore
 
 
 HttpPolicyClass::HttpPolicyClass()
-	: mSetMask(0UL),
-	  mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
+	: mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
 	  mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
 	  mPipelining(HTTP_PIPELINING_DEFAULT)
 {}
@@ -49,7 +48,6 @@ HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other)
 {
 	if (this != &other)
 	{
-		mSetMask = other.mSetMask;
 		mConnectionLimit = other.mConnectionLimit;
 		mPerHostConnectionLimit = other.mPerHostConnectionLimit;
 		mPipelining = other.mPipelining;
@@ -59,26 +57,25 @@ HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other)
 
 
 HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other)
-	: mSetMask(other.mSetMask),
-	  mConnectionLimit(other.mConnectionLimit),
+	: mConnectionLimit(other.mConnectionLimit),
 	  mPerHostConnectionLimit(other.mPerHostConnectionLimit),
 	  mPipelining(other.mPipelining)
 {}
 
 
-HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value)
+HttpStatus HttpPolicyClass::set(HttpRequest::EPolicyOption opt, long value)
 {
 	switch (opt)
 	{
-	case HttpRequest::CP_CONNECTION_LIMIT:
+	case HttpRequest::PO_CONNECTION_LIMIT:
 		mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
 		break;
 
-	case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT:
+	case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT:
 		mPerHostConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), mConnectionLimit);
 		break;
 
-	case HttpRequest::CP_ENABLE_PIPELINING:
+	case HttpRequest::PO_ENABLE_PIPELINING:
 		mPipelining = llclamp(value, 0L, 1L);
 		break;
 
@@ -86,38 +83,30 @@ HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value)
 		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
 	}
 
-	mSetMask |= 1UL << int(opt);
 	return HttpStatus();
 }
 
 
-HttpStatus HttpPolicyClass::get(HttpRequest::EClassPolicy opt, long * value)
+HttpStatus HttpPolicyClass::get(HttpRequest::EPolicyOption opt, long * value) const
 {
-	static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
-	long * src(NULL);
-	
 	switch (opt)
 	{
-	case HttpRequest::CP_CONNECTION_LIMIT:
-		src = &mConnectionLimit;
+	case HttpRequest::PO_CONNECTION_LIMIT:
+		*value = mConnectionLimit;
 		break;
 
-	case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT:
-		src = &mPerHostConnectionLimit;
+	case HttpRequest::PO_PER_HOST_CONNECTION_LIMIT:
+		*value = mPerHostConnectionLimit;
 		break;
 
-	case HttpRequest::CP_ENABLE_PIPELINING:
-		src = &mPipelining;
+	case HttpRequest::PO_ENABLE_PIPELINING:
+		*value = mPipelining;
 		break;
 
 	default:
 		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
 	}
 
-	if (! (mSetMask & (1UL << int(opt))))
-		return not_set;
-
-	*value = *src;
 	return HttpStatus();
 }
 
diff --git a/indra/llcorehttp/_httppolicyclass.h b/indra/llcorehttp/_httppolicyclass.h
index d175413cbd..69fb459d22 100755
--- a/indra/llcorehttp/_httppolicyclass.h
+++ b/indra/llcorehttp/_httppolicyclass.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -34,6 +34,18 @@
 namespace LLCore
 {
 
+/// Options struct for per-class policy options.
+///
+/// Combines both raw blob data access with semantics-enforcing
+/// set/get interfaces.  For internal operations by the worker
+/// thread, just grab the setting directly from instance and test/use
+/// as needed.  When attached to external APIs (the public API
+/// options interfaces) the set/get methods are available to
+/// enforce correct ranges, data types, contexts, etc. and suitable
+/// status values are returned.
+///
+/// Threading:  Single-threaded.  In practice, init thread before
+/// worker starts, worker thread after.
 class HttpPolicyClass
 {
 public:
@@ -44,11 +56,10 @@ public:
 	HttpPolicyClass(const HttpPolicyClass &);			// Not defined
 
 public:
-	HttpStatus set(HttpRequest::EClassPolicy opt, long value);
-	HttpStatus get(HttpRequest::EClassPolicy opt, long * value);
+	HttpStatus set(HttpRequest::EPolicyOption opt, long value);
+	HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
 	
 public:
-	unsigned long				mSetMask;
 	long						mConnectionLimit;
 	long						mPerHostConnectionLimit;
 	long						mPipelining;
diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp
index 72f409d3b1..1dc95f3dce 100755
--- a/indra/llcorehttp/_httppolicyglobal.cpp
+++ b/indra/llcorehttp/_httppolicyglobal.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -34,8 +34,7 @@ namespace LLCore
 
 
 HttpPolicyGlobal::HttpPolicyGlobal()
-	: mSetMask(0UL),
-	  mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
+	: mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
 	  mTrace(HTTP_TRACE_OFF),
 	  mUseLLProxy(0)
 {}
@@ -49,7 +48,6 @@ HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other)
 {
 	if (this != &other)
 	{
-		mSetMask = other.mSetMask;
 		mConnectionLimit = other.mConnectionLimit;
 		mCAPath = other.mCAPath;
 		mCAFile = other.mCAFile;
@@ -61,19 +59,19 @@ HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other)
 }
 
 
-HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value)
+HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, long value)
 {
 	switch (opt)
 	{
-	case HttpRequest::GP_CONNECTION_LIMIT:
+	case HttpRequest::PO_CONNECTION_LIMIT:
 		mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX));
 		break;
 
-	case HttpRequest::GP_TRACE:
+	case HttpRequest::PO_TRACE:
 		mTrace = llclamp(value, long(HTTP_TRACE_MIN), long(HTTP_TRACE_MAX));
 		break;
 
-	case HttpRequest::GP_LLPROXY:
+	case HttpRequest::PO_LLPROXY:
 		mUseLLProxy = llclamp(value, 0L, 1L);
 		break;
 
@@ -81,24 +79,23 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value)
 		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
 	}
 
-	mSetMask |= 1UL << int(opt);
 	return HttpStatus();
 }
 
 
-HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::string & value)
+HttpStatus HttpPolicyGlobal::set(HttpRequest::EPolicyOption opt, const std::string & value)
 {
 	switch (opt)
 	{
-	case HttpRequest::GP_CA_PATH:
+	case HttpRequest::PO_CA_PATH:
 		mCAPath = value;
 		break;
 
-	case HttpRequest::GP_CA_FILE:
+	case HttpRequest::PO_CA_FILE:
 		mCAFile = value;
 		break;
 
-	case HttpRequest::GP_HTTP_PROXY:
+	case HttpRequest::PO_HTTP_PROXY:
 		mCAFile = value;
 		break;
 
@@ -106,69 +103,54 @@ HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::stri
 		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
 	}
 	
-	mSetMask |= 1UL << int(opt);
 	return HttpStatus();
 }
 
 
-HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long * value)
+HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, long * value) const
 {
-	static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
-	long * src(NULL);
-	
 	switch (opt)
 	{
-	case HttpRequest::GP_CONNECTION_LIMIT:
-		src = &mConnectionLimit;
+	case HttpRequest::PO_CONNECTION_LIMIT:
+		*value = mConnectionLimit;
 		break;
 
-	case HttpRequest::GP_TRACE:
-		src = &mTrace;
+	case HttpRequest::PO_TRACE:
+		*value = mTrace;
 		break;
 
-	case HttpRequest::GP_LLPROXY:
-		src = &mUseLLProxy;
+	case HttpRequest::PO_LLPROXY:
+		*value = mUseLLProxy;
 		break;
 
 	default:
 		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
 	}
 
-	if (! (mSetMask & (1UL << int(opt))))
-		return not_set;
-
-	*value = *src;
 	return HttpStatus();
 }
 
 
-HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string ** value)
+HttpStatus HttpPolicyGlobal::get(HttpRequest::EPolicyOption opt, std::string * value) const
 {
-	static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET);
-	const std::string * src(NULL);
-
 	switch (opt)
 	{
-	case HttpRequest::GP_CA_PATH:
-		src = &mCAPath;
+	case HttpRequest::PO_CA_PATH:
+		*value = mCAPath;
 		break;
 
-	case HttpRequest::GP_CA_FILE:
-		src = &mCAFile;
+	case HttpRequest::PO_CA_FILE:
+		*value = mCAFile;
 		break;
 
-	case HttpRequest::GP_HTTP_PROXY:
-		src = &mHttpProxy;
+	case HttpRequest::PO_HTTP_PROXY:
+		*value = mHttpProxy;
 		break;
 
 	default:
 		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
 	}
 	
-	if (! (mSetMask & (1UL << int(opt))))
-		return not_set;
-
-	*value = src;
 	return HttpStatus();
 }
 
diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h
index a50d0e4188..67c4ba9481 100755
--- a/indra/llcorehttp/_httppolicyglobal.h
+++ b/indra/llcorehttp/_httppolicyglobal.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -34,6 +34,18 @@
 namespace LLCore
 {
 
+/// Options struct for global policy options.
+///
+/// Combines both raw blob data access with semantics-enforcing
+/// set/get interfaces.  For internal operations by the worker
+/// thread, just grab the setting directly from instance and test/use
+/// as needed.  When attached to external APIs (the public API
+/// options interfaces) the set/get methods are available to
+/// enforce correct ranges, data types, contexts, etc. and suitable
+/// status values are returned.
+///
+/// Threading:  Single-threaded.  In practice, init thread before
+/// worker starts, worker thread after.
 class HttpPolicyGlobal
 {
 public:
@@ -46,13 +58,12 @@ private:
 	HttpPolicyGlobal(const HttpPolicyGlobal &);			// Not defined
 
 public:
-	HttpStatus set(HttpRequest::EGlobalPolicy opt, long value);
-	HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value);
-	HttpStatus get(HttpRequest::EGlobalPolicy opt, long * value);
-	HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string ** value);
+	HttpStatus set(HttpRequest::EPolicyOption opt, long value);
+	HttpStatus set(HttpRequest::EPolicyOption opt, const std::string & value);
+	HttpStatus get(HttpRequest::EPolicyOption opt, long * value) const;
+	HttpStatus get(HttpRequest::EPolicyOption opt, std::string * value) const;
 	
 public:
-	unsigned long		mSetMask;
 	long				mConnectionLimit;
 	std::string			mCAPath;
 	std::string			mCAFile;
diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp
index 0821401289..e21d196a3e 100755
--- a/indra/llcorehttp/_httpservice.cpp
+++ b/indra/llcorehttp/_httpservice.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -43,6 +43,17 @@
 namespace LLCore
 {
 
+const HttpService::OptionDescriptor HttpService::sOptionDesc[] =
+{ //    isLong     isDynamic  isGlobal    isClass
+	{	true,		true,		true,		true	},		// PO_CONNECTION_LIMIT
+	{	true,		true,		false,		true	},		// PO_PER_HOST_CONNECTION_LIMIT
+	{	false,		false,		true,		false	},		// PO_CA_PATH
+	{	false,		false,		true,		false	},		// PO_CA_FILE
+	{	false,		true,		true,		false	},		// PO_HTTP_PROXY
+	{	true,		true,		true,		false	},		// PO_LLPROXY
+	{	true,		true,		true,		false	},		// PO_TRACE
+	{	true,		true,		false,		true	}		// PO_ENABLE_PIPELINING
+};
 HttpService * HttpService::sInstance(NULL);
 volatile HttpService::EState HttpService::sState(NOT_INITIALIZED);
 
@@ -51,12 +62,9 @@ HttpService::HttpService()
 	  mExitRequested(0U),
 	  mThread(NULL),
 	  mPolicy(NULL),
-	  mTransport(NULL)
-{
-	// Create the default policy class
-	HttpPolicyClass pol_class;
-	mPolicyClasses.push_back(pol_class);
-}
+	  mTransport(NULL),
+	  mLastPolicy(0)
+{}
 
 
 HttpService::~HttpService()
@@ -146,13 +154,8 @@ void HttpService::term()
 
 HttpRequest::policy_t HttpService::createPolicyClass()
 {
-	const HttpRequest::policy_t policy_class(mPolicyClasses.size());
-	if (policy_class >= HTTP_POLICY_CLASS_LIMIT)
-	{
-		return 0;
-	}
-	mPolicyClasses.push_back(HttpPolicyClass());
-	return policy_class;
+	mLastPolicy = mPolicy->createPolicyClass();
+	return mLastPolicy;
 }
 
 
@@ -185,8 +188,8 @@ void HttpService::startThread()
 	}
 
 	// Push current policy definitions, enable policy & transport components
-	mPolicy->start(mPolicyGlobal, mPolicyClasses);
-	mTransport->start(mPolicyClasses.size());
+	mPolicy->start();
+	mTransport->start(mLastPolicy + 1);
 
 	mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1));
 	sState = RUNNING;
@@ -319,7 +322,7 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
 		{
 			// Setup for subsequent tracing
 			long tracing(HTTP_TRACE_OFF);
-			mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing);
+			mPolicy->getGlobalOptions().get(HttpRequest::PO_TRACE, &tracing);
 			op->mTracing = (std::max)(op->mTracing, int(tracing));
 
 			if (op->mTracing > HTTP_TRACE_OFF)
@@ -342,4 +345,137 @@ HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop)
 }
 
 
+HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
+										long * ret_value)
+{
+	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range
+		|| opt >= HttpRequest::PO_LAST													// ditto
+		|| (! sOptionDesc[opt].mIsLong)													// datatype is long
+		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range
+		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted
+		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass))	// class setting permitted
+																						// can always get, no dynamic check
+	{
+		return HttpStatus(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
+	}
+
+	HttpStatus status;
+	if (pclass == HttpRequest::GLOBAL_POLICY_ID)
+	{
+		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
+		
+		status = opts.get(opt, ret_value);
+	}
+	else
+	{
+		HttpPolicyClass & opts(mPolicy->getClassOptions(pclass));
+
+		status = opts.get(opt, ret_value);
+	}
+
+	return status;
+}
+
+
+HttpStatus HttpService::getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
+										std::string * ret_value)
+{
+	HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
+
+	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range
+		|| opt >= HttpRequest::PO_LAST													// ditto
+		|| (sOptionDesc[opt].mIsLong)													// datatype is string
+		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range
+		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted
+		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass))	// class setting permitted
+																						// can always get, no dynamic check
+	{
+		return status;
+	}
+
+	// Only global has string values
+	if (pclass == HttpRequest::GLOBAL_POLICY_ID)
+	{
+		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
+
+		status = opts.get(opt, ret_value);
+	}
+
+	return status;
+}
+
+
+HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
+										long value, long * ret_value)
+{
+	HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
+	
+	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range
+		|| opt >= HttpRequest::PO_LAST													// ditto
+		|| (! sOptionDesc[opt].mIsLong)													// datatype is long
+		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range
+		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted
+		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)		// class setting permitted
+		|| (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic))						// dynamic setting permitted
+	{
+		return status;
+	}
+
+	if (pclass == HttpRequest::GLOBAL_POLICY_ID)
+	{
+		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
+		
+		status = opts.set(opt, value);
+		if (status && ret_value)
+		{
+			status = opts.get(opt, ret_value);
+		}
+	}
+	else
+	{
+		HttpPolicyClass & opts(mPolicy->getClassOptions(pclass));
+
+		status = opts.set(opt, value);
+		if (status && ret_value)
+		{
+			status = opts.get(opt, ret_value);
+		}
+	}
+
+	return status;
+}
+
+
+HttpStatus HttpService::setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t pclass,
+										const std::string & value, std::string * ret_value)
+{
+	HttpStatus status(HttpStatus::LLCORE, LLCore::HE_INVALID_ARG);
+	
+	if (opt < HttpRequest::PO_CONNECTION_LIMIT											// option must be in range
+		|| opt >= HttpRequest::PO_LAST													// ditto
+		|| (sOptionDesc[opt].mIsLong)													// datatype is string
+		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && pclass > mLastPolicy)			// pclass in valid range
+		|| (pclass == HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsGlobal)	// global setting permitted
+		|| (pclass != HttpRequest::GLOBAL_POLICY_ID && ! sOptionDesc[opt].mIsClass)		// class setting permitted
+		|| (RUNNING == sState && ! sOptionDesc[opt].mIsDynamic))						// dynamic setting permitted
+	{
+		return status;
+	}
+
+	// Only string values are global at this time
+	if (pclass == HttpRequest::GLOBAL_POLICY_ID)
+	{
+		HttpPolicyGlobal & opts(mPolicy->getGlobalOptions());
+		
+		status = opts.set(opt, value);
+		if (status && ret_value)
+		{
+			status = opts.get(opt, ret_value);
+		}
+	}
+
+	return status;
+}
+	
+
 }  // end namespace LLCore
diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h
index ffe0349d4d..cf23f3ab61 100755
--- a/indra/llcorehttp/_httpservice.h
+++ b/indra/llcorehttp/_httpservice.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -53,6 +53,7 @@ namespace LLCore
 class HttpRequestQueue;
 class HttpPolicy;
 class HttpLibcurl;
+class HttpOpSetGet;
 
 
 /// The HttpService class does the work behind the request queue.  It
@@ -106,7 +107,7 @@ public:
 		NORMAL,					///< continuous polling of request, ready, active queues
 		REQUEST_SLEEP			///< can sleep indefinitely waiting for request queue write
 	};
-		
+
 	static void init(HttpRequestQueue *);
 	static void term();
 
@@ -136,7 +137,7 @@ public:
 	/// acquires its weaknesses.
 	static bool isStopped();
 
-	/// Threading:  callable by consumer thread *once*.
+	/// Threading:  callable by init thread *once*.
 	void startThread();
 
 	/// Threading:  callable by worker thread.
@@ -180,28 +181,39 @@ public:
 			return *mRequestQueue;
 		}
 
-	/// Threading:  callable by consumer thread.
-	HttpPolicyGlobal & getGlobalOptions()
-		{
-			return mPolicyGlobal;
-		}
-
 	/// Threading:  callable by consumer thread.
 	HttpRequest::policy_t createPolicyClass();
 	
-	/// Threading:  callable by consumer thread.
-	HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class)
-		{
-			llassert(policy_class >= 0 && policy_class < mPolicyClasses.size());
-			return mPolicyClasses[policy_class];
-		}
-	
 protected:
 	void threadRun(LLCoreInt::HttpThread * thread);
 	
 	ELoopSpeed processRequestQueue(ELoopSpeed loop);
+
+protected:
+	friend class HttpOpSetGet;
+	friend class HttpRequest;
+	
+	// Used internally to describe what operations are allowed
+	// on each policy option.
+	struct OptionDescriptor
+	{
+		bool		mIsLong;
+		bool		mIsDynamic;
+		bool		mIsGlobal;
+		bool		mIsClass;
+	};
+		
+	HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
+							   long * ret_value);
+	HttpStatus getPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
+							   std::string * ret_value);
+	HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
+							   long value, long * ret_value);
+	HttpStatus setPolicyOption(HttpRequest::EPolicyOption opt, HttpRequest::policy_t,
+							   const std::string & value, std::string * ret_value);
 	
 protected:
+	static const OptionDescriptor		sOptionDesc[HttpRequest::PO_LAST];
 	static HttpService *				sInstance;
 	
 	// === shared data ===
@@ -210,13 +222,13 @@ protected:
 	LLAtomicU32							mExitRequested;
 	LLCoreInt::HttpThread *				mThread;
 	
-	// === consumer-thread-only data ===
-	HttpPolicyGlobal					mPolicyGlobal;
-	std::vector<HttpPolicyClass>		mPolicyClasses;
-	
 	// === working-thread-only data ===
 	HttpPolicy *						mPolicy;		// Simple pointer, has ownership
 	HttpLibcurl *						mTransport;		// Simple pointer, has ownership
+	
+	// === main-thread-only data ===
+	HttpRequest::policy_t				mLastPolicy;
+	
 };  // end class HttpService
 
 }  // end namespace LLCore
diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp
index 88c8102b27..73c49687d7 100755
--- a/indra/llcorehttp/examples/http_texture_load.cpp
+++ b/indra/llcorehttp/examples/http_texture_load.cpp
@@ -236,9 +236,10 @@ int main(int argc, char** argv)
 	// Initialization
 	init_curl();
 	LLCore::HttpRequest::createService();
-	LLCore::HttpRequest::setPolicyClassOption(LLCore::HttpRequest::DEFAULT_POLICY_ID,
-											  LLCore::HttpRequest::CP_CONNECTION_LIMIT,
-											  concurrency_limit);
+	LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT,
+											   LLCore::HttpRequest::DEFAULT_POLICY_ID,
+											   concurrency_limit,
+											   NULL);
 	LLCore::HttpRequest::startThread();
 	
 	// Get service point
diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h
index c0d4ec5aad..9db884057f 100755
--- a/indra/llcorehttp/httpcommon.h
+++ b/indra/llcorehttp/httpcommon.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -29,9 +29,9 @@
 
 /// @package LLCore::HTTP
 ///
-/// This library implements a high-level, Indra-code-free client interface to
-/// HTTP services based on actual patterns found in the viewer and simulator.
-/// Interfaces are similar to those supplied by the legacy classes
+/// This library implements a high-level, Indra-code-free (somewhat) client
+/// interface to HTTP services based on actual patterns found in the viewer
+/// and simulator.  Interfaces are similar to those supplied by the legacy classes
 /// LLCurlRequest and LLHTTPClient.  To that is added a policy scheme that
 /// allows an application to specify connection behaviors:  limits on
 /// connections, HTTP keepalive, HTTP pipelining, retry-on-error limits, etc.
@@ -52,7 +52,7 @@
 /// - "llcorehttp/httprequest.h"
 /// - "llcorehttp/httpresponse.h"
 ///
-/// The library is still under early development and particular users
+/// The library is still under development and particular users
 /// may need access to internal implementation details that are found
 /// in the _*.h header files.  But this is a crutch to be avoided if at
 /// all possible and probably indicates some interface work is neeeded.
@@ -66,6 +66,8 @@
 ///   .  CRYPTO_set_id_callback(...)
 /// - HttpRequest::createService() called to instantiate singletons
 ///   and support objects.
+/// - HttpRequest::startThread() to kick off the worker thread and
+///   begin servicing requests.
 ///
 /// An HTTP consumer in an application, and an application may have many
 /// consumers, does a few things:
@@ -91,10 +93,12 @@
 ///   objects.
 /// - Do completion processing in your onCompletion() method.
 ///
-/// Code fragments:
-/// Rather than a poorly-maintained example in comments, look in the
-/// example subdirectory which is a minimal yet functional tool to do
-/// GET request performance testing.  With four calls:
+/// Code fragments.
+///
+/// Initialization.  Rather than a poorly-maintained example in
+/// comments, look in the example subdirectory which is a minimal
+/// yet functional tool to do GET request performance testing.
+/// With four calls:
 ///
 ///   	init_curl();
 ///     LLCore::HttpRequest::createService();
@@ -103,7 +107,85 @@
 ///
 /// the program is basically ready to issue requests.
 ///
-
+/// HttpHandler.  Having started life as a non-indra library,
+/// this code broke away from the classic Responder model and
+/// introduced a handler class to represent an interface for
+/// request responses.  This is a non-reference-counted entity
+/// which can be used as a base class or a mixin.  An instance
+/// of a handler can be used for each request or can be shared
+/// among any number of requests.  Your choice but expect to
+/// code something like the following:
+///
+///     class AppHandler : public LLCore::HttpHandler
+///     {
+///     public:
+///         virtual void onCompleted(HttpHandle handle,
+///                                  HttpResponse * response)
+///         {
+///             ...
+///         }
+///         ...
+///     };
+///     ...
+///     handler = new handler(...);
+///
+///
+/// Issuing requests.  Using 'hr' above,
+///
+///     hr->requestGet(HttpRequest::DEFAULT_POLICY_ID,
+///                    0,				// Priority, not used yet
+///                    url,
+///                    NULL,			// options
+///                    NULL,            // additional headers
+///                    handler);
+///
+/// If that returns a value other than LLCORE_HTTP_HANDLE_INVALID,
+/// the request was successfully issued and there will eventally
+/// be a status delivered to the handler.  If invalid is returnedd,
+/// the actual status can be retrieved by calling hr->getStatus().
+///
+/// Completing requests and delivering notifications.  Operations
+/// are all performed by the worker thread and will be driven to
+/// completion regardless of caller actions.  Notification of
+/// completion (success or failure) is done by calls to
+/// HttpRequest::update() which will invoke handlers for completed
+/// requests:
+///
+///     hr->update(0);
+///       // Callbacks into handler->onCompleted()
+///
+///
+/// Threads.
+///
+/// Threads are supported and used by this library.  The various
+/// classes, methods and members are documented with thread
+/// constraints which programmers must follow and which are
+/// defined as follows:
+///
+/// consumer	Any thread that has instanced HttpRequest and is
+///             issuing requests.  A particular instance can only
+///             be used by one consumer thread but a consumer may
+///             have many instances available to it.
+/// init		Special consumer thread, usually the main thread,
+///             involved in setting up the library at startup.
+/// worker      Thread used internally by the library to perform
+///             HTTP operations.  Consumers will not have to deal
+///             with this thread directly but some APIs are reserved
+///             to it.
+/// any         Consumer or worker thread.
+///
+/// For the most part, API users will not have to do much in the
+/// way of ensuring thread safely.  However, there is a tremendous
+/// amount of sharing between threads of read-only data.  So when
+/// documentation declares that an option or header instance
+/// becomes shared between consumer and worker, the consumer must
+/// not modify the shared object.
+///
+/// Internally, there is almost no thread synchronization.  During
+/// normal operations (non-init, non-term), only the request queue
+/// and the multiple reply queues are shared between threads and
+/// only here are mutexes used.
+///
 
 #include "linden_common.h"		// Modifies curl/curl.h interfaces
 
@@ -239,9 +321,10 @@ struct HttpStatus
 			return *this;
 		}
 	
-	static const type_enum_t EXT_CURL_EASY = 0;
-	static const type_enum_t EXT_CURL_MULTI = 1;
-	static const type_enum_t LLCORE = 2;
+	static const type_enum_t EXT_CURL_EASY = 0;			///< mStatus is an error from a curl_easy_*() call
+	static const type_enum_t EXT_CURL_MULTI = 1;		///< mStatus is an error from a curl_multi_*() call
+	static const type_enum_t LLCORE = 2;				///< mStatus is an HE_* error code
+														///< 100-999 directly represent HTTP status codes
 	
 	type_enum_t			mType;
 	short				mStatus;
diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp
index 9b739a8825..7b1888e3eb 100755
--- a/indra/llcorehttp/httprequest.cpp
+++ b/indra/llcorehttp/httprequest.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -54,12 +54,8 @@ namespace LLCore
 // ====================================
 
 
-HttpRequest::policy_t HttpRequest::sNextPolicyID(1);
-
-
 HttpRequest::HttpRequest()
-	: //HttpHandler(),
-	  mReplyQueue(NULL),
+	: mReplyQueue(NULL),
 	  mRequestQueue(NULL)
 {
 	mRequestQueue = HttpRequestQueue::instanceOf();
@@ -90,45 +86,91 @@ HttpRequest::~HttpRequest()
 // ====================================
 
 
-HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value)
+HttpRequest::policy_t HttpRequest::createPolicyClass()
 {
 	if (HttpService::RUNNING == HttpService::instanceOf()->getState())
 	{
-		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
+		return 0;
 	}
-	return HttpService::instanceOf()->getGlobalOptions().set(opt, value);
+	return HttpService::instanceOf()->createPolicyClass();
 }
 
 
-HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value)
+HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
+											  long value, long * ret_value)
 {
 	if (HttpService::RUNNING == HttpService::instanceOf()->getState())
 	{
 		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
 	}
-	return HttpService::instanceOf()->getGlobalOptions().set(opt, value);
+	return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
 }
 
 
-HttpRequest::policy_t HttpRequest::createPolicyClass()
+HttpStatus HttpRequest::setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
+											  const std::string & value, std::string * ret_value)
 {
 	if (HttpService::RUNNING == HttpService::instanceOf()->getState())
 	{
-		return 0;
+		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
 	}
-	return HttpService::instanceOf()->createPolicyClass();
+	return HttpService::instanceOf()->setPolicyOption(opt, pclass, value, ret_value);
 }
 
 
-HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id,
-											 EClassPolicy opt,
-											 long value)
+HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
+										long value, HttpHandler * handler)
 {
-	if (HttpService::RUNNING == HttpService::instanceOf()->getState())
+	HttpStatus status;
+	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
+
+	HttpOpSetGet * op = new HttpOpSetGet();
+	if (! (status = op->setupSet(opt, pclass, value)))
 	{
-		return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC);
+		op->release();
+		mLastReqStatus = status;
+		return handle;
+	}
+	op->setReplyPath(mReplyQueue, handler);
+	if (! (status = mRequestQueue->addOp(op)))			// transfers refcount
+	{
+		op->release();
+		mLastReqStatus = status;
+		return handle;
+	}
+	
+	mLastReqStatus = status;
+	handle = static_cast<HttpHandle>(op);
+	
+	return handle;
+}
+
+
+HttpHandle HttpRequest::setPolicyOption(EPolicyOption opt, policy_t pclass,
+										const std::string & value, HttpHandler * handler)
+{
+	HttpStatus status;
+	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
+
+	HttpOpSetGet * op = new HttpOpSetGet();
+	if (! (status = op->setupSet(opt, pclass, value)))
+	{
+		op->release();
+		mLastReqStatus = status;
+		return handle;
+	}
+	op->setReplyPath(mReplyQueue, handler);
+	if (! (status = mRequestQueue->addOp(op)))			// transfers refcount
+	{
+		op->release();
+		mLastReqStatus = status;
+		return handle;
 	}
-	return HttpService::instanceOf()->getClassOptions(policy_id).set(opt, value);
+	
+	mLastReqStatus = status;
+	handle = static_cast<HttpHandle>(op);
+	
+	return handle;
 }
 
 
@@ -474,31 +516,6 @@ HttpHandle HttpRequest::requestSpin(int mode)
 	return handle;
 }
 
-// ====================================
-// Dynamic Policy Methods
-// ====================================
-
-HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandler * handler)
-{
-	HttpStatus status;
-	HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
-
-	HttpOpSetGet * op = new HttpOpSetGet();
-	op->setupSet(GP_HTTP_PROXY, proxy);
-	op->setReplyPath(mReplyQueue, handler);
-	if (! (status = mRequestQueue->addOp(op)))			// transfers refcount
-	{
-		op->release();
-		mLastReqStatus = status;
-		return handle;
-	}
-
-	mLastReqStatus = status;
-	handle = static_cast<HttpHandle>(op);
-
-	return handle;
-}
-
 
 }   // end namespace LLCore
 
diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h
index 5000f47d0d..5c54d35a21 100755
--- a/indra/llcorehttp/httprequest.h
+++ b/indra/llcorehttp/httprequest.h
@@ -56,6 +56,9 @@ class BufferArray;
 /// The class supports the current HTTP request operations:
 ///
 /// - requestGetByteRange:  GET with Range header for a single range of bytes
+/// - requestGet:
+/// - requestPost:
+/// - requestPut:
 ///
 /// Policy Classes
 ///
@@ -100,9 +103,26 @@ public:
 
 	/// Represents a default, catch-all policy class that guarantees
 	/// eventual service for any HTTP request.
-	static const int DEFAULT_POLICY_ID = 0;
+	static const policy_t DEFAULT_POLICY_ID = 0;
+	static const policy_t INVALID_POLICY_ID = 0xFFFFFFFFU;
+	static const policy_t GLOBAL_POLICY_ID = 0xFFFFFFFEU;
 
-	enum EGlobalPolicy
+	/// Create a new policy class into which requests can be made.
+	///
+	/// All class creation must occur before threads are started and
+	/// transport begins.  Policy classes are limited to a small value.
+	/// Currently that limit is the default class + 1.
+	///
+	/// @return			If positive, the policy_id used to reference
+	///					the class in other methods.  If 0, requests
+	///					for classes have exceeded internal limits
+	///					or caller has tried to create a class after
+	///					threads have been started.  Caller must fallback
+	///					and recover.
+	///
+	static policy_t createPolicyClass();
+
+	enum EPolicyOption
 	{
 		/// Maximum number of connections the library will use to
 		/// perform operations.  This is somewhat soft as the underlying
@@ -113,24 +133,30 @@ public:
 		/// a somewhat soft value.  There may be an additional five
 		/// connections per policy class depending upon runtime
 		/// behavior.
-		GP_CONNECTION_LIMIT,
+		///
+		/// Both global and per-class
+		PO_CONNECTION_LIMIT,
+
+		/// Limits the number of connections used for a single
+		/// literal address/port pair within the class.
+		PO_PER_HOST_CONNECTION_LIMIT,
 
 		/// String containing a system-appropriate directory name
 		/// where SSL certs are stored.
-		GP_CA_PATH,
+		PO_CA_PATH,
 
 		/// String giving a full path to a file containing SSL certs.
-		GP_CA_FILE,
+		PO_CA_FILE,
 
 		/// String of host/port to use as simple HTTP proxy.  This is
 		/// going to change in the future into something more elaborate
 		/// that may support richer schemes.
-		GP_HTTP_PROXY,
+		PO_HTTP_PROXY,
 
 		/// Long value that if non-zero enables the use of the
 		/// traditional LLProxy code for http/socks5 support.  If
-		/// enabled, has priority over GP_HTTP_PROXY.
-		GP_LLPROXY,
+		// enabled, has priority over GP_HTTP_PROXY.
+		PO_LLPROXY,
 
 		/// Long value setting the logging trace level for the
 		/// library.  Possible values are:
@@ -143,57 +169,46 @@ public:
 		/// These values are also used in the trace modes for
 		/// individual requests in HttpOptions.  Also be aware that
 		/// tracing tends to impact performance of the viewer.
-		GP_TRACE
-	};
-
-	/// Set a parameter on a global policy option.  Calls
-	/// made after the start of the servicing thread are
-	/// not honored and return an error status.
-	///
-	/// @param opt		Enum of option to be set.
-	/// @param value	Desired value of option.
-	/// @return			Standard status code.
-	static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value);
-	static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value);
-
-	/// Create a new policy class into which requests can be made.
-	///
-	/// All class creation must occur before threads are started and
-	/// transport begins.  Policy classes are limited to a small value.
-	/// Currently that limit is the default class + 1.
-	///
-	/// @return			If positive, the policy_id used to reference
-	///					the class in other methods.  If 0, requests
-	///					for classes have exceeded internal limits
-	///					or caller has tried to create a class after
-	///					threads have been started.  Caller must fallback
-	///					and recover.
-	///
-	static policy_t createPolicyClass();
-
-	enum EClassPolicy
-	{
-		/// Limits the number of connections used for the class.
-		CP_CONNECTION_LIMIT,
-
-		/// Limits the number of connections used for a single
-		/// literal address/port pair within the class.
-		CP_PER_HOST_CONNECTION_LIMIT,
+		PO_TRACE,
 
 		/// Suitable requests are allowed to pipeline on their
 		/// connections when they ask for it.
-		CP_ENABLE_PIPELINING
+		PO_ENABLE_PIPELINING,
+
+		PO_LAST  // Always at end
 	};
-	
+
+	/// Set a policy option for a global or class parameter at
+	/// startup time (prior to thread start).
+	///
+	/// @param opt			Enum of option to be set.
+	/// @param pclass		For class-based options, the policy class ID to
+	///					    be changed.  For globals, specify GLOBAL_POLICY_ID.
+	/// @param value		Desired value of option.
+	/// @param ret_value	Pointer to receive effective set value
+	///						if successful.  May be NULL if effective
+	///						value not wanted.
+	/// @return				Standard status code.
+	static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
+											long value, long * ret_value);
+	static HttpStatus setStaticPolicyOption(EPolicyOption opt, policy_t pclass,
+											const std::string & value, std::string * ret_value);
+
 	/// Set a parameter on a class-based policy option.  Calls
 	/// made after the start of the servicing thread are
 	/// not honored and return an error status.
 	///
-	/// @param policy_id		ID of class as returned by @see createPolicyClass().
-	/// @param opt				Enum of option to be set.
-	/// @param value			Desired value of option.
-	/// @return					Standard status code.
-	static HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value);
+	/// @param opt			Enum of option to be set.
+	/// @param pclass		For class-based options, the policy class ID to
+	///					    be changed.  Ignored for globals but recommend
+	///					    using INVALID_POLICY_ID in this case.
+	/// @param value		Desired value of option.
+	/// @return				Handle of dynamic request.  Use @see getStatus() if
+	///						the returned handle is invalid.
+	HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, long value,
+							   HttpHandler * handler);
+	HttpHandle setPolicyOption(EPolicyOption opt, policy_t pclass, const std::string & value,
+							   HttpHandler * handler);
 
 	/// @}
 
@@ -495,16 +510,6 @@ public:
 
 	/// @}
 	
-	/// @name DynamicPolicyMethods
-	///
-	/// @{
-
-	/// Request that a running transport pick up a new proxy setting.
-	/// An empty string will indicate no proxy is to be used.
-	HttpHandle requestSetHttpProxy(const std::string & proxy, HttpHandler * handler);
-
-    /// @}
-
 protected:
 	void generateNotification(HttpOperation * op);
 
@@ -526,7 +531,6 @@ private:
 	/// Must be established before any threading is allowed to
 	/// start.
 	///
-	static policy_t		sNextPolicyID;
 	
 	/// @}
 	// End Global State
diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp
index 27d65f171e..f1b9c02393 100755
--- a/indra/llcorehttp/tests/test_httprequest.hpp
+++ b/indra/llcorehttp/tests/test_httprequest.hpp
@@ -1213,7 +1213,7 @@ void HttpRequestTestObjectType::test<12>()
 		HttpRequest::createService();
 
 		// Enable tracing
-		HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2);
+		HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);
 
 		// Start threading early so that thread memory is invariant
 		// over the test.
@@ -1331,7 +1331,7 @@ void HttpRequestTestObjectType::test<13>()
 		HttpRequest::createService();
 
 		// Enable tracing
-		HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2);
+		HttpRequest::setStaticPolicyOption(HttpRequest::PO_TRACE, HttpRequest::DEFAULT_POLICY_ID, 2, NULL);
 
 		// Start threading early so that thread memory is invariant
 		// over the test.
@@ -2972,6 +2972,142 @@ void HttpRequestTestObjectType::test<21>()
 }
 
 
+template <> template <>
+void HttpRequestTestObjectType::test<22>()
+{
+	ScopedCurlInit ready;
+
+	set_test_name("HttpRequest GET 503s with 'Retry-After'");
+
+	// This tests mainly that the code doesn't fall over if
+	// various well- and mis-formed Retry-After headers are
+	// sent along with the response.  Direct inspection of
+	// the parsing result isn't supported.
+	
+	// Handler can be stack-allocated *if* there are no dangling
+	// references to it after completion of this method.
+	// Create before memory record as the string copy will bump numbers.
+	TestHandler2 handler(this, "handler");
+	std::string url_base(get_base_url() + "/503/");	// path to 503 generators
+		
+	// record the total amount of dynamically allocated memory
+	mMemTotal = GetMemTotal();
+	mHandlerCalls = 0;
+
+	HttpRequest * req = NULL;
+	HttpOptions * opts = NULL;
+	
+	try
+	{
+		// Get singletons created
+		HttpRequest::createService();
+		
+		// Start threading early so that thread memory is invariant
+		// over the test.
+		HttpRequest::startThread();
+
+		// create a new ref counted object with an implicit reference
+		req = new HttpRequest();
+		ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
+
+		opts = new HttpOptions();
+		opts->setRetries(1);			// Retry once only
+		opts->setUseRetryAfter(true);	// Try to parse the retry-after header
+		
+		// Issue a GET that 503s with valid retry-after
+		mStatus = HttpStatus(503);
+		int url_limit(6);
+		for (int i(0); i < url_limit; ++i)
+		{
+			std::ostringstream url;
+			url << url_base << i << "/";
+			HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
+														 0U,
+														 url.str(),
+														 0,
+														 0,
+														 opts,
+														 NULL,
+														 &handler);
+
+			std::ostringstream testtag;
+			testtag << "Valid handle returned for 503 request #" << i;
+			ensure(testtag.str(), handle != LLCORE_HTTP_HANDLE_INVALID);
+		}
+		
+
+		// Run the notification pump.
+		int count(0);
+		int limit(300);				// One retry but several seconds needed
+		while (count++ < limit && mHandlerCalls < url_limit)
+		{
+			req->update(0);
+			usleep(100000);
+		}
+		ensure("Request executed in reasonable time", count < limit);
+		ensure("One handler invocation for request", mHandlerCalls == url_limit);
+
+		// Okay, request a shutdown of the servicing thread
+		mStatus = HttpStatus();
+		mHandlerCalls = 0;
+		HttpHandle handle = req->requestStopThread(&handler);
+		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
+	
+		// Run the notification pump again
+		count = 0;
+		limit = 100;
+		while (count++ < limit && mHandlerCalls < 1)
+		{
+			req->update(1000000);
+			usleep(100000);
+		}
+		ensure("Second request executed in reasonable time", count < limit);
+		ensure("Second handler invocation", mHandlerCalls == 1);
+
+		// See that we actually shutdown the thread
+		count = 0;
+		limit = 10;
+		while (count++ < limit && ! HttpService::isStopped())
+		{
+			usleep(100000);
+		}
+		ensure("Thread actually stopped running", HttpService::isStopped());
+
+		// release options
+		opts->release();
+		opts = NULL;
+		
+		// release the request object
+		delete req;
+		req = NULL;
+
+		// Shut down service
+		HttpRequest::destroyService();
+	
+#if defined(WIN32)
+		// Can only do this memory test on Windows.  On other platforms,
+		// the LL logging system holds on to memory and produces what looks
+		// like memory leaks...
+	
+		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal());
+		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
+#endif
+	}
+	catch (...)
+	{
+		stop_thread(req);
+		if (opts)
+		{
+			opts->release();
+			opts = NULL;
+		}
+		delete req;
+		HttpRequest::destroyService();
+		throw;
+	}
+}
+
+
 }  // end namespace tut
 
 namespace
diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py
index 75a3c39ef2..f6c4d1a820 100755
--- a/indra/llcorehttp/tests/test_llcorehttp_peer.py
+++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py
@@ -9,7 +9,7 @@
 
 $LicenseInfo:firstyear=2008&license=viewerlgpl$
 Second Life Viewer Source Code
-Copyright (C) 2012, Linden Research, Inc.
+Copyright (C) 2012-2013, Linden Research, Inc.
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -47,6 +47,17 @@ from testrunner import freeport, run, debug, VERBOSE
 class TestHTTPRequestHandler(BaseHTTPRequestHandler):
     """This subclass of BaseHTTPRequestHandler is to receive and echo
     LLSD-flavored messages sent by the C++ LLHTTPClient.
+
+    [Merge with viewer-cat later]
+    - '/503/'           Generate 503 responses with various kinds
+                        of 'retry-after' headers
+    -- '/503/0/'            "Retry-After: 2"   
+    -- '/503/1/'            "Retry-After: Thu, 31 Dec 2043 23:59:59 GMT"
+    -- '/503/2/'            "Retry-After: Fri, 31 Dec 1999 23:59:59 GMT"
+    -- '/503/3/'            "Retry-After: "
+    -- '/503/4/'            "Retry-After: (*#*(@*(@(")"
+    -- '/503/5/'            "Retry-After: aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo"
+    -- '/503/6/'            "Retry-After: 1 2 3 4 5 6 7 8 9 10"
     """
     def read(self):
         # The following logic is adapted from the library module
@@ -107,7 +118,41 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):
         if "/sleep/" in self.path:
             time.sleep(30)
 
-        if "fail" not in self.path:
+        if "/503/" in self.path:
+            # Tests for various kinds of 'Retry-After' header parsing
+            body = None
+            if "/503/0/" in self.path:
+                self.send_response(503)
+                self.send_header("retry-after", "2")
+            elif "/503/1/" in self.path:
+                self.send_response(503)
+                self.send_header("retry-after", "Thu, 31 Dec 2043 23:59:59 GMT")
+            elif "/503/2/" in self.path:
+                self.send_response(503)
+                self.send_header("retry-after", "Fri, 31 Dec 1999 23:59:59 GMT")
+            elif "/503/3/" in self.path:
+                self.send_response(503)
+                self.send_header("retry-after", "")
+            elif "/503/4/" in self.path:
+                self.send_response(503)
+                self.send_header("retry-after", "(*#*(@*(@(")
+            elif "/503/5/" in self.path:
+                self.send_response(503)
+                self.send_header("retry-after", "aklsjflajfaklsfaklfasfklasdfklasdgahsdhgasdiogaioshdgo")
+            elif "/503/6/" in self.path:
+                self.send_response(503)
+                self.send_header("retry-after", "1 2 3 4 5 6 7 8 9 10")
+            else:
+                # Unknown request
+                self.send_response(400)
+                body = "Unknown /503/ path in server"
+            if "/reflect/" in self.path:
+                self.reflect_headers()
+            self.send_header("Content-type", "text/plain")
+            self.end_headers()
+            if body:
+                self.wfile.write(body)
+        elif "fail" not in self.path:
             data = data.copy()          # we're going to modify
             # Ensure there's a "reply" key in data, even if there wasn't before
             data["reply"] = data.get("reply", llsd.LLSD("success"))
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index 0c242e57db..104debe023 100755
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -105,8 +105,9 @@ void LLAppCoreHttp::init()
 	}
 
 	// Point to our certs or SSH/https: will fail on connect
-	status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE,
-														gDirUtilp->getCAFile());
+	status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE,
+														LLCore::HttpRequest::GLOBAL_POLICY_ID,
+														gDirUtilp->getCAFile(), NULL);
 	if (! status)
 	{
 		LL_ERRS("Init") << "Failed to set CA File for HTTP services.  Reason:  " << status.toString()
@@ -114,7 +115,9 @@ void LLAppCoreHttp::init()
 	}
 
 	// Establish HTTP Proxy, if desired.
-	status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1);
+	status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_LLPROXY,
+														LLCore::HttpRequest::GLOBAL_POLICY_ID,
+														1, NULL);
 	if (! status)
 	{
 		LL_WARNS("Init") << "Failed to set HTTP proxy for HTTP services.  Reason:  " << status.toString()
@@ -131,7 +134,9 @@ void LLAppCoreHttp::init()
 	{
 		long trace_level(0L);
 		trace_level = long(gSavedSettings.getU32(http_trace));
-		status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level);
+		status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_TRACE,
+															LLCore::HttpRequest::GLOBAL_POLICY_ID,
+															trace_level, NULL);
 	}
 	
 	// Setup default policy and constrain if directed to
@@ -164,6 +169,9 @@ void LLAppCoreHttp::init()
 		}
 	}
 
+	// Need a request object to handle dynamic options before setting them
+	mRequest = new LLCore::HttpRequest;
+
 	// Apply initial settings
 	refreshSettings(true);
 	
@@ -175,8 +183,6 @@ void LLAppCoreHttp::init()
 						<< LL_ENDL;
 	}
 
-	mRequest = new LLCore::HttpRequest;
-
 	// Register signals for settings and state changes
 	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
 	{
@@ -287,12 +293,13 @@ void LLAppCoreHttp::refreshSettings(bool initial)
 		// Set it and report
 		// *TODO:  These are intended to be per-host limits when we can
 		// support that in llcorehttp/libcurl.
-		LLCore::HttpStatus status;
-		status = LLCore::HttpRequest::setPolicyClassOption(mPolicies[policy],
-														   LLCore::HttpRequest::CP_CONNECTION_LIMIT,
-														   setting);
-		if (! status)
+		LLCore::HttpHandle handle;
+		handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT,
+										   mPolicies[policy],
+										   setting, NULL);
+		if (LLCORE_HTTP_HANDLE_INVALID == handle)
 		{
+			LLCore::HttpStatus status(mRequest->getStatus());
 			LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
 							 << " concurrency.  Reason:  " << status.toString()
 							 << LL_ENDL;
-- 
cgit v1.2.3


From d027db3cfc7c757cc8846e1a5187d1a816f63d9a Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 18 Jul 2013 15:14:55 -0400
Subject: SH-4325  Viewer working mixed grid with GetMesh and GetMesh2 caps
 Viewer modified for preference for GetMesh2 caps.  When found, uses this cap
 and uses 1/4 the connection concurrency specified by
 MeshMaxConcurrentRequests.  Also uses a modified calculation for high/low
 water feeding into the llcorehttp library.

---
 indra/newview/llmeshrepository.cpp | 68 ++++++++++++++++++++++----------------
 indra/newview/llmeshrepository.h   |  3 +-
 2 files changed, 41 insertions(+), 30 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 3af9ce7342..913841ab71 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -152,7 +152,11 @@
 //     [5]  Read of a double-checked lock.
 //
 //   So, in addition to documentation, take this as a to-do/review
-//   list and see if you can improve things.
+//   list and see if you can improve things.  For porters to non-x86
+//   architectures, including amd64, the weaker memory models will
+//   make these platforms probabilistically more susceptible to hitting
+//   race conditions.  True here and in other multi-thread code such
+//   as texture fetching.
 //
 //   LLMeshRepository:
 //
@@ -237,8 +241,6 @@ U32 LLMeshRepository::sCacheBytesWritten = 0;
 LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true);	// true -> gather cpu metrics
 
 	
-const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5;
-
 static S32 dump_num = 0;
 std::string make_dump_name(std::string prefix, S32 num)
 {
@@ -797,7 +799,6 @@ void LLMeshRepoThread::run()
 		}
 
 		mWaiting = true;
-		ms_sleep(5);
 		mSignal->wait();
 		mWaiting = false;
 		
@@ -982,20 +983,17 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 }
 
 // Constructs a Cap URL for the mesh.  Prefers a GetMesh2 cap
-// over a GetMesh cap and returns what it finds to the caller
-// as an int ([1..2]).
+// over a GetMesh cap.
 //
 //static 
-std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id, int * cap_version)
+std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
 {
-	int version(1);
 	std::string http_url;
 	
 	if (gAgent.getRegion())
 	{
 		if (! gMeshRepo.mGetMesh2Capability.empty())
 		{
-			version = 2;
 			http_url = gMeshRepo.mGetMesh2Capability;
 		}
 		else
@@ -1015,7 +1013,6 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id, int * cap_version)
 								<< mesh_id << ".mesh" << LL_ENDL;
 	}
 
-	*cap_version = version;
 	return http_url;
 }
 
@@ -1134,8 +1131,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count)
 			{
 				return false;
 			}
-			int cap_version(1);
-			std::string http_url = constructUrl(mesh_id, &cap_version);
+			int cap_version(gMeshRepo.mGetMeshVersion);
+			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
 				LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size);
@@ -1229,8 +1226,8 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id, U32& count)
 			{
 				return false;
 			}
-			int cap_version(1);
-			std::string http_url = constructUrl(mesh_id, &cap_version);
+			int cap_version(gMeshRepo.mGetMeshVersion);
+			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
 				LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size);
@@ -1323,8 +1320,8 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count)
 			{
 				return false;
 			}
-			int cap_version(1);
-			std::string http_url = constructUrl(mesh_id, &cap_version);
+			int cap_version(gMeshRepo.mGetMeshVersion);
+			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
 				LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size);
@@ -1414,8 +1411,8 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 
 	//either cache entry doesn't exist or is corrupt, request header from simulator	
 	bool retval = true;
-	int cap_version(1);
-	std::string http_url = constructUrl(mesh_params.getSculptID(), &cap_version);
+	int cap_version(gMeshRepo.mGetMeshVersion);
+	std::string http_url = constructUrl(mesh_params.getSculptID());
 	if (!http_url.empty())
 	{
 		//grab first 4KB if we're going to bother with a fetch.  Cache will prevent future fetches if a full mesh fits
@@ -1500,8 +1497,8 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			int cap_version(1);
-			std::string http_url = constructUrl(mesh_id, &cap_version);
+			int cap_version(gMeshRepo.mGetMeshVersion);
+			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
 			{
 				LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size);
@@ -2647,7 +2644,8 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat
 LLMeshRepository::LLMeshRepository()
 : mMeshMutex(NULL),
   mMeshThreadCount(0),
-  mThread(NULL)
+  mThread(NULL),
+  mGetMeshVersion(2)
 {
 
 }
@@ -2827,13 +2825,24 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
 
 void LLMeshRepository::notifyLoadedMeshes()
 { //called from main thread
-	// *FIXME:  Scaling down the setting by a factor of 4 for now to reflect
-	// target goal.  May want to rename the setting before release.  Also
-	// want/need to get these in a coordinated fashion from llappcorehttp.
-	LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests") / 4;
-	LLMeshRepoThread::sRequestHighWater = llclamp(10 * S32(LLMeshRepoThread::sMaxConcurrentRequests),
-												  REQUEST_HIGH_WATER_MIN,
-												  REQUEST_HIGH_WATER_MAX);
+	if (1 == mGetMeshVersion)
+	{
+		// Legacy GetMesh operation with high connection concurrency
+		LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests");
+		LLMeshRepoThread::sRequestHighWater = llclamp(2 * S32(LLMeshRepoThread::sMaxConcurrentRequests),
+													  REQUEST_HIGH_WATER_MIN,
+													  REQUEST_HIGH_WATER_MAX);
+	}
+	else
+	{
+		// GetMesh2 operation with keepalives, etc.
+		// *TODO:  Logic here is replicated from llappcorehttp.cpp, should really
+		// unify this and keep it in one place only.
+		LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests") / 4;
+		LLMeshRepoThread::sRequestHighWater = llclamp(5 * S32(LLMeshRepoThread::sMaxConcurrentRequests),
+													  REQUEST_HIGH_WATER_MIN,
+													  REQUEST_HIGH_WATER_MAX);
+	}
 	LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2,
 												 REQUEST_LOW_WATER_MIN,
 												 REQUEST_LOW_WATER_MAX);
@@ -2929,6 +2938,7 @@ void LLMeshRepository::notifyLoadedMeshes()
 			region_name = gAgent.getRegion()->getName();
 			mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh");
 			mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2");
+			mGetMeshVersion = mGetMesh2Capability.empty() ? 1 : 2;
 			LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name
 								<< "', GetMesh2:  " << mGetMesh2Capability
 								<< ", GetMesh:  " << mGetMeshCapability
@@ -4152,7 +4162,7 @@ void teleport_started()
 	LLMeshRepository::metricsStart();
 }
 
-// This comes from an edit in viewer-cat.  Unify this once that's
+// *TODO:  This comes from an edit in viewer-cat.  Unify this once that's
 // available everywhere.
 bool is_retryable(LLCore::HttpStatus status)
 {
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 9bf14a3715..96e9feea9d 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -337,7 +337,7 @@ public:
 	typedef std::set<LLCore::HttpHandler *> http_request_set;
 	http_request_set					mHttpRequestSet;			// Outstanding HTTP requests
 
-	static std::string constructUrl(LLUUID mesh_id, int * cap_version);
+	static std::string constructUrl(LLUUID mesh_id);
 
 	LLMeshRepoThread();
 	~LLMeshRepoThread();
@@ -600,6 +600,7 @@ public:
 
 	std::string mGetMeshCapability;
 	std::string mGetMesh2Capability;
+	int mGetMeshVersion;
 };
 
 extern LLMeshRepository gMeshRepo;
-- 
cgit v1.2.3


From b7f14a7b39610ac1cb6db5fafeab568aa9b662c5 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 25 Jul 2013 18:36:08 -0400
Subject: SH-4365 SH-4367  Conversion of mesh uploaders to llcorehttp. With
 this checkin, legacy LLCurl is out of the mesh code. Uploaders and responders
 are converted and functioning.  Logging has been cleaned up throughout the
 code to use the macro form with tag/subtag capability.  DEBUGS-level logging
 added for some upload path milestones.  Better error information flow from
 failed responses to viewer alert boxes but I'd really, really like to do
 better here.  Mesh upload problems are completely opaque as a user.  Minor
 cleanups (removed dead members, method signatures tidied, less data
 conversion).  Could almost call this complete, will likely have platform
 cleanups, however.

---
 indra/newview/llmeshrepository.cpp | 514 +++++++++++++++++++++----------------
 indra/newview/llmeshrepository.h   |  48 ++--
 2 files changed, 328 insertions(+), 234 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 2d003dd6d7..1f40026e80 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -68,6 +68,7 @@
 #include "llviewerparcelmgr.h"
 #include "lluploadfloaterobservers.h"
 #include "bufferarray.h"
+#include "bufferstream.h"
 
 #include "boost/lexical_cast.hpp"
 
@@ -203,6 +204,7 @@
 //     mUnavailableQ            mMutex        rw.repo.none [0], ro.main.none [3], rw.main.mMutex
 //     mLoadedQ                 mMutex        rw.repo.mMutex, ro.main.none [3], rw.main.mMutex
 //     mPendingLOD              mMutex        rw.repo.mMutex, rw.any.mMutex
+//     mHttp*                   none          rw.repo.none
 //
 //   LLPhysicsDecomp:
 //    
@@ -210,7 +212,19 @@
 //     mCurRequest
 //     mCompletedQ
 //
-
+//
+// QA/Development Testing
+//
+//   Debug variable 'MeshUploadFakeErrors' takes a mask of bits that will
+//   simulate an error on fee query or upload.  Defined bits are:
+//
+//   0x01            Simulate application error on fee check reading
+//                   response body from file "fake_upload_error.xml"
+//   0x02            Same as 0x01 but for actual upload attempt.
+//   0x04            Simulate a transport problem on fee check with a
+//                   locally-generated 500 status.
+//   0x08            As with 0x04 but for the upload operation.
+//
 
 LLMeshRepository gMeshRepo;
 
@@ -362,6 +376,14 @@ S32 LLMeshRepoThread::sRequestHighWater = REQUEST_HIGH_WATER_MIN;
 // processFailure() methods to customize handling and
 // error messages.
 //
+// LLCore::HttpHandler
+//   LLMeshHandlerBase
+//     LLMeshHeaderHandler
+//     LLMeshLODHandler
+//     LLMeshSkinInfoHandler
+//     LLMeshDecompositionHandler
+//     LLMeshPhysicsShapeHandler
+
 class LLMeshHandlerBase : public LLCore::HttpHandler
 {
 public:
@@ -534,28 +556,31 @@ public:
 };
 
 
-void log_upload_error(S32 status, const LLSD& content, std::string stage, std::string model_name)
+void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
+					  const char * const stage, const std::string & model_name)
 {
 	// Add notification popup.
 	LLSD args;
-	std::string message = content["error"]["message"];
-	std::string identifier = content["error"]["identifier"];
+	std::string message = content["error"]["message"].asString();
+	std::string identifier = content["error"]["identifier"].asString();
 	args["MESSAGE"] = message;
 	args["IDENTIFIER"] = identifier;
 	args["LABEL"] = model_name;
 	gMeshRepo.uploadError(args);
 
 	// Log details.
-	llwarns << "stage: " << stage << " http status: " << status << llendl;
+	LL_WARNS(LOG_MESH) << "Error in stage:  " << stage
+					   << ", Reason:  " << status.toString()
+					   << " (" << status.toHex() << ")" << LL_ENDL;
 	if (content.has("error"))
 	{
 		const LLSD& err = content["error"];
-		llwarns << "err: " << err << llendl;
-		llwarns << "mesh upload failed, stage '" << stage
-				<< "' error '" << err["error"].asString()
-				<< "', message '" << err["message"].asString()
-				<< "', id '" << err["identifier"].asString()
-				<< "'" << llendl;
+		LL_WARNS(LOG_MESH) << "error: " << err << LL_ENDL;
+		LL_WARNS(LOG_MESH) << "  mesh upload failed, stage '" << stage
+						   << "', error '" << err["error"].asString()
+						   << "', message '" << err["message"].asString()
+						   << "', id '" << err["identifier"].asString()
+						   << "'" << LL_ENDL;
 		if (err.has("errors"))
 		{
 			S32 error_num = 0;
@@ -565,13 +590,13 @@ void log_upload_error(S32 status, const LLSD& content, std::string stage, std::s
 				 ++it)
 			{
 				const LLSD& err_entry = *it;
-				llwarns << "error[" << error_num << "]:" << llendl;
+				LL_WARNS(LOG_MESH) << "  error[" << error_num << "]:" << LL_ENDL;
 				for (LLSD::map_const_iterator map_it = err_entry.beginMap();
 					 map_it != err_entry.endMap();
 					 ++map_it)
 				{
-					llwarns << "\t" << map_it->first << ": "
-							<< map_it->second << llendl;
+					LL_WARNS(LOG_MESH) << "    " << map_it->first << ":  "
+									   << map_it->second << LL_ENDL;
 				}
 				error_num++;
 			}
@@ -579,141 +604,10 @@ void log_upload_error(S32 status, const LLSD& content, std::string stage, std::s
 	}
 	else
 	{
-		llwarns << "bad mesh, no error information available" << llendl;
+		LL_WARNS(LOG_MESH) << "Bad response to mesh request, no additional error information available." << LL_ENDL;
 	}
 }
 
-class LLWholeModelFeeResponder: public LLCurl::Responder
-{
-	LLMeshUploadThread* mThread;
-	LLSD mModelData;
-	LLHandle<LLWholeModelFeeObserver> mObserverHandle;
-public:
-	LLWholeModelFeeResponder(LLMeshUploadThread* thread, LLSD& model_data, LLHandle<LLWholeModelFeeObserver> observer_handle):
-		mThread(thread),
-		mModelData(model_data),
-		mObserverHandle(observer_handle)
-	{
-		if (mThread)
-		{
-			mThread->startRequest();
-		}
-	}
-
-	~LLWholeModelFeeResponder()
-	{
-		if (mThread)
-		{
-			mThread->stopRequest();
-		}
-	}
-
-	virtual void completed(U32 status,
-						   const std::string& reason,
-						   const LLSD& content)
-	{
-		LLSD cc = content;
-		if (gSavedSettings.getS32("MeshUploadFakeErrors")&1)
-		{
-			cc = llsd_from_file("fake_upload_error.xml");
-		}
-			
-		dump_llsd_to_file(cc,make_dump_name("whole_model_fee_response_",dump_num));
-
-		LLWholeModelFeeObserver* observer = mObserverHandle.get();
-
-		if (isGoodStatus(status) &&
-			cc["state"].asString() == "upload")
-		{
-			mThread->mWholeModelUploadURL = cc["uploader"].asString();
-
-			if (observer)
-			{
-				cc["data"]["upload_price"] = cc["upload_price"];
-				observer->onModelPhysicsFeeReceived(cc["data"], mThread->mWholeModelUploadURL);
-			}
-		}
-		else
-		{
-			llwarns << "fee request failed" << llendl;
-			log_upload_error(status,cc,"fee",mModelData["name"]);
-			mThread->mWholeModelUploadURL = "";
-
-			if (observer)
-			{
-				observer->setModelPhysicsFeeErrorStatus(status, reason);
-			}
-		}
-	}
-
-};
-
-class LLWholeModelUploadResponder: public LLCurl::Responder
-{
-	LLMeshUploadThread* mThread;
-	LLSD mModelData;
-	LLHandle<LLWholeModelUploadObserver> mObserverHandle;
-	
-public:
-	LLWholeModelUploadResponder(LLMeshUploadThread* thread, LLSD& model_data, LLHandle<LLWholeModelUploadObserver> observer_handle):
-		mThread(thread),
-		mModelData(model_data),
-		mObserverHandle(observer_handle)
-	{
-		if (mThread)
-		{
-			mThread->startRequest();
-		}
-	}
-
-	~LLWholeModelUploadResponder()
-	{
-		if (mThread)
-		{
-			mThread->stopRequest();
-		}
-	}
-
-	virtual void completed(U32 status,
-						   const std::string& reason,
-						   const LLSD& content)
-	{
-		LLSD cc = content;
-		if (gSavedSettings.getS32("MeshUploadFakeErrors")&2)
-		{
-			cc = llsd_from_file("fake_upload_error.xml");
-		}
-
-		dump_llsd_to_file(cc,make_dump_name("whole_model_upload_response_",dump_num));
-		
-		LLWholeModelUploadObserver* observer = mObserverHandle.get();
-
-		// requested "mesh" asset type isn't actually the type
-		// of the resultant object, fix it up here.
-		if (isGoodStatus(status) &&
-			cc["state"].asString() == "complete")
-		{
-			mModelData["asset_type"] = "object";
-			gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mModelData,cc));
-
-			if (observer)
-			{
-				doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadSuccess, observer));
-			}
-		}
-		else
-		{
-			llwarns << "upload failed" << llendl;
-			std::string model_name = mModelData["name"].asString();
-			log_upload_error(status,cc,"upload",model_name);
-
-			if (observer)
-			{
-				doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer));
-			}
-		}
-	}
-};
 
 LLMeshRepoThread::LLMeshRepoThread()
 : LLThread("mesh repo"),
@@ -788,7 +682,7 @@ void LLMeshRepoThread::run()
 	LLCDResult res = LLConvexDecomposition::initThread();
 	if (res != LLCD_OK)
 	{
-		llwarns << "convex decomposition unable to be loaded" << llendl;
+		LL_WARNS(LOG_MESH) << "Convex decomposition unable to be loaded.  Expect severe problems." << LL_ENDL;
 	}
 
 	while (!LLApp::isQuitting())
@@ -923,7 +817,7 @@ void LLMeshRepoThread::run()
 	res = LLConvexDecomposition::quitThread();
 	if (res != LLCD_OK)
 	{
-		llwarns << "convex decomposition unable to be quit" << llendl;
+		LL_WARNS(LOG_MESH) << "Convex decomposition unable to be quit." << LL_ENDL;
 	}
 }
 
@@ -951,7 +845,6 @@ void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32
 }
 
 
-
 void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 { //could be called from any thread
 	LLMutexLock lock(mMutex);
@@ -1648,7 +1541,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
 		LLMeshSkinInfo info(skin);
 		info.mMeshID = mesh_id;
 
-		//llinfos<<"info pelvis offset"<<info.mPelvisOffset<<llendl;
+		// LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL;
 		mSkinInfoQ.push(info);
 	}
 
@@ -1739,10 +1632,13 @@ bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32
 	return true;
 }
 
+
 LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures,
-										bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload,
-					   LLHandle<LLWholeModelFeeObserver> fee_observer, LLHandle<LLWholeModelUploadObserver> upload_observer)
-: LLThread("mesh upload"),
+									   bool upload_skin, bool upload_joints, const std::string & upload_url, bool do_upload,
+									   LLHandle<LLWholeModelFeeObserver> fee_observer,
+									   LLHandle<LLWholeModelUploadObserver> upload_observer)
+  : LLThread("mesh upload"),
+	LLCore::HttpHandler(),
 	mDiscarded(FALSE),
 	mDoUpload(do_upload),
 	mWholeModelUploadURL(upload_url),
@@ -1754,7 +1650,6 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,
 	mUploadSkin = upload_skin;
 	mUploadJoints = upload_joints;
 	mMutex = new LLMutex(NULL);
-	mCurlRequest = NULL;
 	mPendingUploads = 0;
 	mFinished = false;
 	mOrigin = gAgent.getPositionAgent();
@@ -1764,12 +1659,31 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,
 
 	mOrigin += gAgent.getAtAxis() * scale.magVec();
 
-	mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut") ;
+	mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut");
+
+	mHttpRequest = new LLCore::HttpRequest;
+	mHttpOptions = new LLCore::HttpOptions;
+	mHttpOptions->setTransferTimeout(mMeshUploadTimeOut);
+	mHttpHeaders = new LLCore::HttpHeaders;
+	mHttpHeaders->append("Content-Type", "application/llsd+xml");
+	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS);
+	mHttpPriority = 0;
 }
 
 LLMeshUploadThread::~LLMeshUploadThread()
 {
-
+	if (mHttpHeaders)
+	{
+		mHttpHeaders->release();
+		mHttpHeaders = NULL;
+	}
+	if (mHttpOptions)
+	{
+		mHttpOptions->release();
+		mHttpOptions = NULL;
+	}
+	delete mHttpRequest;
+	mHttpRequest = NULL;
 }
 
 LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread)
@@ -1811,14 +1725,14 @@ void LLMeshUploadThread::preStart()
 
 void LLMeshUploadThread::discard()
 {
-	LLMutexLock lock(mMutex) ;
-	mDiscarded = TRUE ;
+	LLMutexLock lock(mMutex);
+	mDiscarded = TRUE;
 }
 
-BOOL LLMeshUploadThread::isDiscarded()
+BOOL LLMeshUploadThread::isDiscarded() const
 {
-	LLMutexLock lock(mMutex) ;
-	return mDiscarded ;
+	LLMutexLock lock(mMutex);
+	return mDiscarded;
 }
 
 void LLMeshUploadThread::run()
@@ -2069,7 +1983,7 @@ void LLMeshUploadThread::generateHulls()
 		}
 	}
 		
-	if(has_valid_requests)
+	if (has_valid_requests)
 	{
 		while (!mPhysicsComplete)
 		{
@@ -2080,7 +1994,7 @@ void LLMeshUploadThread::generateHulls()
 
 void LLMeshUploadThread::doWholeModelUpload()
 {
-	mCurlRequest = new LLCurlRequest();
+	LL_DEBUGS(LOG_MESH) << "Starting model upload.  Instances:  " << mInstance.size() << LL_ENDL;
 
 	if (mWholeModelUploadURL.empty())
 	{
@@ -2090,75 +2004,240 @@ void LLMeshUploadThread::doWholeModelUpload()
 	else
 	{
 		generateHulls();
-
-		LLSD full_model_data;
-		wholeModelToLLSD(full_model_data, true);
-		LLSD body = full_model_data["asset_resources"];
-		dump_llsd_to_file(body,make_dump_name("whole_model_body_",dump_num));
-		LLCurlRequest::headers_t headers;
-
+		LL_DEBUGS(LOG_MESH) << "Hull generation completed." << LL_ENDL;
+
+		mModelData = LLSD::emptyMap();
+		wholeModelToLLSD(mModelData, true);
+		LLSD body = mModelData["asset_resources"];
+		dump_llsd_to_file(body, make_dump_name("whole_model_body_", dump_num));
+
+		LLCore::BufferArray * ba = new LLCore::BufferArray;
+		LLCore::BufferArrayStream bas(ba);
+		LLSDSerialize::toXML(body, bas);
+		// LLSDSerialize::toXML(mModelData, bas);		// <- This will generate a convenient upload error
+		LLCore::HttpHandle handle = mHttpRequest->requestPost(mHttpPolicyClass,
+															  mHttpPriority,
+															  mWholeModelUploadURL,
+															  ba,
+															  mHttpOptions,
+															  mHttpHeaders,
+															  this);
+		ba->release();
+		
+		if (LLCORE_HTTP_HANDLE_INVALID == handle)
 		{
-			LLCurl::ResponderPtr responder = new LLWholeModelUploadResponder(this, full_model_data, mUploadObserverHandle) ;
-
-			while(!mCurlRequest->post(mWholeModelUploadURL, headers, body, responder, mMeshUploadTimeOut))
+			mHttpStatus = mHttpRequest->getStatus();
+		
+			LL_WARNS(LOG_MESH) << "Couldn't issue request for full model upload.  Reason:  " << mHttpStatus.toString()
+							   << " (" << mHttpStatus.toHex() << ")"
+							   << LL_ENDL;
+		}
+		else
+		{
+			U32 sleep_time(10);
+		
+			LL_DEBUGS(LOG_MESH) << "POST request issued." << LL_ENDL;
+			
+			mHttpRequest->update(0);
+			while (! LLApp::isQuitting() && ! mFinished)
 			{
-				//sleep for 10ms to prevent eating a whole core
-				apr_sleep(10000);
+				ms_sleep(sleep_time);
+				sleep_time = llmin(250U, sleep_time + sleep_time);
+				mHttpRequest->update(0);
 			}
+			LL_DEBUGS(LOG_MESH) << "Mesh upload operation completed." << LL_ENDL;
 		}
-
-		do
-		{
-			mCurlRequest->process();
-			//sleep for 10ms to prevent eating a whole core
-			apr_sleep(10000);
-		} while (!LLAppViewer::isQuitting() && mPendingUploads > 0);
 	}
-
-	delete mCurlRequest;
-	mCurlRequest = NULL;
-
-	// Currently a no-op.
-	mFinished = true;
 }
 
 void LLMeshUploadThread::requestWholeModelFee()
 {
 	dump_num++;
 
-	mCurlRequest = new LLCurlRequest();
-
 	generateHulls();
 
-	LLSD model_data;
-	wholeModelToLLSD(model_data,false);
-	dump_llsd_to_file(model_data,make_dump_name("whole_model_fee_request_",dump_num));
-
-	LLCurlRequest::headers_t headers;
+	mModelData = LLSD::emptyMap();
+	wholeModelToLLSD(mModelData, false);
+	dump_llsd_to_file(mModelData, make_dump_name("whole_model_fee_request_", dump_num));
 
+	LLCore::BufferArray * ba = new LLCore::BufferArray;
+	LLCore::BufferArrayStream bas(ba);
+	LLSDSerialize::toXML(mModelData, bas);
+		
+	LLCore::HttpHandle handle = mHttpRequest->requestPost(mHttpPolicyClass,
+														  mHttpPriority,
+														  mWholeModelFeeCapability,
+														  ba,
+														  mHttpOptions,
+														  mHttpHeaders,
+														  this);
+	ba->release();
+	if (LLCORE_HTTP_HANDLE_INVALID == handle)
 	{
-		LLCurl::ResponderPtr responder = new LLWholeModelFeeResponder(this,model_data, mFeeObserverHandle) ;
-		while(!mCurlRequest->post(mWholeModelFeeCapability, headers, model_data, responder, mMeshUploadTimeOut))
+		mHttpStatus = mHttpRequest->getStatus();
+		
+		LL_WARNS(LOG_MESH) << "Couldn't issue request for model fee.  Reason:  " << mHttpStatus.toString()
+						   << " (" << mHttpStatus.toHex() << ")"
+						   << LL_ENDL;
+	}
+	else
+	{
+		U32 sleep_time(10);
+		
+		mHttpRequest->update(0);
+		while (! LLApp::isQuitting() && ! mFinished)
 		{
-			//sleep for 10ms to prevent eating a whole core
-			apr_sleep(10000);
+			ms_sleep(sleep_time);
+			sleep_time = llmin(250U, sleep_time + sleep_time);
+			mHttpRequest->update(0);
 		}
 	}
+}
 
-	do
-	{
-		mCurlRequest->process();
-		//sleep for 10ms to prevent eating a whole core
-		apr_sleep(10000);
-	} while (!LLApp::isQuitting() && mPendingUploads > 0);
 
-	delete mCurlRequest;
-	mCurlRequest = NULL;
+// Does completion duty for both fee queries and actual uploads.
+void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)
+{
+	// QA/Devel:  0x2 to enable fake error import on upload, 0x1 on fee check
+	const S32 fake_error(gSavedSettings.getS32("MeshUploadFakeErrors") & (mDoUpload ? 0xa : 0x5));
+	LLCore::HttpStatus status(response->getStatus());
+	if (fake_error)
+	{
+		status = (fake_error & 0x0c) ? LLCore::HttpStatus(500) : LLCore::HttpStatus(200);
+	}
+	std::string reason(status.toString());
+	LLSD body;
 
-	// Currently a no-op.
 	mFinished = true;
+
+	if (mDoUpload)
+	{
+		// model upload case
+		LLWholeModelUploadObserver * observer(mUploadObserverHandle.get());
+
+		if (! status)
+		{
+			LL_WARNS(LOG_MESH) << "Upload failed.  Reason:  " << reason
+							   << " (" << status.toHex() << ")"
+							   << LL_ENDL;
+
+			// Build a fake body for the alert generator
+			body["error"] = LLSD::emptyMap();
+			body["error"]["message"] = reason;
+			body["error"]["identifier"] = "NetworkError";		// from asset-upload/upload_util.py
+			log_upload_error(status, body, "upload", mModelData["name"].asString());
+
+			if (observer)
+			{
+				doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer));
+			}
+		}
+		else
+		{
+			if (fake_error & 0x2)
+			{
+				body = llsd_from_file("fake_upload_error.xml");
+			}
+			else
+			{
+				LLCore::BufferArray * ba(response->getBody());
+				if (ba && ba->size())
+				{
+					LLCore::BufferArrayStream bas(ba);
+					LLSDSerialize::fromXML(body, bas);
+				}
+			}
+			dump_llsd_to_file(body, make_dump_name("whole_model_upload_response_", dump_num));
+
+			if (body["state"].asString() == "complete")
+			{
+				// requested "mesh" asset type isn't actually the type
+				// of the resultant object, fix it up here.
+				mModelData["asset_type"] = "object";
+				gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mModelData, body));
+
+				if (observer)
+				{
+					doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadSuccess, observer));
+				}
+			}
+			else
+			{
+				LL_WARNS(LOG_MESH) << "Upload failed.  Not in expected 'complete' state." << LL_ENDL;
+				log_upload_error(status, body, "upload", mModelData["name"].asString());
+
+				if (observer)
+				{
+					doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer));
+				}
+			}
+		}
+	}
+	else
+	{
+		// model fee case
+		LLWholeModelFeeObserver* observer(mFeeObserverHandle.get());
+		mWholeModelUploadURL.clear();
+		
+		if (! status)
+		{
+			LL_WARNS(LOG_MESH) << "Fee request failed.  Reason:  " << reason
+							   << " (" << status.toHex() << ")"
+							   << LL_ENDL;
+
+			// Build a fake body for the alert generator
+			body["error"] = LLSD::emptyMap();
+			body["error"]["message"] = reason;
+			body["error"]["identifier"] = "NetworkError";		// from asset-upload/upload_util.py
+			log_upload_error(status, body, "fee", mModelData["name"].asString());
+
+			if (observer)
+			{
+				observer->setModelPhysicsFeeErrorStatus(status.toULong(), reason);
+			}
+		}
+		else
+		{
+			if (fake_error & 0x1)
+			{
+				body = llsd_from_file("fake_upload_error.xml");
+			}
+			else
+			{
+				LLCore::BufferArray * ba(response->getBody());
+				if (ba && ba->size())
+				{
+					LLCore::BufferArrayStream bas(ba);
+					LLSDSerialize::fromXML(body, bas);
+				}
+			}
+			dump_llsd_to_file(body, make_dump_name("whole_model_fee_response_", dump_num));
+		
+			if (body["state"].asString() == "upload")
+			{
+				mWholeModelUploadURL = body["uploader"].asString();
+
+				if (observer)
+				{
+					body["data"]["upload_price"] = body["upload_price"];
+					observer->onModelPhysicsFeeReceived(body["data"], mWholeModelUploadURL);
+				}
+			}
+			else
+			{
+				LL_WARNS(LOG_MESH) << "Fee request failed.  Not in expected 'upload' state." << LL_ENDL;
+				log_upload_error(status, body, "fee", mModelData["name"].asString());
+
+				if (observer)
+				{
+					observer->setModelPhysicsFeeErrorStatus(status.toULong(), reason);
+				}
+			}
+		}
+	}
 }
 
+
 void LLMeshRepoThread::notifyLoadedMeshes()
 {
 	if (!mMutex)
@@ -2674,13 +2753,13 @@ void LLMeshRepository::init()
 
 void LLMeshRepository::shutdown()
 {
-	llinfos << "Shutting down mesh repository." << llendl;
+	LL_INFOS(LOG_MESH) << "Shutting down mesh repository." << LL_ENDL;
 
 	metrics_teleport_started_signal.disconnect();
 
 	for (U32 i = 0; i < mUploads.size(); ++i)
 	{
-		llinfos << "Discard the pending mesh uploads " << llendl;
+		LL_INFOS(LOG_MESH) << "Discard the pending mesh uploads." << LL_ENDL;
 		mUploads[i]->discard() ; //discard the uploading requests.
 	}
 
@@ -2695,7 +2774,7 @@ void LLMeshRepository::shutdown()
 
 	for (U32 i = 0; i < mUploads.size(); ++i)
 	{
-		llinfos << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << llendl;
+		LL_INFOS(LOG_MESH) << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << LL_ENDL;
 		while (!mUploads[i]->isStopped())
 		{
 			apr_sleep(10);
@@ -2708,7 +2787,7 @@ void LLMeshRepository::shutdown()
 	delete mMeshMutex;
 	mMeshMutex = NULL;
 
-	llinfos << "Shutting down decomposition system." << llendl;
+	LL_INFOS(LOG_MESH) << "Shutting down decomposition system." << LL_ENDL;
 
 	if (mDecompThread)
 	{
@@ -3591,7 +3670,7 @@ void LLPhysicsDecomp::setMeshData(LLCDMeshData& mesh, bool vertex_based)
 
 		if (ret)
 		{
-			llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl;
+			LL_ERRS(LOG_MESH) << "Convex Decomposition thread valid but could not set mesh data." << LL_ENDL;
 		}
 	}
 }
@@ -3667,7 +3746,8 @@ void LLPhysicsDecomp::doDecomposition()
 
 	if (ret)
 	{
-		llwarns << "Convex Decomposition thread valid but could not execute stage " << stage << llendl;
+		LL_WARNS(LOG_MESH) << "Convex Decomposition thread valid but could not execute stage " << stage << "."
+						   << LL_ENDL;
 		LLMutexLock lock(mMutex);
 
 		mCurRequest->mHull.clear();
@@ -3796,9 +3876,9 @@ void LLPhysicsDecomp::doDecompositionSingleHull()
 	setMeshData(mesh, true);
 
 	LLCDResult ret = decomp->buildSingleHull() ;
-	if(ret)
+	if (ret)
 	{
-		llwarns << "Could not execute decomposition stage when attempting to create single hull." << llendl;
+		LL_WARNS(LOG_MESH) << "Could not execute decomposition stage when attempting to create single hull." << LL_ENDL;
 		make_box(mCurRequest);
 	}
 	else
@@ -4152,7 +4232,7 @@ void LLMeshRepository::metricsUpdate()
 		metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count);
 		metrics["user_cpu"] = double(user_cpu) / 1.0e6;
 		metrics["sys_cpu"] = double(sys_cpu) / 1.0e6;
-		llinfos << "EventMarker " << metrics << llendl;
+		LL_INFOS(LOG_MESH) << "EventMarker " << metrics << LL_ENDL;
 	}
 }
 
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 96e9feea9d..70079eed23 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -45,8 +45,6 @@
 #include "lluploadfloaterobservers.h"
 
 class LLVOVolume;
-class LLMeshResponder;
-class LLCurlRequest;
 class LLMutex;
 class LLCondition;
 class LLVFS;
@@ -396,7 +394,14 @@ private:
 	U32 mHttpLargeGetCount;
 };
 
-class LLMeshUploadThread : public LLThread 
+
+// Class whose instances represent a single upload-type request for
+// meshes:  one fee query or one actual upload attempt.  Yes, it creates
+// a unique thread for that single request.  As it is 1:1, it can also
+// trivially serve as the HttpHandler object for request completion
+// notifications.
+
+class LLMeshUploadThread : public LLThread, public LLCore::HttpHandler 
 {
 private:
 	S32 mMeshUploadTimeOut ; //maximum time in seconds to execute an uploading request.
@@ -417,44 +422,41 @@ public:
 	};
 
 	LLPointer<DecompRequest> mFinalDecomp;
-	bool mPhysicsComplete;
+	volatile bool	mPhysicsComplete;
 
 	typedef std::map<LLPointer<LLModel>, std::vector<LLVector3> > hull_map;
-	hull_map mHullMap;
+	hull_map		mHullMap;
 
 	typedef std::vector<LLModelInstance> instance_list;
-	instance_list mInstanceList;
+	instance_list	mInstanceList;
 
 	typedef std::map<LLPointer<LLModel>, instance_list> instance_map;
-	instance_map mInstance;
+	instance_map	mInstance;
 
-	LLMutex*					mMutex;
-	LLCurlRequest* mCurlRequest;
+	LLMutex*		mMutex;
 	S32				mPendingUploads;
 	LLVector3		mOrigin;
 	bool			mFinished;	
 	bool			mUploadTextures;
 	bool			mUploadSkin;
 	bool			mUploadJoints;
-	BOOL            mDiscarded ;
+	BOOL            mDiscarded;
 
 	LLHost			mHost;
 	std::string		mWholeModelFeeCapability;
 	std::string		mWholeModelUploadURL;
 
 	LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures,
-			bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload = true,
-					   LLHandle<LLWholeModelFeeObserver> fee_observer= (LLHandle<LLWholeModelFeeObserver>()), LLHandle<LLWholeModelUploadObserver> upload_observer = (LLHandle<LLWholeModelUploadObserver>()));
+					   bool upload_skin, bool upload_joints, const std::string & upload_url, bool do_upload = true,
+					   LLHandle<LLWholeModelFeeObserver> fee_observer = (LLHandle<LLWholeModelFeeObserver>()),
+					   LLHandle<LLWholeModelUploadObserver> upload_observer = (LLHandle<LLWholeModelUploadObserver>()));
 	~LLMeshUploadThread();
 
-	void startRequest() { ++mPendingUploads; }
-	void stopRequest() { --mPendingUploads; }
-		
-	bool finished() { return mFinished; }
+	bool finished() const { return mFinished; }
 	virtual void run();
 	void preStart();
 	void discard() ;
-	BOOL isDiscarded();
+	BOOL isDiscarded() const;
 
 	void generateHulls();
 
@@ -471,11 +473,23 @@ public:
 	void setFeeObserverHandle(LLHandle<LLWholeModelFeeObserver> observer_handle) { mFeeObserverHandle = observer_handle; }
 	void setUploadObserverHandle(LLHandle<LLWholeModelUploadObserver> observer_handle) { mUploadObserverHandle = observer_handle; }
 
+	// Inherited from LLCore::HttpHandler
+	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response);
+
 private:
 	LLHandle<LLWholeModelFeeObserver> mFeeObserverHandle;
 	LLHandle<LLWholeModelUploadObserver> mUploadObserverHandle;
 
 	bool mDoUpload; // if FALSE only model data will be requested, otherwise the model will be uploaded
+	LLSD mModelData;
+	
+	// llcorehttp library interface objects.
+	LLCore::HttpStatus					mHttpStatus;
+	LLCore::HttpRequest *				mHttpRequest;
+	LLCore::HttpOptions *				mHttpOptions;
+	LLCore::HttpHeaders *				mHttpHeaders;
+	LLCore::HttpRequest::policy_t		mHttpPolicyClass;
+	LLCore::HttpRequest::priority_t		mHttpPriority;
 };
 
 class LLMeshRepository
-- 
cgit v1.2.3


From c9e64823c05a493e6c926deebff1b0aaf0fb50be Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Mon, 29 Jul 2013 12:42:27 -0400
Subject: SH-4368 Adjust upload timeout parameters for slow networks. Generally
 sorted the mesh timeout parameters for maximum transport time (staying with
 default 30 for connect).  60S for normal meshes, 600S for large.  Also
 documented default option values in httpoptions.h.  Useful to have these.  In
 the future, the timeouts might go into standard llsd options where they can
 be tracked a bit more.

---
 indra/llcorehttp/httpoptions.h     | 10 ++++++++--
 indra/newview/llmeshrepository.cpp |  4 +++-
 2 files changed, 11 insertions(+), 3 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h
index 04531425d8..f49a3555aa 100755
--- a/indra/llcorehttp/httpoptions.h
+++ b/indra/llcorehttp/httpoptions.h
@@ -68,36 +68,42 @@ protected:
 	void operator=(const HttpOptions &);		// Not defined
 
 public:
+	// Default:   false
 	void				setWantHeaders(bool wanted);
 	bool				getWantHeaders() const
 		{
 			return mWantHeaders;
 		}
-	
+
+	// Default:  0
 	void				setTrace(int long);
 	int					getTrace() const
 		{
 			return mTracing;
 		}
 
+	// Default:  30
 	void				setTimeout(unsigned int timeout);
 	unsigned int		getTimeout() const
 		{
 			return mTimeout;
 		}
 
+	// Default:  0
 	void				setTransferTimeout(unsigned int timeout);
 	unsigned int		getTransferTimeout() const
 		{
 			return mTransferTimeout;
 		}
 
+	// Default:  8
 	void				setRetries(unsigned int retries);
 	unsigned int		getRetries() const
 		{
 			return mRetries;
 		}
-	
+
+	// Default:  false
 	void				setUseRetryAfter(bool use_retry);
 	bool				getUseRetryAfter() const
 		{
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 1f40026e80..d02384f87c 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -235,7 +235,8 @@ const S32 REQUEST_HIGH_WATER_MAX = 80;
 const S32 REQUEST_LOW_WATER_MIN = 16;
 const S32 REQUEST_LOW_WATER_MAX = 40;
 const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;		// Size at which requests goes to narrow/slow queue
-const long LARGE_MESH_XFER_TIMEOUT = 240L;				// Seconds to complete xfer
+const long SMALL_MESH_XFER_TIMEOUT = 60L;				// Seconds to complete xfer, small mesh downloads
+const long LARGE_MESH_XFER_TIMEOUT = 600L;				// Seconds to complete xfer, large downloads
 
 // Maximum mesh version to support.  Three least significant digits are reserved for the minor version, 
 // with major version changes indicating a format change that is not backwards compatible and should not
@@ -629,6 +630,7 @@ LLMeshRepoThread::LLMeshRepoThread()
 	mSignal = new LLCondition(NULL);
 	mHttpRequest = new LLCore::HttpRequest;
 	mHttpOptions = new LLCore::HttpOptions;
+	mHttpOptions->setTransferTimeout(SMALL_MESH_XFER_TIMEOUT);
 	mHttpLargeOptions = new LLCore::HttpOptions;
 	mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT);
 	mHttpHeaders = new LLCore::HttpHeaders;
-- 
cgit v1.2.3


From eb2869bd7fe3ea856c7d25a1518699cb905a8954 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Mon, 29 Jul 2013 16:09:22 -0400
Subject: SH-4371  Reduce 22mS inter-conenction latency on HTTP operations.
 Simple change dropped this value by 7-10mS or so.  Any time we complete an
 operation on a transport pass, arrange to skip the run-loop sleep so that we
 fill a possible empty slot as quickly as possible.  With pipelining, this
 kind of thing should become unnecessary but for now, we'll do this to
 increase throughput.

---
 indra/llcorehttp/_httplibcurl.cpp | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp
index d187697e7b..b079dff864 100755
--- a/indra/llcorehttp/_httplibcurl.cpp
+++ b/indra/llcorehttp/_httplibcurl.cpp
@@ -132,12 +132,10 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()
 				CURL * handle(msg->easy_handle);
 				CURLcode result(msg->data.result);
 
-				if (completeRequest(mMultiHandles[policy_class], handle, result))
-				{
-					// Request is still active, don't get too sleepy
-					ret = HttpService::NORMAL;
-				}
-				handle = NULL;			// No longer valid on return
+				completeRequest(mMultiHandles[policy_class], handle, result);
+				handle = NULL;					// No longer valid on return
+				ret = HttpService::NORMAL;		// If anything completes, we may have a free slot.
+												// Turning around quickly reduces connection gap by 7-10mS.
 			}
 			else if (CURLMSG_NONE == msg->msg)
 			{
-- 
cgit v1.2.3


From 46dd3df73370590f61eb9a2cffcd732463a4319b Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Mon, 29 Jul 2013 16:26:02 -0400
Subject: Reduce HTTP request retry log spam.  Thought I'd done this in an
 earlier maintenance branch but the code never showed up.  I'll do it again. 
 Spam is still available by bumping 'CoreHttp' tag up to DEBUGS level logging.
  Needed for QA.  Can also get this data from tracing.

---
 indra/llcorehttp/_httppolicy.cpp | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp
index 2754e8ef07..32a9ba282a 100755
--- a/indra/llcorehttp/_httppolicy.cpp
+++ b/indra/llcorehttp/_httppolicy.cpp
@@ -162,15 +162,17 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
 	{
 		++op->mPolicy503Retries;
 	}
-	LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
-						 << " retry " << op->mPolicyRetries
-						 << " scheduled in " << (delta / HttpTime(1000))
-						 << " mS.  Status:  " << op->mStatus.toHex()
-						 << LL_ENDL;
+	LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
+						  << " retry " << op->mPolicyRetries
+						  << " scheduled in " << (delta / HttpTime(1000))
+						  << " mS.  Status:  " << op->mStatus.toHex()
+						  << LL_ENDL;
 	if (op->mTracing > HTTP_TRACE_OFF)
 	{
 		LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle:  "
 							 << static_cast<HttpHandle>(op)
+							 << ", Delta:  " << (delta / HttpTime(1000))
+							 << ", Retries:  " << op->mPolicyRetries
 							 << LL_ENDL;
 	}
 	mClasses[policy_class]->mRetryQueue.push(op);
@@ -362,9 +364,9 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
 	}
 	else if (op->mPolicyRetries)
 	{
-		LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
-							 << " succeeded on retry " << op->mPolicyRetries << "."
-							 << LL_ENDL;
+		LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
+							  << " succeeded on retry " << op->mPolicyRetries << "."
+							  << LL_ENDL;
 	}
 
 	op->stageFromActive(mService);
-- 
cgit v1.2.3


From f3927c6ca2aad757fe88fdd59b87986ca8b207a8 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Tue, 30 Jul 2013 15:21:31 -0400
Subject: SH-4371  Reduce 22mS inter-connection latency. This really extended
 into the client-side request throttling. Moved this from llmeshrepository
 (which doesn't really want to do connection management) into llcorehttp. 
 It's now a class option with configurable rate.  This still isn't the right
 thing to do as it creates coupling between viewer and services.  When we get
 to pipelining, this notion becomes invalid.

---
 indra/llcorehttp/_httpinternal.h      |  1 +
 indra/llcorehttp/_httppolicy.cpp      | 67 ++++++++++++++++++++++++++++++++---
 indra/llcorehttp/_httppolicyclass.cpp | 15 ++++++--
 indra/llcorehttp/_httppolicyclass.h   |  1 +
 indra/llcorehttp/_httpservice.cpp     |  3 +-
 indra/llcorehttp/httprequest.h        | 23 ++++++++++++
 indra/newview/llappcorehttp.cpp       | 31 ++++++++++++----
 indra/newview/llmeshrepository.cpp    | 59 ++++++++++--------------------
 indra/newview/llmeshrepository.h      | 11 +++---
 9 files changed, 152 insertions(+), 59 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h
index 80f4f34942..effc6a42c5 100755
--- a/indra/llcorehttp/_httpinternal.h
+++ b/indra/llcorehttp/_httpinternal.h
@@ -143,6 +143,7 @@ const int HTTP_CONNECTION_LIMIT_MAX = 256;
 // Miscellaneous defaults
 const long HTTP_PIPELINING_DEFAULT = 0L;
 const bool HTTP_USE_RETRY_AFTER_DEFAULT = true;
+const long HTTP_THROTTLE_RATE_DEFAULT = 0L;
 
 // Tuning parameters
 
diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp
index 32a9ba282a..808eebc6cc 100755
--- a/indra/llcorehttp/_httppolicy.cpp
+++ b/indra/llcorehttp/_httppolicy.cpp
@@ -49,12 +49,18 @@ struct HttpPolicy::ClassState
 {
 public:
 	ClassState()
+		: mThrottleEnd(0),
+		  mThrottleLeft(0L),
+		  mRequestCount(0L)
 		{}
 	
 	HttpReadyQueue		mReadyQueue;
 	HttpRetryQueue		mRetryQueue;
 
 	HttpPolicyClass		mOptions;
+	HttpTime			mThrottleEnd;
+	long				mThrottleLeft;
+	long				mRequestCount;
 };
 
 
@@ -190,6 +196,13 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
 // the worker thread may sleep hard otherwise will ask for
 // normal polling frequency.
 //
+// Implements a client-side request rate throttle as well.
+// This is intended to mimic and predict throttling behavior
+// of grid services but that is difficult to do with different
+// time bases.  This also represents a rigid coupling between
+// viewer and server that makes it hard to change parameters
+// and I hope we can make this go away with pipelining.
+//
 HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
 {
 	const HttpTime now(totalTime());
@@ -199,12 +212,22 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
 	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
 	{
 		ClassState & state(*mClasses[policy_class]);
+		const bool throttle_enabled(state.mOptions.mThrottleRate > 0L);
+		const bool throttle_current(throttle_enabled && now < state.mThrottleEnd);
+
+		if (throttle_current && state.mThrottleLeft <= 0)
+		{
+			// Throttled condition, don't serve this class but don't sleep hard.
+			result = HttpService::NORMAL;
+			continue;
+		}
+
 		int active(transport.getActiveCountInClass(policy_class));
 		int needed(state.mOptions.mConnectionLimit - active);		// Expect negatives here
 
 		HttpRetryQueue & retryq(state.mRetryQueue);
 		HttpReadyQueue & readyq(state.mReadyQueue);
-		
+
 		if (needed > 0)
 		{
 			// First see if we have any retries...
@@ -218,10 +241,27 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
 				
 				op->stageFromReady(mService);
 				op->release();
-					
+
+				++state.mRequestCount;
 				--needed;
+				if (throttle_enabled)
+				{
+					if (now >= state.mThrottleEnd)
+					{
+						// Throttle expired, move to next window
+						LL_DEBUGS("CoreHttp") << "Throttle expired with " << state.mThrottleLeft
+											  << " requests to go and " << state.mRequestCount
+											  << " requests issued." << LL_ENDL;
+						state.mThrottleLeft = state.mOptions.mThrottleRate;
+						state.mThrottleEnd = now + HttpTime(1000000);
+					}
+					if (--state.mThrottleLeft <= 0)
+					{
+						goto throttle_on;
+					}
+				}
 			}
-		
+			
 			// Now go on to the new requests...
 			while (needed > 0 && ! readyq.empty())
 			{
@@ -231,10 +271,29 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
 				op->stageFromReady(mService);
 				op->release();
 					
+				++state.mRequestCount;
 				--needed;
+				if (throttle_enabled)
+				{
+					if (now >= state.mThrottleEnd)
+					{
+						// Throttle expired, move to next window
+						LL_DEBUGS("CoreHttp") << "Throttle expired with " << state.mThrottleLeft
+											  << " requests to go and " << state.mRequestCount
+											  << " requests issued." << LL_ENDL;
+						state.mThrottleLeft = state.mOptions.mThrottleRate;
+						state.mThrottleEnd = now + HttpTime(1000000);
+					}
+					if (--state.mThrottleLeft <= 0)
+					{
+						goto throttle_on;
+					}
+				}
 			}
 		}
-				
+
+	throttle_on:
+		
 		if (! readyq.empty() || ! retryq.empty())
 		{
 			// If anything is ready, continue looping...
diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp
index fe4359081a..f34a8e9f1e 100755
--- a/indra/llcorehttp/_httppolicyclass.cpp
+++ b/indra/llcorehttp/_httppolicyclass.cpp
@@ -36,7 +36,8 @@ namespace LLCore
 HttpPolicyClass::HttpPolicyClass()
 	: mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
 	  mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT),
-	  mPipelining(HTTP_PIPELINING_DEFAULT)
+	  mPipelining(HTTP_PIPELINING_DEFAULT),
+	  mThrottleRate(HTTP_THROTTLE_RATE_DEFAULT)
 {}
 
 
@@ -51,6 +52,7 @@ HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other)
 		mConnectionLimit = other.mConnectionLimit;
 		mPerHostConnectionLimit = other.mPerHostConnectionLimit;
 		mPipelining = other.mPipelining;
+		mThrottleRate = other.mThrottleRate;
 	}
 	return *this;
 }
@@ -59,7 +61,8 @@ HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other)
 HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other)
 	: mConnectionLimit(other.mConnectionLimit),
 	  mPerHostConnectionLimit(other.mPerHostConnectionLimit),
-	  mPipelining(other.mPipelining)
+	  mPipelining(other.mPipelining),
+	  mThrottleRate(other.mThrottleRate)
 {}
 
 
@@ -79,6 +82,10 @@ HttpStatus HttpPolicyClass::set(HttpRequest::EPolicyOption opt, long value)
 		mPipelining = llclamp(value, 0L, 1L);
 		break;
 
+	case HttpRequest::PO_THROTTLE_RATE:
+		mThrottleRate = llclamp(value, 0L, 1000000L);
+		break;
+
 	default:
 		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
 	}
@@ -103,6 +110,10 @@ HttpStatus HttpPolicyClass::get(HttpRequest::EPolicyOption opt, long * value) co
 		*value = mPipelining;
 		break;
 
+	case HttpRequest::PO_THROTTLE_RATE:
+		*value = mThrottleRate;
+		break;
+
 	default:
 		return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG);
 	}
diff --git a/indra/llcorehttp/_httppolicyclass.h b/indra/llcorehttp/_httppolicyclass.h
index 69fb459d22..38f1194ded 100755
--- a/indra/llcorehttp/_httppolicyclass.h
+++ b/indra/llcorehttp/_httppolicyclass.h
@@ -63,6 +63,7 @@ public:
 	long						mConnectionLimit;
 	long						mPerHostConnectionLimit;
 	long						mPipelining;
+	long						mThrottleRate;
 };  // end class HttpPolicyClass
 
 }  // end namespace LLCore
diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp
index e21d196a3e..c94249dc2d 100755
--- a/indra/llcorehttp/_httpservice.cpp
+++ b/indra/llcorehttp/_httpservice.cpp
@@ -52,7 +52,8 @@ const HttpService::OptionDescriptor HttpService::sOptionDesc[] =
 	{	false,		true,		true,		false	},		// PO_HTTP_PROXY
 	{	true,		true,		true,		false	},		// PO_LLPROXY
 	{	true,		true,		true,		false	},		// PO_TRACE
-	{	true,		true,		false,		true	}		// PO_ENABLE_PIPELINING
+	{	true,		true,		false,		true	},		// PO_ENABLE_PIPELINING
+	{	true,		true,		false,		true	}		// PO_THROTTLE_RATE
 };
 HttpService * HttpService::sInstance(NULL);
 volatile HttpService::EState HttpService::sState(NOT_INITIALIZED);
diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h
index 5c54d35a21..651654844a 100755
--- a/indra/llcorehttp/httprequest.h
+++ b/indra/llcorehttp/httprequest.h
@@ -139,23 +139,33 @@ public:
 
 		/// Limits the number of connections used for a single
 		/// literal address/port pair within the class.
+		///
+		/// Per-class only
 		PO_PER_HOST_CONNECTION_LIMIT,
 
 		/// String containing a system-appropriate directory name
 		/// where SSL certs are stored.
+		///
+		/// Global only
 		PO_CA_PATH,
 
 		/// String giving a full path to a file containing SSL certs.
+		///
+		/// Global only
 		PO_CA_FILE,
 
 		/// String of host/port to use as simple HTTP proxy.  This is
 		/// going to change in the future into something more elaborate
 		/// that may support richer schemes.
+		///
+		/// Global only
 		PO_HTTP_PROXY,
 
 		/// Long value that if non-zero enables the use of the
 		/// traditional LLProxy code for http/socks5 support.  If
 		// enabled, has priority over GP_HTTP_PROXY.
+		///
+		/// Global only
 		PO_LLPROXY,
 
 		/// Long value setting the logging trace level for the
@@ -169,12 +179,25 @@ public:
 		/// These values are also used in the trace modes for
 		/// individual requests in HttpOptions.  Also be aware that
 		/// tracing tends to impact performance of the viewer.
+		///
+		/// Global only
 		PO_TRACE,
 
 		/// Suitable requests are allowed to pipeline on their
 		/// connections when they ask for it.
+		///
+		/// Per-class only
 		PO_ENABLE_PIPELINING,
 
+		/// Controls whether client-side throttling should be
+		/// performed on this policy class.  Positive values
+		/// enable throttling and specify the request rate
+		/// (requests per second) that should be targetted.
+		/// A value of zero, the default, specifies no throttling.
+		///
+		/// Per-class only
+		PO_THROTTLE_RATE,
+		
 		PO_LAST  // Always at end
 	};
 
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index 104debe023..b90b9749f9 100755
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -40,32 +40,33 @@ static const struct
 	U32							mMin;
 	U32							mMax;
 	U32							mDivisor;
+	U32							mRate;
 	std::string					mKey;
 	const char *				mUsage;
 } init_data[] =					//  Default and dynamic values for classes
 {
 	{
-		LLAppCoreHttp::AP_TEXTURE,			8,		1,		12,		1,
+		LLAppCoreHttp::AP_TEXTURE,			8,		1,		12,		1,		0,
 		"TextureFetchConcurrency",
 		"texture fetch"
 	},
 	{
-		LLAppCoreHttp::AP_MESH1,			32,		1,		128,	1,
+		LLAppCoreHttp::AP_MESH1,			32,		1,		128,	1,		100,
 		"MeshMaxConcurrentRequests",
 		"mesh fetch"
 	},
 	{
-		LLAppCoreHttp::AP_MESH2,			8,		1,		32,		4,
+		LLAppCoreHttp::AP_MESH2,			8,		1,		32,		4,		100,
 		"MeshMaxConcurrentRequests",
 		"mesh2 fetch"
 	},
 	{
-		LLAppCoreHttp::AP_LARGE_MESH,		2,		1,		8,		1,
+		LLAppCoreHttp::AP_LARGE_MESH,		2,		1,		8,		1,		0,
 		"",
 		"large mesh fetch"
 	},
 	{
-		LLAppCoreHttp::AP_UPLOADS,			2,		1,		8,		1,
+		LLAppCoreHttp::AP_UPLOADS,			2,		1,		8,		1,		0,
 		"",
 		"asset upload"
 	}
@@ -267,10 +268,28 @@ void LLAppCoreHttp::cleanup()
 
 void LLAppCoreHttp::refreshSettings(bool initial)
 {
+	LLCore::HttpStatus status;
+	
 	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)
 	{
 		const EAppPolicy policy(init_data[i].mPolicy);
 
+		// Set any desired throttle
+		if (initial && init_data[i].mRate)
+		{
+			// Init-time only, can use the static setters here
+			status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_THROTTLE_RATE,
+																mPolicies[policy],
+																init_data[i].mRate,
+																NULL);
+			if (! status)
+			{
+				LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
+								 << " throttle rate.  Reason:  " << status.toString()
+								 << LL_ENDL;
+			}
+		}
+
 		// Get target connection concurrency value
 		U32 setting(init_data[i].mDefault);
 		if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey))
@@ -299,7 +318,7 @@ void LLAppCoreHttp::refreshSettings(bool initial)
 										   setting, NULL);
 		if (LLCORE_HTTP_HANDLE_INVALID == handle)
 		{
-			LLCore::HttpStatus status(mRequest->getStatus());
+			status = mRequest->getStatus();
 			LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage
 							 << " concurrency.  Reason:  " << status.toString()
 							 << LL_ENDL;
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index d02384f87c..e9b1a10e73 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -229,7 +229,6 @@
 LLMeshRepository gMeshRepo;
 
 const S32 MESH_HEADER_SIZE = 4096;                      // Important:  assumption is that headers fit in this space
-const U32 MAX_MESH_REQUESTS_PER_SECOND = 100;
 const S32 REQUEST_HIGH_WATER_MIN = 32;
 const S32 REQUEST_HIGH_WATER_MAX = 80;
 const S32 REQUEST_LOW_WATER_MIN = 16;
@@ -613,7 +612,6 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
 LLMeshRepoThread::LLMeshRepoThread()
 : LLThread("mesh repo"),
   mWaiting(false),
-  mHttpRetries(0U),
   mHttpRequest(NULL),
   mHttpOptions(NULL),
   mHttpLargeOptions(NULL),
@@ -701,23 +699,9 @@ void LLMeshRepoThread::run()
 		
 		if (! LLApp::isQuitting())
 		{
-			static U32 count = 0;
-			static F32 last_hundred = gFrameTimeSeconds;
-
-			if (gFrameTimeSeconds - last_hundred > 1.f)
-			{ //a second has gone by, clear count
-				last_hundred = gFrameTimeSeconds;
-				count = 0;
-			}
-			else
-			{
-				count += mHttpRetries;
-			}
-			mHttpRetries = 0U;
-			
-			// NOTE: throttling intentionally favors LOD requests over header requests
+			// NOTE: order of queue processing intentionally favors LOD requests over header requests
 			
-			while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater)
+			while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
 			{
 				if (! mMutex)
 				{
@@ -728,7 +712,7 @@ void LLMeshRepoThread::run()
 				mLODReqQ.pop();
 				LLMeshRepository::sLODProcessing--;
 				mMutex->unlock();
-				if (!fetchMeshLOD(req.mMeshParams, req.mLOD, count))//failed, resubmit
+				if (!fetchMeshLOD(req.mMeshParams, req.mLOD))//failed, resubmit
 				{
 					mMutex->lock();
 					mLODReqQ.push(req) ; 
@@ -737,7 +721,7 @@ void LLMeshRepoThread::run()
 				}
 			}
 
-			while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater)
+			while (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
 			{
 				if (! mMutex)
 				{
@@ -747,7 +731,7 @@ void LLMeshRepoThread::run()
 				HeaderRequest req = mHeaderReqQ.front();
 				mHeaderReqQ.pop();
 				mMutex->unlock();
-				if (!fetchMeshHeader(req.mMeshParams, count))//failed, resubmit
+				if (!fetchMeshHeader(req.mMeshParams))//failed, resubmit
 				{
 					mMutex->lock();
 					mHeaderReqQ.push(req) ;
@@ -762,14 +746,14 @@ void LLMeshRepoThread::run()
 			// order will lose.  Keep to the throttle enforcement and pay
 			// attention to the highwater level (enforced in each fetchXXX()
 			// method).
-			if (! mSkinRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater)
+			if (! mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
 			{
 				// *FIXME:  this really does need a lock as do the following ones
 				std::set<LLUUID> incomplete;
 				for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter)
 				{
 					LLUUID mesh_id = *iter;
-					if (!fetchMeshSkinInfo(mesh_id, count))
+					if (!fetchMeshSkinInfo(mesh_id))
 					{
 						incomplete.insert(mesh_id);
 					}
@@ -777,13 +761,13 @@ void LLMeshRepoThread::run()
 				mSkinRequests.swap(incomplete);
 			}
 
-			if (! mDecompositionRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater)
+			if (! mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
 			{
 				std::set<LLUUID> incomplete;
 				for (std::set<LLUUID>::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter)
 				{
 					LLUUID mesh_id = *iter;
-					if (!fetchMeshDecomposition(mesh_id, count))
+					if (!fetchMeshDecomposition(mesh_id))
 					{
 						incomplete.insert(mesh_id);
 					}
@@ -791,13 +775,13 @@ void LLMeshRepoThread::run()
 				mDecompositionRequests.swap(incomplete);
 			}
 
-			if (! mPhysicsShapeRequests.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && mHttpRequestSet.size() < sRequestHighWater)
+			if (! mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
 			{
 				std::set<LLUUID> incomplete;
 				for (std::set<LLUUID>::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter)
 				{
 					LLUUID mesh_id = *iter;
-					if (!fetchMeshPhysicsShape(mesh_id, count))
+					if (!fetchMeshPhysicsShape(mesh_id))
 					{
 						incomplete.insert(mesh_id);
 					}
@@ -965,7 +949,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c
 }
 
 
-bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count)
+bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 {
 	
 	if (!mHeaderMutex)
@@ -1023,7 +1007,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater)
+			if (mHttpRequestSet.size() >= sRequestHighWater)
 			{
 				return false;
 			}
@@ -1060,7 +1044,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count)
 	return ret;
 }
 
-bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id, U32& count)
+bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 {
 	if (!mHeaderMutex)
 	{
@@ -1118,7 +1102,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id, U32& count)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater)
+			if (mHttpRequestSet.size() >= sRequestHighWater)
 			{
 				return false;
 			}
@@ -1155,7 +1139,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id, U32& count)
 	return ret;
 }
 
-bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count)
+bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 {
 	if (!mHeaderMutex)
 	{
@@ -1212,7 +1196,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			if (count >= MAX_MESH_REQUESTS_PER_SECOND || mHttpRequestSet.size() >= sRequestHighWater)
+			if (mHttpRequestSet.size() >= sRequestHighWater)
 			{
 				return false;
 			}
@@ -1282,7 +1266,7 @@ void LLMeshRepoThread::decActiveHeaderRequests()
 }
 
 //return false if failed to get header
-bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& count)
+bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
 {
 	{
 		//look for mesh in asset in vfs
@@ -1331,7 +1315,6 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 			handler->mHttpHandle = handle;
 			mHttpRequestSet.insert(handler);
 			++LLMeshRepository::sHTTPRequestCount;
-			++count;
 		}
 	}
 
@@ -1339,7 +1322,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c
 }
 
 //return false if failed to get mesh lod.
-bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count)
+bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 {
 	if (!mHeaderMutex)
 	{
@@ -1413,7 +1396,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
 					++LLMeshRepository::sHTTPRequestCount;
-					++count;
 				}
 			}
 			else
@@ -2374,11 +2356,8 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
 {
 	mProcessed = true;
 
-	// Accumulate retries, we'll use these to offset the HTTP
-	// count and maybe hold to a throttle better.
 	unsigned int retries(0U);
 	response->getRetries(NULL, &retries);
-	gMeshRepo.mThread->mHttpRetries += retries;
 	LLMeshRepository::sHTTPRetryCount += retries;
 
 	LLCore::HttpStatus status(response->getStatus());
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 70079eed23..400ceb4ad7 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -322,7 +322,6 @@ public:
 
 	// llcorehttp library interface objects.
 	LLCore::HttpStatus					mHttpStatus;
-	unsigned int						mHttpRetries;
 	LLCore::HttpRequest *				mHttpRequest;
 	LLCore::HttpOptions *				mHttpOptions;
 	LLCore::HttpOptions *				mHttpLargeOptions;
@@ -345,8 +344,8 @@ public:
 	void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
 	void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
 
-	bool fetchMeshHeader(const LLVolumeParams& mesh_params, U32& count);
-	bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count);
+	bool fetchMeshHeader(const LLVolumeParams& mesh_params);
+	bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
 	bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size);
 	bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size);
 	bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size);
@@ -363,15 +362,15 @@ public:
 
 	//send request for skin info, returns true if header info exists 
 	//  (should hold onto mesh_id and try again later if header info does not exist)
-	bool fetchMeshSkinInfo(const LLUUID& mesh_id, U32& count);
+	bool fetchMeshSkinInfo(const LLUUID& mesh_id);
 
 	//send request for decomposition, returns true if header info exists 
 	//  (should hold onto mesh_id and try again later if header info does not exist)
-	bool fetchMeshDecomposition(const LLUUID& mesh_id, U32& count);
+	bool fetchMeshDecomposition(const LLUUID& mesh_id);
 
 	//send request for PhysicsShape, returns true if header info exists 
 	//  (should hold onto mesh_id and try again later if header info does not exist)
-	bool fetchMeshPhysicsShape(const LLUUID& mesh_id, U32& count);
+	bool fetchMeshPhysicsShape(const LLUUID& mesh_id);
 
 	static void incActiveLODRequests();
 	static void decActiveLODRequests();
-- 
cgit v1.2.3


From 549e20cf7766dc7fba2f42883659a6bcac863d91 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Mon, 5 Aug 2013 13:54:14 -0400
Subject: Change the setting for GetMesh2 meshes to Mesh2MaxConcurrentRequests.
 While linking GetMesh2 to the old setting was simpler from a user
 point-of-view, they really shouldn't be linked and the old one will go away. 
 This one may be renamed to AssetMaxConcurrentRequests or something similar if
 we get to the mesh/texture unification step.

---
 indra/newview/app_settings/settings.xml | 14 ++++++++++++--
 indra/newview/llappcorehttp.cpp         | 23 ++++++++++++++---------
 indra/newview/llmeshrepository.cpp      |  4 +---
 3 files changed, 27 insertions(+), 14 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 344079b640..770a8529f1 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -9708,11 +9708,21 @@
     <key>Value</key>
     <real>16</real>
   </map>
-
+  <key>Mesh2MaxConcurrentRequests</key>
+  <map>
+    <key>Comment</key>
+    <string>Number of connections to use for loading meshes.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>8</integer>
+  </map>
   <key>MeshMaxConcurrentRequests</key>
   <map>
     <key>Comment</key>
-    <string>Number of threads to use for loading meshes.</string>
+    <string>Number of connections to use for loading meshes (legacy system).</string>
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index b90b9749f9..01317fe32f 100755
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -32,6 +32,13 @@
 #include "llviewercontrol.h"
 
 
+// Here is where we begin to get our connection usage under control.
+// This establishes llcorehttp policy classes that, among other
+// things, limit the maximum number of connections to outside
+// services.  Each of the entries below maps to a policy class and
+// has a limit, sometimes configurable, of how many connections can
+// be open at a time.
+
 const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0);
 static const struct
 {
@@ -39,34 +46,33 @@ static const struct
 	U32							mDefault;
 	U32							mMin;
 	U32							mMax;
-	U32							mDivisor;
 	U32							mRate;
 	std::string					mKey;
 	const char *				mUsage;
 } init_data[] =					//  Default and dynamic values for classes
 {
 	{
-		LLAppCoreHttp::AP_TEXTURE,			8,		1,		12,		1,		0,
+		LLAppCoreHttp::AP_TEXTURE,			8,		1,		12,		0,
 		"TextureFetchConcurrency",
 		"texture fetch"
 	},
 	{
-		LLAppCoreHttp::AP_MESH1,			32,		1,		128,	1,		100,
+		LLAppCoreHttp::AP_MESH1,			32,		1,		128,	100,
 		"MeshMaxConcurrentRequests",
 		"mesh fetch"
 	},
 	{
-		LLAppCoreHttp::AP_MESH2,			8,		1,		32,		4,		100,
-		"MeshMaxConcurrentRequests",
+		LLAppCoreHttp::AP_MESH2,			8,		1,		32,		100,
+		"Mesh2MaxConcurrentRequests",
 		"mesh2 fetch"
 	},
 	{
-		LLAppCoreHttp::AP_LARGE_MESH,		2,		1,		8,		1,		0,
+		LLAppCoreHttp::AP_LARGE_MESH,		2,		1,		8,		0,
 		"",
 		"large mesh fetch"
 	},
 	{
-		LLAppCoreHttp::AP_UPLOADS,			2,		1,		8,		1,		0,
+		LLAppCoreHttp::AP_UPLOADS,			2,		1,		8,		0,
 		"",
 		"asset upload"
 	}
@@ -298,8 +304,7 @@ void LLAppCoreHttp::refreshSettings(bool initial)
 			if (new_setting)
 			{
 				// Treat zero settings as an ask for default
-				setting = new_setting / init_data[i].mDivisor;
-				setting = llclamp(setting, init_data[i].mMin, init_data[i].mMax);
+				setting = llclamp(new_setting, init_data[i].mMin, init_data[i].mMax);
 			}
 		}
 
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index e9b1a10e73..7146c7f4f5 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2897,9 +2897,7 @@ void LLMeshRepository::notifyLoadedMeshes()
 	else
 	{
 		// GetMesh2 operation with keepalives, etc.
-		// *TODO:  Logic here is replicated from llappcorehttp.cpp, should really
-		// unify this and keep it in one place only.
-		LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests") / 4;
+		LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("Mesh2MaxConcurrentRequests");
 		LLMeshRepoThread::sRequestHighWater = llclamp(5 * S32(LLMeshRepoThread::sMaxConcurrentRequests),
 													  REQUEST_HIGH_WATER_MIN,
 													  REQUEST_HIGH_WATER_MAX);
-- 
cgit v1.2.3


From 43788d612042deb6f9329746a101774370f7c67b Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Mon, 5 Aug 2013 19:04:08 -0400
Subject: Added some simple counters to the mesh repository code and then added
 a Mesh status line to the texture fetch console.  Mesh is often in
 competition with textures and so the mesh information seems appropriate
 there.  Do get a nice feel for progress and you definitely see when the
 throttles kick in.

---
 indra/newview/llmeshrepository.cpp | 134 ++++++++++++++++++++++++-------------
 indra/newview/llmeshrepository.h   |  11 +--
 indra/newview/lltextureview.cpp    |  41 ++++++++----
 3 files changed, 123 insertions(+), 63 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 7146c7f4f5..97d6c57a78 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -155,20 +155,26 @@
 //
 //   So, in addition to documentation, take this as a to-do/review
 //   list and see if you can improve things.  For porters to non-x86
-//   architectures, including amd64, the weaker memory models will
-//   make these platforms probabilistically more susceptible to hitting
-//   race conditions.  True here and in other multi-thread code such
-//   as texture fetching.
+//   architectures, the weaker memory models will make these platforms
+//   probabilistically more susceptible to hitting race conditions.
+//   True here and in other multi-thread code such as texture fetching.
+//   (Strong memory models make weak programmers.  Weak memory models
+//   make strong programmers.  Ref:  arm, ppc, mips, alpha)
 //
 //   LLMeshRepository:
 //
 //     sBytesReceived
+//     sMeshRequestCount
 //     sHTTPRequestCount
+//     sHTTPLargeRequestCount
 //     sHTTPRetryCount
+//     sHTTPErrorCount
 //     sLODPending
 //     sLODProcessing
 //     sCacheBytesRead
 //     sCacheBytesWritten
+//     sCacheReads
+//     sCacheWrites
 //     mLoadingMeshes                  none            rw.main.none, rw.main.mMeshMutex [4]
 //     mSkinMap                        none            rw.main.none
 //     mDecompositionMap               none            rw.main.none
@@ -246,13 +252,19 @@ const long LARGE_MESH_XFER_TIMEOUT = 600L;				// Seconds to complete xfer, large
 const S32 MAX_MESH_VERSION = 999;
 
 U32 LLMeshRepository::sBytesReceived = 0;
+U32 LLMeshRepository::sMeshRequestCount = 0;
 U32 LLMeshRepository::sHTTPRequestCount = 0;
+U32 LLMeshRepository::sHTTPLargeRequestCount = 0;
 U32 LLMeshRepository::sHTTPRetryCount = 0;
+U32 LLMeshRepository::sHTTPErrorCount = 0;
 U32 LLMeshRepository::sLODProcessing = 0;
 U32 LLMeshRepository::sLODPending = 0;
 
 U32 LLMeshRepository::sCacheBytesRead = 0;
 U32 LLMeshRepository::sCacheBytesWritten = 0;
+U32 LLMeshRepository::sCacheReads = 0;
+U32 LLMeshRepository::sCacheWrites = 0;
+
 LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true);	// true -> gather cpu metrics
 
 	
@@ -366,6 +378,7 @@ volatile S32 LLMeshRepoThread::sActiveLODRequests = 0;
 U32	LLMeshRepoThread::sMaxConcurrentRequests = 1;
 S32 LLMeshRepoThread::sRequestLowWater = REQUEST_LOW_WATER_MIN;
 S32 LLMeshRepoThread::sRequestHighWater = REQUEST_HIGH_WATER_MIN;
+S32 LLMeshRepoThread::sRequestWaterLevel = 0;
 
 // Base handler class for all mesh users of llcorehttp.
 // This is roughly equivalent to a Responder class in
@@ -619,9 +632,7 @@ LLMeshRepoThread::LLMeshRepoThread()
   mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
   mHttpLegacyPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
   mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
-  mHttpPriority(0),
-  mHttpGetCount(0U),
-  mHttpLargeGetCount(0U)
+  mHttpPriority(0)
 { 
 	mMutex = new LLMutex(NULL);
 	mHeaderMutex = new LLMutex(NULL);
@@ -641,8 +652,8 @@ LLMeshRepoThread::LLMeshRepoThread()
 
 LLMeshRepoThread::~LLMeshRepoThread()
 {
-	LL_INFOS(LOG_MESH) << "Small GETs issued:  " << mHttpGetCount
-					   << ", Large GETs issued:  " << mHttpLargeGetCount
+	LL_INFOS(LOG_MESH) << "Small GETs issued:  " << LLMeshRepository::sHTTPRequestCount
+					   << ", Large GETs issued:  " << LLMeshRepository::sHTTPLargeRequestCount
 					   << LL_ENDL;
 
 	for (http_request_set::iterator iter(mHttpRequestSet.begin());
@@ -700,7 +711,8 @@ void LLMeshRepoThread::run()
 		if (! LLApp::isQuitting())
 		{
 			// NOTE: order of queue processing intentionally favors LOD requests over header requests
-			
+
+			sRequestWaterLevel = mHttpRequestSet.size();
 			while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
 			{
 				if (! mMutex)
@@ -743,19 +755,26 @@ void LLMeshRepoThread::run()
 			// list, we scan the entire thing.  This gets us through any requests
 			// which can be resolved in the cache.  It also keeps the request
 			// set somewhat fresher otherwise items at the end of the set
-			// order will lose.  Keep to the throttle enforcement and pay
-			// attention to the highwater level (enforced in each fetchXXX()
-			// method).
+			// order will lose.
 			if (! mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
 			{
 				// *FIXME:  this really does need a lock as do the following ones
 				std::set<LLUUID> incomplete;
 				for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter)
 				{
-					LLUUID mesh_id = *iter;
-					if (!fetchMeshSkinInfo(mesh_id))
+					if (mHttpRequestSet.size() < sRequestHighWater)
+					{
+						LLUUID mesh_id = *iter;
+						if (!fetchMeshSkinInfo(mesh_id))
+						{
+							incomplete.insert(mesh_id);
+						}
+					}
+					else
 					{
-						incomplete.insert(mesh_id);
+						// Hit high-water mark, copy remaining to incomplete.
+						incomplete.insert(iter, mSkinRequests.end());
+						break;
 					}
 				}
 				mSkinRequests.swap(incomplete);
@@ -766,10 +785,19 @@ void LLMeshRepoThread::run()
 				std::set<LLUUID> incomplete;
 				for (std::set<LLUUID>::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter)
 				{
-					LLUUID mesh_id = *iter;
-					if (!fetchMeshDecomposition(mesh_id))
+					if (mHttpRequestSet.size() < sRequestHighWater)
 					{
-						incomplete.insert(mesh_id);
+						LLUUID mesh_id = *iter;
+						if (!fetchMeshDecomposition(mesh_id))
+						{
+							incomplete.insert(mesh_id);
+						}
+					}
+					else
+					{
+						// Hit high-water mark, copy remaining to incomplete.
+						incomplete.insert(iter, mDecompositionRequests.end());
+						break;
 					}
 				}
 				mDecompositionRequests.swap(incomplete);
@@ -780,10 +808,19 @@ void LLMeshRepoThread::run()
 				std::set<LLUUID> incomplete;
 				for (std::set<LLUUID>::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter)
 				{
-					LLUUID mesh_id = *iter;
-					if (!fetchMeshPhysicsShape(mesh_id))
+					if (mHttpRequestSet.size() < sRequestHighWater)
+					{
+						LLUUID mesh_id = *iter;
+						if (!fetchMeshPhysicsShape(mesh_id))
+						{
+							incomplete.insert(mesh_id);
+						}
+					}
+					else
 					{
-						incomplete.insert(mesh_id);
+						// Hit high-water mark, copy remaining to incomplete.
+						incomplete.insert(iter, mPhysicsShapeRequests.end());
+						break;
 					}
 				}
 				mPhysicsShapeRequests.swap(incomplete);
@@ -926,7 +963,10 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c
 												   mHttpOptions,
 												   mHttpHeaders,
 												   handler);
-		++mHttpGetCount;
+		if (LLCORE_HTTP_HANDLE_INVALID != handle)
+		{
+			++LLMeshRepository::sHTTPRequestCount;
+		}
 	}
 	else
 	{
@@ -938,7 +978,10 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c
 												   mHttpLargeOptions,
 												   mHttpHeaders,
 												   handler);
-		++mHttpLargeGetCount;
+		if (LLCORE_HTTP_HANDLE_INVALID != handle)
+		{
+			++LLMeshRepository::sHTTPLargeRequestCount;
+		}
 	}
 	if (LLCORE_HTTP_HANDLE_INVALID == handle)
 	{
@@ -965,7 +1008,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 		return false;
 	}
 
-	bool ret = true ;
+	++LLMeshRepository::sMeshRequestCount;
+	bool ret = true;
 	U32 header_size = mMeshHeaderSize[mesh_id];
 	
 	if (header_size > 0)
@@ -983,6 +1027,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 			if (file.getSize() >= offset+size)
 			{				
 				LLMeshRepository::sCacheBytesRead += size;
+				++LLMeshRepository::sCacheReads;
 				file.seek(offset);
 				U8* buffer = new U8[size];
 				file.read(buffer, size);
@@ -1007,10 +1052,6 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			if (mHttpRequestSet.size() >= sRequestHighWater)
-			{
-				return false;
-			}
 			int cap_version(gMeshRepo.mGetMeshVersion);
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
@@ -1025,12 +1066,12 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 									   << LL_ENDL;
 					delete handler;
 					ret = false;
+
 				}
 				else
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-					++LLMeshRepository::sHTTPRequestCount;
 				}
 			}
 		}
@@ -1059,8 +1100,9 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 		return false;
 	}
 
+	++LLMeshRepository::sMeshRequestCount;
 	U32 header_size = mMeshHeaderSize[mesh_id];
-	bool ret = true ;
+	bool ret = true;
 	
 	if (header_size > 0)
 	{
@@ -1077,6 +1119,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 			if (file.getSize() >= offset+size)
 			{
 				LLMeshRepository::sCacheBytesRead += size;
+				++LLMeshRepository::sCacheReads;
 
 				file.seek(offset);
 				U8* buffer = new U8[size];
@@ -1102,10 +1145,6 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			if (mHttpRequestSet.size() >= sRequestHighWater)
-			{
-				return false;
-			}
 			int cap_version(gMeshRepo.mGetMeshVersion);
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
@@ -1125,7 +1164,6 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-					++LLMeshRepository::sHTTPRequestCount;
 				}
 			}
 		}
@@ -1154,8 +1192,9 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 		return false;
 	}
 
+	++LLMeshRepository::sMeshRequestCount;
 	U32 header_size = mMeshHeaderSize[mesh_id];
-	bool ret = true ;
+	bool ret = true;
 
 	if (header_size > 0)
 	{
@@ -1172,6 +1211,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 			if (file.getSize() >= offset+size)
 			{
 				LLMeshRepository::sCacheBytesRead += size;
+				++LLMeshRepository::sCacheReads;
 				file.seek(offset);
 				U8* buffer = new U8[size];
 				file.read(buffer, size);
@@ -1196,10 +1236,6 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			if (mHttpRequestSet.size() >= sRequestHighWater)
-			{
-				return false;
-			}
 			int cap_version(gMeshRepo.mGetMeshVersion);
 			std::string http_url = constructUrl(mesh_id);
 			if (!http_url.empty())
@@ -1219,7 +1255,6 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-					++LLMeshRepository::sHTTPRequestCount;
 				}
 			}
 		}
@@ -1268,6 +1303,8 @@ void LLMeshRepoThread::decActiveHeaderRequests()
 //return false if failed to get header
 bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
 {
+	++LLMeshRepository::sMeshRequestCount;
+
 	{
 		//look for mesh in asset in vfs
 		LLVFile file(gVFS, mesh_params.getSculptID(), LLAssetType::AT_MESH);
@@ -1280,6 +1317,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
 			U8 buffer[MESH_HEADER_SIZE];
 			S32 bytes = llmin(size, MESH_HEADER_SIZE);
 			LLMeshRepository::sCacheBytesRead += bytes;	
+			++LLMeshRepository::sCacheReads;
 			file.read(buffer, bytes);
 			if (headerReceived(mesh_params, buffer, bytes))
 			{
@@ -1314,7 +1352,6 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
 		{
 			handler->mHttpHandle = handle;
 			mHttpRequestSet.insert(handler);
-			++LLMeshRepository::sHTTPRequestCount;
 		}
 	}
 
@@ -1331,6 +1368,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 
 	mHeaderMutex->lock();
 
+	++LLMeshRepository::sMeshRequestCount;
 	bool retval = true;
 
 	LLUUID mesh_id = mesh_params.getSculptID();
@@ -1352,6 +1390,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 			if (file.getSize() >= offset+size)
 			{
 				LLMeshRepository::sCacheBytesRead += size;
+				++LLMeshRepository::sCacheReads;
 				file.seek(offset);
 				U8* buffer = new U8[size];
 				file.read(buffer, size);
@@ -1395,7 +1434,6 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
-					++LLMeshRepository::sHTTPRequestCount;
 				}
 			}
 			else
@@ -2364,6 +2402,7 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
 	if (! status)
 	{
 		processFailure(status);
+		++LLMeshRepository::sHTTPErrorCount;
 	}
 	else
 	{
@@ -2484,6 +2523,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32
 			if (file.getMaxSize() >= bytes || file.setMaxSize(bytes))
 			{
 				LLMeshRepository::sCacheBytesWritten += data_size;
+				++LLMeshRepository::sCacheWrites;
 
 				file.write(data, data_size);
 			
@@ -2556,6 +2596,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da
 			file.seek(offset);
 			file.write(data, size);
 			LLMeshRepository::sCacheBytesWritten += size;
+			++LLMeshRepository::sCacheWrites;
 		}
 	}
 	// *TODO:  Mark mesh unavailable on error
@@ -2601,6 +2642,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S
 		if (file.getSize() >= offset+size)
 		{
 			LLMeshRepository::sCacheBytesWritten += size;
+			++LLMeshRepository::sCacheWrites;
 			file.seek(offset);
 			file.write(data, size);
 		}
@@ -2648,6 +2690,7 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * da
 		if (file.getSize() >= offset+size)
 		{
 			LLMeshRepository::sCacheBytesWritten += size;
+			++LLMeshRepository::sCacheWrites;
 			file.seek(offset);
 			file.write(data, size);
 		}
@@ -2695,6 +2738,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat
 		if (file.getSize() >= offset+size)
 		{
 			LLMeshRepository::sCacheBytesWritten += size;
+			++LLMeshRepository::sCacheWrites;
 			file.seek(offset);
 			file.write(data, size);
 		}
@@ -4204,7 +4248,7 @@ void LLMeshRepository::metricsUpdate()
 		LLSD metrics;
 
 		metrics["reason"] = "Mesh Download Quiescent";
-		metrics["scope"] = "Login";
+		metrics["scope"] = metrics_teleport_start_count > 1 ? "Teleport" : "Login";
 		metrics["start"] = started;
 		metrics["stop"] = stopped;
 		metrics["fetches"] = LLSD::Integer(total_count);
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 400ceb4ad7..7e89f60bc3 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -224,6 +224,7 @@ public:
 	static U32 sMaxConcurrentRequests;
 	static S32 sRequestLowWater;
 	static S32 sRequestHighWater;
+	static S32 sRequestWaterLevel;			// Stats-use only, may read outside of thread
 
 	LLMutex*	mMutex;
 	LLMutex*	mHeaderMutex;
@@ -387,10 +388,6 @@ private:
 	LLCore::HttpHandle getByteRange(const std::string & url, int cap_version,
 									size_t offset, size_t len, 
 									LLCore::HttpHandler * handler);
-
-private:
-	U32 mHttpGetCount;
-	U32 mHttpLargeGetCount;
 };
 
 
@@ -497,12 +494,18 @@ public:
 
 	//metrics
 	static U32 sBytesReceived;
+	static U32 sMeshRequestCount;
 	static U32 sHTTPRequestCount;
+	static U32 sHTTPLargeRequestCount;
 	static U32 sHTTPRetryCount;
+	static U32 sHTTPErrorCount;
 	static U32 sLODPending;
 	static U32 sLODProcessing;
 	static U32 sCacheBytesRead;
 	static U32 sCacheBytesWritten;
+	static U32 sCacheReads;
+	static U32 sCacheWrites;
+	
 	static LLDeadmanTimer sQuiescentTimer;  // time-to-complete-mesh-downloads after significant events
 
 	static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index e80136b286..50edbb61a8 100755
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -49,6 +49,7 @@
 #include "llviewertexturelist.h"
 #include "llvovolume.h"
 #include "llviewerstats.h"
+#include "llmeshrepository.h"
 
 // For avatar texture view
 #include "llvoavatarself.h"
@@ -517,6 +518,8 @@ void LLGLTexMemBar::draw()
 	F32 total_texture_downloaded = (F32)gTotalTextureBytes / (1024 * 1024);
 	F32 total_object_downloaded = (F32)gTotalObjectBytes / (1024 * 1024);
 	U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests();
+	F32 x_right = 0.0;
+	
 	//----------------------------------------------------------------------------
 	LLGLSUIDefault gls_ui;
 	LLColor4 text_color(1.f, 1.f, 1.f, 0.75f);
@@ -543,7 +546,7 @@ void LLGLTexMemBar::draw()
 					cache_max_usage);
 	//, cache_entries, cache_max_entries
 
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4,
+	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*5,
 											 text_color, LLFontGL::LEFT, LLFontGL::TOP);
 
 	U32 cache_read(0U), cache_write(0U), res_wait(0U);
@@ -557,13 +560,12 @@ void LLGLTexMemBar::draw()
 					cache_write,
 					res_wait);
 
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3,
+	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4,
 											 text_color, LLFontGL::LEFT, LLFontGL::TOP);
 
-	S32 left = 0 ;
 	//----------------------------------------------------------------------------
 
-	text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d RAW:%d HTP:%d DEC:%d CRE:%d",
+	text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d RAW:%d HTP:%d DEC:%d CRE:%d ",
 					gTextureList.getNumImages(),
 					LLAppViewer::getTextureFetch()->getNumRequests(), LLAppViewer::getTextureFetch()->getNumDeletes(),
 					LLAppViewer::getTextureFetch()->mPacketCount, LLAppViewer::getTextureFetch()->mBadPacketCount, 
@@ -574,19 +576,30 @@ void LLGLTexMemBar::draw()
 					LLAppViewer::getImageDecodeThread()->getPending(), 
 					gTextureList.mCreateTextureList.size());
 
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*2,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-
+	x_right = 550.0;
+	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3,
+											 text_color, LLFontGL::LEFT, LLFontGL::TOP,
+											 LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX,
+											 &x_right, FALSE);
 
-	left = 550;
 	F32 bandwidth = LLAppViewer::getTextureFetch()->getTextureBandwidth();
 	F32 max_bandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
-	color = bandwidth > max_bandwidth ? LLColor4::red : bandwidth > max_bandwidth*.75f ? LLColor4::yellow : text_color;
+	color = bandwidth > max_bandwidth ? LLColor4::red : bandwidth > max_bandwidth * .75f ? LLColor4::yellow : text_color;
 	color[VALPHA] = text_color[VALPHA];
-	text = llformat("BW:%.0f/%.0f",bandwidth, max_bandwidth);
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, v_offset + line_height*2,
+	text = llformat("BW:%.0f/%.0f", bandwidth, max_bandwidth);
+	LLFontGL::getFontMonospace()->renderUTF8(text, 0, x_right, v_offset + line_height*3,
 											 color, LLFontGL::LEFT, LLFontGL::TOP);
-	
+
+	// Mesh status line
+	text = llformat("Mesh: Reqs(Tot/Htp/Big): %u/%u/%u Rtr/Err: %u/%u Cread/Cwrite: %u/%u Low/At/High: %d/%d/%d",
+					LLMeshRepository::sMeshRequestCount, LLMeshRepository::sHTTPRequestCount, LLMeshRepository::sHTTPLargeRequestCount,
+					LLMeshRepository::sHTTPRetryCount, LLMeshRepository::sHTTPErrorCount,
+					LLMeshRepository::sCacheReads, LLMeshRepository::sCacheWrites,
+					LLMeshRepoThread::sRequestLowWater, LLMeshRepoThread::sRequestWaterLevel, LLMeshRepoThread::sRequestHighWater);
+	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*2,
+											 text_color, LLFontGL::LEFT, LLFontGL::TOP);
+
+	// Header for texture table columns
 	S32 dx1 = 0;
 	if (LLAppViewer::getTextureFetch()->mDebugPause)
 	{
@@ -629,7 +642,7 @@ BOOL LLGLTexMemBar::handleMouseDown(S32 x, S32 y, MASK mask)
 LLRect LLGLTexMemBar::getRequiredRect()
 {
 	LLRect rect;
-	rect.mTop = 50; //LLFontGL::getFontMonospace()->getLineHeight() * 6;
+	rect.mTop = 68; //LLFontGL::getFontMonospace()->getLineHeight() * 6;
 	return rect;
 }
 
-- 
cgit v1.2.3


From 0d93247359531388f88f22f85326e1142d801e85 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Tue, 6 Aug 2013 18:05:34 -0400
Subject: SH-4411  Thread/mutex rework between main and worker thread Have the
 ::notifyLoadedMeshes() method doing correct locking and stall avoidance at
 the same time.  This method now does lazy mutex lock acquisition (trylock())
 and if it fails on either, it gives up and comes back later.  Capture the
 maximum number of sequential failures and report this at the end of the run
 in the log.  (So far, with big mesh regions, I've only seen 1s and 2s.) 
 Locking/mutex requirements sorted in other locations as well.  LLMutex gets
 trylock() method as well as new LLMutexTrylock scoped locking class.  Clean
 up some documentation, more to do.

---
 indra/llcommon/llthread.cpp        |  32 +++-
 indra/llcommon/llthread.h          |  46 +++++-
 indra/newview/llmeshrepository.cpp | 292 ++++++++++++++++++++++---------------
 indra/newview/llmeshrepository.h   |  17 +--
 4 files changed, 257 insertions(+), 130 deletions(-)

(limited to 'indra')

diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index 1d56a52c32..7563975959 100755
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -3,7 +3,7 @@
  *
  * $LicenseInfo:firstyear=2004&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2010-2013, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -372,6 +372,36 @@ void LLMutex::lock()
 #endif
 }
 
+bool LLMutex::trylock()
+{
+	if(isSelfLocked())
+	{ //redundant lock
+		mCount++;
+		return true;
+	}
+	
+	apr_status_t status(apr_thread_mutex_trylock(mAPRMutexp));
+	if (APR_STATUS_IS_EBUSY(status))
+	{
+		return false;
+	}
+	
+#if MUTEX_DEBUG
+	// Have to have the lock before we can access the debug info
+	U32 id = LLThread::currentID();
+	if (mIsLocked[id] != FALSE)
+		llerrs << "Already locked in Thread: " << id << llendl;
+	mIsLocked[id] = TRUE;
+#endif
+
+#if LL_DARWIN
+	mLockingThread = LLThread::currentID();
+#else
+	mLockingThread = sThreadID;
+#endif
+	return true;
+}
+
 void LLMutex::unlock()
 {
 	if (mCount > 0)
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
index 0fb89c5613..376df398d7 100755
--- a/indra/llcommon/llthread.h
+++ b/indra/llcommon/llthread.h
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2004&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2010-2013, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -156,7 +156,8 @@ public:
 	virtual ~LLMutex();
 	
 	void lock();		// blocks
-	void unlock();
+	bool trylock();		// non-blocking, returns true if lock held.
+	void unlock();		// undefined behavior when called on mutex not being held
 	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
@@ -174,6 +175,8 @@ protected:
 #endif
 };
 
+//============================================================================
+
 // Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
 class LL_COMMON_API LLCondition : public LLMutex
 {
@@ -189,6 +192,8 @@ protected:
 	apr_thread_cond_t *mAPRCondp;
 };
 
+//============================================================================
+
 class LLMutexLock
 {
 public:
@@ -210,6 +215,43 @@ private:
 
 //============================================================================
 
+// Scoped locking class similar in function to LLMutexLock but uses
+// the trylock() method to conditionally acquire lock without
+// blocking.  Caller resolves the resulting condition by calling
+// the isLocked() method and either punts or continues as indicated.
+//
+// Mostly of interest to callers needing to avoid stalls who can
+// guarantee another attempt at a later time.
+
+class LLMutexTrylock
+{
+public:
+	LLMutexTrylock(LLMutex* mutex)
+		: mMutex(mutex),
+		  mLocked(false)
+	{
+		if (mMutex)
+			mLocked = mMutex->trylock();
+	}
+
+	~LLMutexTrylock()
+	{
+		if (mMutex && mLocked)
+			mMutex->unlock();
+	}
+
+	bool isLocked() const
+	{
+		return mLocked;
+	}
+	
+private:
+	LLMutex*	mMutex;
+	bool		mLocked;
+};
+
+//============================================================================
+
 void LLThread::lockData()
 {
 	mDataLock->lock();
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 97d6c57a78..59100a68f9 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -197,7 +197,6 @@
 //     sActiveHeaderRequests    mMutex        rw.any.mMutex, ro.repo.none [1]
 //     sActiveLODRequests       mMutex        rw.any.mMutex, ro.repo.none [1]
 //     sMaxConcurrentRequests   mMutex        wo.main.none, ro.repo.none, ro.main.mMutex
-//     mWaiting                 mMutex        rw.repo.none, ro.main.none [2] (race - hint)
 //     mMeshHeader              mHeaderMutex  rw.repo.mHeaderMutex, ro.main.mHeaderMutex, ro.main.none [0]
 //     mMeshHeaderSize          mHeaderMutex  rw.repo.mHeaderMutex
 //     mSkinRequests            none          rw.repo.none, rw.main.none [0]
@@ -264,6 +263,7 @@ U32 LLMeshRepository::sCacheBytesRead = 0;
 U32 LLMeshRepository::sCacheBytesWritten = 0;
 U32 LLMeshRepository::sCacheReads = 0;
 U32 LLMeshRepository::sCacheWrites = 0;
+U32 LLMeshRepository::sMaxLockHoldoffs = 0;
 
 LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true);	// true -> gather cpu metrics
 
@@ -288,7 +288,7 @@ const char * const LOG_MESH = "Mesh";
 
 // Static data and functions to measure mesh load
 // time metrics for a new region scene.
-static unsigned int metrics_teleport_start_count(0);
+static unsigned int metrics_teleport_start_count = 0;
 boost::signals2::connection metrics_teleport_started_signal;
 static void teleport_started();
 static bool is_retryable(LLCore::HttpStatus status);
@@ -396,6 +396,7 @@ S32 LLMeshRepoThread::sRequestWaterLevel = 0;
 //     LLMeshSkinInfoHandler
 //     LLMeshDecompositionHandler
 //     LLMeshPhysicsShapeHandler
+//   LLMeshUploadThread
 
 class LLMeshHandlerBase : public LLCore::HttpHandler
 {
@@ -624,7 +625,6 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
 
 LLMeshRepoThread::LLMeshRepoThread()
 : LLThread("mesh repo"),
-  mWaiting(false),
   mHttpRequest(NULL),
   mHttpOptions(NULL),
   mHttpLargeOptions(NULL),
@@ -654,6 +654,7 @@ LLMeshRepoThread::~LLMeshRepoThread()
 {
 	LL_INFOS(LOG_MESH) << "Small GETs issued:  " << LLMeshRepository::sHTTPRequestCount
 					   << ", Large GETs issued:  " << LLMeshRepository::sHTTPLargeRequestCount
+					   << ", Max Lock Holdoffs:  " << LLMeshRepository::sMaxLockHoldoffs
 					   << LL_ENDL;
 
 	for (http_request_set::iterator iter(mHttpRequestSet.begin());
@@ -698,138 +699,171 @@ void LLMeshRepoThread::run()
 
 	while (!LLApp::isQuitting())
 	{
+		// *TODO:  Revise sleep/wake strategy and try to move away'
+		// from polling operations in this thread.  We can sleep
+		// this thread hard when:
+		// * All Http requests are serviced
+		// * LOD request queue empty
+		// * Header request queue empty
+		// * Skin info request queue empty
+		// * Decomposition request queue empty
+		// * Physics shape request queue empty
+		// We wake the thread when any of the above become untrue.
+		// Will likely need a correctly-implemented condition variable to do this.
+
+		mSignal->wait();
+
+		if (LLApp::isQuitting())
+		{
+			break;
+		}
+		
 		if (! mHttpRequestSet.empty())
 		{
 			// Dispatch all HttpHandler notifications
 			mHttpRequest->update(0L);
 		}
+		sRequestWaterLevel = mHttpRequestSet.size();			// Stats data update
+			
+		// NOTE: order of queue processing intentionally favors LOD requests over header requests
 
-		mWaiting = true;
-		mSignal->wait();
-		mWaiting = false;
-		
-		if (! LLApp::isQuitting())
+		while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
 		{
-			// NOTE: order of queue processing intentionally favors LOD requests over header requests
-
-			sRequestWaterLevel = mHttpRequestSet.size();
-			while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
+			if (! mMutex)
+			{
+				break;
+			}
+			mMutex->lock();
+			LODRequest req = mLODReqQ.front();
+			mLODReqQ.pop();
+			LLMeshRepository::sLODProcessing--;
+			mMutex->unlock();
+			if (!fetchMeshLOD(req.mMeshParams, req.mLOD))//failed, resubmit
 			{
-				if (! mMutex)
-				{
-					break;
-				}
 				mMutex->lock();
-				LODRequest req = mLODReqQ.front();
-				mLODReqQ.pop();
-				LLMeshRepository::sLODProcessing--;
+				mLODReqQ.push(req) ; 
+				++LLMeshRepository::sLODProcessing;
 				mMutex->unlock();
-				if (!fetchMeshLOD(req.mMeshParams, req.mLOD))//failed, resubmit
-				{
-					mMutex->lock();
-					mLODReqQ.push(req) ; 
-					++LLMeshRepository::sLODProcessing;
-					mMutex->unlock();
-				}
 			}
+		}
 
-			while (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
+		while (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater)
+		{
+			if (! mMutex)
+			{
+				break;
+			}
+			mMutex->lock();
+			HeaderRequest req = mHeaderReqQ.front();
+			mHeaderReqQ.pop();
+			mMutex->unlock();
+			if (!fetchMeshHeader(req.mMeshParams))//failed, resubmit
 			{
-				if (! mMutex)
-				{
-					break;
-				}
 				mMutex->lock();
-				HeaderRequest req = mHeaderReqQ.front();
-				mHeaderReqQ.pop();
+				mHeaderReqQ.push(req) ;
 				mMutex->unlock();
-				if (!fetchMeshHeader(req.mMeshParams))//failed, resubmit
-				{
-					mMutex->lock();
-					mHeaderReqQ.push(req) ;
-					mMutex->unlock();
-				}
 			}
+		}
 
-			// For the final three request lists, if we scan any part of one
-			// list, we scan the entire thing.  This gets us through any requests
-			// which can be resolved in the cache.  It also keeps the request
-			// set somewhat fresher otherwise items at the end of the set
-			// order will lose.
+		// For the final three request lists, similar goal to above but
+		// slightly different queue structures.  Stay off the mutex when
+		// performing long-duration actions.
+
+		if (mHttpRequestSet.size() < sRequestHighWater
+			&& (! mSkinRequests.empty()
+				|| ! mDecompositionRequests.empty()
+				|| ! mPhysicsShapeRequests.empty()))
+		{
+			// Something to do probably, lock and double-check.  We don't want
+			// to hold the lock long here.  That will stall main thread activities
+			// so we bounce it.
+
+			mMutex->lock();
 			if (! mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
 			{
-				// *FIXME:  this really does need a lock as do the following ones
 				std::set<LLUUID> incomplete;
-				for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter)
+				std::set<LLUUID>::iterator iter(mSkinRequests.begin());
+				while (iter != mSkinRequests.end() && mHttpRequestSet.size() < sRequestHighWater)
 				{
-					if (mHttpRequestSet.size() < sRequestHighWater)
-					{
-						LLUUID mesh_id = *iter;
-						if (!fetchMeshSkinInfo(mesh_id))
-						{
-							incomplete.insert(mesh_id);
-						}
-					}
-					else
+					LLUUID mesh_id = *iter;
+					mSkinRequests.erase(iter);
+					mMutex->unlock();
+
+					if (! fetchMeshSkinInfo(mesh_id))
 					{
-						// Hit high-water mark, copy remaining to incomplete.
-						incomplete.insert(iter, mSkinRequests.end());
-						break;
+						incomplete.insert(mesh_id);
 					}
+
+					mMutex->lock();
+					iter = mSkinRequests.begin();
+				}
+
+				if (! incomplete.empty())
+				{
+					mSkinRequests.insert(incomplete.begin(), incomplete.end());
 				}
-				mSkinRequests.swap(incomplete);
 			}
 
+			// holding lock, try next list
+			// *TODO:  For UI/debug-oriented lists, we might drop the fine-
+			// grained locking as there's lowered expectations of smoothness
+			// in these cases.
 			if (! mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
 			{
 				std::set<LLUUID> incomplete;
-				for (std::set<LLUUID>::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter)
+				std::set<LLUUID>::iterator iter(mDecompositionRequests.begin());
+				while (iter != mDecompositionRequests.end() && mHttpRequestSet.size() < sRequestHighWater)
 				{
-					if (mHttpRequestSet.size() < sRequestHighWater)
-					{
-						LLUUID mesh_id = *iter;
-						if (!fetchMeshDecomposition(mesh_id))
-						{
-							incomplete.insert(mesh_id);
-						}
-					}
-					else
+					LLUUID mesh_id = *iter;
+					mDecompositionRequests.erase(iter);
+					mMutex->unlock();
+					
+					if (! fetchMeshDecomposition(mesh_id))
 					{
-						// Hit high-water mark, copy remaining to incomplete.
-						incomplete.insert(iter, mDecompositionRequests.end());
-						break;
+						incomplete.insert(mesh_id);
 					}
+
+					mMutex->lock();
+					iter = mDecompositionRequests.begin();
+				}
+
+				if (! incomplete.empty())
+				{
+					mDecompositionRequests.insert(incomplete.begin(), incomplete.end());
 				}
-				mDecompositionRequests.swap(incomplete);
 			}
 
+			// holding lock, final list
 			if (! mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
 			{
 				std::set<LLUUID> incomplete;
-				for (std::set<LLUUID>::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter)
+				std::set<LLUUID>::iterator iter(mPhysicsShapeRequests.begin());
+				while (iter != mPhysicsShapeRequests.end() && mHttpRequestSet.size() < sRequestHighWater)
 				{
-					if (mHttpRequestSet.size() < sRequestHighWater)
-					{
-						LLUUID mesh_id = *iter;
-						if (!fetchMeshPhysicsShape(mesh_id))
-						{
-							incomplete.insert(mesh_id);
-						}
-					}
-					else
+					LLUUID mesh_id = *iter;
+					mPhysicsShapeRequests.erase(iter);
+					mMutex->unlock();
+					
+					if (! fetchMeshPhysicsShape(mesh_id))
 					{
-						// Hit high-water mark, copy remaining to incomplete.
-						incomplete.insert(iter, mPhysicsShapeRequests.end());
-						break;
+						incomplete.insert(mesh_id);
 					}
+
+					mMutex->lock();
+					iter = mPhysicsShapeRequests.begin();
 				}
-				mPhysicsShapeRequests.swap(incomplete);
-			}
 
-			// For dev purposes, a dynamic change could make this false
-			// and that shouldn't assert.
-			// llassert_always(mHttpRequestSet.size() <= sRequestHighWater);
+				if (! incomplete.empty())
+				{
+					mPhysicsShapeRequests.insert(incomplete.begin(), incomplete.end());
+				}
+			}
+			mMutex->unlock();
 		}
+
+		// For dev purposes only.  A dynamic change could make this false
+		// and that shouldn't assert.
+		// llassert_always(mHttpRequestSet.size() <= sRequestHighWater);
 	}
 	
 	if (mSignal->isLocked())
@@ -844,18 +878,21 @@ void LLMeshRepoThread::run()
 	}
 }
 
+// Mutex:  LLMeshRepoThread::mMutex must be held on entry
 void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id)
-{ //protected by mSignal, no locking needed here
+{
 	mSkinRequests.insert(mesh_id);
 }
 
+// Mutex:  LLMeshRepoThread::mMutex must be held on entry
 void LLMeshRepoThread::loadMeshDecomposition(const LLUUID& mesh_id)
-{ //protected by mSignal, no locking needed here
+{
 	mDecompositionRequests.insert(mesh_id);
 }
 
+// Mutex:  LLMeshRepoThread::mMutex must be held on entry
 void LLMeshRepoThread::loadMeshPhysicsShape(const LLUUID& mesh_id)
-{ //protected by mSignal, no locking needed here
+{
 	mPhysicsShapeRequests.insert(mesh_id);
 }
 
@@ -2406,13 +2443,18 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
 	}
 	else
 	{
-		// From texture fetch code and applies here:
+		// From texture fetch code and may apply here:
 		//
 		// A warning about partial (HTTP 206) data.  Some grid services
 		// do *not* return a 'Content-Range' header in the response to
 		// Range requests with a 206 status.  We're forced to assume
 		// we get what we asked for in these cases until we can fix
 		// the services.
+		//
+		// May also need to deal with 200 status (full asset returned
+		// rather than partial) and 416 (request completely unsatisfyable).
+		// Always been exposed to these but are less likely here where
+		// speculative loads aren't done.
 		static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT);
 
 		LLCore::BufferArray * body(response->getBody());
@@ -2422,7 +2464,9 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
 		if (data_size > 0)
 		{
 			// *TODO: Try to get rid of data copying and add interfaces
-			// that support BufferArray directly.
+			// that support BufferArray directly.  Introduce a two-phase
+			// handler, optional first that takes a body, fallback second
+			// that requires a temporary allocation and data copy.
 			data = new U8[data_size];
 			body->read(0, (char *) data, data_size);
 			LLMeshRepository::sBytesReceived += data_size;
@@ -2459,6 +2503,10 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
 {
 	if (is_retryable(status))
 	{
+		// *TODO:  This and the other processFailure() methods should
+		// probably just fail hard (as llcorehttp has done the retries).
+		// Or we could implement a slow/forever retry class.
+		
 		LL_WARNS(LOG_MESH) << "Error during mesh header handling.  Reason:  " << status.toString()
 						   << " (" << status.toHex() << ").  Retrying."
 						   << LL_ENDL;
@@ -3026,32 +3074,40 @@ void LLMeshRepository::notifyLoadedMeshes()
 
 	//call completed callbacks on finished decompositions
 	mDecompThread->notifyCompleted();
-	
-	if (!mThread->mWaiting && mPendingRequests.empty())
-	{ //curl thread is churning, wait for it to go idle
-		return;
-	}
 
-	static std::string region_name("never name a region this");
+	// For major operations, attempt to get the required locks
+	// without blocking and punt if they're not available.
+	{
+		LLMutexTrylock lock1(mMeshMutex);
+		LLMutexTrylock lock2(mThread->mMutex);
 
-	if (gAgent.getRegion())
-	{ //update capability url 
-		if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived())
+		static U32 hold_offs(0);
+		if (! lock1.isLocked() || ! lock2.isLocked())
 		{
-			region_name = gAgent.getRegion()->getName();
-			mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh");
-			mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2");
-			mGetMeshVersion = mGetMesh2Capability.empty() ? 1 : 2;
-			LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name
-								<< "', GetMesh2:  " << mGetMesh2Capability
-								<< ", GetMesh:  " << mGetMeshCapability
-								<< LL_ENDL;
+			// If we can't get the locks, skip and pick this up later.
+			++hold_offs;
+			sMaxLockHoldoffs = llmax(sMaxLockHoldoffs, hold_offs);
+			return;
+		}
+		hold_offs = 0;
+		
+		if (gAgent.getRegion())
+		{
+			// Update capability urls
+			static std::string region_name("never name a region this");
+			
+			if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived())
+			{
+				region_name = gAgent.getRegion()->getName();
+				mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh");
+				mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2");
+				mGetMeshVersion = mGetMesh2Capability.empty() ? 1 : 2;
+				LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name
+									<< "', GetMesh2:  " << mGetMesh2Capability
+									<< ", GetMesh:  " << mGetMeshCapability
+									<< LL_ENDL;
+			}
 		}
-	}
-
-	{
-		LLMutexLock lock1(mMeshMutex);
-		LLMutexLock lock2(mThread->mMutex);
 		
 		//popup queued error messages from background threads
 		while (!mUploadErrorQ.empty())
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 7e89f60bc3..c79278da1a 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -230,8 +230,6 @@ public:
 	LLMutex*	mHeaderMutex;
 	LLCondition* mSignal;
 
-	volatile bool mWaiting;
-
 	//map of known mesh headers
 	typedef std::map<LLUUID, LLSD> mesh_header_map;
 	mesh_header_map mMeshHeader;
@@ -494,19 +492,20 @@ public:
 
 	//metrics
 	static U32 sBytesReceived;
-	static U32 sMeshRequestCount;
-	static U32 sHTTPRequestCount;
-	static U32 sHTTPLargeRequestCount;
-	static U32 sHTTPRetryCount;
-	static U32 sHTTPErrorCount;
+	static U32 sMeshRequestCount;				// Total request count, http or cached, all component types
+	static U32 sHTTPRequestCount;				// Http GETs issued (not large)
+	static U32 sHTTPLargeRequestCount;			// Http GETs issued for large requests
+	static U32 sHTTPRetryCount;					// Total request retries whether successful or failed
+	static U32 sHTTPErrorCount;					// Requests ending in error
 	static U32 sLODPending;
 	static U32 sLODProcessing;
 	static U32 sCacheBytesRead;
 	static U32 sCacheBytesWritten;
-	static U32 sCacheReads;
+	static U32 sCacheReads;						
 	static U32 sCacheWrites;
+	static U32 sMaxLockHoldoffs;				// Maximum sequential locking failures
 	
-	static LLDeadmanTimer sQuiescentTimer;  // time-to-complete-mesh-downloads after significant events
+	static LLDeadmanTimer sQuiescentTimer;		// Time-to-complete-mesh-downloads after significant events
 
 	static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
 
-- 
cgit v1.2.3


From 8b78642a475d9a1095970a3be39de47117b35d9f Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 7 Aug 2013 21:31:41 -0400
Subject: Create separate high/low water level limits for GetMesh and GetMesh2
 capabilities.  They should be independent now.

---
 indra/newview/llmeshrepository.cpp | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 59100a68f9..b19f6281e7 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -235,9 +235,13 @@ LLMeshRepository gMeshRepo;
 
 const S32 MESH_HEADER_SIZE = 4096;                      // Important:  assumption is that headers fit in this space
 const S32 REQUEST_HIGH_WATER_MIN = 32;
-const S32 REQUEST_HIGH_WATER_MAX = 80;
+const S32 REQUEST_HIGH_WATER_MAX = 200;
 const S32 REQUEST_LOW_WATER_MIN = 16;
-const S32 REQUEST_LOW_WATER_MAX = 40;
+const S32 REQUEST_LOW_WATER_MAX = 100;
+const S32 REQUEST2_HIGH_WATER_MIN = 32;
+const S32 REQUEST2_HIGH_WATER_MAX = 80;
+const S32 REQUEST2_LOW_WATER_MIN = 16;
+const S32 REQUEST2_LOW_WATER_MAX = 40;
 const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;		// Size at which requests goes to narrow/slow queue
 const long SMALL_MESH_XFER_TIMEOUT = 60L;				// Seconds to complete xfer, small mesh downloads
 const long LARGE_MESH_XFER_TIMEOUT = 600L;				// Seconds to complete xfer, large downloads
@@ -376,8 +380,8 @@ void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res,
 volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0;
 volatile S32 LLMeshRepoThread::sActiveLODRequests = 0;
 U32	LLMeshRepoThread::sMaxConcurrentRequests = 1;
-S32 LLMeshRepoThread::sRequestLowWater = REQUEST_LOW_WATER_MIN;
-S32 LLMeshRepoThread::sRequestHighWater = REQUEST_HIGH_WATER_MIN;
+S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN;
+S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN;
 S32 LLMeshRepoThread::sRequestWaterLevel = 0;
 
 // Base handler class for all mesh users of llcorehttp.
@@ -2985,18 +2989,21 @@ void LLMeshRepository::notifyLoadedMeshes()
 		LLMeshRepoThread::sRequestHighWater = llclamp(2 * S32(LLMeshRepoThread::sMaxConcurrentRequests),
 													  REQUEST_HIGH_WATER_MIN,
 													  REQUEST_HIGH_WATER_MAX);
+		LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2,
+													 REQUEST_LOW_WATER_MIN,
+													 REQUEST_LOW_WATER_MAX);
 	}
 	else
 	{
 		// GetMesh2 operation with keepalives, etc.
 		LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("Mesh2MaxConcurrentRequests");
 		LLMeshRepoThread::sRequestHighWater = llclamp(5 * S32(LLMeshRepoThread::sMaxConcurrentRequests),
-													  REQUEST_HIGH_WATER_MIN,
-													  REQUEST_HIGH_WATER_MAX);
+													  REQUEST2_HIGH_WATER_MIN,
+													  REQUEST2_HIGH_WATER_MAX);
+		LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2,
+													 REQUEST2_LOW_WATER_MIN,
+													 REQUEST2_LOW_WATER_MAX);
 	}
-	LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2,
-												 REQUEST_LOW_WATER_MIN,
-												 REQUEST_LOW_WATER_MAX);
 	
 	//clean up completed upload threads
 	for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); )
-- 
cgit v1.2.3


From f84a8104b80eacad78b30c09cb016774e5c459c5 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 15 Aug 2013 19:00:43 -0400
Subject: SH-4410 Internal Documentation.  Update and correct the mutex/data
 lists to reflect current.  Describe the functional flow of things for a
 single LOD request.  Put together to-do list for follow on work.  Knock down
 the low/high water limits for GetMesh a bit, 100/200 too high, 75/150 should
 be better, vis-a-vis pathological failures.

---
 indra/newview/llmeshrepository.cpp | 178 ++++++++++++++++++++++++++-----------
 1 file changed, 127 insertions(+), 51 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index b19f6281e7..7d64a9f63f 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -79,10 +79,6 @@
 #include <queue>
 
 
-// [ Disclaimer:  this documentation isn't by one of the original authors
-//   but by someone coming through later and extracting intent and function.
-//   Some of this will be wrong so use judgement. ]
-//
 // Purpose
 //
 //   The purpose of this module is to provide access between the viewer
@@ -101,6 +97,7 @@
 //     * getMeshHeader (For structural details, see:
 //       http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format)
 //     * notifyLoadedMeshes
+//     * getSkinInfo
 //
 // Threads
 //
@@ -108,7 +105,54 @@
 //   repo     Overseeing worker thread associated with the LLMeshRepoThread class
 //   decom    Worker thread for mesh decomposition requests
 //   core     HTTP worker thread:  does the work but doesn't intrude here
-//   uploadN  0-N temporary mesh upload threads
+//   uploadN  0-N temporary mesh upload threads (0-1 in practice)
+//
+// Sequence of Operations
+//
+//   What follows is a description of the retrieval of one LOD for
+//   a new mesh object.  Work is performed by a series of short, quick
+//   actions distributed over a number of threads.  Each is meant
+//   to proceed without stalling and the whole forms a deep request
+//   pipeline to achieve throughput.  Ellipsis indicates a return
+//   or break in processing which is resumed elsewhere.
+//
+//         main thread         repo thread (run() method)
+//
+//         loadMesh() invoked to request LOD
+//           append LODRequest to mPendingRequests
+//         ...
+//         other mesh requests may be made
+//         ...
+//         notifyLoadedMeshes() invoked to stage work
+//           append HeaderRequest to mHeaderReqQ
+//         ...
+//                             scan mHeaderReqQ
+//                             issue 4096-byte GET for header
+//                             ...
+//                             onCompleted() invoked for GET
+//                               data copied
+//                               headerReceived() invoked
+//                                 LLSD parsed
+//                                 mMeshHeader, mMeshHeaderSize updated
+//                                 scan mPendingLOD for LOD request
+//                                 push LODRequest to mLODReqQ
+//                             ...
+//                             scan mLODReqQ
+//                             fetchMeshLOD() invoked
+//                               issue Byte-Range GET for LOD
+//                             ...
+//                             onCompleted() invoked for GET
+//                               data copied
+//                               lodReceived() invoked
+//                                 unpack data into LLVolume
+//                                 append LoadedMesh to mLoadedQ
+//                             ...
+//         notifyLoadedMeshes() invoked again
+//           scan mLoadedQ
+//           notifyMeshLoaded() for LOD
+//             setMeshAssetLoaded() invoked for system volume
+//             notifyMeshLoaded() invoked for each interested object
+//         ...
 //
 // Mutexes
 //
@@ -163,19 +207,19 @@
 //
 //   LLMeshRepository:
 //
-//     sBytesReceived
-//     sMeshRequestCount
-//     sHTTPRequestCount
-//     sHTTPLargeRequestCount
-//     sHTTPRetryCount
-//     sHTTPErrorCount
-//     sLODPending
-//     sLODProcessing
-//     sCacheBytesRead
-//     sCacheBytesWritten
-//     sCacheReads
-//     sCacheWrites
-//     mLoadingMeshes                  none            rw.main.none, rw.main.mMeshMutex [4]
+//     sBytesReceived                  none            rw.repo.none, ro.main.none [1]
+//     sMeshRequestCount               "
+//     sHTTPRequestCount               "
+//     sHTTPLargeRequestCount          "
+//     sHTTPRetryCount                 "
+//     sHTTPErrorCount                 "
+//     sLODPending                     mMeshMutex [4]  rw.main.mMeshMutex
+//     sLODProcessing                  Repo::mMutex    rw.any.Repo::mMutex
+//     sCacheBytesRead                 none            rw.repo.none, ro.main.none [1]
+//     sCacheBytesWritten              "
+//     sCacheReads                     "
+//     sCacheWrites                    "
+//     mLoadingMeshes                  mMeshMutex [4]  rw.main.none, rw.any.mMeshMutex
 //     mSkinMap                        none            rw.main.none
 //     mDecompositionMap               none            rw.main.none
 //     mPendingRequests                mMeshMutex [4]  rw.main.mMeshMutex
@@ -199,25 +243,18 @@
 //     sMaxConcurrentRequests   mMutex        wo.main.none, ro.repo.none, ro.main.mMutex
 //     mMeshHeader              mHeaderMutex  rw.repo.mHeaderMutex, ro.main.mHeaderMutex, ro.main.none [0]
 //     mMeshHeaderSize          mHeaderMutex  rw.repo.mHeaderMutex
-//     mSkinRequests            none          rw.repo.none, rw.main.none [0]
-//     mSkinInfoQ               none          rw.repo.none, rw.main.none [0]
-//     mDecompositionRequests   none          rw.repo.none, rw.main.none [0]
-//     mPhysicsShapeRequests    none          rw.repo.none, rw.main.none [0]
-//     mDecompositionQ          none          rw.repo.none, rw.main.none [0]
-//     mHeaderReqQ              mMutex        ro.repo.none [3], rw.repo.mMutex, rw.any.mMutex
-//     mLODReqQ                 mMutex        ro.repo.none [3], rw.repo.mMutex, rw.any.mMutex
-//     mUnavailableQ            mMutex        rw.repo.none [0], ro.main.none [3], rw.main.mMutex
-//     mLoadedQ                 mMutex        rw.repo.mMutex, ro.main.none [3], rw.main.mMutex
+//     mSkinRequests            mMutex        rw.repo.mMutex, ro.repo.none [5]
+//     mSkinInfoQ               none          rw.repo.none, rw.main.mMutex [0]
+//     mDecompositionRequests   mMutex        rw.repo.mMutex, ro.repo.none [5]
+//     mPhysicsShapeRequests    mMutex        rw.repo.mMutex, ro.repo.none [5]
+//     mDecompositionQ          none          rw.repo.none, rw.main.mMutex [0]
+//     mHeaderReqQ              mMutex        ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex
+//     mLODReqQ                 mMutex        ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex
+//     mUnavailableQ            mMutex        rw.repo.none [0], ro.main.none [5], rw.main.mMutex
+//     mLoadedQ                 mMutex        rw.repo.mMutex, ro.main.none [5], rw.main.mMutex
 //     mPendingLOD              mMutex        rw.repo.mMutex, rw.any.mMutex
 //     mHttp*                   none          rw.repo.none
 //
-//   LLPhysicsDecomp:
-//    
-//     mRequestQ
-//     mCurRequest
-//     mCompletedQ
-//
-//
 // QA/Development Testing
 //
 //   Debug variable 'MeshUploadFakeErrors' takes a mask of bits that will
@@ -230,15 +267,27 @@
 //                   locally-generated 500 status.
 //   0x08            As with 0x04 but for the upload operation.
 //
+// *TODO:  Work list for followup actions:
+//   * Review anything marked as unsafe above, verify if there are real issues.
+//   * See if we can put ::run() into a hard sleep.  May not actually perform better
+//     than the current scheme so be prepared for disappointment.  You'll likely
+//     need to introduce a condition variable class that references a mutex in
+//     methods rather than derives from mutex which isn't correct.
+//   * On upload failures, make more information available to the alerting
+//     dialog.  Get the structured information going into the log into a
+//     tree there.
+//   * Header parse failures come without much explanation.  Elaborate.
+//   * Need a final failure state for requests that are retried and just won't
+//     complete.  We can fail a LOD request, others we don't.
 
 LLMeshRepository gMeshRepo;
 
 const S32 MESH_HEADER_SIZE = 4096;                      // Important:  assumption is that headers fit in this space
-const S32 REQUEST_HIGH_WATER_MIN = 32;
-const S32 REQUEST_HIGH_WATER_MAX = 200;
+const S32 REQUEST_HIGH_WATER_MIN = 32;					// Limits for GetMesh regions
+const S32 REQUEST_HIGH_WATER_MAX = 150;					// Should remain under 2X throttle
 const S32 REQUEST_LOW_WATER_MIN = 16;
-const S32 REQUEST_LOW_WATER_MAX = 100;
-const S32 REQUEST2_HIGH_WATER_MIN = 32;
+const S32 REQUEST_LOW_WATER_MAX = 75;
+const S32 REQUEST2_HIGH_WATER_MIN = 32;					// Limits for GetMesh2 regions
 const S32 REQUEST2_HIGH_WATER_MAX = 80;
 const S32 REQUEST2_LOW_WATER_MIN = 16;
 const S32 REQUEST2_LOW_WATER_MAX = 40;
@@ -269,7 +318,7 @@ U32 LLMeshRepository::sCacheReads = 0;
 U32 LLMeshRepository::sCacheWrites = 0;
 U32 LLMeshRepository::sMaxLockHoldoffs = 0;
 
-LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true);	// true -> gather cpu metrics
+LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, false);	// true -> gather cpu metrics
 
 	
 static S32 dump_num = 0;
@@ -703,7 +752,7 @@ void LLMeshRepoThread::run()
 
 	while (!LLApp::isQuitting())
 	{
-		// *TODO:  Revise sleep/wake strategy and try to move away'
+		// *TODO:  Revise sleep/wake strategy and try to move away
 		// from polling operations in this thread.  We can sleep
 		// this thread hard when:
 		// * All Http requests are serviced
@@ -714,7 +763,8 @@ void LLMeshRepoThread::run()
 		// * Physics shape request queue empty
 		// We wake the thread when any of the above become untrue.
 		// Will likely need a correctly-implemented condition variable to do this.
-
+		// On the other hand, this may actually be an effective and efficient scheme...
+		
 		mSignal->wait();
 
 		if (LLApp::isQuitting())
@@ -810,7 +860,7 @@ void LLMeshRepoThread::run()
 
 			// holding lock, try next list
 			// *TODO:  For UI/debug-oriented lists, we might drop the fine-
-			// grained locking as there's lowered expectations of smoothness
+			// grained locking as there's a lowered expectation of smoothness
 			// in these cases.
 			if (! mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
 			{
@@ -2303,24 +2353,26 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp
 
 void LLMeshRepoThread::notifyLoadedMeshes()
 {
+	bool update_metrics(false);
+	
 	if (!mMutex)
 	{
 		return;
 	}
 
-	if (!mLoadedQ.empty() || !mUnavailableQ.empty())
-	{
-		// Ping time-to-load metrics for mesh download operations.
-		LLMeshRepository::metricsProgress(0);
-	}
-	
 	while (!mLoadedQ.empty())
 	{
 		mMutex->lock();
+		if (mLoadedQ.empty())
+		{
+			mMutex->unlock();
+			break;
+		}
 		LoadedMesh mesh = mLoadedQ.front();
 		mLoadedQ.pop();
 		mMutex->unlock();
 		
+		update_metrics = true;
 		if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0)
 		{
 			gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume);
@@ -2335,10 +2387,17 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 	while (!mUnavailableQ.empty())
 	{
 		mMutex->lock();
+		if (mUnavailableQ.empty())
+		{
+			mMutex->unlock();
+			break;
+		}
+		
 		LODRequest req = mUnavailableQ.front();
 		mUnavailableQ.pop();
 		mMutex->unlock();
-		
+
+		update_metrics = true;
 		gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
 	}
 
@@ -2353,6 +2412,13 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 		gMeshRepo.notifyDecompositionReceived(mDecompositionQ.front());
 		mDecompositionQ.pop();
 	}
+
+	if (update_metrics)
+	{
+		// Ping time-to-load metrics for mesh download operations.
+		LLMeshRepository::metricsProgress(0);
+	}
+	
 }
 
 S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod) 
@@ -2461,6 +2527,12 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
 		// speculative loads aren't done.
 		static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT);
 
+		if (par_status != status)
+		{
+			LL_WARNS_ONCE(LOG_MESH) << "Non-206 successful status received for fetch:  "
+									<< status.toHex() << LL_ENDL;
+		}
+		
 		LLCore::BufferArray * body(response->getBody());
 		S32 data_size(body ? body->size() : 0);
 		U8 * data(NULL);
@@ -2995,7 +3067,8 @@ void LLMeshRepository::notifyLoadedMeshes()
 	}
 	else
 	{
-		// GetMesh2 operation with keepalives, etc.
+		// GetMesh2 operation with keepalives, etc.  With pipelining,
+		// we'll increase this.
 		LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("Mesh2MaxConcurrentRequests");
 		LLMeshRepoThread::sRequestHighWater = llclamp(5 * S32(LLMeshRepoThread::sMaxConcurrentRequests),
 													  REQUEST2_HIGH_WATER_MIN,
@@ -3083,7 +3156,10 @@ void LLMeshRepository::notifyLoadedMeshes()
 	mDecompThread->notifyCompleted();
 
 	// For major operations, attempt to get the required locks
-	// without blocking and punt if they're not available.
+	// without blocking and punt if they're not available.  The
+	// longest run of holdoffs is kept in sMaxLockHoldoffs just
+	// to collect the data.  In testing, I've never seen a value
+	// greater than 2 (written to log on exit).
 	{
 		LLMutexTrylock lock1(mMeshMutex);
 		LLMutexTrylock lock2(mThread->mMutex);
-- 
cgit v1.2.3


From e764a2a565e18ce2157788f634e85bc3641976b3 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 16 Aug 2013 18:07:49 -0400
Subject: SH-4407 Tuning to get new code working as well. Do some runtime code
 avoidance and skip unnecessary libcurl and syscall invocations.

---
 indra/llcorehttp/_httplibcurl.cpp | 32 +++++++++++++++++---------------
 indra/llcorehttp/_httplibcurl.h   |  1 +
 indra/llcorehttp/_httppolicy.cpp  | 11 ++++++++---
 3 files changed, 26 insertions(+), 18 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp
index b079dff864..0cb4e9d8b7 100755
--- a/indra/llcorehttp/_httplibcurl.cpp
+++ b/indra/llcorehttp/_httplibcurl.cpp
@@ -41,7 +41,8 @@ namespace LLCore
 HttpLibcurl::HttpLibcurl(HttpService * service)
 	: mService(service),
 	  mPolicyCount(0),
-	  mMultiHandles(NULL)
+	  mMultiHandles(NULL),
+	  mActiveHandles(NULL)
 {}
 
 
@@ -77,6 +78,9 @@ void HttpLibcurl::shutdown()
 
 		delete [] mMultiHandles;
 		mMultiHandles = NULL;
+
+		delete [] mActiveHandles;
+		mActiveHandles = NULL;
 	}
 
 	mPolicyCount = 0;
@@ -90,9 +94,12 @@ void HttpLibcurl::start(int policy_count)
 	
 	mPolicyCount = policy_count;
 	mMultiHandles = new CURLM * [mPolicyCount];
+	mActiveHandles = new int [mPolicyCount];
+	
 	for (int policy_class(0); policy_class < mPolicyCount; ++policy_class)
 	{
 		mMultiHandles[policy_class] = curl_multi_init();
+		mActiveHandles[policy_class] = 0;
 	}
 }
 
@@ -110,8 +117,10 @@ HttpService::ELoopSpeed HttpLibcurl::processTransport()
 	// Give libcurl some cycles to do I/O & callbacks
 	for (int policy_class(0); policy_class < mPolicyCount; ++policy_class)
 	{
-		if (! mMultiHandles[policy_class])
+		if (! mActiveHandles[policy_class] || ! mMultiHandles[policy_class])
+		{
 			continue;
+		}
 		
 		int running(0);
 		CURLMcode status(CURLM_CALL_MULTI_PERFORM);
@@ -191,6 +200,7 @@ void HttpLibcurl::addOp(HttpOpRequest * op)
 	
 	// On success, make operation active
 	mActiveOps.insert(op);
+	++mActiveHandles[op->mReqPolicy];
 }
 
 
@@ -212,6 +222,7 @@ bool HttpLibcurl::cancel(HttpHandle handle)
 
 	// Drop references
 	mActiveOps.erase(it);
+	--mActiveHandles[op->mReqPolicy];
 	op->release();
 
 	return true;
@@ -273,6 +284,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode
 
 	// Deactivate request
 	mActiveOps.erase(it);
+	--mActiveHandles[op->mReqPolicy];
 	op->mCurlActive = false;
 
 	// Set final status of request if it hasn't failed by other mechanisms yet
@@ -334,19 +346,9 @@ int HttpLibcurl::getActiveCount() const
 
 int HttpLibcurl::getActiveCountInClass(int policy_class) const
 {
-	int count(0);
-	
-	for (active_set_t::const_iterator iter(mActiveOps.begin());
-		 mActiveOps.end() != iter;
-		 ++iter)
-	{
-		if ((*iter)->mReqPolicy == policy_class)
-		{
-			++count;
-		}
-	}
-	
-	return count;
+	llassert_always(policy_class < mPolicyCount);
+
+	return mActiveHandles ? mActiveHandles[policy_class] : 0;
 }
 
 
diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h
index 0ec90437bb..67f98dd4f0 100755
--- a/indra/llcorehttp/_httplibcurl.h
+++ b/indra/llcorehttp/_httplibcurl.h
@@ -133,6 +133,7 @@ protected:
 	active_set_t		mActiveOps;
 	int					mPolicyCount;
 	CURLM **			mMultiHandles;			// One handle per policy class
+	int *				mActiveHandles;			// Active count per policy class
 }; // end class HttpLibcurl
 
 }  // end namespace LLCore
diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp
index 808eebc6cc..c4758aee88 100755
--- a/indra/llcorehttp/_httppolicy.cpp
+++ b/indra/llcorehttp/_httppolicy.cpp
@@ -212,6 +212,14 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
 	for (int policy_class(0); policy_class < mClasses.size(); ++policy_class)
 	{
 		ClassState & state(*mClasses[policy_class]);
+		HttpRetryQueue & retryq(state.mRetryQueue);
+		HttpReadyQueue & readyq(state.mReadyQueue);
+
+		if (retryq.empty() && readyq.empty())
+		{
+			continue;
+		}
+		
 		const bool throttle_enabled(state.mOptions.mThrottleRate > 0L);
 		const bool throttle_current(throttle_enabled && now < state.mThrottleEnd);
 
@@ -225,9 +233,6 @@ HttpService::ELoopSpeed HttpPolicy::processReadyQueue()
 		int active(transport.getActiveCountInClass(policy_class));
 		int needed(state.mOptions.mConnectionLimit - active);		// Expect negatives here
 
-		HttpRetryQueue & retryq(state.mRetryQueue);
-		HttpReadyQueue & readyq(state.mReadyQueue);
-
 		if (needed > 0)
 		{
 			// First see if we have any retries...
-- 
cgit v1.2.3


From 146a5c3f6c3d1b8e9e92f71dce1e7f058091ea20 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Mon, 19 Aug 2013 12:01:26 -0400
Subject: Add 'internal'/'external' token to DEBUG retry message so that dev/QA
 can know exactly where a retry value was sourced.

---
 indra/llcorehttp/_httppolicy.cpp | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp
index c4758aee88..ac79a77659 100755
--- a/indra/llcorehttp/_httppolicy.cpp
+++ b/indra/llcorehttp/_httppolicy.cpp
@@ -153,14 +153,16 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
 		};
 	static const int delta_max(int(LL_ARRAY_SIZE(retry_deltas)) - 1);
 	static const HttpStatus error_503(503);
-	
+
 	const HttpTime now(totalTime());
 	const int policy_class(op->mReqPolicy);
 	HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]);
+	bool external_delta(false);
 
 	if (op->mReplyRetryAfter > 0 && op->mReplyRetryAfter < 30)
 	{
 		delta = op->mReplyRetryAfter * U64L(1000000);
+		external_delta = true;
 	}
 	op->mPolicyRetryAt = now + delta;
 	++op->mPolicyRetries;
@@ -171,7 +173,8 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
 	LL_DEBUGS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
 						  << " retry " << op->mPolicyRetries
 						  << " scheduled in " << (delta / HttpTime(1000))
-						  << " mS.  Status:  " << op->mStatus.toHex()
+						  << " mS (" << (external_delta ? "external" : "internal")
+						  << ").  Status:  " << op->mStatus.toHex()
 						  << LL_ENDL;
 	if (op->mTracing > HTTP_TRACE_OFF)
 	{
-- 
cgit v1.2.3


From 42e3d55fb2d0579b968ff8df20e9a814229b3e72 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 28 Aug 2013 18:55:29 -0400
Subject: Add conditional LLFastTimer blocks to stallable main thread methods

---
 indra/newview/llmeshrepository.cpp | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 6dc834e852..0599fcfd2d 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -67,6 +67,7 @@
 #include "llfoldertype.h"
 #include "llviewerparcelmgr.h"
 #include "lluploadfloaterobservers.h"
+#include "llfasttimer.h"
 
 #include "boost/lexical_cast.hpp"
 
@@ -125,6 +126,16 @@ static bool metrics_inited(false);
 static boost::signals2::connection metrics_teleport_connection;
 static unsigned int metrics_teleport_start_count(0);
 static void metrics_teleport_started();
+static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch");
+
+// Enable here or in build environment to get fasttimer data on mesh fetches.
+#define LL_FASTTIMER_MESH_ENABLE		1
+#if LL_FASTTIMER_MESH_ENABLE
+#define	MESH_FASTTIMER_DEFBLOCK			LLFastTimer meshtimer(FTM_MESH_FETCH)
+#else
+#define	MESH_FASTTIMER_DEFBLOCK
+#endif // LL_FASTTIMER_MESH_ENABLE
+ 
 
 //get the number of bytes resident in memory for given volume
 U32 get_volume_memory_size(const LLVolume* volume)
@@ -2403,6 +2414,8 @@ S32 LLMeshRepository::update()
 
 S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)
 {
+	MESH_FASTTIMER_DEFBLOCK;
+	
 	// Manage time-to-load metrics for mesh download operations.
 	metricsProgress(1);
 
@@ -2484,6 +2497,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
 
 void LLMeshRepository::notifyLoadedMeshes()
 { //called from main thread
+	MESH_FASTTIMER_DEFBLOCK;
 
 	LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests");
 
@@ -2791,6 +2805,8 @@ S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo
 
 const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj)
 {
+	MESH_FASTTIMER_DEFBLOCK;
+
 	if (mesh_id.notNull())
 	{
 		skin_map::iterator iter = mSkinMap.find(mesh_id);
@@ -2817,6 +2833,8 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const
 
 void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
 {
+	MESH_FASTTIMER_DEFBLOCK;
+
 	if (mesh_id.notNull())
 	{
 		LLModel::Decomposition* decomp = NULL;
@@ -2844,6 +2862,8 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
 
 LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id)
 {
+	MESH_FASTTIMER_DEFBLOCK;
+
 	LLModel::Decomposition* ret = NULL;
 
 	if (mesh_id.notNull())
@@ -2906,6 +2926,8 @@ bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id)
 
 LLSD& LLMeshRepository::getMeshHeader(const LLUUID& mesh_id)
 {
+	MESH_FASTTIMER_DEFBLOCK;
+
 	return mThread->getMeshHeader(mesh_id);
 }
 
-- 
cgit v1.2.3


From d706b5717785a318c053055c49589b16f9633681 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 29 Aug 2013 13:04:05 -0400
Subject: Document the fast timer testing apparatus a bit.

---
 indra/newview/llmeshrepository.cpp | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 2f43ee3ccb..3c964160b5 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -268,6 +268,9 @@
 //                   locally-generated 500 status.
 //   0x08            As with 0x04 but for the upload operation.
 //
+//   For major changes, see the LL_MESH_FASTTIMER_ENABLE below and
+//   instructions for looking for frame stalls using fast timers.
+//
 // *TODO:  Work list for followup actions:
 //   * Review anything marked as unsafe above, verify if there are real issues.
 //   * See if we can put ::run() into a hard sleep.  May not actually perform better
@@ -346,15 +349,25 @@ static unsigned int metrics_teleport_start_count = 0;
 boost::signals2::connection metrics_teleport_started_signal;
 static void teleport_started();
 static bool is_retryable(LLCore::HttpStatus status);
-static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch");
 
 // Enable here or in build environment to get fasttimer data on mesh fetches.
-#define LL_FASTTIMER_MESH_ENABLE		1
-#if LL_FASTTIMER_MESH_ENABLE
+//
+// Typically, this is used to perform A/B testing using the
+// fasttimer console (shift-ctrl-9).  This is done by looking
+// for stalls due to lock contention between the main thread
+// and the repository and HTTP code.  In a release viewer,
+// these appear as ping-time or worse spikes in frame time.
+// With this instrumentation enabled, a stall will appear
+// under the 'Mesh Fetch' timer which will be either top-level
+// or under 'Render' time.
+#define LL_MESH_FASTTIMER_ENABLE		1
+#if LL_MESH_FASTTIMER_ENABLE
+static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch");
+
 #define	MESH_FASTTIMER_DEFBLOCK			LLFastTimer meshtimer(FTM_MESH_FETCH)
 #else
 #define	MESH_FASTTIMER_DEFBLOCK
-#endif // LL_FASTTIMER_MESH_ENABLE
+#endif // LL_MESH_FASTTIMER_ENABLE
  
 //get the number of bytes resident in memory for given volume
 U32 get_volume_memory_size(const LLVolume* volume)
-- 
cgit v1.2.3


From 6a1f91fa3e0f82ad58fd13add8a093d88eff2c70 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 6 Sep 2013 16:37:31 -0400
Subject: SH-4478 Corrected/updated error handling for all retrieval
 operations. In case of HTTP errors or parsing/processing errors, fail the
 fetch request rather than do a retry spin.  Add logging for all such failure
 paths.  Added a development/debug flag to create probabilistic failures to
 test these modes and general error recovery by higher-level layers.

---
 indra/newview/llmeshrepository.cpp | 277 +++++++++++++++++--------------------
 1 file changed, 126 insertions(+), 151 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 3c964160b5..f6a85ac94f 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -44,6 +44,7 @@
 #include "lleconomy.h"
 #include "llimagej2c.h"
 #include "llhost.h"
+#include "llmath.h"
 #include "llnotificationsutil.h"
 #include "llsd.h"
 #include "llsdutil_math.h"
@@ -284,6 +285,52 @@
 //   * Need a final failure state for requests that are retried and just won't
 //     complete.  We can fail a LOD request, others we don't.
 
+
+// --------------------------------------------------------------------------
+//                    Development/Debug/QA Tools
+//
+// Enable here or in build environment to get fasttimer data on mesh fetches.
+//
+// Typically, this is used to perform A/B testing using the
+// fasttimer console (shift-ctrl-9).  This is done by looking
+// for stalls due to lock contention between the main thread
+// and the repository and HTTP code.  In a release viewer,
+// these appear as ping-time or worse spikes in frame time.
+// With this instrumentation enabled, a stall will appear
+// under the 'Mesh Fetch' timer which will be either top-level
+// or under 'Render' time.
+#define LL_MESH_FASTTIMER_ENABLE		1
+#if LL_MESH_FASTTIMER_ENABLE
+static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch");
+
+#define	MESH_FASTTIMER_DEFBLOCK			LLFastTimer meshtimer(FTM_MESH_FETCH)
+#else
+#define	MESH_FASTTIMER_DEFBLOCK
+#endif // LL_MESH_FASTTIMER_ENABLE
+
+
+// Random failure testing for development/QA.
+//
+// Set the MESH_*_FAILED macros to either 'false' or to
+// an invocation of MESH_RANDOM_NTH_TRUE() with some
+// suitable number.  In production, all must be false.
+//
+// Example:
+// #define	MESH_HTTP_RESPONSE_FAILED				MESH_RANDOM_NTH_TRUE(9)
+
+// 1-in-N calls will test true
+#define	MESH_RANDOM_NTH_TRUE(_N)				( ll_rand(S32(_N)) == 0 )
+
+#define	MESH_HTTP_RESPONSE_FAILED				false
+#define	MESH_HEADER_PROCESS_FAILED				false
+#define	MESH_LOD_PROCESS_FAILED					false
+#define	MESH_SKIN_INFO_PROCESS_FAILED			false
+#define	MESH_DECOMP_PROCESS_FAILED				false
+#define MESH_PHYS_SHAPE_PROCESS_FAILED			false
+
+// --------------------------------------------------------------------------
+
+
 LLMeshRepository gMeshRepo;
 
 const S32 MESH_HEADER_SIZE = 4096;                      // Important:  assumption is that headers fit in this space
@@ -348,27 +395,7 @@ const char * const LOG_MESH = "Mesh";
 static unsigned int metrics_teleport_start_count = 0;
 boost::signals2::connection metrics_teleport_started_signal;
 static void teleport_started();
-static bool is_retryable(LLCore::HttpStatus status);
 
-// Enable here or in build environment to get fasttimer data on mesh fetches.
-//
-// Typically, this is used to perform A/B testing using the
-// fasttimer console (shift-ctrl-9).  This is done by looking
-// for stalls due to lock contention between the main thread
-// and the repository and HTTP code.  In a release viewer,
-// these appear as ping-time or worse spikes in frame time.
-// With this instrumentation enabled, a stall will appear
-// under the 'Mesh Fetch' timer which will be either top-level
-// or under 'Render' time.
-#define LL_MESH_FASTTIMER_ENABLE		1
-#if LL_MESH_FASTTIMER_ENABLE
-static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch");
-
-#define	MESH_FASTTIMER_DEFBLOCK			LLFastTimer meshtimer(FTM_MESH_FETCH)
-#else
-#define	MESH_FASTTIMER_DEFBLOCK
-#endif // LL_MESH_FASTTIMER_ENABLE
- 
 //get the number of bytes resident in memory for given volume
 U32 get_volume_memory_size(const LLVolume* volume)
 {
@@ -815,7 +842,7 @@ void LLMeshRepoThread::run()
 			mLODReqQ.pop();
 			LLMeshRepository::sLODProcessing--;
 			mMutex->unlock();
-			if (!fetchMeshLOD(req.mMeshParams, req.mLOD))//failed, resubmit
+			if (!fetchMeshLOD(req.mMeshParams, req.mLOD))		// failed, resubmit
 			{
 				mMutex->lock();
 				mLODReqQ.push(req) ; 
@@ -1548,6 +1575,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
+					// *NOTE:  Allowing a re-request, not marking as unavailable.  Is that correct?
 				}
 			}
 			else
@@ -2529,7 +2557,7 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
 	LLMeshRepository::sHTTPRetryCount += retries;
 
 	LLCore::HttpStatus status(response->getStatus());
-	if (! status)
+	if (! status || MESH_HTTP_RESPONSE_FAILED)
 	{
 		processFailure(status);
 		++LLMeshRepository::sHTTPErrorCount;
@@ -2600,39 +2628,37 @@ LLMeshHeaderHandler::~LLMeshHeaderHandler()
 
 void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
 {
-	if (is_retryable(status))
-	{
-		// *TODO:  This and the other processFailure() methods should
-		// probably just fail hard (as llcorehttp has done the retries).
-		// Or we could implement a slow/forever retry class.
-		
-		LL_WARNS(LOG_MESH) << "Error during mesh header handling.  Reason:  " << status.toString()
-						   << " (" << status.toHex() << ").  Retrying."
-						   << LL_ENDL;
-		LLMeshRepoThread::HeaderRequest req(mMeshParams);
-		LLMutexLock lock(gMeshRepo.mThread->mMutex);
-		gMeshRepo.mThread->mHeaderReqQ.push(req);
-	}
-	else
+	LL_WARNS(LOG_MESH) << "Error during mesh header handling.  ID:  " << mMeshParams.getSculptID()
+					   << ", Reason:  " << status.toString()
+					   << " (" << status.toHex() << ").  Not retrying."
+					   << LL_ENDL;
+
+	// Can't get the header so none of the LODs will be available
+	LLMutexLock lock(gMeshRepo.mThread->mMutex);
+	for (int i(0); i < 4; ++i)
 	{
-		// *TODO:  Mark mesh unavailable
-		LL_WARNS(LOG_MESH) << "Error during mesh header handling.  Reason:  " << status.toString()
-						   << " (" << status.toHex() << ").  Not retrying."
-						   << LL_ENDL;
+		gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i));
 	}
 }
 
 void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
 {
 	LLUUID mesh_id = mMeshParams.getSculptID();
-	bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size);
+	bool success = (! MESH_HEADER_PROCESS_FAILED) && gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size);
 	llassert(success);
 	if (! success)
 	{
-		// *TODO:  Mark mesh unavailable
-		// *TODO:  Get real reason for parse failure here
+		// *TODO:  Get real reason for parse failure here.  Might we want to retry?
 		LL_WARNS(LOG_MESH) << "Unable to parse mesh header.  ID:  " << mesh_id
+						   << ", Unknown reason.  Not retrying."
 						   << LL_ENDL;
+
+		// Can't get the header so none of the LODs will be available
+		LLMutexLock lock(gMeshRepo.mThread->mMutex);
+		for (int i(0); i < 4; ++i)
+		{
+			gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i));
+		}
 	}
 	else if (data && data_size > 0)
 	{
@@ -2708,29 +2734,18 @@ LLMeshLODHandler::~LLMeshLODHandler()
 
 void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)
 {
-	if (is_retryable(status))
-	{
-		LL_WARNS(LOG_MESH) << "Error during mesh header handling.  Reason:  " << status.toString()
-						   << " (" << status.toHex() << ").  Retrying."
-						   << LL_ENDL;
-		{
-			LLMutexLock lock(gMeshRepo.mThread->mMutex);
+	LL_WARNS(LOG_MESH) << "Error during mesh LOD handling.  ID:  " << mMeshParams.getSculptID()
+					   << ", Reason:  " << status.toString()
+					   << " (" << status.toHex() << ").  Not retrying."
+					   << LL_ENDL;
 
-			gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD);
-		}
-	}
-	else
-	{
-		// *TODO:  Mark mesh unavailable
-		LL_WARNS(LOG_MESH) << "Error during mesh LOD handling.  Reason:  " << status.toString()
-						   << " (" << status.toHex() << ").  Not retrying."
-						   << LL_ENDL;
-	}
+	LLMutexLock lock(gMeshRepo.mThread->mMutex);
+	gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
 }
 
 void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
 {
-	if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size))
+	if ((! MESH_LOD_PROCESS_FAILED) && gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size))
 	{
 		// good fetch from sim, write to VFS for caching
 		LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE);
@@ -2746,7 +2761,14 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 da
 			++LLMeshRepository::sCacheWrites;
 		}
 	}
-	// *TODO:  Mark mesh unavailable on error
+	else
+	{
+		LL_WARNS(LOG_MESH) << "Error during mesh LOD processing.  ID:  " << mMeshParams.getSculptID()
+						   << ", Unknown reason.  Not retrying."
+						   << LL_ENDL;
+		LLMutexLock lock(gMeshRepo.mThread->mMutex);
+		gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
+	}
 }
 
 LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler()
@@ -2756,29 +2778,18 @@ LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler()
 
 void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)
 {
-	if (is_retryable(status))
-	{
-		LL_WARNS(LOG_MESH) << "Error during mesh skin info handling.  Reason:  " << status.toString()
-						   << " (" << status.toHex() << ").  Retrying."
-						   << LL_ENDL;
-		{
-			LLMutexLock lock(gMeshRepo.mThread->mMutex);
+	LL_WARNS(LOG_MESH) << "Error during mesh skin info handling.  ID:  " << mMeshID
+					   << ", Reason:  " << status.toString()
+					   << " (" << status.toHex() << ").  Not retrying."
+					   << LL_ENDL;
 
-			gMeshRepo.mThread->loadMeshSkinInfo(mMeshID);
-		}
-	}
-	else
-	{
-		// *TODO:  Mark mesh unavailable on error
-		LL_WARNS(LOG_MESH) << "Error during mesh skin info handling.  Reason:  " << status.toString()
-						   << " (" << status.toHex() << ").  Not retrying."
-						   << LL_ENDL;
-	}
+	// *TODO:  Mark mesh unavailable on error.  For now, simply leave
+	// request unfulfilled rather than retry forever.
 }
 
 void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
 {
-	if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
+	if ((! MESH_SKIN_INFO_PROCESS_FAILED) && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))
 	{
 		// good fetch from sim, write to VFS for caching
 		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
@@ -2794,7 +2805,13 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S
 			file.write(data, size);
 		}
 	}
-	// *TODO:  Mark mesh unavailable on error
+	else
+	{
+		LL_WARNS(LOG_MESH) << "Error during mesh skin info processing.  ID:  " << mMeshID
+						   << ", Unknown reason.  Not retrying."
+						   << LL_ENDL;
+		// *TODO:  Mark mesh unavailable on error
+	}
 }
 
 LLMeshDecompositionHandler::~LLMeshDecompositionHandler()
@@ -2804,29 +2821,17 @@ LLMeshDecompositionHandler::~LLMeshDecompositionHandler()
 
 void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status)
 {
-	if (is_retryable(status))
-	{
-		LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling.  Reason:  " << status.toString()
-						   << " (" << status.toHex() << ").  Retrying."
-						   << LL_ENDL;
-		{
-			LLMutexLock lock(gMeshRepo.mThread->mMutex);
-
-			gMeshRepo.mThread->loadMeshDecomposition(mMeshID);
-		}
-	}
-	else
-	{
-		// *TODO:  Mark mesh unavailable on error
-		LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling.  Reason:  " << status.toString()
-						   << " (" << status.toHex() << ").  Not retrying."
-						   << LL_ENDL;
-	}
+	LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling.  ID:  " << mMeshID
+					   << ", Reason:  " << status.toString()
+					   << " (" << status.toHex() << ").  Not retrying."
+					   << LL_ENDL;
+	// *TODO:  Mark mesh unavailable on error.  For now, simply leave
+	// request unfulfilled rather than retry forever.
 }
 
 void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
 {
-	if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size))
+	if ((! MESH_DECOMP_PROCESS_FAILED) && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size))
 	{
 		// good fetch from sim, write to VFS for caching
 		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
@@ -2842,7 +2847,13 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * da
 			file.write(data, size);
 		}
 	}
-	// *TODO:  Mark mesh unavailable on error
+	else
+	{
+		LL_WARNS(LOG_MESH) << "Error during mesh decomposition processing.  ID:  " << mMeshID
+						   << ", Unknown reason.  Not retrying."
+						   << LL_ENDL;
+		// *TODO:  Mark mesh unavailable on error
+	}
 }
 
 LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler()
@@ -2852,29 +2863,16 @@ LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler()
 
 void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status)
 {
-	if (is_retryable(status))
-	{
-		LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling.  Reason:  " << status.toString()
-						   << " (" << status.toHex() << ").  Retrying."
-						   << LL_ENDL;
-		{
-			LLMutexLock lock(gMeshRepo.mThread->mMutex);
-
-			gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID);
-		}
-	}
-	else
-	{
-		// *TODO:  Mark mesh unavailable on error
-		LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling.  Reason:  " << status.toString()
-						   << " (" << status.toHex() << ").  Not retrying."
-						   << LL_ENDL;
-	}
+	LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling.  ID:  " << mMeshID
+					   << ", Reason:  " << status.toString()
+					   << " (" << status.toHex() << ").  Not retrying."
+					   << LL_ENDL;
+	// *TODO:  Mark mesh unavailable on error
 }
 
 void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size)
 {
-	if (gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size))
+	if ((! MESH_PHYS_SHAPE_PROCESS_FAILED) && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size))
 	{
 		// good fetch from sim, write to VFS for caching
 		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);
@@ -2890,7 +2888,13 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * dat
 			file.write(data, size);
 		}
 	}
-	// *TODO:  Mark mesh unavailable on error
+	else
+	{
+		LL_WARNS(LOG_MESH) << "Error during mesh physics shape processing.  ID:  " << mMeshID
+						   << ", Unknown reason.  Not retrying."
+						   << LL_ENDL;
+		// *TODO:  Mark mesh unavailable on error
+	}
 }
 
 LLMeshRepository::LLMeshRepository()
@@ -3000,7 +3004,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
 	// Manage time-to-load metrics for mesh download operations.
 	metricsProgress(1);
 
-	if (detail < 0 || detail > 4)
+	if (detail < 0 || detail >= 4)
 	{
 		return detail;
 	}
@@ -4439,32 +4443,3 @@ void teleport_started()
 	LLMeshRepository::metricsStart();
 }
 
-// *TODO:  This comes from an edit in viewer-cat.  Unify this once that's
-// available everywhere.
-bool is_retryable(LLCore::HttpStatus status)
-{
-	static const LLCore::HttpStatus cant_connect(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
-	static const LLCore::HttpStatus cant_res_proxy(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY);
-	static const LLCore::HttpStatus cant_res_host(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST);
-	static const LLCore::HttpStatus send_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR);
-	static const LLCore::HttpStatus recv_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR);
-	static const LLCore::HttpStatus upload_failed(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED);
-	static const LLCore::HttpStatus op_timedout(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT);
-	static const LLCore::HttpStatus post_error(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR);
-	static const LLCore::HttpStatus partial_file(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_PARTIAL_FILE);
-	static const LLCore::HttpStatus inv_cont_range(LLCore::HttpStatus::LLCORE, LLCore::HE_INV_CONTENT_RANGE_HDR);
-	
-	return ((! status) &&
-			((status.isHttpStatus() && status.mType >= 499 && status.mType <= 599) ||		// Include special 499 in retryables
-			 status == cant_connect ||			// Connection reset/endpoint problems
-			 status == cant_res_proxy ||		// DNS problems
-			 status == cant_res_host ||			// DNS problems
-			 status == send_error ||			// General socket problems
-			 status == recv_error ||			// General socket problems
-			 status == upload_failed ||			// Transport problem
-			 status == op_timedout ||			// Timer expired
-			 status == post_error ||			// Transport problem
-			 status == partial_file ||			// Data inconsistency in response
-			 status == inv_cont_range));		// Short data read disagrees with content-range
-}
-
-- 
cgit v1.2.3


From 2e8e40cf7974a4ab6ca13d264104dbb8b80419b7 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 11 Sep 2013 18:00:55 -0400
Subject: SH-4489 New debug/dev settings for control over new mesh behavior
 Added 'MeshUseGetMesh1' and 'MeshUseHttpRetryAfter' debug settings to control
 mesh transport behavior.  First forces the use of the legacy mesh fetch style
 with high concurrency and connection churn. The second, on by default, honors
 Retry-After values if they are reasonable.  If off or unreasonable, internal
 delay times are used.

---
 indra/llcorehttp/httpoptions.h          |  2 +-
 indra/newview/app_settings/settings.xml | 22 ++++++++++++++++++++++
 indra/newview/llmeshrepository.cpp      |  8 +++++++-
 3 files changed, 30 insertions(+), 2 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h
index f49a3555aa..4ab5ff18c4 100755
--- a/indra/llcorehttp/httpoptions.h
+++ b/indra/llcorehttp/httpoptions.h
@@ -103,7 +103,7 @@ public:
 			return mRetries;
 		}
 
-	// Default:  false
+	// Default:  true
 	void				setUseRetryAfter(bool use_retry);
 	bool				getUseRetryAfter() const
 		{
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 47a2f402af..9948f9974a 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -9979,6 +9979,28 @@
     <string>U32</string>
     <key>Value</key>
     <integer>32</integer>
+  </map>
+  <key>MeshUseHttpRetryAfter</key>
+  <map>
+    <key>Comment</key>
+    <string>If TRUE, use Retry-After response headers when rescheduling a mesh request that fails with an HTTP 503 status.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <boolean>1</boolean>
+  </map>
+  <key>MeshUseGetMesh1</key>
+  <map>
+    <key>Comment</key>
+    <string>If TRUE, use the legacy GetMesh capability for mesh download requests.</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <boolean>0</boolean>
   </map>
    <key>RunMultipleThreads</key>
     <map>
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index f6a85ac94f..507797a85d 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -743,8 +743,10 @@ LLMeshRepoThread::LLMeshRepoThread()
 	mHttpRequest = new LLCore::HttpRequest;
 	mHttpOptions = new LLCore::HttpOptions;
 	mHttpOptions->setTransferTimeout(SMALL_MESH_XFER_TIMEOUT);
+	mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter"));
 	mHttpLargeOptions = new LLCore::HttpOptions;
 	mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT);
+	mHttpLargeOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter"));
 	mHttpHeaders = new LLCore::HttpHeaders;
 	mHttpHeaders->append("Accept", "application/vnd.ll.mesh");
 	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH2);
@@ -1828,6 +1830,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,
 	mHttpRequest = new LLCore::HttpRequest;
 	mHttpOptions = new LLCore::HttpOptions;
 	mHttpOptions->setTransferTimeout(mMeshUploadTimeOut);
+	mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter"));
 	mHttpHeaders = new LLCore::HttpHeaders;
 	mHttpHeaders->append("Content-Type", "application/llsd+xml");
 	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS);
@@ -3211,13 +3214,16 @@ void LLMeshRepository::notifyLoadedMeshes()
 			
 			if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived())
 			{
+				const bool use_v1(gSavedSettings.getBOOL("MeshUseGetMesh1"));
+
 				region_name = gAgent.getRegion()->getName();
 				mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh");
 				mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2");
-				mGetMeshVersion = mGetMesh2Capability.empty() ? 1 : 2;
+				mGetMeshVersion = (mGetMesh2Capability.empty() || use_v1) ? 1 : 2;
 				LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name
 									<< "', GetMesh2:  " << mGetMesh2Capability
 									<< ", GetMesh:  " << mGetMeshCapability
+									<< ", using version:  " << mGetMeshVersion
 									<< LL_ENDL;
 			}
 		}
-- 
cgit v1.2.3


From 622eae65551df9a4ca6843a6a657777ff5e2140e Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 11 Sep 2013 19:21:31 -0400
Subject: SH-4490 More 'humane' error code presentation from llcorehttp callers
 Added toTerseString() conversion on HttpStatus to generate a string that's
 more descriptive than the hex value of the HttpStatus value but still forms a
 short, searchable token (e.g. "Http_503" or "Core_7").  Using this throughout
 the viewer now, no live cases of toHex(), I believe.

---
 indra/llcorehttp/_httplibcurl.cpp          |  4 +-
 indra/llcorehttp/_httppolicy.cpp           |  4 +-
 indra/llcorehttp/httpcommon.cpp            | 42 +++++++++++++++++++-
 indra/llcorehttp/httpcommon.h              |  8 ++++
 indra/llcorehttp/tests/test_httpstatus.hpp | 61 +++++++++++++++++++++++++++++-
 indra/newview/llmeshrepository.cpp         | 32 ++++++++--------
 indra/newview/lltexturefetch.cpp           | 12 +++---
 7 files changed, 135 insertions(+), 28 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp
index 0cb4e9d8b7..fc257fb0c1 100755
--- a/indra/llcorehttp/_httplibcurl.cpp
+++ b/indra/llcorehttp/_httplibcurl.cpp
@@ -249,7 +249,7 @@ void HttpLibcurl::cancelRequest(HttpOpRequest * op)
 	{
 		LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle:  "
 							 << static_cast<HttpHandle>(op)
-							 << ", Status:  " << op->mStatus.toHex()
+							 << ", Status:  " << op->mStatus.toTerseString()
 							 << LL_ENDL;
 	}
 
@@ -326,7 +326,7 @@ bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode
 	{
 		LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle:  "
 							 << static_cast<HttpHandle>(op)
-							 << ", Status:  " << op->mStatus.toHex()
+							 << ", Status:  " << op->mStatus.toTerseString()
 							 << LL_ENDL;
 	}
 
diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp
index ac79a77659..edaf0a5307 100755
--- a/indra/llcorehttp/_httppolicy.cpp
+++ b/indra/llcorehttp/_httppolicy.cpp
@@ -174,7 +174,7 @@ void HttpPolicy::retryOp(HttpOpRequest * op)
 						  << " retry " << op->mPolicyRetries
 						  << " scheduled in " << (delta / HttpTime(1000))
 						  << " mS (" << (external_delta ? "external" : "internal")
-						  << ").  Status:  " << op->mStatus.toHex()
+						  << ").  Status:  " << op->mStatus.toTerseString()
 						  << LL_ENDL;
 	if (op->mTracing > HTTP_TRACE_OFF)
 	{
@@ -426,7 +426,7 @@ bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op)
 		LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op)
 							 << " failed after " << op->mPolicyRetries
 							 << " retries.  Reason:  " << op->mStatus.toString()
-							 << " (" << op->mStatus.toHex() << ")"
+							 << " (" << op->mStatus.toTerseString() << ")"
 							 << LL_ENDL;
 	}
 	else if (op->mPolicyRetries)
diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp
index f2fcbf77a3..ca57a18578 100755
--- a/indra/llcorehttp/httpcommon.cpp
+++ b/indra/llcorehttp/httpcommon.cpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -174,6 +174,46 @@ std::string HttpStatus::toString() const
 	}
 	return std::string("Unknown error");
 }
+
+
+std::string HttpStatus::toTerseString() const
+{
+	std::ostringstream result;
+
+	unsigned int error_value((unsigned short) mStatus);
+	
+	switch (mType)
+	{
+	case EXT_CURL_EASY:
+		result << "Easy_";
+		break;
+		
+	case EXT_CURL_MULTI:
+		result << "Multi_";
+		break;
+		
+	case LLCORE:
+		result << "Core_";
+		break;
+
+	default:
+		if (isHttpStatus())
+		{
+			result << "Http_";
+			error_value = mType;
+		}
+		else
+		{
+			result << "Unknown_";
+		}
+		break;
+	}
+	
+	result << error_value;
+	return result.str();
+}
+
+
 		
 } // end namespace LLCore
 
diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h
index 9db884057f..a04b344a9e 100755
--- a/indra/llcorehttp/httpcommon.h
+++ b/indra/llcorehttp/httpcommon.h
@@ -380,6 +380,14 @@ struct HttpStatus
 	/// LLCore itself).
 	std::string toString() const;
 
+	/// Convert status to a compact string representation
+	/// of the form:  "<type>_<value>".  The <type> will be
+	/// one of:  Core, Http, Easy, Multi, Unknown.  And
+	/// <value> will be an unsigned integer.  More easily
+	/// interpreted than the hex representation, it's still
+	/// compact and easily searched.
+	std::string toTerseString() const;
+
 	/// Returns true if the status value represents an
 	/// HTTP response status (100 - 999).
 	bool isHttpStatus() const
diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp
index 887315befc..ae8f665f8e 100755
--- a/indra/llcorehttp/tests/test_httpstatus.hpp
+++ b/indra/llcorehttp/tests/test_httpstatus.hpp
@@ -4,7 +4,7 @@
  *
  * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2012, Linden Research, Inc.
+ * Copyright (C) 2012-2013, Linden Research, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -277,6 +277,65 @@ void HttpStatusTestObjectType::test<7>()
 	ensure(msg == "Unknown error");
 }
 
+
+template <> template <>
+void HttpStatusTestObjectType::test<8>()
+{
+	set_test_name("HttpStatus toHex() nominal function");
+	
+	HttpStatus status(404);
+	std::string msg = status.toHex();
+	// std::cout << "Result:  " << msg << std::endl;
+	ensure(msg == "01940001");
+}
+
+
+template <> template <>
+void HttpStatusTestObjectType::test<9>()
+{
+	set_test_name("HttpStatus toTerseString() nominal function");
+	
+	HttpStatus status(404);
+	std::string msg = status.toTerseString();
+	// std::cout << "Result:  " << msg << std::endl;
+	ensure("Normal HTTP 404", msg == "Http_404");
+
+	status = HttpStatus(200);
+	msg = status.toTerseString();
+	// std::cout << "Result:  " << msg << std::endl;
+	ensure("Normal HTTP 200", msg == "Http_200");
+
+	status = HttpStatus(200, HE_REPLY_ERROR);
+	msg = status.toTerseString();
+	// std::cout << "Result:  " << msg << std::endl;
+	ensure("Unsuccessful HTTP 200", msg == "Http_200");			// No distinction for error
+
+	status = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT);
+	msg = status.toTerseString();
+	// std::cout << "Result:  " << msg << std::endl;
+	ensure("Easy couldn't connect error", msg == "Easy_7");
+
+	status = HttpStatus(HttpStatus::EXT_CURL_MULTI, CURLM_OUT_OF_MEMORY);
+	msg = status.toTerseString();
+	// std::cout << "Result:  " << msg << std::endl;
+	ensure("Multi out-of-memory error", msg == "Multi_3");
+
+	status = HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_SET);
+	msg = status.toTerseString();
+	// std::cout << "Result:  " << msg << std::endl;
+	ensure("Core option not set error", msg == "Core_7");
+
+	status = HttpStatus(22000, 1);
+	msg = status.toTerseString();
+	// std::cout << "Result:  " << msg << std::endl;
+	ensure("Undecodable error", msg == "Unknown_1");
+
+	status = HttpStatus(22000, -1);
+	msg = status.toTerseString();
+	// std::cout << "Result:  " << msg << std::endl;
+	ensure("Undecodable error 65535", msg == "Unknown_65535");
+}
+
 } // end namespace tut
 
 #endif	// TEST_HTTP_STATUS_H
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 507797a85d..353e7e9a7f 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -688,7 +688,7 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content,
 	// Log details.
 	LL_WARNS(LOG_MESH) << "Error in stage:  " << stage
 					   << ", Reason:  " << status.toString()
-					   << " (" << status.toHex() << ")" << LL_ENDL;
+					   << " (" << status.toTerseString() << ")" << LL_ENDL;
 	if (content.has("error"))
 	{
 		const LLSD& err = content["error"];
@@ -1205,7 +1205,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 				{
 					LL_WARNS(LOG_MESH) << "HTTP GET request failed for skin info on mesh " << mID
 									   << ".  Reason:  " << mHttpStatus.toString()
-									   << " (" << mHttpStatus.toHex() << ")"
+									   << " (" << mHttpStatus.toTerseString() << ")"
 									   << LL_ENDL;
 					delete handler;
 					ret = false;
@@ -1298,7 +1298,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 				{
 					LL_WARNS(LOG_MESH) << "HTTP GET request failed for decomposition mesh " << mID
 									   << ".  Reason:  " << mHttpStatus.toString()
-									   << " (" << mHttpStatus.toHex() << ")"
+									   << " (" << mHttpStatus.toTerseString() << ")"
 									   << LL_ENDL;
 					delete handler;
 					ret = false;
@@ -1389,7 +1389,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 				{
 					LL_WARNS(LOG_MESH) << "HTTP GET request failed for physics shape on mesh " << mID
 									   << ".  Reason:  " << mHttpStatus.toString()
-									   << " (" << mHttpStatus.toHex() << ")"
+									   << " (" << mHttpStatus.toTerseString() << ")"
 									   << LL_ENDL;
 					delete handler;
 					ret = false;
@@ -1486,7 +1486,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
 		{
 			LL_WARNS(LOG_MESH) << "HTTP GET request failed for mesh header " << mID
 							   << ".  Reason:  " << mHttpStatus.toString()
-							   << " (" << mHttpStatus.toHex() << ")"
+							   << " (" << mHttpStatus.toTerseString() << ")"
 							   << LL_ENDL;
 			delete handler;
 			retval = false;
@@ -1568,7 +1568,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 				{
 					LL_WARNS(LOG_MESH) << "HTTP GET request failed for LOD on mesh " << mID
 									   << ".  Reason:  " << mHttpStatus.toString()
-									   << " (" << mHttpStatus.toHex() << ")"
+									   << " (" << mHttpStatus.toTerseString() << ")"
 									   << LL_ENDL;
 					delete handler;
 					retval = false;
@@ -2196,7 +2196,7 @@ void LLMeshUploadThread::doWholeModelUpload()
 			mHttpStatus = mHttpRequest->getStatus();
 		
 			LL_WARNS(LOG_MESH) << "Couldn't issue request for full model upload.  Reason:  " << mHttpStatus.toString()
-							   << " (" << mHttpStatus.toHex() << ")"
+							   << " (" << mHttpStatus.toTerseString() << ")"
 							   << LL_ENDL;
 		}
 		else
@@ -2244,7 +2244,7 @@ void LLMeshUploadThread::requestWholeModelFee()
 		mHttpStatus = mHttpRequest->getStatus();
 		
 		LL_WARNS(LOG_MESH) << "Couldn't issue request for model fee.  Reason:  " << mHttpStatus.toString()
-						   << " (" << mHttpStatus.toHex() << ")"
+						   << " (" << mHttpStatus.toTerseString() << ")"
 						   << LL_ENDL;
 	}
 	else
@@ -2285,7 +2285,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp
 		if (! status)
 		{
 			LL_WARNS(LOG_MESH) << "Upload failed.  Reason:  " << reason
-							   << " (" << status.toHex() << ")"
+							   << " (" << status.toTerseString() << ")"
 							   << LL_ENDL;
 
 			// Build a fake body for the alert generator
@@ -2349,7 +2349,7 @@ void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResp
 		if (! status)
 		{
 			LL_WARNS(LOG_MESH) << "Fee request failed.  Reason:  " << reason
-							   << " (" << status.toHex() << ")"
+							   << " (" << status.toTerseString() << ")"
 							   << LL_ENDL;
 
 			// Build a fake body for the alert generator
@@ -2584,7 +2584,7 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo
 		if (par_status != status)
 		{
 			LL_WARNS_ONCE(LOG_MESH) << "Non-206 successful status received for fetch:  "
-									<< status.toHex() << LL_ENDL;
+									<< status.toTerseString() << LL_ENDL;
 		}
 		
 		LLCore::BufferArray * body(response->getBody());
@@ -2633,7 +2633,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
 {
 	LL_WARNS(LOG_MESH) << "Error during mesh header handling.  ID:  " << mMeshParams.getSculptID()
 					   << ", Reason:  " << status.toString()
-					   << " (" << status.toHex() << ").  Not retrying."
+					   << " (" << status.toTerseString() << ").  Not retrying."
 					   << LL_ENDL;
 
 	// Can't get the header so none of the LODs will be available
@@ -2739,7 +2739,7 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)
 {
 	LL_WARNS(LOG_MESH) << "Error during mesh LOD handling.  ID:  " << mMeshParams.getSculptID()
 					   << ", Reason:  " << status.toString()
-					   << " (" << status.toHex() << ").  Not retrying."
+					   << " (" << status.toTerseString() << ").  Not retrying."
 					   << LL_ENDL;
 
 	LLMutexLock lock(gMeshRepo.mThread->mMutex);
@@ -2783,7 +2783,7 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)
 {
 	LL_WARNS(LOG_MESH) << "Error during mesh skin info handling.  ID:  " << mMeshID
 					   << ", Reason:  " << status.toString()
-					   << " (" << status.toHex() << ").  Not retrying."
+					   << " (" << status.toTerseString() << ").  Not retrying."
 					   << LL_ENDL;
 
 	// *TODO:  Mark mesh unavailable on error.  For now, simply leave
@@ -2826,7 +2826,7 @@ void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status)
 {
 	LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling.  ID:  " << mMeshID
 					   << ", Reason:  " << status.toString()
-					   << " (" << status.toHex() << ").  Not retrying."
+					   << " (" << status.toTerseString() << ").  Not retrying."
 					   << LL_ENDL;
 	// *TODO:  Mark mesh unavailable on error.  For now, simply leave
 	// request unfulfilled rather than retry forever.
@@ -2868,7 +2868,7 @@ void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status)
 {
 	LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling.  ID:  " << mMeshID
 					   << ", Reason:  " << status.toString()
-					   << " (" << status.toHex() << ").  Not retrying."
+					   << " (" << status.toTerseString() << ").  Not retrying."
 					   << LL_ENDL;
 	// *TODO:  Mark mesh unavailable on error
 }
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 141198bc16..bcb55c4bbe 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -1552,7 +1552,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 				else
 				{
 					llinfos << "HTTP GET failed for: " << mUrl
-							<< " Status: " << mGetStatus.toHex()
+							<< " Status: " << mGetStatus.toTerseString()
 							<< " Reason: '" << mGetReason << "'"
 							<< llendl;
 				}
@@ -1896,7 +1896,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe
 	LLCore::HttpStatus status(response->getStatus());
 	
 	LL_DEBUGS("Texture") << "HTTP COMPLETE: " << mID
-			 << " status: " << status.toHex()
+			 << " status: " << status.toTerseString()
 			 << " '" << status.toString() << "'"
 			 << llendl;
 //	unsigned int offset(0), length(0), full_length(0);
@@ -1912,7 +1912,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe
 		success = false;
 		std::string reason(status.toString());
 		setGetStatus(status, reason);
-		llwarns << "CURL GET FAILED, status: " << status.toHex()
+		llwarns << "CURL GET FAILED, status: " << status.toTerseString()
 				<< " reason: " << reason << llendl;
 	}
 	else
@@ -3781,7 +3781,7 @@ public:
 		else
 		{
 			LL_WARNS("Texture") << "Error delivering asset metrics to grid.  Status:  "
-								<< status.toHex()
+								<< status.toTerseString()
 								<< ", Reason:  " << status.toString() << LL_ENDL;
 		}
 	}
@@ -4470,7 +4470,7 @@ S32 LLTextureFetchDebugger::fillCurlQueue()
 
 			LL_WARNS("Texture") << "Couldn't issue HTTP request in debugger for texture "
 								<< mFetchingHistory[i].mID
-								<< ", status: " << status.toHex()
+								<< ", status: " << status.toTerseString()
 								<< " reason:  " << status.toString()
 								<< LL_ENDL;
 			mFetchingHistory[i].mCurlState = FetchEntry::CURL_DONE;
@@ -4863,7 +4863,7 @@ void LLTextureFetchDebugger::callbackHTTP(FetchEntry & fetch, LLCore::HttpRespon
 	else //failed
 	{
 		llinfos << "Fetch Debugger : CURL GET FAILED,  ID = " << fetch.mID
-				<< ", status: " << status.toHex()
+				<< ", status: " << status.toTerseString()
 				<< " reason:  " << status.toString() << llendl;
 	}
 }
-- 
cgit v1.2.3


From 99c60b83f15a3c40ecf0e5f144cfc060b37c4cf8 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 12 Sep 2013 20:15:20 -0400
Subject: SH-4489 New debug/dev settings for control over new mesh behavior
 While giving myself a full project code review, found a bug in the
 MeshUseGetMesh1 setting.  Mostly defaulted to old configuration but used the
 GetMesh2 caps which would have been a huge DoS resource sink.  Did some
 documentation maintenance as well while I was in there.  More for the to-do
 list, etc.

---
 indra/newview/app_settings/settings.xml |  4 ++--
 indra/newview/llmeshrepository.cpp      | 17 +++++++++++------
 2 files changed, 13 insertions(+), 8 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 9948f9974a..60fc2d349d 100755
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -9983,7 +9983,7 @@
   <key>MeshUseHttpRetryAfter</key>
   <map>
     <key>Comment</key>
-    <string>If TRUE, use Retry-After response headers when rescheduling a mesh request that fails with an HTTP 503 status.</string>
+    <string>If TRUE, use Retry-After response headers when rescheduling a mesh request that fails with an HTTP 503 status.  Static.</string>
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
@@ -9994,7 +9994,7 @@
   <key>MeshUseGetMesh1</key>
   <map>
     <key>Comment</key>
-    <string>If TRUE, use the legacy GetMesh capability for mesh download requests.</string>
+    <string>If TRUE, use the legacy GetMesh capability for mesh download requests.  Semi-dynamic (read at region crossings).</string>
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 353e7e9a7f..c3d149db9a 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -282,10 +282,15 @@
 //     dialog.  Get the structured information going into the log into a
 //     tree there.
 //   * Header parse failures come without much explanation.  Elaborate.
-//   * Need a final failure state for requests that are retried and just won't
-//     complete.  We can fail a LOD request, others we don't.
-
-
+//   * Work queue for uploads?  Any need for this or is the current scheme good
+//     enough?
+//   * Various temp buffers used in VFS I/O might be allocated once or even
+//     statically.  Look for some wins here.
+//   * Move data structures holding mesh data used by main thread into main-
+//     thread-only access so that no locking is needed.  May require duplication
+//     of some data so that worker thread has a minimal data set to guide
+//     operations.
+//
 // --------------------------------------------------------------------------
 //                    Development/Debug/QA Tools
 //
@@ -1052,7 +1057,7 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
 	
 	if (gAgent.getRegion())
 	{
-		if (! gMeshRepo.mGetMesh2Capability.empty())
+		if (! gMeshRepo.mGetMesh2Capability.empty() && gMeshRepo.mGetMeshVersion > 1)
 		{
 			http_url = gMeshRepo.mGetMesh2Capability;
 		}
@@ -2181,7 +2186,7 @@ void LLMeshUploadThread::doWholeModelUpload()
 		LLCore::BufferArray * ba = new LLCore::BufferArray;
 		LLCore::BufferArrayStream bas(ba);
 		LLSDSerialize::toXML(body, bas);
-		// LLSDSerialize::toXML(mModelData, bas);		// <- This will generate a convenient upload error
+		// LLSDSerialize::toXML(mModelData, bas);		// <- Enabling this will generate a convenient upload error
 		LLCore::HttpHandle handle = mHttpRequest->requestPost(mHttpPolicyClass,
 															  mHttpPriority,
 															  mWholeModelUploadURL,
-- 
cgit v1.2.3


From dab920c26b36e032876592ca827a3a31f067a9ba Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Tue, 17 Sep 2013 21:55:44 +0000
Subject: SH-4492 Create a useful README for llcorehttp. First edit complete. 
 Use the library in 15 minutes.  Describe the code.  Refinements to the
 initial try.  Describe that code.  Still to do:  more refinements, how to
 choose a policy class, FAQ.

---
 indra/llcorehttp/README.Linden | 467 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 467 insertions(+)
 create mode 100644 indra/llcorehttp/README.Linden

(limited to 'indra')

diff --git a/indra/llcorehttp/README.Linden b/indra/llcorehttp/README.Linden
new file mode 100644
index 0000000000..e5ff824388
--- /dev/null
+++ b/indra/llcorehttp/README.Linden
@@ -0,0 +1,467 @@
+
+
+
+1.  HTTP fetching in 15 Minutes
+
+    Let's start with a trivial working example.  You'll need a throwaway
+    build of the viewer.  And we'll use indra/newview/llappviewer.cpp as
+    our host.
+
+    Add some needed headers:
+
+
+        #include "httpcommon.h"
+        #include "httprequest.h"
+        #include "httphandler.h"
+
+
+    You'll need to derive a class from HttpHandler (not HttpHandle).
+    This is used to deliver notifications of HTTP completion to your
+    code.  Place it near the top, before LLDeferredTaskList, say:
+
+
+        class MyHandler : public LLCore::HttpHandler
+        {
+        public:
+            MyHandler()
+            : LLCore::HttpHandler()
+            {}
+
+            virtual void onCompleted(LLCore::HttpHandle /* handle */,
+                                     LLCore::HttpResponse * /* response */)
+            {
+                LL_INFOS("Hack") << "It is happening again." << LL_ENDL;
+
+                delete this;    // Last statement
+            }
+        };
+
+
+    Add some statics up there as well:
+
+
+        // Our request object.  Allocate during initialiation.
+        static LLCore::HttpRequest * my_request(NULL);
+
+        // The policy class for HTTP traffic.
+        // Use HttpRequest::DEFAULT_POLICY_ID, but DO NOT SHIP WITH THIS VALUE!!
+        static LLCore::HttpRequest::policy_t my_policy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+
+        // Priority for HTTP requests.  Use 0U.
+        static LLCore::HttpRequest::priority_t my_priority(0U);
+
+
+    In LLAppViewer::init() after mAppCoreHttp.init(), create a request object:
+
+
+        my_request = new LLCore::HttpRequest();
+
+
+    In LLAppViewer::mainLoop(), just before entering the while loop,
+    we'll kick off one HTTP request:
+
+
+        // Construct a handler object (we'll use the heap this time):
+        MyHandler * my_handler = new MyHandler;
+
+        // Issue a GET request to 'http://www.example.com/' kicking off
+        // all the I/O, retry logic, etc.
+        LLCore::HttpHandle handle;
+        handle = my_request->requestGet(my_policy,
+                                        my_priority,
+                                        "http://www.example.com/",
+                                        NULL,
+                                        NULL,
+                                        my_handler);
+        if (LLCORE_HTTP_HANDLE_INVALID == handle)
+        {
+            LL_WARNS("Hack") << "Failed to launch HTTP request.  Try again."
+                             << LL_ENDL;
+        }
+
+
+    Finally, arrange to periodically call update() on the request object
+    to find out when the request completes.  This will be done by
+    calling the onCompleted() method with status information and
+    response data from the HTTP operation.  Add this to the
+    LLAppViewer::idle() method after the ping:
+
+
+        my_request->update(0);
+
+
+    That's it.  Build it, run it and watch the log file.  You should get
+    the "It is happening again." message indicating that the HTTP
+    operation completed in some manner.
+
+
+2.  What Does All That Mean
+
+    MyHandler/HttpHandler.  This class replaces the Responder-style in
+    legacy code.  One method is currently defined.  It is used for all
+    request completions, successful or failed:
+
+
+        void onCompleted(LLCore::HttpHandle /* handle */,
+                         LLCore::HttpResponse * /* response */);
+
+
+    The onCompleted() method is invoked as a callback during calls to
+    HttpRequest::update().  All I/O is completed asynchronously in
+    another thread.  But notifications are polled by calling update()
+    and invoking a handler for completed requests.
+
+    In this example, the invocation also deletes the handler (which is
+    never referenced by the llcorehttp code again).  But other
+    allocation models are possible including handlers shared by many
+    requests, stack-based handlers and handlers mixed in with other,
+    unrelated classes.
+
+    LLCore::HttpRequest().  Instances of this class are used to request
+    all major functions of the library.  Initialization, starting
+    requests, delivering final notification of completion and various
+    utility operations are all done via instances.  There is one very
+    important rule for instances:
+
+        Request objects may NOT be shared between threads.
+
+    my_priority.  The APIs support the idea of priority ordering of
+    requests but it hasn't been implemented and the hope is that this
+    will become useless and removed from the interface.  Use 0U except
+    as noted.
+
+    my_policy.  This is an important one.  This library attempts to
+    manage TCP connection usage more rigorously than in the past.  This
+    is done by issuing requests to a queue that has various settable
+    properties.  These establish connection usage for the queue as well
+    as how queues compete with one another.  (This is patterned after
+    class-based queueing used in various networking stacks.)  Several
+    classes are pre-defined.  Deciding when to use an existing class and
+    when to create a new one will determine what kind of experience
+    users have.  We'll pick up this question in detail below.
+
+    requestGet().  Issues an ordinary HTTP GET request to a given URL
+    and associating the request with a policy class, a priority and an
+    response handler.  Two additional arguments, not used here, allow
+    for additional headers on the request and for per-request options.
+    If successful, the call returns a handle whose value is other than
+    LLCORE_HTTP_HANDLE_INVALID.  The HTTP operation is then performed
+    asynchronously by another thread without any additional work by the
+    caller.  If the handle returned is invalid, you can get the status
+    code by calling my_request->getStatus().
+
+    update().  To get notification that the request has completed, a
+    call to update() will invoke onCompleted() methods.
+
+
+3.  Refinements, Necessary and Otherwise
+
+    MyHandler::onCompleted().  You'll want to do something useful with
+    your response.  Distinguish errors from successes and getting the
+    response body back in some form.
+
+    Add a new header:
+
+
+        #include "bufferarray.h"
+
+
+    Replace the existing MyHandler::onCompleted() definition with:
+
+
+        virtual void onCompleted(LLCore::HttpHandle /* handle */,
+                                 LLCore::HttpResponse * response)
+        {
+            LLCore::HttpStatus status = response->getStatus();
+            if (status)
+            {
+                // Successful request.  Try to fetch the data
+                LLCore::BufferArray * data = response->getBody();
+
+                if (data && data->size())
+                {
+                    // There's some data.  A BufferArray is a linked list
+                    // of buckets.  We'll create a linear buffer and copy
+                    // it into it.
+                    size_t data_len = data->size();
+                    char * data_blob = new char [data_len + 1];
+                    data->read(0, data_blob, data_len);
+                    data_blob[data_len] = '\0';
+
+                    // Process the data now in NUL-terminated string.
+                    // Needs more scrubbing but this will do.
+                    LL_INFOS("Hack") << "Received:  " << data_blob << LL_ENDL;
+
+                    // Free the temporary data
+                    delete [] data_blob;
+                }
+            }
+            else
+            {
+                // Something went wrong.  Translate the status to
+                // a meaningful message.
+                LL_WARNS("Hack") << "HTTP GET failed.  Status:  "
+                                 << status.toTerseString()
+                                 << ", Reason:  " << status.toString()
+                                 << LL_ENDL;
+            }           
+
+            delete this;    // Last statement
+        }
+
+
+    HttpHeaders.  The header file "httprequest.h" documents the expected
+    important headers that will go out with the request.  You can add to
+    these by including an HttpHeaders object with the requestGet() call.
+    These are typically setup once as part of init rather than
+    dynamically created.
+
+    Add another header:
+
+
+        #include "httpheaders.h"
+
+
+    In LLAppViewer::mainLoop(), add this alongside the allocation of
+    my_handler:
+
+
+        // Additional headers for all requests
+        LLCore::HttpHeaders * my_headers = new LLCore::HttpHeaders();
+        my_headers->append("Accept", "text/html, application/llsd+xml");
+
+
+    HttpOptions.  Options are similar and include a mix of value types.
+    One interesting per-request option is the trace setting.  This
+    enables various debug-type messages in the log file that show the
+    progress of the request through the library.  It takes values from
+    zero to three with higher values giving more verbose logging.  We'll
+    use '2' and this will also give us a chance to verify that
+    HttpHeaders works as expected.
+
+    Same as above, a new header:
+
+
+        #include "httpoptions.h"
+
+
+    And in LLAppView::mainLoop():
+
+
+        // Special options for requests
+        LLCore::HttpOptions * my_options = new LLCore::HttpOptions();
+        my_options->setTrace(2);
+
+
+    Now let's put that all together into a more complete requesting
+    sequence.  Replace the existing invocation of requestGet() with this
+    slightly more elaborate block:
+
+
+        LLCore::HttpHandle handle;
+        handle = my_request->requestGet(my_policy,
+                                        my_priority,
+                                        "http://www.example.com/",
+                                        my_options,
+                                        my_headers,
+                                        my_handler);
+        if (LLCORE_HTTP_HANDLE_INVALID == handle)
+        {
+             LLCore::HttpStatus status = my_request->getStatus();
+
+             LL_WARNS("Hack") << "Failed to request HTTP GET.  Status:  "
+                              << status.toTerseString()
+                              << ", Reason:  " << status.toString()
+                              << LL_ENDL;
+
+             delete my_handler;    // No longer needed.
+             my_handler = NULL;
+        }
+
+
+    Build, run and examine the log file.  You'll get some new data with
+    this run.  First, you should get the www.example.com home page
+    content:
+
+
+----------------------------------------------------------------------------
+2013-09-17T20:26:51Z INFO: MyHandler::onCompleted: Received:  <!doctype html>
+<html>
+<head>
+    <title>Example Domain</title>
+
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type="text/css">
+    body {
+        background-color: #f0f0f2;
+        margin: 0;
+        padding: 0;
+        font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+        
+    }
+    div {
+        width: 600px;
+        margin: 5em auto;
+        padding: 50px;
+        background-color: #fff;
+        border-radius: 1em;
+    }
+    a:link, a:visited {
+        color: #38488f;
+        text-decoration: none;
+    }
+    @media (max-width: 700px) {
+        body {
+            background-color: #fff;
+        }
+        div {
+            width: auto;
+            margin: 0 auto;
+            border-radius: 0;
+            padding: 1em;
+        }
+    }
+    </style>    
+</head>
+
+<body>
+<div>
+    <h1>Example Domain</h1>
+    <p>This domain is established to be used for illustrative examples in documents. You may use this
+    domain in examples without prior coordination or asking for permission.</p>
+    <p><a href="http://www.iana.org/domains/example">More information...</a></p>
+</div>
+</body>
+</html>
+----------------------------------------------------------------------------
+
+
+    You'll also get a detailed trace of the HTTP operation itself.  Note
+    the HEADEROUT line which shows the additional header added to the
+    request.
+
+
+----------------------------------------------------------------------------
+HttpService::processRequestQueue: TRACE, FromRequestQueue, Handle:  086D3148
+HttpLibcurl::addOp: TRACE, ToActiveQueue, Handle:  086D3148, Actives:  0, Readies:  0
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  TEXT, Data:  About to connect() to www.example.com port 80 (#0) 
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  TEXT, Data:    Trying 93.184.216.119... 
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  TEXT, Data:  Connected to www.example.com (93.184.216.119) port 80 (#0) 
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  TEXT, Data:  Connected to www.example.com (93.184.216.119) port 80 (#0) 
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADEROUT, Data:  GET / HTTP/1.1  Host: www.example.com  Accept-Encoding: deflate, gzip  Connection: keep-alive  Keep-alive: 300  Accept: text/html, application/llsd+xml
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  HTTP/1.1 200 OK  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Accept-Ranges: bytes  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Cache-Control: max-age=604800  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Content-Type: text/html  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Date: Tue, 17 Sep 2013 20:26:56 GMT  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Etag: "3012602696"  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Expires: Tue, 24 Sep 2013 20:26:56 GMT  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Server: ECS (ewr/1590)  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  X-Cache: HIT  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  x-ec-custom-error: 1  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:  Content-Length: 1270  
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  HEADERIN, Data:    
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  DATAIN, Data:  256 Bytes
+HttpOpRequest::debugCallback: TRACE, LibcurlDebug, Handle:  086D3148, Type:  TEXT, Data:  Connection #0 to host www.example.com left intact 
+HttpLibcurl::completeRequest: TRACE, RequestComplete, Handle:  086D3148, Status:  Http_200
+HttpOperation::addAsReply: TRACE, ToReplyQueue, Handle:  086D3148
+----------------------------------------------------------------------------
+
+
+4.  What Does All That Mean, Part 2
+
+    HttpStatus.  The HttpStatus object encodes errors from libcurl, the
+    library itself and HTTP status values.  It does this to avoid
+    collapsing all non-HTTP error into a single '499' HTTP status and to
+    make errors distinct.
+
+    To aid programming, the usual bool conversions are available so that
+    you can write 'if (status)' and the expected thing will happen
+    whether it's an HTTP, libcurl or library error.  There's also
+    provision to override the treatment of HTTP errors (making 404 a
+    success, say).
+
+    Share data, don't copy it.  The library was started with the goal of
+    avoiding data copies as much as possible.  Instead, read-only data
+    sharing across threads with atomic reference counts is used for a
+    number of data types.  These currently are:
+
+        * BufferArray.  Linked list of data blocks/HTTP bodies.
+        * HttpHeaders.  Shared headers for both requests and responses.
+        * HttpOptions.  Request-only data modifying HTTP behavior.
+        * HttpResponse.  HTTP response description given to onCompleted.
+
+    Using objects of these types requires a few rules:
+
+        * Constructor always gives a reference to caller.
+        * References are dropped with release() not delete.
+        * Additional references may be taken out with addRef().
+        * Unless otherwise stated, once an object is shared with another
+          thread it should be treated as read-only.  There's no
+          synchronization on the objects themselves.
+
+    HttpResponse.  You'll encounter this mainly in onCompleted() methods.
+    Commonly-used interfaces on this object:
+
+        * getStatus() to return the final status of the request.
+        * getBody() to retrieve the response body which may be NULL or
+          zero-length.
+        * getContentType() to return the value of the 'Content-Type'
+          header or an empty string if none was sent.
+
+    This is a reference-counted object so you can call addRef() on it
+    and hold onto the response for an arbitrary time.  But you'll
+    usually just call a few methods and return from onCompleted() whose
+    caller will release the object.
+
+    BufferArray.  The core data representation for request and response
+    bodies.  In HTTP responses, it's fetched with the getBody() method
+    and may be NULL or non-NULL but zero length.  All successful data
+    handling should check both conditions before attempting to fetch
+    data from the object.  Data access model uses simple read/write
+    semantics:
+
+        * append()
+        * size()
+        * read()
+        * write()
+
+    There is a more sophisticated stream adapter that extends these
+    methods and will be covered below.  So, one way to retrieve data
+    from a request is as follows:
+
+
+        LLCore::BufferArray * data = response->getBody();
+        if (data && data->size())
+        {
+            size_t data_len = data->size();
+            char * data_blob = new char [data_len + 1];
+            data->read(0, data_blob, data_len);
+
+
+    HttpOptions and HttpResponse.  Really just simple containers of POD
+    and std::string pairs.  But reference counted and the rule about not
+    modifying after sharing must be followed.  You'll have the urge to
+    change options dynamically at some point.  And you'll try to do that
+    by just writing new values to the shared object.  And in tests
+    everything will appear to work.  Then you ship and people in the
+    real world start hitting read/write races in strings and then crash.
+
+    HttpHandle.  Uniquely identifies a request and can be used to
+    identify it in an onCompleted() method or cancel it if it's still
+    queued.  But as soon as a request's onCompleted() invocation
+    returns, the handle becomes invalid and may be reused immediately
+    for new requests.  Don't hold on to handles after notification.
+
+
+5.  And Still More Refinements
+
+
+6.  Choosing a Policy Class
+
+
+7.  FAQ
+
-- 
cgit v1.2.3


From 195d319f65239238577ae15c22188da3839ae5cf Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 18 Sep 2013 18:44:41 -0400
Subject: SH-4492  Create a useful README for llcorehttp Last bit for this
 release.  Describe stream adapters and how to select a policy class.  Slight
 changes to setup code to make reality reflect documentation.

---
 indra/llcorehttp/README.Linden  | 205 ++++++++++++++++++++++++++++++++++++++--
 indra/newview/llappcorehttp.cpp |  38 ++++----
 indra/newview/llappcorehttp.h   |  97 +++++++++++++++++++
 3 files changed, 316 insertions(+), 24 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/README.Linden b/indra/llcorehttp/README.Linden
index e5ff824388..8d18ed1a11 100644
--- a/indra/llcorehttp/README.Linden
+++ b/indra/llcorehttp/README.Linden
@@ -1,13 +1,13 @@
 
 
 
-1.  HTTP fetching in 15 Minutes
+1.  HTTP Fetching in 15 Minutes
 
     Let's start with a trivial working example.  You'll need a throwaway
     build of the viewer.  And we'll use indra/newview/llappviewer.cpp as
-    our host.
+    the host module for these hacks.
 
-    Add some needed headers:
+    First, add some headers:
 
 
         #include "httpcommon.h"
@@ -182,7 +182,7 @@
                 {
                     // There's some data.  A BufferArray is a linked list
                     // of buckets.  We'll create a linear buffer and copy
-                    // it into it.
+                    // the data into it.
                     size_t data_len = data->size();
                     char * data_blob = new char [data_len + 1];
                     data->read(0, data_blob, data_len);
@@ -419,7 +419,7 @@ HttpOperation::addAsReply: TRACE, ToReplyQueue, Handle:  086D3148
 
     BufferArray.  The core data representation for request and response
     bodies.  In HTTP responses, it's fetched with the getBody() method
-    and may be NULL or non-NULL but zero length.  All successful data
+    and may be NULL or non-NULL with zero length.  All successful data
     handling should check both conditions before attempting to fetch
     data from the object.  Data access model uses simple read/write
     semantics:
@@ -429,8 +429,8 @@ HttpOperation::addAsReply: TRACE, ToReplyQueue, Handle:  086D3148
         * read()
         * write()
 
-    There is a more sophisticated stream adapter that extends these
-    methods and will be covered below.  So, one way to retrieve data
+    (There is a more sophisticated stream adapter that extends these
+    methods and will be covered below.)  So, one way to retrieve data
     from a request is as follows:
 
 
@@ -449,6 +449,7 @@ HttpOperation::addAsReply: TRACE, ToReplyQueue, Handle:  086D3148
     by just writing new values to the shared object.  And in tests
     everything will appear to work.  Then you ship and people in the
     real world start hitting read/write races in strings and then crash.
+    Don't be lazy.
 
     HttpHandle.  Uniquely identifies a request and can be used to
     identify it in an onCompleted() method or cancel it if it's still
@@ -459,9 +460,197 @@ HttpOperation::addAsReply: TRACE, ToReplyQueue, Handle:  086D3148
 
 5.  And Still More Refinements
 
+    (Note: The following refinements are just code fragments.  They
+    don't directly fit into the working example above.  But they
+    demonstrate several idioms you'll want to copy.)
 
-6.  Choosing a Policy Class
+    LLSD, std::streambuf, std::iostream.  The read(), write() and
+    append() methods may be adequate for your purposes.  But we use a
+    lot of LLSD.  Its interfaces aren't particularly compatible with
+    BufferArray.  And so two adapters are available to give
+    stream-like behaviors:  BufferArrayStreamBuf and BufferArrayStream,
+    which implement the std::streambuf and std::iostream interfaces,
+    respectively.
+
+    A std::streambuf interface isn't something you'll want to use
+    directly.  Instead, you'll use the much friendlier std::iostream
+    interface found in BufferArrayStream.  This adapter gives you all
+    the '>>' and '<<' operators you'll want as well as working
+    directly with the LLSD conversion operators.
+
+    Some new headers:
+
+
+        #include "bufferstream.h"
+        #include "llsdserialize.h"
+
+
+    And an updated fragment based on onCompleted() above:
+
+
+                // Successful request.  Try to fetch the data
+                LLCore::BufferArray * data = response->getBody();
+                LLSD resp_llsd;
+
+                if (data && data->size())
+                {
+                    // There's some data and we expect this to be
+                    // LLSD.  Checking of content type and validation
+                    // during parsing would be admirable additions.
+                    // But we'll forgo that now.
+                    LLCore::BufferArrayStream data_stream(data);
+                    LLSDSerialize::fromXML(resp_llsd, data_stream);
+                }
+                LL_INFOS("Hack") << "LLSD Received:  " << resp_llsd << LL_ENDL;
+            }
+            else
+            {
+
+
+    Converting an LLSD object into an XML stream stored in a
+    BufferArray is just the reverse of the above:
 
 
+        BufferArray * data = new BufferArray();
+        LLCore::BufferArrayStream data_stream(data);
+
+        LLSD src_llsd;
+        src_llsd["foo"] = "bar";
+
+        LLSDSerialize::toXML(src_llsd, data_stream);
+
+        // 'data' now contains an XML payload and can be sent
+        // to a web service using the requestPut() or requestPost()
+        //  methods.
+        ...  requestPost(...);
+
+        // And don't forget to release the BufferArray.
+        data->release();
+        data = NULL;
+
+
+    LLSD will often go hand-in-hand with BufferArray and data
+    transport.  But you can also do all the streaming I/O you'd expect
+    of a std::iostream object:
+
+
+        BufferArray * data = new BufferArray();
+        LLCore::BufferArrayStream data_stream(data);
+
+        data_stream << "Hello, World!" << 29.4 << '\n';
+        std::string str;
+        data_stream >> str;
+        std::cout << str << std::endl;
+
+        data->release();
+        // Actual delete will occur when 'data_stream'
+        // falls out of scope and is destructed.
+
+
+    Scoping objects and cleaning up.  The examples haven't bothered
+    with cleanup of objects that are no longer needed.  Instead, most
+    objects have been allocated as if they were global and eternal.
+    You'll put the objects in more appropriate feature objects and
+    clean them up as a group.  Here's a checklist for actions you may
+    need to take on cleanup:
+
+    * Call delete on:
+      o HttpHandlers created on the heap
+      o HttpRequest objects
+    * Call release() on:
+      o BufferArray objects
+      o HttpHeaders objects
+      o HttpOptions objects
+      o HttpResponse objects
+
+    On program exit, as threads wind down, the library continues to
+    operate safely.  Threads don't interact via the library and even
+    dangling references to HttpHandler objects are safe.  If you don't
+    call HttpRequest::update(), handler references are never
+    dereferenced.
+
+    You can take a more thorough approach to wind-down.  Keep a list
+    of HttpHandles (not HttpHandlers) of outstanding requests.  For
+    each of these, call HttpRequest::requestCancel() to cancel the
+    operation.  (Don't add the cancel requests' handled to the list.)
+    This will cancel the outstanding requests that haven't completed.
+    Canceled or completed, all requests will queue notifications.  You
+    can now cycle calling update() discarding responses.  Continue
+    until all requests notify or a few seconds have passed.
+
+    Global startup and shutdown is handled in the viewer.  But you can
+    learn about it in the code or in the documentation in the headers.
+
+
+6.  Choosing a Policy Class
+
+    Now it's time to get rid of the default policy class.  Take a look
+    at the policy class definitions in newview/llappcorehttp.h.
+    Ideally, you'll find one that's compatible with what you're doing.
+    Some of the compatibility guidelines are:
+
+    * Destination: Pair of host and port.  Mixing requests with
+      different destinations may cause more connection setup and tear
+      down.
+
+    * Method: http or https.  Usually moot given destination.  But
+      mixing these may also cause connection churn.
+
+    * Transfer size: If you're moving 100MB at a time and you make your
+      requests to the same policy class as a lot of small, fast event
+      information that fast traffic is going to get stuck behind you
+      and someone's experience is going to be miserable.
+
+    * Long poll requests: These are long-lived, must- do operations.
+      They have a special home called AP_LONG_POLL.
+
+    * Concurrency: High concurrency (5 or more) and large transfer
+      sizes are incompatible.  Another head-of-the-line problem.  High
+      concurrency is tolerated when it's desired to get maximal
+      throughput.  Mesh and texture downloads, for example.
+
+    * Pipelined: If your requests are not idempotent, stay away from
+      anything marked 'soon' or 'yes'.  Hidden retries may be a
+      problem for you.  For now, would also recommend keeping PUT and
+      POST requests out of classes that may be pipelined.  Support for
+      that is still a bit new.
+
+    If you haven't found a compatible match, you can either create a
+    new class (llappcorehttp.*) or just use AP_DEFAULT, the catchall
+    class when all else fails.  Inventory query operations might be a
+    candidate for a new class that supported pipelining on https:.
+    Same with display name lookups and other bursty-at-login
+    operations.  For other things, AP_DEFAULT will do what it can and
+    will, in some way or another, tolerate any usage.  Whether the
+    users' experiences are good are for you to determine.
+
+    
 7.  FAQ
 
+    Q1.  What do these policy classes achieve?
+
+    A1.  Previously, HTTP-using code in the viewer was written as if
+    it were some isolated, local operation that didn't have to
+    consider resources, contention or impact on services and the
+    larger environment.  The result was an application with on the
+    order of 100 HTTP launch points in its codebase that could create
+    dozens or even 100's of TCP connections zeroing in on grid
+    services and disrupting networking equipment, web services and
+    innocent users.  The use of policy classes (modeled on
+    http://en.wikipedia.org/wiki/Class-based_queueing) is a means to
+    restrict connection concurrency, good and necessary in itself.  In
+    turn, that reduces demands on an expensive resource (connection
+    setup and concurrency) which relieves strain on network points.
+    That enables connection keepalive and opportunites for true
+    improvements in throughput and user experience.
+
+    Another aspect of the classes is that they give some control over
+    how competing demands for the network will be apportioned.  If
+    mesh fetches, texture fetches and inventory queries are all being
+    made at once, the relative weights of their classes' concurrency
+    limits established that apportioning.  We now have an opportunity
+    to balance the entire viewer system.
+
+    Q2.  How's that data sharing with refcounts working for you?
+
+    A2.  Meh.
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index 01317fe32f..70dcffefb2 100755
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -51,6 +51,11 @@ static const struct
 	const char *				mUsage;
 } init_data[] =					//  Default and dynamic values for classes
 {
+	{
+		LLAppCoreHttp::AP_DEFAULT,			8,		8,		8,		0,
+		"",
+		"other"
+	},
 	{
 		LLAppCoreHttp::AP_TEXTURE,			8,		1,		12,		0,
 		"TextureFetchConcurrency",
@@ -75,6 +80,11 @@ static const struct
 		LLAppCoreHttp::AP_UPLOADS,			2,		1,		8,		0,
 		"",
 		"asset upload"
+	},
+	{
+		LLAppCoreHttp::AP_LONG_POLL,		32,		32,		32,		0,
+		"",
+		"long poll"
 	}
 };
 
@@ -154,25 +164,21 @@ void LLAppCoreHttp::init()
 	{
 		const EAppPolicy policy(init_data[i].mPolicy);
 
-		// Create a policy class but use default for texture for now.
-		// This also has the side-effect of initializing the default
-		// class to desired values.
-		if (AP_TEXTURE == policy)
+		if (AP_DEFAULT == policy)
 		{
-			mPolicies[policy] = mPolicies[AP_DEFAULT];
+			// Pre-created
+			continue;
 		}
-		else
+
+		mPolicies[policy] = LLCore::HttpRequest::createPolicyClass();
+		if (! mPolicies[policy])
 		{
-			mPolicies[policy] = LLCore::HttpRequest::createPolicyClass();
-			if (! mPolicies[policy])
-			{
-				// Use default policy (but don't accidentally modify default)
-				LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage
-								 << ".  Using default policy."
-								 << LL_ENDL;
-				mPolicies[policy] = mPolicies[AP_DEFAULT];
-				continue;
-			}
+			// Use default policy (but don't accidentally modify default)
+			LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage
+							 << ".  Using default policy."
+							 << LL_ENDL;
+			mPolicies[policy] = mPolicies[AP_DEFAULT];
+			continue;
 		}
 	}
 
diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h
index 6dc3bb2130..40e3042b84 100755
--- a/indra/newview/llappcorehttp.h
+++ b/indra/newview/llappcorehttp.h
@@ -45,12 +45,109 @@ public:
 
 	enum EAppPolicy
 	{
+		/// Catchall policy class.  Not used yet
+		/// but will have a generous concurrency
+		/// limit.  Deep queueing possible by having
+		/// a chatty HTTP user.
+		///
+		/// Destination:     anywhere
+		/// Protocol:        http: or https:
+		/// Transfer size:   KB-MB
+		/// Long poll:       no
+		/// Concurrency:     high 
+		/// Request rate:    unknown
+		/// Pipelined:       no
 		AP_DEFAULT,
+
+		/// Texture fetching policy class.  Used to
+		/// download textures via capability or SSA
+		/// baking service.  Deep queueing of requests.
+		/// Do not share.
+		///
+		/// Destination:     simhost:12046 & bake-texture:80
+		/// Protocol:        http:
+		/// Transfer size:   KB-MB
+		/// Long poll:       no
+		/// Concurrency:     high
+		/// Request rate:    high
+		/// Pipelined:       soon
 		AP_TEXTURE,
+
+		/// Legacy mesh fetching policy class.  Used to
+		/// download textures via 'GetMesh' capability.
+		/// To be deprecated.  Do not share.
+		///
+		/// Destination:     simhost:12046
+		/// Protocol:        http:
+		/// Transfer size:   KB-MB
+		/// Long poll:       no
+		/// Concurrency:     dangerously high
+		/// Request rate:    high
+		/// Pipelined:       no
 		AP_MESH1,
+
+		/// New mesh fetching policy class.  Used to
+		/// download textures via 'GetMesh2' capability.
+		/// Used when fetch request (typically one LOD)
+		/// is 'small', currently defined as 2MB.
+		/// Very deeply queued.  Do not share.
+		///
+		/// Destination:     simhost:12046
+		/// Protocol:        http:
+		/// Transfer size:   KB-MB
+		/// Long poll:       no
+		/// Concurrency:     high
+		/// Request rate:    high
+		/// Pipelined:       soon
 		AP_MESH2,
+
+		/// Large mesh fetching policy class.  Used to
+		/// download textures via 'GetMesh' or 'GetMesh2'
+		/// capability.  Used when fetch request
+		/// is not small to avoid head-of-line problem
+		/// when large requests block a sequence of small,
+		/// fast requests.  Can be shared with similar
+		/// traffic that can wait for longish stalls
+		/// (default timeout 600S).
+		///
+		/// Destination:     simhost:12046
+		/// Protocol:        http:
+		/// Transfer size:   MB
+		/// Long poll:       no
+		/// Concurrency:     low
+		/// Request rate:    low
+		/// Pipelined:       soon
 		AP_LARGE_MESH,
+
+		/// Asset upload policy class.  Used to store
+		/// assets (mesh only at the moment) via
+		/// changeable URL.  Responses may take some
+		/// time (default timeout 240S).
+		///
+		/// Destination:     simhost:12043
+		/// Protocol:        https:
+		/// Transfer size:   KB-MB
+		/// Long poll:       no
+		/// Concurrency:     low
+		/// Request rate:    low
+		/// Pipelined:       no
 		AP_UPLOADS,
+
+		/// Long-poll-type HTTP requests.  Not
+		/// bound by a connection limit.  Requests
+		/// will typically hang around for a long
+		/// time (~30S).  Only shareable with other
+		/// long-poll requests.
+		///
+		/// Destination:     simhost:12043
+		/// Protocol:        https:
+		/// Transfer size:   KB
+		/// Long poll:       yes
+		/// Concurrency:     unlimited but low in practice
+		/// Request rate:    low
+		/// Pipelined:       no
+		AP_LONG_POLL,
+
 		AP_COUNT						// Must be last
 	};
 	
-- 
cgit v1.2.3


From a232fa4d9f4bf8f29d2783da8c2e01f7c9f80fd9 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 19 Sep 2013 19:20:31 -0400
Subject: SH-4516  Fix a subset of thread races in llmeshrepository Much
 earlier identified some thread races including two on mskininfoq and
 mdecompositionq.  I actually hit one in testing the other day so I'm fixing
 them now.  Put them under the mMutex lock and use the mutex in such a way
 that main thread stalls are not added.

---
 indra/newview/llmeshrepository.cpp | 49 ++++++++++++++++++++++++++++----------
 1 file changed, 36 insertions(+), 13 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index c3d149db9a..5174a7af00 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -246,10 +246,10 @@
 //     mMeshHeader              mHeaderMutex  rw.repo.mHeaderMutex, ro.main.mHeaderMutex, ro.main.none [0]
 //     mMeshHeaderSize          mHeaderMutex  rw.repo.mHeaderMutex
 //     mSkinRequests            mMutex        rw.repo.mMutex, ro.repo.none [5]
-//     mSkinInfoQ               none          rw.repo.none, rw.main.mMutex [0]
+//     mSkinInfoQ               mMutex        rw.repo.mMutex, rw.main.mMutex [5] (was:  [0])
 //     mDecompositionRequests   mMutex        rw.repo.mMutex, ro.repo.none [5]
 //     mPhysicsShapeRequests    mMutex        rw.repo.mMutex, ro.repo.none [5]
-//     mDecompositionQ          none          rw.repo.none, rw.main.mMutex [0]
+//     mDecompositionQ          mMutex        rw.repo.mMutex, rw.main.mMutex [5] (was:  [0])
 //     mHeaderReqQ              mMutex        ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex
 //     mLODReqQ                 mMutex        ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex
 //     mUnavailableQ            mMutex        rw.repo.none [0], ro.main.none [5], rw.main.mMutex
@@ -1713,7 +1713,10 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
 		info.mMeshID = mesh_id;
 
 		// LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL;
-		mSkinInfoQ.push(info);
+		{
+			LLMutexLock lock(mMutex);
+			mSkinInfoQ.push(info);
+		}
 	}
 
 	return true;
@@ -1740,7 +1743,10 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
 	{
 		LLModel::Decomposition* d = new LLModel::Decomposition(decomp);
 		d->mMeshID = mesh_id;
-		mDecompositionQ.push(d);
+		{
+			LLMutexLock lock(mMutex);
+			mDecompositionQ.push(d);
+		}
 	}
 
 	return true;
@@ -1799,7 +1805,10 @@ bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32
 		}
 	}
 
-	mDecompositionQ.push(d);
+	{
+		LLMutexLock lock(mMutex);
+		mDecompositionQ.push(d);
+	}
 	return true;
 }
 
@@ -2460,16 +2469,30 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 		gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
 	}
 
-	while (!mSkinInfoQ.empty())
+	if (! mSkinInfoQ.empty() || ! mDecompositionQ.empty())
 	{
-		gMeshRepo.notifySkinInfoReceived(mSkinInfoQ.front());
-		mSkinInfoQ.pop();
-	}
+		std::queue<LLMeshSkinInfo> skin_info_q;
+		std::queue<LLModel::Decomposition*> decomp_q;
 
-	while (!mDecompositionQ.empty())
-	{
-		gMeshRepo.notifyDecompositionReceived(mDecompositionQ.front());
-		mDecompositionQ.pop();
+		if (mMutex->trylock())
+		{
+			// Make thread-shared data private with swap under lock.
+			skin_info_q.swap(mSkinInfoQ);
+			decomp_q.swap(mDecompositionQ);
+			mMutex->unlock();
+
+			while (! skin_info_q.empty())
+			{
+				gMeshRepo.notifySkinInfoReceived(skin_info_q.front());
+				skin_info_q.pop();
+			}
+
+			while (! decomp_q.empty())
+			{
+				gMeshRepo.notifyDecompositionReceived(decomp_q.front());
+				decomp_q.pop();
+			}
+		}
 	}
 
 	if (update_metrics)
-- 
cgit v1.2.3


From cbf7e90954e69f86cbf530676e165f2feb6501bb Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 20 Sep 2013 05:58:32 +0000
Subject: Used a c++11 feature unintentionally.  Use a more traditional
 swap-less object transfer.

---
 indra/newview/llmeshrepository.cpp | 21 +++++++++++++++------
 1 file changed, 15 insertions(+), 6 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 5174a7af00..23a9640761 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2471,16 +2471,25 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 
 	if (! mSkinInfoQ.empty() || ! mDecompositionQ.empty())
 	{
-		std::queue<LLMeshSkinInfo> skin_info_q;
-		std::queue<LLModel::Decomposition*> decomp_q;
-
 		if (mMutex->trylock())
 		{
-			// Make thread-shared data private with swap under lock.
-			skin_info_q.swap(mSkinInfoQ);
-			decomp_q.swap(mDecompositionQ);
+			std::queue<LLMeshSkinInfo> skin_info_q;
+			std::queue<LLModel::Decomposition*> decomp_q;
+
+			// swap() comes to std::queue in c++11 so copy manually for now
+			while (! mSkinInfoQ.empty())
+			{
+				skin_info_q.push(mSkinInfoQ.front());
+				mSkinInfoQ.pop();
+			}
+			while (! mDecompositionQ.empty())
+			{
+				decomp_q.push(mDecompositionQ.front());
+				mDecompositionQ.pop();
+			}
 			mMutex->unlock();
 
+			// Process the elements free of the lock
 			while (! skin_info_q.empty())
 			{
 				gMeshRepo.notifySkinInfoReceived(skin_info_q.front());
-- 
cgit v1.2.3


From 2462162539053e8cbf26aea68940f300bca5aa87 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 20 Sep 2013 07:13:14 +0000
Subject: Move from std::queue to std::list which has better behavior and
 swap() as well.  Should probably do this for the other queues at some point.

---
 indra/newview/llmeshrepository.cpp | 26 ++++++++++++--------------
 indra/newview/llmeshrepository.h   |  8 ++++----
 2 files changed, 16 insertions(+), 18 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 23a9640761..b55ba758e1 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -1715,7 +1715,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
 		// LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL;
 		{
 			LLMutexLock lock(mMutex);
-			mSkinInfoQ.push(info);
+			mSkinInfoQ.push_back(info);
 		}
 	}
 
@@ -1745,7 +1745,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
 		d->mMeshID = mesh_id;
 		{
 			LLMutexLock lock(mMutex);
-			mDecompositionQ.push(d);
+			mDecompositionQ.push_back(d);
 		}
 	}
 
@@ -1807,7 +1807,7 @@ bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32
 
 	{
 		LLMutexLock lock(mMutex);
-		mDecompositionQ.push(d);
+		mDecompositionQ.push_back(d);
 	}
 	return true;
 }
@@ -2473,33 +2473,31 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 	{
 		if (mMutex->trylock())
 		{
-			std::queue<LLMeshSkinInfo> skin_info_q;
-			std::queue<LLModel::Decomposition*> decomp_q;
+			std::list<LLMeshSkinInfo> skin_info_q;
+			std::list<LLModel::Decomposition*> decomp_q;
 
-			// swap() comes to std::queue in c++11 so copy manually for now
-			while (! mSkinInfoQ.empty())
+			if (! mSkinInfoQ.empty())
 			{
-				skin_info_q.push(mSkinInfoQ.front());
-				mSkinInfoQ.pop();
+				skin_info_q.swap(mSkinInfoQ);
 			}
-			while (! mDecompositionQ.empty())
+			if (! mDecompositionQ.empty())
 			{
-				decomp_q.push(mDecompositionQ.front());
-				mDecompositionQ.pop();
+				decomp_q.swap(mDecompositionQ);
 			}
+
 			mMutex->unlock();
 
 			// Process the elements free of the lock
 			while (! skin_info_q.empty())
 			{
 				gMeshRepo.notifySkinInfoReceived(skin_info_q.front());
-				skin_info_q.pop();
+				skin_info_q.pop_front();
 			}
 
 			while (! decomp_q.empty())
 			{
 				gMeshRepo.notifyDecompositionReceived(decomp_q.front());
-				decomp_q.pop();
+				decomp_q.pop_front();
 			}
 		}
 	}
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index c79278da1a..e09f39f7a8 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -291,8 +291,8 @@ public:
 	//set of requested skin info
 	std::set<LLUUID> mSkinRequests;
 	
-	//queue of completed skin info requests
-	std::queue<LLMeshSkinInfo> mSkinInfoQ;
+	// list of completed skin info requests
+	std::list<LLMeshSkinInfo> mSkinInfoQ;
 
 	//set of requested decompositions
 	std::set<LLUUID> mDecompositionRequests;
@@ -300,8 +300,8 @@ public:
 	//set of requested physics shapes
 	std::set<LLUUID> mPhysicsShapeRequests;
 
-	//queue of completed Decomposition info requests
-	std::queue<LLModel::Decomposition*> mDecompositionQ;
+	// list of completed Decomposition info requests
+	std::list<LLModel::Decomposition*> mDecompositionQ;
 
 	//queue of requested headers
 	std::queue<HeaderRequest> mHeaderReqQ;
-- 
cgit v1.2.3


From 200bea5b418a3dc61431d26271932f8c489f0d18 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Tue, 24 Sep 2013 14:49:26 -0400
Subject: SH-3690 SH-4505  Cleanup pass through code. Start using DNS cache in
 legacy LLCurl code.  Go to 15 seconds particularly as we're using threaded
 resolver at this point. Documentation cleanup.  Add libcurl status checking
 and logging for curl_easy_setopt() operations that fail.  Shouldn't happen
 and we'll just continue anyway but there's info in the logs to track these
 down now.  Cleaned up logic around FASTTIMER enable defines used to evaluate
 pipeline stalls in main thread. Removed long-standing thread race around caps
 strings and URL construction.  Not a significant risk but refactoring the
 code to get rid of them removed one huge eyesore.  It can be made even
 slicker if desired (see notes).

---
 indra/llcorehttp/_httpoperation.cpp |   6 +-
 indra/llcorehttp/_httpoprequest.cpp | 156 +++++++++++++++++++++++++++---------
 indra/llcorehttp/httpcommon.cpp     |   3 +-
 indra/llcorehttp/httpcommon.h       |   5 +-
 indra/llmessage/llcurl.cpp          |  11 ++-
 indra/newview/llmeshrepository.cpp  | 101 +++++++++++++++--------
 indra/newview/llmeshrepository.h    |  19 ++++-
 7 files changed, 214 insertions(+), 87 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp
index 7acd728bbd..5bb0654652 100755
--- a/indra/llcorehttp/_httpoperation.cpp
+++ b/indra/llcorehttp/_httpoperation.cpp
@@ -94,7 +94,7 @@ void HttpOperation::stageFromRequest(HttpService *)
 	// Default implementation should never be called.  This
 	// indicates an operation making a transition that isn't
 	// defined.
-	LL_ERRS("HttpCore") << "Default stageFromRequest method may not be called."
+	LL_ERRS("CoreHttp") << "Default stageFromRequest method may not be called."
 						<< LL_ENDL;
 }
 
@@ -104,7 +104,7 @@ void HttpOperation::stageFromReady(HttpService *)
 	// Default implementation should never be called.  This
 	// indicates an operation making a transition that isn't
 	// defined.
-	LL_ERRS("HttpCore") << "Default stageFromReady method may not be called."
+	LL_ERRS("CoreHttp") << "Default stageFromReady method may not be called."
 						<< LL_ENDL;
 }
 
@@ -114,7 +114,7 @@ void HttpOperation::stageFromActive(HttpService *)
 	// Default implementation should never be called.  This
 	// indicates an operation making a transition that isn't
 	// defined.
-	LL_ERRS("HttpCore") << "Default stageFromActive method may not be called."
+	LL_ERRS("CoreHttp") << "Default stageFromActive method may not be called."
 						<< LL_ENDL;
 }
 
diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index d72f8f6119..63c4e71258 100755
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -90,6 +90,10 @@ char * os_strtrim(char * str);
 char * os_strltrim(char * str);
 void os_strlower(char * str);
 
+// Error testing and reporting for libcurl status codes
+void check_curl_easy_code(CURLcode code);
+void check_curl_easy_code(CURLcode code, int curl_setopt_option);
+
 } // end anonymous namespace
 
 
@@ -373,6 +377,8 @@ void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id,
 //
 HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 {
+	CURLcode code;
+	
 	// Scrub transport and result data for retried op case
 	mCurlActive = false;
 	mCurlHandle = NULL;
@@ -406,12 +412,25 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 	HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions());
 	
 	mCurlHandle = curl_easy_init();
-	curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
-	curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);
-	curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1);
-	curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str());
-	curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
-	curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
+	if (! mCurlHandle)
+	{
+		// We're in trouble.  We'll continue but it won't go well.
+		LL_WARNS("CoreHttp") << "Failed to allocate libcurl easy handle.  Continuing."
+							 << LL_ENDL;
+		return HttpStatus(HttpStatus::LLCORE, HE_BAD_ALLOC);
+	}
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+	check_curl_easy_code(code, CURLOPT_IPRESOLVE);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);
+	check_curl_easy_code(code, CURLOPT_NOSIGNAL);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1);
+	check_curl_easy_code(code, CURLOPT_NOPROGRESS);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str());
+	check_curl_easy_code(code, CURLOPT_URL);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
+	check_curl_easy_code(code, CURLOPT_PRIVATE);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
+	check_curl_easy_code(code, CURLOPT_ENCODING);
 
 	if (HTTP_ENABLE_LINKSYS_WRT54G_V5_DNS_FIX)
 	{
@@ -421,7 +440,8 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 		// about 700 or so requests and starts issuing TCP RSTs to
 		// new connections.  Reuse the DNS lookups for even a few
 		// seconds and no RSTs.
-		curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
+		check_curl_easy_code(code, CURLOPT_DNS_CACHE_TIMEOUT);
 	}
 	else
 	{
@@ -429,17 +449,27 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 		// I don't think this is valid anymore, the Multi shared DNS
 		// cache is working well.  For the case of naked easy handles,
 		// consider using a shared DNS object.
-		curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
+		check_curl_easy_code(code, CURLOPT_DNS_CACHE_TIMEOUT);
 	}
-	curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1);
-	curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1);
-	curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);
-	curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
-	curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this);
-	curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback);
-	curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this);
-	curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1);
-	curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1);
+	check_curl_easy_code(code, CURLOPT_AUTOREFERER);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1);
+	check_curl_easy_code(code, CURLOPT_FOLLOWLOCATION);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT);
+	check_curl_easy_code(code, CURLOPT_MAXREDIRS);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
+	check_curl_easy_code(code, CURLOPT_WRITEFUNCTION);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this);
+	check_curl_easy_code(code, CURLOPT_WRITEDATA);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback);
+	check_curl_easy_code(code, CURLOPT_READFUNCTION);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this);
+	check_curl_easy_code(code, CURLOPT_READDATA);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1);
+	check_curl_easy_code(code, CURLOPT_SSL_VERIFYPEER);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);
+	check_curl_easy_code(code, CURLOPT_SSL_VERIFYHOST);
 
 	if (policy.mUseLLProxy)
 	{
@@ -452,37 +482,46 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 	{
 		// *TODO:  This is fine for now but get fuller socks5/
 		// authentication thing going later....
-		curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, policy.mHttpProxy.c_str());
-		curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, policy.mHttpProxy.c_str());
+		check_curl_easy_code(code, CURLOPT_PROXY);
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
+		check_curl_easy_code(code, CURLOPT_PROXYTYPE);
 	}
 	if (policy.mCAPath.size())
 	{
-		curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, policy.mCAPath.c_str());
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, policy.mCAPath.c_str());
+		check_curl_easy_code(code, CURLOPT_CAPATH);
 	}
 	if (policy.mCAFile.size())
 	{
-		curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, policy.mCAFile.c_str());
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, policy.mCAFile.c_str());
+		check_curl_easy_code(code, CURLOPT_CAINFO);
 	}
 	
 	switch (mReqMethod)
 	{
 	case HOR_GET:
-		curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1);
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1);
+		check_curl_easy_code(code, CURLOPT_HTTPGET);
 		mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
 		mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
 		break;
 		
 	case HOR_POST:
 		{
-			curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1);
-			curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
+			code = curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1);
+			check_curl_easy_code(code, CURLOPT_POST);
+			code = curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
+			check_curl_easy_code(code, CURLOPT_ENCODING);
 			long data_size(0);
 			if (mReqBody)
 			{
 				data_size = mReqBody->size();
 			}
-			curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast<void *>(NULL));
-			curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size);
+			code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast<void *>(NULL));
+			check_curl_easy_code(code, CURLOPT_POSTFIELDS);
+			code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size);
+			check_curl_easy_code(code, CURLOPT_POSTFIELDSIZE);
 			mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
 			mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
 			mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
@@ -491,14 +530,17 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 		
 	case HOR_PUT:
 		{
-			curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1);
+			code = curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1);
+			check_curl_easy_code(code, CURLOPT_UPLOAD);
 			long data_size(0);
 			if (mReqBody)
 			{
 				data_size = mReqBody->size();
 			}
-			curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size);
-			curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL);
+			code = curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size);
+			check_curl_easy_code(code, CURLOPT_INFILESIZE);
+			code = curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL);
+			check_curl_easy_code(code, CURLOPT_POSTFIELDS);
 			mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:");
 			mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive");
 			mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300");
@@ -515,9 +557,12 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 	// Tracing
 	if (mTracing >= HTTP_TRACE_CURL_HEADERS)
 	{
-		curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1);
-		curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, this);
-		curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback);
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1);
+		check_curl_easy_code(code, CURLOPT_VERBOSE);
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, this);
+		check_curl_easy_code(code, CURLOPT_DEBUGDATA);
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback);
+		check_curl_easy_code(code, CURLOPT_DEBUGFUNCTION);
 	}
 	
 	// There's a CURLOPT for this now...
@@ -557,8 +602,10 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 	{
 		xfer_timeout = timeout;
 	}
-	curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, xfer_timeout);
-	curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, xfer_timeout);
+	check_curl_easy_code(code, CURLOPT_TIMEOUT);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout);
+	check_curl_easy_code(code, CURLOPT_CONNECTTIMEOUT);
 
 	// Request headers
 	if (mReqHeaders)
@@ -566,12 +613,15 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
 		// Caller's headers last to override
 		mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders);
 	}
-	curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders);
+	code = curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders);
+	check_curl_easy_code(code, CURLOPT_HTTPHEADER);
 
 	if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS | PF_USE_RETRY_AFTER))
 	{
-		curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback);
-		curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this);
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback);
+		check_curl_easy_code(code, CURLOPT_HEADERFUNCTION);
+		code = curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this);
+		check_curl_easy_code(code, CURLOPT_HEADERDATA);
 	}
 	
 	if (status)
@@ -612,7 +662,7 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void
 		{
 			// Warn but continue if the read position moves beyond end-of-body
 			// for some reason.
-			LL_WARNS("HttpCore") << "Request body position beyond body size.  Truncating request body."
+			LL_WARNS("CoreHttp") << "Request body position beyond body size.  Truncating request body."
 								 << LL_ENDL;
 		}
 		return 0;
@@ -1046,6 +1096,32 @@ char * os_strltrim(char * lstr)
 }
 
 
-}  // end anonymous namespace
+void check_curl_easy_code(CURLcode code, int curl_setopt_option)
+{
+	if (CURLE_OK != code)
+	{
+		// Comment from old llcurl code which may no longer apply:
+		//
+		// linux appears to throw a curl error once per session for a bad initialization
+		// at a pretty random time (when enabling cookies).
+		LL_WARNS("CoreHttp") << "libcurl error detected:  " << curl_easy_strerror(code)
+							 << ", curl_easy_setopt option:  " << curl_setopt_option
+							 << LL_ENDL;
+	}
+}
 
-		
+
+void check_curl_easy_code(CURLcode code)
+{
+	if (CURLE_OK != code)
+	{
+		// Comment from old llcurl code which may no longer apply:
+		//
+		// linux appears to throw a curl error once per session for a bad initialization
+		// at a pretty random time (when enabling cookies).
+		LL_WARNS("CoreHttp") << "libcurl error detected:  " << curl_easy_strerror(code)
+							 << LL_ENDL;
+	}
+}
+
+}  // end anonymous namespace
diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp
index ca57a18578..0cf415223e 100755
--- a/indra/llcorehttp/httpcommon.cpp
+++ b/indra/llcorehttp/httpcommon.cpp
@@ -70,7 +70,8 @@ std::string HttpStatus::toString() const
 			"Invalid datatype for argument or option",
 			"Option has not been explicitly set",
 			"Option is not dynamic and must be set early",
-			"Invalid HTTP status code received from server"
+			"Invalid HTTP status code received from server",
+			"Could not allocate required resource"
 		};
 	static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0]));
 
diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h
index a04b344a9e..3a0bd68108 100755
--- a/indra/llcorehttp/httpcommon.h
+++ b/indra/llcorehttp/httpcommon.h
@@ -246,7 +246,10 @@ enum HttpError
 	HE_OPT_NOT_DYNAMIC = 8,
 	
 	// Invalid HTTP status code returned by server
-	HE_INVALID_HTTP_STATUS = 9
+	HE_INVALID_HTTP_STATUS = 9,
+	
+	// Couldn't allocate resource, typically libcurl handle
+	HE_BAD_ALLOC = 10
 	
 }; // end enum HttpError
 
diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp
index f2a3e059ef..5193799ade 100755
--- a/indra/llmessage/llcurl.cpp
+++ b/indra/llmessage/llcurl.cpp
@@ -6,7 +6,7 @@
  *
  * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2010-2013, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -293,9 +293,12 @@ LLCurl::Easy* LLCurl::Easy::getEasy()
 		return NULL;
 	}
 	
-	// set no DNS caching as default for all easy handles. This prevents them adopting a
-	// multi handles cache if they are added to one.
-	CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0);
+	// Enable a brief cache period for now.  This was zero for the longest time
+	// which caused some routers grief and generated unneeded traffic.  For the
+	// threded resolver, we're using system resolution libraries and non-zero values
+	// are preferred.  The c-ares resolver is another matter and it might not
+	// track server changes as well.
+	CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
 	check_curl_code(result);
 	result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
 	check_curl_code(result);
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index b55ba758e1..42952909d7 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -78,8 +78,6 @@
 #include "netdb.h"
 #endif
 
-#include <queue>
-
 
 // Purpose
 //
@@ -235,8 +233,7 @@
 //     mUploadWaitList                 none            rw.main.none (upload thread accessing objects)
 //     mInventoryQ                     mMeshMutex [4]  rw.main.mMeshMutex, ro.main.none [5]
 //     mUploadErrorQ                   mMeshMutex      rw.main.mMeshMutex, rw.any.mMeshMutex
-//     mGetMeshCapability              none            rw.main.none [0], ro.any.none
-//     mGetMesh2Capability             none            rw.main.none [0], ro.any.none
+//     mGetMeshVersion                 none            rw.main.none
 //
 //   LLMeshRepoThread:
 //
@@ -255,6 +252,9 @@
 //     mUnavailableQ            mMutex        rw.repo.none [0], ro.main.none [5], rw.main.mMutex
 //     mLoadedQ                 mMutex        rw.repo.mMutex, ro.main.none [5], rw.main.mMutex
 //     mPendingLOD              mMutex        rw.repo.mMutex, rw.any.mMutex
+//     mGetMeshCapability       mMutex        rw.main.mMutex, ro.repo.mMutex (was:  [0])
+//     mGetMesh2Capability      mMutex        rw.main.mMutex, ro.repo.mMutex (was:  [0])
+//     mGetMeshVersion          mMutex        rw.main.mMutex, ro.repo.mMutex
 //     mHttp*                   none          rw.repo.none
 //
 // QA/Development Testing
@@ -304,7 +304,10 @@
 // With this instrumentation enabled, a stall will appear
 // under the 'Mesh Fetch' timer which will be either top-level
 // or under 'Render' time.
+
+#ifndef	LL_MESH_FASTTIMER_ENABLE
 #define LL_MESH_FASTTIMER_ENABLE		1
+#endif
 #if LL_MESH_FASTTIMER_ENABLE
 static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch");
 
@@ -381,7 +384,6 @@ static S32 dump_num = 0;
 std::string make_dump_name(std::string prefix, S32 num)
 {
 	return prefix + boost::lexical_cast<std::string>(num) + std::string(".xml");
-	
 }
 void dump_llsd_to_file(const LLSD& content, std::string filename);
 LLSD llsd_from_file(std::string filename);
@@ -740,7 +742,8 @@ LLMeshRepoThread::LLMeshRepoThread()
   mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
   mHttpLegacyPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
   mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID),
-  mHttpPriority(0)
+  mHttpPriority(0),
+  mGetMeshVersion(2)
 { 
 	mMutex = new LLMutex(NULL);
 	mHeaderMutex = new LLMutex(NULL);
@@ -1047,30 +1050,50 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 	}
 }
 
+// Mutex:  must be holding mMutex when called
+void LLMeshRepoThread::setGetMeshCaps(const std::string & get_mesh1,
+									  const std::string & get_mesh2,
+									  int pref_version)
+{
+	mGetMeshCapability = get_mesh1;
+	mGetMesh2Capability = get_mesh2;
+	mGetMeshVersion = pref_version;
+}
+
+
 // Constructs a Cap URL for the mesh.  Prefers a GetMesh2 cap
 // over a GetMesh cap.
 //
-//static 
-std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
+// Mutex:  acquires mMutex
+void LLMeshRepoThread::constructUrl(LLUUID mesh_id, std::string * url, int * version)
 {
-	std::string http_url;
+	std::string res_url;
+	int res_version(2);
 	
 	if (gAgent.getRegion())
 	{
-		if (! gMeshRepo.mGetMesh2Capability.empty() && gMeshRepo.mGetMeshVersion > 1)
+		LLMutexLock lock(mMutex);
+
+		// Get a consistent pair of (cap string, version).  The
+		// locking could be eliminated here without loss of safety
+		// by using a set of staging values in setGetMeshCaps().
+		
+		if (! mGetMesh2Capability.empty() && mGetMeshVersion > 1)
 		{
-			http_url = gMeshRepo.mGetMesh2Capability;
+			res_url = mGetMesh2Capability;
+			res_version = 2;
 		}
 		else
 		{
-			http_url = gMeshRepo.mGetMeshCapability;
+			res_url = mGetMeshCapability;
+			res_version = 1;
 		}
 	}
 
-	if (!http_url.empty())
+	if (! res_url.empty())
 	{
-		http_url += "/?mesh_id=";
-		http_url += mesh_id.asString().c_str();
+		res_url += "/?mesh_id=";
+		res_url += mesh_id.asString().c_str();
 	}
 	else
 	{
@@ -1078,7 +1101,8 @@ std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id)
 								<< mesh_id << ".mesh" << LL_ENDL;
 	}
 
-	return http_url;
+	*url = res_url;
+	*version = res_version;
 }
 
 // Issue an HTTP GET request with byte range using the right
@@ -1200,8 +1224,10 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			int cap_version(gMeshRepo.mGetMeshVersion);
-			std::string http_url = constructUrl(mesh_id);
+			int cap_version(2);
+			std::string http_url;
+			constructUrl(mesh_id, &http_url, &cap_version);
+
 			if (!http_url.empty())
 			{
 				LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size);
@@ -1293,8 +1319,10 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			int cap_version(gMeshRepo.mGetMeshVersion);
-			std::string http_url = constructUrl(mesh_id);
+			int cap_version(2);
+			std::string http_url;
+			constructUrl(mesh_id, &http_url, &cap_version);
+			
 			if (!http_url.empty())
 			{
 				LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size);
@@ -1384,8 +1412,10 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			int cap_version(gMeshRepo.mGetMeshVersion);
-			std::string http_url = constructUrl(mesh_id);
+			int cap_version(2);
+			std::string http_url;
+			constructUrl(mesh_id, &http_url, &cap_version);
+			
 			if (!http_url.empty())
 			{
 				LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size);
@@ -1477,8 +1507,10 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)
 
 	//either cache entry doesn't exist or is corrupt, request header from simulator	
 	bool retval = true;
-	int cap_version(gMeshRepo.mGetMeshVersion);
-	std::string http_url = constructUrl(mesh_params.getSculptID());
+	int cap_version(2);
+	std::string http_url;
+	constructUrl(mesh_params.getSculptID(), &http_url, &cap_version);
+	
 	if (!http_url.empty())
 	{
 		//grab first 4KB if we're going to bother with a fetch.  Cache will prevent future fetches if a full mesh fits
@@ -1563,8 +1595,10 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 			}
 
 			//reading from VFS failed for whatever reason, fetch from sim
-			int cap_version(gMeshRepo.mGetMeshVersion);
-			std::string http_url = constructUrl(mesh_id);
+			int cap_version(2);
+			std::string http_url;
+			constructUrl(mesh_id, &http_url, &cap_version);
+			
 			if (!http_url.empty())
 			{
 				LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size);
@@ -1812,7 +1846,6 @@ bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32
 	return true;
 }
 
-
 LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures,
 									   bool upload_skin, bool upload_joints, const std::string & upload_url, bool do_upload,
 									   LLHandle<LLWholeModelFeeObserver> fee_observer,
@@ -3249,15 +3282,15 @@ void LLMeshRepository::notifyLoadedMeshes()
 			
 			if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived())
 			{
-				const bool use_v1(gSavedSettings.getBOOL("MeshUseGetMesh1"));
-
 				region_name = gAgent.getRegion()->getName();
-				mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh");
-				mGetMesh2Capability = gAgent.getRegion()->getCapability("GetMesh2");
-				mGetMeshVersion = (mGetMesh2Capability.empty() || use_v1) ? 1 : 2;
+				const bool use_v1(gSavedSettings.getBOOL("MeshUseGetMesh1"));
+				const std::string mesh1(gAgent.getRegion()->getCapability("GetMesh"));
+				const std::string mesh2(gAgent.getRegion()->getCapability("GetMesh2"));
+				mGetMeshVersion = (mesh2.empty() || use_v1) ? 1 : 2;
+				mThread->setGetMeshCaps(mesh1, mesh2, mGetMeshVersion);
 				LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name
-									<< "', GetMesh2:  " << mGetMesh2Capability
-									<< ", GetMesh:  " << mGetMeshCapability
+									<< "', GetMesh2:  " << mesh2
+									<< ", GetMesh:  " << mesh1
 									<< ", using version:  " << mGetMeshVersion
 									<< LL_ENDL;
 			}
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index e09f39f7a8..9d8b102110 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -333,7 +333,9 @@ public:
 	typedef std::set<LLCore::HttpHandler *> http_request_set;
 	http_request_set					mHttpRequestSet;			// Outstanding HTTP requests
 
-	static std::string constructUrl(LLUUID mesh_id);
+	std::string mGetMeshCapability;
+	std::string mGetMesh2Capability;
+	int mGetMeshVersion;
 
 	LLMeshRepoThread();
 	~LLMeshRepoThread();
@@ -376,6 +378,17 @@ public:
 	static void incActiveHeaderRequests();
 	static void decActiveHeaderRequests();
 
+	// Set the caps strings and preferred version for constructing
+	// mesh fetch URLs.
+	//
+	// Mutex:  must be holding mMutex when called
+	void setGetMeshCaps(const std::string & get_mesh1,
+						const std::string & get_mesh2,
+						int pref_version);
+
+	// Mutex:  acquires mMutex
+	void constructUrl(LLUUID mesh_id, std::string * url, int * version);
+
 private:
 	// Issue a GET request to a URL with 'Range' header using
 	// the correct policy class and other attributes.  If an invalid
@@ -613,9 +626,7 @@ public:
 	void uploadError(LLSD& args);
 	void updateInventory(inventory_data data);
 
-	std::string mGetMeshCapability;
-	std::string mGetMesh2Capability;
-	int mGetMeshVersion;
+	int mGetMeshVersion;		// Shadows value in LLMeshRepoThread
 };
 
 extern LLMeshRepository gMeshRepo;
-- 
cgit v1.2.3


From 56e2f11417183d8dcc3d681f79fc63446b236abb Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 27 Sep 2013 17:22:31 -0400
Subject: Up the transfer timeout of small meshes to 120S.  This matches the
 change made for MAINT-2347.  Large transfers are still 10 minutes. 
 Add/update to-do list and add some more info to the FAQ in the Readme.

---
 indra/llcorehttp/README.Linden     | 17 ++++++++++++++++-
 indra/llcorehttp/_httpinternal.h   |  7 ++++++-
 indra/newview/llmeshrepository.cpp |  2 +-
 3 files changed, 23 insertions(+), 3 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/README.Linden b/indra/llcorehttp/README.Linden
index 8d18ed1a11..eb6ccab3bc 100644
--- a/indra/llcorehttp/README.Linden
+++ b/indra/llcorehttp/README.Linden
@@ -653,4 +653,19 @@ HttpOperation::addAsReply: TRACE, ToReplyQueue, Handle:  086D3148
 
     Q2.  How's that data sharing with refcounts working for you?
 
-    A2.  Meh.
+    A2.  Meh.  It does reduce memory churn and the frequency at which
+    free blocks must be moved between threads.  But it's also a design
+    for static configuration and dynamic reconfiguration (not
+    requiring a restart) is favored.  Creating new options for every
+    request isn't too bad, it a sequence of "new, fill, request,
+    release" for each requested operation.  That in contrast to doing
+    the "new, fill, release" at startup.  The bad comes in getting at
+    the source data.  One rule in this work was "no new thread
+    problems."  And one source for those is pulling setting values out
+    of gSettings in threads.  None of that is thread safe though we
+    tend to get away with it.
+
+    Q3.  What needs to be done?
+
+    A3.  There's a To-Do list in _httpinternal.h.  It has both large
+    and small projects here if someone would like to try changes.
diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h
index effc6a42c5..54e87b0a4d 100755
--- a/indra/llcorehttp/_httpinternal.h
+++ b/indra/llcorehttp/_httpinternal.h
@@ -66,6 +66,12 @@
 //   This won't help in the face of the router problems we've looked
 //   at, however.  Detect starvation due to UDP activity and provide
 //   feedback to it.
+// - Change the transfer timeout scheme.  We're less interested in
+//   absolute time, in most cases, than in continuous progress.
+// - Many of the policy class settings are currently applied to the
+//   entire class.  Some, like connection limits, would be better
+//   applied to each destination target making multiple targets
+//   independent.
 //
 // Integration to-do list
 // - LLTextureFetch still needs a major refactor.  The use of
@@ -75,7 +81,6 @@
 //   the main source file.
 // - Expand areas of usage eventually leading to the removal of LLCurl.
 //   Rough order of expansion:
-//   .  Mesh fetch [Underway]
 //   .  Avatar names
 //   .  Group membership lists
 //   .  Caps access in general
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 42952909d7..044cf27b07 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -351,7 +351,7 @@ const S32 REQUEST2_HIGH_WATER_MAX = 80;
 const S32 REQUEST2_LOW_WATER_MIN = 16;
 const S32 REQUEST2_LOW_WATER_MAX = 40;
 const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;		// Size at which requests goes to narrow/slow queue
-const long SMALL_MESH_XFER_TIMEOUT = 60L;				// Seconds to complete xfer, small mesh downloads
+const long SMALL_MESH_XFER_TIMEOUT = 120L;				// Seconds to complete xfer, small mesh downloads
 const long LARGE_MESH_XFER_TIMEOUT = 600L;				// Seconds to complete xfer, large downloads
 
 // Maximum mesh version to support.  Three least significant digits are reserved for the minor version, 
-- 
cgit v1.2.3


From 6d405e2d018e6722288e1cfbf477d985b8384a54 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 4 Oct 2013 18:24:59 -0400
Subject: Convert one more unit test over to improved waiting scheme to avoid
 build failures.

---
 indra/llcorehttp/tests/test_httprequest.hpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

(limited to 'indra')

diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp
index 2311753c65..43f7e36da5 100755
--- a/indra/llcorehttp/tests/test_httprequest.hpp
+++ b/indra/llcorehttp/tests/test_httprequest.hpp
@@ -3241,11 +3241,11 @@ void HttpRequestTestObjectType::test<23>()
 
 		// Run the notification pump.
 		int count(0);
-		int limit(300);				// One retry but several seconds needed
+		int limit(LOOP_COUNT_LONG);
 		while (count++ < limit && mHandlerCalls < url_limit)
 		{
 			req->update(0);
-			usleep(100000);
+			usleep(LOOP_SLEEP_INTERVAL);
 		}
 		ensure("Request executed in reasonable time", count < limit);
 		ensure("One handler invocation for request", mHandlerCalls == url_limit);
@@ -3258,21 +3258,21 @@ void HttpRequestTestObjectType::test<23>()
 	
 		// Run the notification pump again
 		count = 0;
-		limit = 100;
+		limit = LOOP_COUNT_LONG;
 		while (count++ < limit && mHandlerCalls < 1)
 		{
 			req->update(1000000);
-			usleep(100000);
+			usleep(LOOP_SLEEP_INTERVAL);
 		}
 		ensure("Second request executed in reasonable time", count < limit);
 		ensure("Second handler invocation", mHandlerCalls == 1);
 
 		// See that we actually shutdown the thread
 		count = 0;
-		limit = 10;
+		limit = LOOP_COUNT_SHORT;
 		while (count++ < limit && ! HttpService::isStopped())
 		{
-			usleep(100000);
+			usleep(LOOP_SLEEP_INTERVAL);
 		}
 		ensure("Thread actually stopped running", HttpService::isStopped());
 
-- 
cgit v1.2.3


From b2d769534c82de2ac7b36f11ce6fab61f3e0d378 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Mon, 18 Nov 2013 13:33:19 -0500
Subject: Code review updates.  All comments so far.

---
 indra/llcommon/lldeadmantimer.h | 10 ++++------
 indra/llmessage/llcurl.cpp      |  2 +-
 2 files changed, 5 insertions(+), 7 deletions(-)

(limited to 'indra')

diff --git a/indra/llcommon/lldeadmantimer.h b/indra/llcommon/lldeadmantimer.h
index 0dde16b717..980976e176 100644
--- a/indra/llcommon/lldeadmantimer.h
+++ b/indra/llcommon/lldeadmantimer.h
@@ -155,11 +155,9 @@ public:
 	///
 	void ringBell(time_type now, unsigned int count);
 	
-	/// Checks on the status of the timer Declare that something interesting happened.  This has two
-	/// effects on an unexpired-timer.  1)  The expiration time
-	/// is extended for 'horizon' seconds after the 'now' value.
-	/// 2)  An internal counter associated with the event is incremented.
-	/// This count is returned via the @see isExpired() method.
+	/// Checks the status of the timer.  If the timer has expired,
+	/// also returns various timer-related stats.  Unlike ringBell(),
+	/// does not extend the horizon, it only checks for expiration.
 	///
 	/// @param now		Current time as returned by @see
 	///					LLTimer::getCurrentClockCount().  If zero,
@@ -192,7 +190,7 @@ public:
 	bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count,
 				   U64 & user_cpu, U64 & sys_cpu);
 
-	/// Identical to the six-arugment form except is does without the
+	/// Identical to the six-arugment form except it does without the
 	/// CPU time return if the caller isn't interested in it.
 	bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count);
 
diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp
index efbc804cbc..25e175f2cc 100755
--- a/indra/llmessage/llcurl.cpp
+++ b/indra/llmessage/llcurl.cpp
@@ -296,7 +296,7 @@ LLCurl::Easy* LLCurl::Easy::getEasy()
 	
 	// Enable a brief cache period for now.  This was zero for the longest time
 	// which caused some routers grief and generated unneeded traffic.  For the
-	// threded resolver, we're using system resolution libraries and non-zero values
+	// threaded resolver, we're using system resolution libraries and non-zero values
 	// are preferred.  The c-ares resolver is another matter and it might not
 	// track server changes as well.
 	CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15);
-- 
cgit v1.2.3


From ea1f6a6343fe83f1352a8a839265c471640acdce Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Fri, 6 Dec 2013 16:02:53 -0500
Subject: SH-4645 Viewer hangs on exit after cancelling a mesh upload. Problem
 involved a 3-way livelock between the main, upload and decomposition threads.
  Viewer is shutting down but an upload is in the 'generate hulls' state. 
 Main thread asks upload request to discard and spins waiting for it to
 finish. Upload thread is in generateHulls spinning waiting for the
 decomposition thread to process a mesh request.  Decomposition thread is
 sleeping waiting for main thread to deliver work that upload thread has asked
 the decomposition thread to do.

---
 indra/newview/llmeshrepository.cpp | 39 ++++++++++++++++++++++++++++++--------
 indra/newview/llmeshrepository.h   |  4 ++--
 2 files changed, 33 insertions(+), 10 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index ebfb22a360..4296abb2cc 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -257,6 +257,11 @@
 //     mGetMeshVersion          mMutex        rw.main.mMutex, ro.repo.mMutex
 //     mHttp*                   none          rw.repo.none
 //
+//   LLMeshUploadThread:
+//
+//     mDiscarded               mMutex        rw.main.mMutex, ro.uploadN.none [1]
+//     ... more ...
+//
 // QA/Development Testing
 //
 //   Debug variable 'MeshUploadFakeErrors' takes a mask of bits that will
@@ -1852,7 +1857,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,
 									   LLHandle<LLWholeModelUploadObserver> upload_observer)
   : LLThread("mesh upload"),
 	LLCore::HttpHandler(),
-	mDiscarded(FALSE),
+	mDiscarded(false),
 	mDoUpload(do_upload),
 	mWholeModelUploadURL(upload_url),
 	mFeeObserverHandle(fee_observer),
@@ -1940,10 +1945,10 @@ void LLMeshUploadThread::preStart()
 void LLMeshUploadThread::discard()
 {
 	LLMutexLock lock(mMutex);
-	mDiscarded = TRUE;
+	mDiscarded = true;
 }
 
-BOOL LLMeshUploadThread::isDiscarded() const
+bool LLMeshUploadThread::isDiscarded() const
 {
 	LLMutexLock lock(mMutex);
 	return mDiscarded;
@@ -2199,7 +2204,13 @@ void LLMeshUploadThread::generateHulls()
 		
 	if (has_valid_requests)
 	{
-		while (!mPhysicsComplete)
+		// *NOTE:  Interesting livelock condition on shutdown.  If there
+		// is an upload request in generateHulls() when shutdown starts,
+		// the main thread isn't available to manage communication between
+		// the decomposition thread and the upload thread and this loop
+		// wouldn't complete in turn stalling the main thread.  The check
+		// on isDiscarded() prevents that.
+		while (! mPhysicsComplete && ! isDiscarded())
 		{
 			apr_sleep(100);
 		}
@@ -2253,13 +2264,21 @@ void LLMeshUploadThread::doWholeModelUpload()
 			LL_DEBUGS(LOG_MESH) << "POST request issued." << LL_ENDL;
 			
 			mHttpRequest->update(0);
-			while (! LLApp::isQuitting() && ! mFinished)
+			while (! LLApp::isQuitting() && ! finished() && ! isDiscarded())
 			{
 				ms_sleep(sleep_time);
 				sleep_time = llmin(250U, sleep_time + sleep_time);
 				mHttpRequest->update(0);
 			}
-			LL_DEBUGS(LOG_MESH) << "Mesh upload operation completed." << LL_ENDL;
+
+			if (isDiscarded())
+			{
+				LL_DEBUGS(LOG_MESH) << "Mesh upload operation discarded." << LL_ENDL;
+			}
+			else
+			{
+				LL_DEBUGS(LOG_MESH) << "Mesh upload operation completed." << LL_ENDL;
+			}
 		}
 	}
 }
@@ -2299,12 +2318,16 @@ void LLMeshUploadThread::requestWholeModelFee()
 		U32 sleep_time(10);
 		
 		mHttpRequest->update(0);
-		while (! LLApp::isQuitting() && ! mFinished)
+		while (! LLApp::isQuitting() && ! finished() && ! isDiscarded())
 		{
 			ms_sleep(sleep_time);
 			sleep_time = llmin(250U, sleep_time + sleep_time);
 			mHttpRequest->update(0);
 		}
+		if (isDiscarded())
+		{
+			LL_DEBUGS(LOG_MESH) << "Mesh fee query operation discarded." << LL_ENDL;
+		}
 	}
 }
 
@@ -3020,7 +3043,7 @@ void LLMeshRepository::shutdown()
 
 	for (U32 i = 0; i < mUploads.size(); ++i)
 	{
-		LL_INFOS(LOG_MESH) << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << LL_ENDL;
+		LL_INFOS(LOG_MESH) << "Waiting for pending mesh upload " << (i + 1) << "/" << mUploads.size() << LL_ENDL;
 		while (!mUploads[i]->isStopped())
 		{
 			apr_sleep(10);
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 9d8b102110..39280bea3a 100755
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -447,7 +447,7 @@ public:
 	bool			mUploadTextures;
 	bool			mUploadSkin;
 	bool			mUploadJoints;
-	BOOL            mDiscarded;
+	volatile bool	mDiscarded;
 
 	LLHost			mHost;
 	std::string		mWholeModelFeeCapability;
@@ -463,7 +463,7 @@ public:
 	virtual void run();
 	void preStart();
 	void discard() ;
-	BOOL isDiscarded() const;
+	bool isDiscarded() const;
 
 	void generateHulls();
 
-- 
cgit v1.2.3


From b2fe32e5725e13930437ae8939d7213de69b35ca Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 9 Jan 2014 15:18:54 -0500
Subject: SH-4667  HTTP Viewer reports network error instead of a misnamed
 joint on mesh upload Tried to add consistent mesh upload retries in the HTTP
 work but a combination of bad status choices in the upload service and the
 one-shot nature of the upload capabilities means that status information can
 be lost.  For now, retain the wonderful manual retry logic.  At some future
 point, we might fix the services or add application-level retry.

---
 indra/newview/llmeshrepository.cpp | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 4296abb2cc..5afd2cb329 100755
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -5,7 +5,7 @@
  *
  * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2010-2013, Linden Research, Inc.
+ * Copyright (C) 2010-2014, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -359,6 +359,16 @@ const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;		// Size at which requests goes
 const long SMALL_MESH_XFER_TIMEOUT = 120L;				// Seconds to complete xfer, small mesh downloads
 const long LARGE_MESH_XFER_TIMEOUT = 600L;				// Seconds to complete xfer, large downloads
 
+// Would normally like to retry on uploads as some
+// retryable failures would be recoverable.  Unfortunately,
+// the mesh service is using 500 (retryable) rather than
+// 400/bad request (permanent) for a bad payload and
+// retrying that just leads to revocation of the one-shot
+// cap which then produces a 404 on retry destroying some
+// (occasionally) useful error information.  We'll leave
+// upload retries to the user as in the past.  SH-4667.
+const long UPLOAD_RETRY_LIMIT = 0L;
+
 // Maximum mesh version to support.  Three least significant digits are reserved for the minor version, 
 // with major version changes indicating a format change that is not backwards compatible and should not
 // be parsed by viewers that don't specifically support that version. For example, if the integer "1" is 
@@ -1883,6 +1893,7 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,
 	mHttpOptions = new LLCore::HttpOptions;
 	mHttpOptions->setTransferTimeout(mMeshUploadTimeOut);
 	mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter"));
+	mHttpOptions->setRetries(UPLOAD_RETRY_LIMIT);
 	mHttpHeaders = new LLCore::HttpHeaders;
 	mHttpHeaders->append("Content-Type", "application/llsd+xml");
 	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS);
-- 
cgit v1.2.3


From dae8c4158aa028be026700d5c7d03d601c40c58a Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Mon, 27 Jan 2014 16:21:56 -0500
Subject: Trivial edit to stimulate a new build.

---
 indra/edit-me-to-trigger-new-build.txt | 1 +
 1 file changed, 1 insertion(+)

(limited to 'indra')

diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt
index 774e8c0676..beeb570496 100755
--- a/indra/edit-me-to-trigger-new-build.txt
+++ b/indra/edit-me-to-trigger-new-build.txt
@@ -4,3 +4,4 @@ Wed Nov  7 00:25:19 UTC 2012
 
 
 
+
-- 
cgit v1.2.3


From 636953b7d74f79c5ada56719f90245f686df4604 Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Wed, 29 Jan 2014 16:27:59 -0500
Subject: another trigger edit

---
 indra/edit-me-to-trigger-new-build.txt | 1 -
 1 file changed, 1 deletion(-)

(limited to 'indra')

diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt
index beeb570496..774e8c0676 100755
--- a/indra/edit-me-to-trigger-new-build.txt
+++ b/indra/edit-me-to-trigger-new-build.txt
@@ -4,4 +4,3 @@ Wed Nov  7 00:25:19 UTC 2012
 
 
 
-
-- 
cgit v1.2.3


From 80ee8a8ca3725265636f8052a2442a74a742338f Mon Sep 17 00:00:00 2001
From: Monty Brandenberg <monty@lindenlab.com>
Date: Thu, 30 Jan 2014 13:17:17 -0500
Subject: Another build trigger

---
 indra/edit-me-to-trigger-new-build.txt | 1 +
 1 file changed, 1 insertion(+)

(limited to 'indra')

diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt
index 774e8c0676..beeb570496 100755
--- a/indra/edit-me-to-trigger-new-build.txt
+++ b/indra/edit-me-to-trigger-new-build.txt
@@ -4,3 +4,4 @@ Wed Nov  7 00:25:19 UTC 2012
 
 
 
+
-- 
cgit v1.2.3


From de8fea13627cc5978b8a6135802a52864a11c39a Mon Sep 17 00:00:00 2001
From: Oz Linden <oz@lindenlab.com>
Date: Mon, 24 Feb 2014 14:50:35 -0500
Subject: increment viewer version to 3.7.3

---
 indra/newview/VIEWER_VERSION.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'indra')

diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 0b2eb36f50..c1e43e6d45 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-3.7.2
+3.7.3
-- 
cgit v1.2.3


From bd73465e51397bac59b4acc61d024d2923f023b3 Mon Sep 17 00:00:00 2001
From: Baker Linden <baker@lindenlab.com>
Date: Tue, 25 Feb 2014 16:41:23 -0800
Subject: [MAINT-3784] - Viewer takes too long to shut down - Removed a lot of
 logging code to reduce application close time

---
 indra/llui/llfloater.cpp               | 15 +++------------
 indra/llui/lllayoutstack.cpp           |  9 ++-------
 indra/llui/llpanel.cpp                 |  6 ++----
 indra/llui/lltabcontainer.cpp          |  8 ++------
 indra/llui/lluictrl.cpp                | 25 +++----------------------
 indra/llui/llview.cpp                  | 15 ++++-----------
 indra/newview/llfloaterimcontainer.cpp | 24 +++++-------------------
 7 files changed, 21 insertions(+), 81 deletions(-)

(limited to 'indra')

diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 96964b528e..cc25bfcfeb 100755
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -505,36 +505,29 @@ void LLFloater::destroy()
 // virtual
 LLFloater::~LLFloater()
 {
-	LL_INFOS("Baker") << "[3555] ~LLFloater() -------------------------------------------------" << LL_ENDL;
+	LL_INFOS("Baker") << "[3555] ~LLFloater() -- " << getTitle() << ":" << (void*) this << " ----------------------" << LL_ENDL;
 	
 	LLFloaterReg::removeInstance(mInstanceName, mKey);
 	
-	LL_INFOS("Baker") << "[3555] ~LLFloater() - Does child have keyboard focus?" << LL_ENDL;
 	if( gFocusMgr.childHasKeyboardFocus(this))
 	{
-		LL_INFOS("Baker") << "[3555] ~LLFloater() - Yes, release the focus." << LL_ENDL;
+		LL_INFOS("Baker") << "[3555] ~LLFloater() - Release keybaord focus." << LL_ENDL;
 		// Just in case we might still have focus here, release it.
 		releaseFocus();
 	}
 
-	LL_INFOS("Baker") << "[3555] ~LLFloater() - Setting minimized to false" << LL_ENDL;
 	// This is important so that floaters with persistent rects (i.e., those
 	// created with rect control rather than an LLRect) are restored in their
 	// correct, non-minimized positions.
 	setMinimized( FALSE );
 
-	LL_INFOS("Baker") << "[3555] ~LLFloater() - Deleting the drag handle." << LL_ENDL;
 	delete mDragHandle;
 	for (S32 i = 0; i < 4; i++) 
 	{
-		LL_INFOS("Baker") << "[3555] ~LLFloater() - Deleting mResizeBar[" << i << "]" << LL_ENDL;
 		delete mResizeBar[i];
-
-		LL_INFOS("Baker") << "[3555] ~LLFloater() - Deleting mResizeHandle[" << i << "]" << LL_ENDL;
 		delete mResizeHandle[i];
 	}
 
-	LL_INFOS("Baker") << "[3555] ~LLFloater() - Setting visibility (false)" << LL_ENDL;
 	setVisible(false); // We're not visible if we're destroyed
 	
 	LL_INFOS("Baker") << "[3555] ~LLFloater() - Storing visibility control" << LL_ENDL;
@@ -542,11 +535,9 @@ LLFloater::~LLFloater()
 	
 	LL_INFOS("Baker") << "[3555] ~LLFloater() - Storing dock state control" << LL_ENDL;
 	storeDockStateControl();
-
-	LL_INFOS("Baker") << "[3555] ~LLFloater() - Delete mMinimizeSignal" << LL_ENDL;
 	delete mMinimizeSignal;
 
-	LL_INFOS("Baker") << "[3555] Exiting ~LLFloater()" << LL_ENDL;
+	LL_INFOS("Baker") << "[3555] Exiting ~LLFloater() " << (void*) this << LL_ENDL;
 }
 
 void LLFloater::storeRectControl()
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index 953025abbf..5da0386928 100755
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -245,18 +245,13 @@ LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p)
 
 LLLayoutStack::~LLLayoutStack()
 {
-	LL_INFOS("Baker") << "[3555] ~LLLayoutStack() -------------------------------------------------" << LL_ENDL;
+	LL_INFOS("Baker") << "[3555] ~LLLayoutStack() -- " << getName() << ":" << (void*) this << " ----------------------" << LL_ENDL;
 	
-	LL_INFOS("Baker") << "[3555] ~LLLayoutStack() - Copying panel pointers." << LL_ENDL;
 	e_panel_list_t panels = mPanels; // copy list of panel pointers
-
-	LL_INFOS("Baker") << "[3555] ~LLLayoutStack() - Clearing mPanels." << LL_ENDL;
 	mPanels.clear(); // clear so that removeChild() calls don't cause trouble
-	
-	LL_INFOS("Baker") << "[3555] ~LLLayoutStack() - Deleing copied panel pointers." << LL_ENDL;
 	std::for_each(panels.begin(), panels.end(), DeletePointer());
 
-	LL_INFOS("Baker") << "[3555] Exiting ~LLLayoutStack()" << LL_ENDL;
+	LL_INFOS("Baker") << "[3555] Exiting ~LLLayoutStack() " << (void*) this << LL_ENDL;
 }
 
 void LLLayoutStack::draw()
diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp
index 3ee93150f5..b1c5f2774d 100755
--- a/indra/llui/llpanel.cpp
+++ b/indra/llui/llpanel.cpp
@@ -126,13 +126,11 @@ LLPanel::LLPanel(const LLPanel::Params& p)
 
 LLPanel::~LLPanel()
 {
-	LL_INFOS("Baker") << "[3555] ~LLPanel() -------------------------------------------------" << LL_ENDL;
-	LL_INFOS("Baker") << "[3555] ~LLPanel() - Deleting mVisibleSignal..." << LL_ENDL;
+	LL_INFOS("Baker") << "[3555] ~LLPanel() -- " << getName() << ":" << (void*) this << " ----------------------" << LL_ENDL;
 
 	delete mVisibleSignal;
 
-	LL_INFOS("Baker") << "[3555] ~LLPanel() - deleted." << LL_ENDL;
-	LL_INFOS("Baker") << "[3555] Exiting ~LLPanel()" << LL_ENDL;
+	LL_INFOS("Baker") << "[3555] Exiting ~LLPanel() " << (void*) this << LL_ENDL;
 }
 
 // virtual
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 203bf5b50e..26189fcb8c 100755
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -280,16 +280,12 @@ LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
 
 LLTabContainer::~LLTabContainer()
 {
-	LL_INFOS("Baker") << "[3555] ~LLTabContainer() -------------------------------------------------" << LL_ENDL;
-	LL_INFOS("Baker") << "[3555] ~LLTabContainer() - Deleting mTabList contents..." << LL_ENDL;
+	LL_INFOS("Baker") << "[3555] ~LLTabContainer() -- " << getPanelTitle(mCurrentTabIdx) << ":" << (void*) this << " ----------------------" << LL_ENDL;
 	
 	std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
-	
-	LL_INFOS("Baker") << "[3555] ~LLTabContainer() - Clearing mTabList" << LL_ENDL;
 	mTabList.clear();
 	
-	LL_INFOS("Baker") << "[3555] ~LLTabContainer() - done." << LL_ENDL;
-	LL_INFOS("Baker") << "[3555] Exiting ~LLTabContainer()" << LL_ENDL;
+	LL_INFOS("Baker") << "[3555] Exiting ~LLTabContainer() " << (void*) this << LL_ENDL;
 }
 
 //virtual
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index 801d39ac70..cfb17c14ba 100755
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -207,47 +207,28 @@ void LLUICtrl::initFromParams(const Params& p)
 
 LLUICtrl::~LLUICtrl()
 {
-	LL_INFOS("Baker") << "[3555] ~LLUICtrl() -------------------------------------------------" << LL_ENDL;
+	//LL_INFOS("Baker") << "[3555] ~LLUICtrl() -- " << getName() << ":" << (void*) this << " ----------------------" << LL_ENDL;
 	
 	gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
 
-	LL_INFOS("Baker") << "[3555] ~LLUICtrl() - Released focus if needed." << LL_ENDL;
-
 	if( gFocusMgr.getTopCtrl() == this )
 	{
-		llinfos << "[3555] ~LLUICtrl() - UI Control holding top ctrl deleted: " << getName() << ".  Top view removed." << llendl;
+		//llinfos << "[3555] ~LLUICtrl() - UI Control holding top ctrl deleted: " << getName() << ".  Top view removed." << llendl;
 		llwarns << "UI Control holding top ctrl deleted: " << getName() << ".  Top view removed." << llendl;
 		gFocusMgr.removeTopCtrlWithoutCallback( this );
 	}
 
-	LL_INFOS("Baker") << "[3555] ~LLUICtrl() - deleting mCommitSignal" << LL_ENDL;
 	delete mCommitSignal;
-
-	LL_INFOS("Baker") << "[3555] ~LLUICtrl() - deleting mValidateSignal" << LL_ENDL;
 	delete mValidateSignal;
-
-	LL_INFOS("Baker") << "[3555] ~LLUICtrl() - deleting mMouseEnterSignal" << LL_ENDL;
 	delete mMouseEnterSignal;
-
-	LL_INFOS("Baker") << "[3555] ~LLUICtrl() - deleting mMouseLeaveSignal" << LL_ENDL;
 	delete mMouseLeaveSignal;
-
-	LL_INFOS("Baker") << "[3555] ~LLUICtrl() - deleting mMouseDownSignal" << LL_ENDL;
 	delete mMouseDownSignal;
-
-	LL_INFOS("Baker") << "[3555] ~LLUICtrl() - deleting mMouseUpSignal" << LL_ENDL;
 	delete mMouseUpSignal;
-
-	LL_INFOS("Baker") << "[3555] ~LLUICtrl() - deleting mRightMouseDownSignal" << LL_ENDL;
 	delete mRightMouseDownSignal;
-
-	LL_INFOS("Baker") << "[3555] ~LLUICtrl() - deleting mRightMouseUpSignal" << LL_ENDL;
 	delete mRightMouseUpSignal;
-
-	LL_INFOS("Baker") << "[3555] ~LLUICtrl() - deleting mDoubleClickSignal" << LL_ENDL;
 	delete mDoubleClickSignal;
 
-	LL_INFOS("Baker") << "[3555] Exiting ~LLUICtrl()" << LL_ENDL;
+	//LL_INFOS("Baker") << "[3555] Exiting ~LLUICtrl()" << LL_ENDL;
 }
 
 void default_commit_handler(LLUICtrl* ctrl, const LLSD& param)
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index daa87e8334..10d9616152 100755
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -150,12 +150,10 @@ LLView::LLView(const LLView::Params& p)
 
 LLView::~LLView()
 {
-	LL_INFOS("Baker") << "[3555] ~LLView -------------------------------------------------" << LL_ENDL;
-	
-	LL_INFOS("Baker") << "[3555] ~LLView() - Dirtying view rect" << LL_ENDL;
+	//LL_INFOS("Baker") << "[3555] ~LLView -- " << mName << ":" << (void*) this << " ----------------------" << LL_ENDL;
+
 	dirtyRect();
 	//llinfos << "Deleting view " << mName << ":" << (void*) this << llendl;
-	LL_INFOS("Baker") << "[3555] ~LLView() - Deleting view " << mName << ":" << (void*) this << LL_ENDL;
 	if (LLView::sIsDrawing)
 	{
 		LL_INFOS("Baker") << "[3555] ~LLView() - Deleting view " << mName << " during UI draw() phase" << LL_ENDL;
@@ -173,26 +171,21 @@ LLView::~LLView()
 		gFocusMgr.removeMouseCaptureWithoutCallback( this );
 	}
 
-	LL_INFOS("Baker") << "[3555] ~LLView() - Deleting all children..." << LL_ENDL;
 	deleteAllChildren();
 
-	LL_INFOS("Baker") << "[3555] ~LLView() - done." << LL_ENDL;
-
 	if (mParentView != NULL)
 	{
-		LL_INFOS("Baker") << "[3555] ~LLView() - Removing this child view" << LL_ENDL;
+	//	LL_INFOS("Baker") << "[3555] ~LLView() - Removing this child view" << LL_ENDL;
 		mParentView->removeChild(this);
 	}
 
 	if (mDefaultWidgets)
 	{
-		LL_INFOS("Baker") << "[3555] ~LLView() - Deleting default widgets" << LL_ENDL;
-
 		delete mDefaultWidgets;
 		mDefaultWidgets = NULL;
 	}
 
-	LL_INFOS("Baker") << "[3555] Exiting ~LLView()" << LL_ENDL;
+	//LL_INFOS("Baker") << "[3555] Exiting ~LLView() " << (void*) this << LL_ENDL;
 }
 
 // virtual
diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp
index 854975d05f..462c731848 100755
--- a/indra/newview/llfloaterimcontainer.cpp
+++ b/indra/newview/llfloaterimcontainer.cpp
@@ -87,44 +87,30 @@ LLFloaterIMContainer::LLFloaterIMContainer(const LLSD& seed, const Params& param
 
 LLFloaterIMContainer::~LLFloaterIMContainer()
 {
-	LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() -------------------------------------------------" << LL_ENDL;
+	LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() -- " << mGeneralTitle << ":" << (void*) this << " ----------------------" << LL_ENDL;
 
-	LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - Stop listening to conversation event stream" << LL_ENDL;
 	mConversationsEventStream.stopListening("ConversationsRefresh");
-
-	LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - Delete idle callback function" << LL_ENDL;
 	gIdleCallbacks.deleteFunction(idle, this);
-
-	LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - Disconnect from new message connection" << LL_ENDL;
 	mNewMessageConnection.disconnect();
-	
-	LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - Remove control view" << LL_ENDL;
 	LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this);
-
-	LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - Are we connected to a signal?" << LL_ENDL;
+	
 	if (mMicroChangedSignal.connected())
 	{
-		LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - Disconnect from it" << LL_ENDL;
+		LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - Disconnect from microsignal" << LL_ENDL;
 		mMicroChangedSignal.disconnect();
 	}
 
-	LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - Save off account settings (state of convo pane)" << LL_ENDL;
 	gSavedPerAccountSettings.setBOOL("ConversationsListPaneCollapsed", mConversationsPane->isCollapsed());
-	
-	LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - Safe off account settings (state of message pane)" << LL_ENDL;
 	gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed());
-	
-	LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - Safe off account settings (state of participant list)" << LL_ENDL;
 	gSavedPerAccountSettings.setBOOL("ConversationsParticipantListCollapsed", !isParticipantListExpanded());
 
-	LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - Checking if LLIMMgr is destroyed" << LL_ENDL;
 	if (!LLSingleton<LLIMMgr>::destroyed())
 	{
-		LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - It is not, so remove the session observer" << LL_ENDL;
+		LL_INFOS("Baker") << "[3555] ~LLFloaterIMContainer() - LLIMMgr is not destroyed, so remove the session observer" << LL_ENDL;
 		LLIMMgr::getInstance()->removeSessionObserver(this);
 	}
 
-	LL_INFOS("Baker") << "[3555] Exiting ~LLFloaterIMContainer()" << LL_ENDL;
+	LL_INFOS("Baker") << "[3555] Exiting ~LLFloaterIMContainer() " << (void*) this << LL_ENDL;
 }
 
 void LLFloaterIMContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, BOOL has_offline_msg)
-- 
cgit v1.2.3