From 99e56eedabfe34dbfbfd8105759403173de72d44 Mon Sep 17 00:00:00 2001
From: Rider Linden <rider@lindenlab.com>
Date: Fri, 28 Aug 2015 16:55:33 -0700
Subject: MAINT-5575: Begin conversion to Singleton<> for Experience Cache. 
 Commited on branch so that I don't trigger a build of it until I'm ready.

--HG--
branch : MAINT-5575
---
 indra/llmessage/llexperiencecache.cpp | 925 +++++++++++++++++-----------------
 indra/llmessage/llexperiencecache.h   |  67 ++-
 indra/newview/llstartup.cpp           |   5 +-
 3 files changed, 515 insertions(+), 482 deletions(-)

(limited to 'indra')

diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp
index 52b60a176e..d196d7da93 100644
--- a/indra/llmessage/llexperiencecache.cpp
+++ b/indra/llmessage/llexperiencecache.cpp
@@ -34,608 +34,613 @@
 #include "boost/tokenizer.hpp"
 
 
-namespace LLExperienceCache
-{
+typedef std::map<LLUUID, LLUUID> KeyMap;
+KeyMap privateToPublicKeyMap;
 
-    typedef std::map<LLUUID, LLUUID> KeyMap;
-    KeyMap privateToPublicKeyMap;
+void mapKeys(const LLSD& legacyKeys);
 
-    void mapKeys(const LLSD& legacyKeys);
+std::string sLookupURL;
 
-	std::string sLookupURL;
+typedef std::map<LLUUID, std::string> ask_queue_t;
+ask_queue_t sAskQueue;
 
-	typedef std::map<LLUUID, std::string> ask_queue_t;
-	ask_queue_t sAskQueue;
+typedef std::map<LLUUID, F64> pending_queue_t;
+pending_queue_t sPendingQueue;
 
-	typedef std::map<LLUUID, F64> pending_queue_t;
-	pending_queue_t sPendingQueue;
+LLExperienceCache::cache_t sCache;
+int sMaximumLookups = 10;
 
-	cache_t sCache;
-	int sMaximumLookups = 10;
+LLFrameTimer sRequestTimer;
 
-	LLFrameTimer sRequestTimer;
+// Periodically clean out expired entries from the cache
+LLFrameTimer sEraseExpiredTimer;
 
-	// Periodically clean out expired entries from the cache
-	LLFrameTimer sEraseExpiredTimer;
+// May have multiple callbacks for a single ID, which are
+// represented as multiple slots bound to the signal.
+// Avoid copying signals via pointers.
+typedef std::map<LLUUID, callback_signal_t*> signal_map_t;
+signal_map_t sSignalMap;
 
-	// May have multiple callbacks for a single ID, which are
-	// represented as multiple slots bound to the signal.
-	// Avoid copying signals via pointers.
-	typedef std::map<LLUUID, callback_signal_t*> signal_map_t;
-	signal_map_t sSignalMap;
 
+bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age);
+void eraseExpired();
 
 
-	bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age);
-	void eraseExpired();
+//=========================================================================
+LLExperienceCache::LLExperienceCache()
+{
+}
 
-	void processExperience( const LLUUID& public_key, const LLSD& experience ) 
-	{
-		sCache[public_key]=experience;
-		LLSD & row = sCache[public_key];
+LLExperienceCache::~LLExperienceCache()
+{
+}
 
-		if(row.has(EXPIRES))
-		{
-			row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds();
-		}
+//-------------------------------------------------------------------------
+void LLExperienceCache::importFile(std::istream& istr)
+{
+    LLSD data;
+    S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr);
+    if (parse_count < 1) return;
 
-		if(row.has(EXPERIENCE_ID))
-		{
-			sPendingQueue.erase(row[EXPERIENCE_ID].asUUID());
-		}
+    LLSD experiences = data["experiences"];
 
-		//signal
-		signal_map_t::iterator sig_it =	sSignalMap.find(public_key);
-		if (sig_it != sSignalMap.end())
-		{
-			callback_signal_t* signal = sig_it->second;
-			(*signal)(experience);
+    LLUUID public_key;
+    LLSD::map_const_iterator it = experiences.beginMap();
+    for (; it != experiences.endMap(); ++it)
+    {
+        public_key.set(it->first);
+        sCache[public_key] = it->second;
+    }
 
-			sSignalMap.erase(public_key);
+    LL_DEBUGS("ExperienceCache") << "importFile() loaded " << sCache.size() << LL_ENDL;
+}
 
-			delete signal;
-		}
-	}
+void LLExperienceCache::exportFile(std::ostream& ostr) const
+{
+    LLSD experiences;
 
-	void initClass( )
-	{
-	}
+    cache_t::const_iterator it = sCache.begin();
+    for (; it != sCache.end(); ++it)
+    {
+        if (!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() ||
+            it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID))
+            continue;
 
-	const cache_t& getCached()
+        experiences[it->first.asString()] = it->second;
+    }
+
+    LLSD data;
+    data["experiences"] = experiences;
+
+    LLSDSerialize::toPrettyXML(data, ostr);
+}
+
+// *TODO$: Rider: These three functions not seem to be used... it may be useful in testing.
+void LLExperienceCache::bootstrap(const LLSD& legacyKeys, int initialExpiration)
+{
+    mapKeys(legacyKeys);
+    LLSD::array_const_iterator it = legacyKeys.beginArray();
+    for (/**/; it != legacyKeys.endArray(); ++it)
+    {
+        LLSD experience = *it;
+        if (experience.has(EXPERIENCE_ID))
+        {
+            if (!experience.has(EXPIRES))
+            {
+                experience[EXPIRES] = initialExpiration;
+            }
+            processExperience(experience[EXPERIENCE_ID].asUUID(), experience);
+        }
+        else
+        {
+            LL_WARNS("ExperienceCache")
+                << "Skipping bootstrap entry which is missing " << EXPERIENCE_ID
+                << LL_ENDL;
+        }
+    }
+}
+
+void LLExperienceCache::mapKeys(const LLSD& legacyKeys)
+{
+    LLSD::array_const_iterator exp = legacyKeys.beginArray();
+    for (/**/; exp != legacyKeys.endArray(); ++exp)
+    {
+        if (exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY))
+        {
+            privateToPublicKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()] = (*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID();
+        }
+    }
+}
+
+LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found)
+{
+    if (private_key.isNull())
+        return LLUUID::null;
+
+    KeyMap::const_iterator it = privateToPublicKeyMap.find(private_key);
+    if (it == privateToPublicKeyMap.end())
+    {
+        if (null_if_not_found)
+        {
+            return LLUUID::null;
+        }
+        return private_key;
+    }
+    LL_WARNS("LLExperience") << "converted private key " << private_key << " to experience_id " << it->second << LL_ENDL;
+    return it->second;
+}
+
+//=========================================================================
+void LLExperienceCache::processExperience(const LLUUID& public_key, const LLSD& experience)
+{
+	sCache[public_key]=experience;
+	LLSD & row = sCache[public_key];
+
+	if(row.has(EXPIRES))
 	{
-		return sCache;
+		row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds();
 	}
 
-	void setMaximumLookups( int maximumLookups)
+	if(row.has(EXPERIENCE_ID))
 	{
-		sMaximumLookups = maximumLookups;
+		sPendingQueue.erase(row[EXPERIENCE_ID].asUUID());
 	}
 
-	void bootstrap(const LLSD& legacyKeys, int initialExpiration)
+	//signal
+	signal_map_t::iterator sig_it =	sSignalMap.find(public_key);
+	if (sig_it != sSignalMap.end())
 	{
-        mapKeys(legacyKeys);
-		LLSD::array_const_iterator it = legacyKeys.beginArray();
-		for(/**/; it != legacyKeys.endArray(); ++it)
-		{
-			LLSD experience = *it;
-			if(experience.has(EXPERIENCE_ID))
-			{
-				if(!experience.has(EXPIRES))
-				{
-					experience[EXPIRES] = initialExpiration;
-				}
-				processExperience(experience[EXPERIENCE_ID].asUUID(), experience);
-			}
-			else
-			{
-				LL_WARNS("ExperienceCache") 
-					<< "Skipping bootstrap entry which is missing " << EXPERIENCE_ID 
-					<< LL_ENDL;
-			}
-		}		
+		callback_signal_t* signal = sig_it->second;
+		(*signal)(experience);
+
+		sSignalMap.erase(public_key);
+
+		delete signal;
 	}
+}
 
+const LLExperienceCache::cache_t& LLExperienceCache::getCached()
+{
+	return sCache;
+}
+
+void LLExperienceCache::setMaximumLookups(int maximumLookups)
+{
+	sMaximumLookups = maximumLookups;
+}
 
 
-	bool expirationFromCacheControl(LLSD headers, F64 *expires)
+bool LLExperienceCache::expirationFromCacheControl(LLSD headers, F64 *expires)
+{
+	// Allow the header to override the default
+	LLSD cache_control_header = headers["cache-control"];
+	if (cache_control_header.isDefined())
 	{
-		// Allow the header to override the default
-		LLSD cache_control_header = headers["cache-control"];
-		if (cache_control_header.isDefined())
+		S32 max_age = 0;
+		std::string cache_control = cache_control_header.asString();
+		if (max_age_from_cache_control(cache_control, &max_age))
 		{
-			S32 max_age = 0;
-			std::string cache_control = cache_control_header.asString();
-			if (max_age_from_cache_control(cache_control, &max_age))
-			{
-				LL_WARNS("ExperienceCache") 
-					<< "got EXPIRES from headers, max_age " << max_age 
-					<< LL_ENDL;
-				F64 now = LLFrameTimer::getTotalSeconds();
-				*expires = now + (F64)max_age;
-				return true;
-			}
+			LL_WARNS("ExperienceCache") 
+				<< "got EXPIRES from headers, max_age " << max_age 
+				<< LL_ENDL;
+			F64 now = LLFrameTimer::getTotalSeconds();
+			*expires = now + (F64)max_age;
+			return true;
 		}
-		return false;
 	}
+	return false;
+}
 
 
-	static const std::string MAX_AGE("max-age");
-	static const boost::char_separator<char> EQUALS_SEPARATOR("=");
-	static const boost::char_separator<char> COMMA_SEPARATOR(",");
+static const std::string MAX_AGE("max-age");
+static const boost::char_separator<char> EQUALS_SEPARATOR("=");
+static const boost::char_separator<char> COMMA_SEPARATOR(",");
+
+bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age)
+{
+	// Split the string on "," to get a list of directives
+	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+	tokenizer directives(cache_control, COMMA_SEPARATOR);
 
-	bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age)
+	tokenizer::iterator token_it = directives.begin();
+	for ( ; token_it != directives.end(); ++token_it)
 	{
-		// Split the string on "," to get a list of directives
-		typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
-		tokenizer directives(cache_control, COMMA_SEPARATOR);
+		// Tokens may have leading or trailing whitespace
+		std::string token = *token_it;
+		LLStringUtil::trim(token);
 
-		tokenizer::iterator token_it = directives.begin();
-		for ( ; token_it != directives.end(); ++token_it)
+		if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
 		{
-			// Tokens may have leading or trailing whitespace
-			std::string token = *token_it;
-			LLStringUtil::trim(token);
-
-			if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
+			// ...this token starts with max-age, so let's chop it up by "="
+			tokenizer subtokens(token, EQUALS_SEPARATOR);
+			tokenizer::iterator subtoken_it = subtokens.begin();
+
+			// Must have a token
+			if (subtoken_it == subtokens.end()) return false;
+			std::string subtoken = *subtoken_it;
+
+			// Must exactly equal "max-age"
+			LLStringUtil::trim(subtoken);
+			if (subtoken != MAX_AGE) return false;
+
+			// Must have another token
+			++subtoken_it;
+			if (subtoken_it == subtokens.end()) return false;
+			subtoken = *subtoken_it;
+
+			// Must be a valid integer
+			// *NOTE: atoi() returns 0 for invalid values, so we have to
+			// check the string first.
+			// *TODO: Do servers ever send "0000" for zero?  We don't handle it
+			LLStringUtil::trim(subtoken);
+			if (subtoken == "0")
 			{
-				// ...this token starts with max-age, so let's chop it up by "="
-				tokenizer subtokens(token, EQUALS_SEPARATOR);
-				tokenizer::iterator subtoken_it = subtokens.begin();
-
-				// Must have a token
-				if (subtoken_it == subtokens.end()) return false;
-				std::string subtoken = *subtoken_it;
-
-				// Must exactly equal "max-age"
-				LLStringUtil::trim(subtoken);
-				if (subtoken != MAX_AGE) return false;
-
-				// Must have another token
-				++subtoken_it;
-				if (subtoken_it == subtokens.end()) return false;
-				subtoken = *subtoken_it;
-
-				// Must be a valid integer
-				// *NOTE: atoi() returns 0 for invalid values, so we have to
-				// check the string first.
-				// *TODO: Do servers ever send "0000" for zero?  We don't handle it
-				LLStringUtil::trim(subtoken);
-				if (subtoken == "0")
-				{
-					*max_age = 0;
-					return true;
-				}
-				S32 val = atoi( subtoken.c_str() );
-				if (val > 0 && val < S32_MAX)
-				{
-					*max_age = val;
-					return true;
-				}
-				return false;
+				*max_age = 0;
+				return true;
 			}
+			S32 val = atoi( subtoken.c_str() );
+			if (val > 0 && val < S32_MAX)
+			{
+				*max_age = val;
+				return true;
+			}
+			return false;
 		}
-		return false;
 	}
+	return false;
+}
 
 
-	void importFile(std::istream& istr)
+class LLExperienceResponder : public LLHTTPClient::Responder
+{
+public:
+	LLExperienceResponder(const ask_queue_t& keys)
+		:mKeys(keys)
 	{
-		LLSD data;
-		S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr);
-		if(parse_count < 1) return;
-
-		LLSD experiences = data["experiences"];
 
-		LLUUID public_key;
-		LLSD::map_const_iterator it = experiences.beginMap();
-		for(; it != experiences.endMap() ; ++it)
-		{
-			public_key.set(it->first);
-			sCache[public_key]=it->second;
-		}
-
-		LL_DEBUGS("ExperienceCache") << "importFile() loaded " << sCache.size() << LL_ENDL;
 	}
 
-	void exportFile(std::ostream& ostr)
+	/*virtual*/ void httpCompleted()
 	{
-		LLSD experiences;
-
-		cache_t::const_iterator it =sCache.begin();
-		for( ; it != sCache.end() ; ++it)
+		LLSD experiences = getContent()["experience_keys"];
+		LLSD::array_const_iterator it = experiences.beginArray();
+		for( /**/ ; it != experiences.endArray(); ++it)
 		{
-			if(!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() ||
-				it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID))
-				continue;
+			const LLSD& row = *it;
+			LLUUID public_key = row[EXPERIENCE_ID].asUUID();
 
-			experiences[it->first.asString()] = it->second;
-		}
 
-		LLSD data;
-		data["experiences"] = experiences;
+			LL_DEBUGS("ExperienceCache") << "Received result for " << public_key 
+				<< " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL ;
 
-		LLSDSerialize::toPrettyXML(data, ostr);
-	}
+			processExperience(public_key, row);
+		}
 
-	class LLExperienceResponder : public LLHTTPClient::Responder
-	{
-	public:
-		LLExperienceResponder(const ask_queue_t& keys)
-			:mKeys(keys)
+		LLSD error_ids = getContent()["error_ids"];
+		LLSD::array_const_iterator errIt = error_ids.beginArray();
+		for( /**/ ; errIt != error_ids.endArray() ; ++errIt )
 		{
-
+			LLUUID id = errIt->asUUID();		
+			LLSD exp;
+			exp[EXPIRES]=DEFAULT_EXPIRATION;
+			exp[EXPERIENCE_ID] = id;
+			exp[PROPERTIES]=PROPERTY_INVALID;
+			exp[MISSING]=true;
+            exp[QUOTA] = DEFAULT_QUOTA;
+
+			processExperience(id, exp);
+			LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL ;
 		}
 
-		/*virtual*/ void httpCompleted()
+		LL_DEBUGS("ExperienceCache") << sCache.size() << " cached experiences" << LL_ENDL;
+	}
+
+	/*virtual*/ void httpFailure()
+	{
+ 		LL_WARNS("ExperienceCache") << "Request failed "<<getStatus()<<" "<<getReason()<< LL_ENDL;
+ 		// We're going to construct a dummy record and cache it for a while,
+ 		// either briefly for a 503 Service Unavailable, or longer for other
+ 		// errors.
+ 		F64 retry_timestamp = errorRetryTimestamp(getStatus());
+ 
+ 
+ 		// Add dummy records for all agent IDs in this request
+ 		ask_queue_t::const_iterator it = mKeys.begin();
+ 		for ( ; it != mKeys.end(); ++it)
 		{
-			LLSD experiences = getContent()["experience_keys"];
-			LLSD::array_const_iterator it = experiences.beginArray();
-			for( /**/ ; it != experiences.endArray(); ++it)
-			{
-				const LLSD& row = *it;
-				LLUUID public_key = row[EXPERIENCE_ID].asUUID();
 
+			LLSD exp = get(it->first);
+            //leave the properties alone if we already have a cache entry for this xp
+            if(exp.isUndefined())
+            {
+                exp[PROPERTIES]=PROPERTY_INVALID;
+            }
+			exp[EXPIRES]=retry_timestamp;
+			exp[EXPERIENCE_ID] = it->first;
+			exp["key_type"] = it->second;
+			exp["uuid"] = it->first;
+			exp["error"] = (LLSD::Integer)getStatus();
+            exp[QUOTA] = DEFAULT_QUOTA;
+
+ 			LLExperienceCache::processExperience(it->first, exp);
+ 		}
 
-				LL_DEBUGS("ExperienceCache") << "Received result for " << public_key 
-					<< " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL ;
+	}
 
-				processExperience(public_key, row);
-			}
+	// Return time to retry a request that generated an error, based on
+	// error type and headers.  Return value is seconds-since-epoch.
+	F64 errorRetryTimestamp(S32 status)
+	{
 
-			LLSD error_ids = getContent()["error_ids"];
-			LLSD::array_const_iterator errIt = error_ids.beginArray();
-			for( /**/ ; errIt != error_ids.endArray() ; ++errIt )
+		// Retry-After takes priority
+		LLSD retry_after = getResponseHeaders()["retry-after"];
+		if (retry_after.isDefined())
+		{
+			// We only support the delta-seconds type
+			S32 delta_seconds = retry_after.asInteger();
+			if (delta_seconds > 0)
 			{
-				LLUUID id = errIt->asUUID();		
-				LLSD exp;
-				exp[EXPIRES]=DEFAULT_EXPIRATION;
-				exp[EXPERIENCE_ID] = id;
-				exp[PROPERTIES]=PROPERTY_INVALID;
-				exp[MISSING]=true;
-                exp[QUOTA] = DEFAULT_QUOTA;
-
-				processExperience(id, exp);
-				LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL ;
+				// ...valid delta-seconds
+				return F64(delta_seconds);
 			}
-
-			LL_DEBUGS("ExperienceCache") << sCache.size() << " cached experiences" << LL_ENDL;
 		}
 
-		/*virtual*/ void httpFailure()
+		// If no Retry-After, look for Cache-Control max-age
+		F64 expires = 0.0;
+		if (LLExperienceCache::expirationFromCacheControl(getResponseHeaders(), &expires))
 		{
- 			LL_WARNS("ExperienceCache") << "Request failed "<<getStatus()<<" "<<getReason()<< LL_ENDL;
- 			// We're going to construct a dummy record and cache it for a while,
- 			// either briefly for a 503 Service Unavailable, or longer for other
- 			// errors.
- 			F64 retry_timestamp = errorRetryTimestamp(getStatus());
- 
- 
- 			// Add dummy records for all agent IDs in this request
- 			ask_queue_t::const_iterator it = mKeys.begin();
- 			for ( ; it != mKeys.end(); ++it)
-			{
-
-				LLSD exp = get(it->first);
-                //leave the properties alone if we already have a cache entry for this xp
-                if(exp.isUndefined())
-                {
-                    exp[PROPERTIES]=PROPERTY_INVALID;
-                }
-				exp[EXPIRES]=retry_timestamp;
-				exp[EXPERIENCE_ID] = it->first;
-				exp["key_type"] = it->second;
-				exp["uuid"] = it->first;
-				exp["error"] = (LLSD::Integer)getStatus();
-                exp[QUOTA] = DEFAULT_QUOTA;
-
- 				LLExperienceCache::processExperience(it->first, exp);
- 			}
-
+			return expires;
 		}
 
-		// Return time to retry a request that generated an error, based on
-		// error type and headers.  Return value is seconds-since-epoch.
-		F64 errorRetryTimestamp(S32 status)
+		// No information in header, make a guess
+		if (status == 503)
 		{
+			// ...service unavailable, retry soon
+			const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min
+			return SERVICE_UNAVAILABLE_DELAY;
+		}
+		else if (status == 499)
+		{
+			// ...we were probably too busy, retry quickly
+			const F64 BUSY_DELAY = 10.0; // 10 seconds
+			return BUSY_DELAY;
 
-			// Retry-After takes priority
-			LLSD retry_after = getResponseHeaders()["retry-after"];
-			if (retry_after.isDefined())
-			{
-				// We only support the delta-seconds type
-				S32 delta_seconds = retry_after.asInteger();
-				if (delta_seconds > 0)
-				{
-					// ...valid delta-seconds
-					return F64(delta_seconds);
-				}
-			}
-
-			// If no Retry-After, look for Cache-Control max-age
-			F64 expires = 0.0;
-			if (LLExperienceCache::expirationFromCacheControl(getResponseHeaders(), &expires))
-			{
-				return expires;
-			}
-
-			// No information in header, make a guess
-			if (status == 503)
-			{
-				// ...service unavailable, retry soon
-				const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min
-				return SERVICE_UNAVAILABLE_DELAY;
-			}
-			else if (status == 499)
-			{
-				// ...we were probably too busy, retry quickly
-				const F64 BUSY_DELAY = 10.0; // 10 seconds
-				return BUSY_DELAY;
-
-			}
-			else
-			{
-				// ...other unexpected error
-				const F64 DEFAULT_DELAY = 3600.0; // 1 hour
-				return DEFAULT_DELAY;
-			}
 		}
+		else
+		{
+			// ...other unexpected error
+			const F64 DEFAULT_DELAY = 3600.0; // 1 hour
+			return DEFAULT_DELAY;
+		}
+	}
 
-	private:
-		ask_queue_t mKeys;
-	};
+private:
+	ask_queue_t mKeys;
+};
 
-	void requestExperiences() 
-	{
-		if(sAskQueue.empty() || sLookupURL.empty())
-			return;
 
-		F64 now = LLFrameTimer::getTotalSeconds();
+void LLExperienceCache::requestExperiences()
+{
+	if(sAskQueue.empty() || sLookupURL.empty())
+		return;
 
-		const U32 EXP_URL_SEND_THRESHOLD = 3000;
-		const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD/UUID_STR_LENGTH;
+	F64 now = LLFrameTimer::getTotalSeconds();
 
-		std::ostringstream ostr;
+	const U32 EXP_URL_SEND_THRESHOLD = 3000;
+	const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD/UUID_STR_LENGTH;
 
-		ask_queue_t keys;
+	std::ostringstream ostr;
 
-		ostr << sLookupURL << "?page_size=" << PAGE_SIZE;
+	ask_queue_t keys;
 
+	ostr << sLookupURL << "?page_size=" << PAGE_SIZE;
 
-		int request_count = 0;
-		while(!sAskQueue.empty() && request_count < sMaximumLookups)
-		{
-			ask_queue_t::iterator it = sAskQueue.begin();
-			const LLUUID& key = it->first;
-			const std::string& key_type = it->second;
 
-			ostr << '&' << key_type << '=' << key.asString() ;
+	int request_count = 0;
+	while(!sAskQueue.empty() && request_count < sMaximumLookups)
+	{
+		ask_queue_t::iterator it = sAskQueue.begin();
+		const LLUUID& key = it->first;
+		const std::string& key_type = it->second;
+
+		ostr << '&' << key_type << '=' << key.asString() ;
 		
-			keys[key]=key_type;
-			request_count++;
+		keys[key]=key_type;
+		request_count++;
 
-			sPendingQueue[key] = now;
+		sPendingQueue[key] = now;
 			
-			if(ostr.tellp() > EXP_URL_SEND_THRESHOLD)
-			{
-				LL_DEBUGS("ExperienceCache") <<  "requestExperiences() query: " << ostr.str() << LL_ENDL;
-				LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys));
-				ostr.clear();
-				ostr.str(sLookupURL);
-				ostr << "?page_size=" << PAGE_SIZE;
-				keys.clear();
-			}
-			sAskQueue.erase(it);
-		}
-
-		if(ostr.tellp() > sLookupURL.size())
+		if(ostr.tellp() > EXP_URL_SEND_THRESHOLD)
 		{
-			LL_DEBUGS("ExperienceCache") <<  "requestExperiences() query 2: " << ostr.str() << LL_ENDL;
+			LL_DEBUGS("ExperienceCache") <<  "requestExperiences() query: " << ostr.str() << LL_ENDL;
 			LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys));
+			ostr.clear();
+			ostr.str(sLookupURL);
+			ostr << "?page_size=" << PAGE_SIZE;
+			keys.clear();
 		}
+		sAskQueue.erase(it);
 	}
 
-	bool isRequestPending(const LLUUID& public_key)
+	if(ostr.tellp() > sLookupURL.size())
 	{
-		bool isPending = false;
-		const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0;
+		LL_DEBUGS("ExperienceCache") <<  "requestExperiences() query 2: " << ostr.str() << LL_ENDL;
+		LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys));
+	}
+}
 
-		pending_queue_t::const_iterator it = sPendingQueue.find(public_key);
 
-		if(it != sPendingQueue.end())
-		{
-			F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS;
-			isPending = (it->second > expire_time);
-		}
+bool LLExperienceCache::isRequestPending(const LLUUID& public_key)
+{
+	bool isPending = false;
+	const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0;
+
+	pending_queue_t::const_iterator it = sPendingQueue.find(public_key);
 
-		return isPending;
+	if(it != sPendingQueue.end())
+	{
+		F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS;
+		isPending = (it->second > expire_time);
 	}
 
+	return isPending;
+}
+
 
-	void setLookupURL( const std::string& lookup_url )
+void LLExperienceCache::setLookupURL(const std::string& lookup_url)
+{
+	sLookupURL = lookup_url;
+	if(!sLookupURL.empty())
 	{
-		sLookupURL = lookup_url;
-		if(!sLookupURL.empty())
-		{
-			sLookupURL += "id/";
-		}
+		sLookupURL += "id/";
 	}
+}
+
+bool LLExperienceCache::hasLookupURL()
+{
+	return !sLookupURL.empty();
+}
 
-	bool hasLookupURL()
+void LLExperienceCache::idle()
+{
+
+	const F32 SECS_BETWEEN_REQUESTS = 0.1f;
+	if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS))
 	{
-		return !sLookupURL.empty();
+		return;
 	}
 
-	void idle()
+	// Must be large relative to above
+	const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds
+	if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT))
 	{
-
-		const F32 SECS_BETWEEN_REQUESTS = 0.1f;
-		if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS))
-		{
-			return;
-		}
-
-		// Must be large relative to above
-		const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds
-		if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT))
-		{
-			eraseExpired();
-		}
+		eraseExpired();
+	}
 
 
-		if(!sAskQueue.empty())
-		{
-			requestExperiences();
-		}
+	if(!sAskQueue.empty())
+	{
+		requestExperiences();
 	}
+}
 
-	void erase( const LLUUID& key )
-	{
-		cache_t::iterator it = sCache.find(key);
+void LLExperienceCache::erase(const LLUUID& key)
+{
+	cache_t::iterator it = sCache.find(key);
 				
-		if(it != sCache.end())
-		{
-			sCache.erase(it);
-		}
+	if(it != sCache.end())
+	{
+		sCache.erase(it);
 	}
+}
 
-	void eraseExpired()
+void LLExperienceCache::eraseExpired()
+{
+	F64 now = LLFrameTimer::getTotalSeconds();
+	cache_t::iterator it = sCache.begin();
+	while (it != sCache.end())
 	{
-		F64 now = LLFrameTimer::getTotalSeconds();
-		cache_t::iterator it = sCache.begin();
-		while (it != sCache.end())
-		{
-			cache_t::iterator cur = it;
-			LLSD& exp = cur->second;
-			++it;
+		cache_t::iterator cur = it;
+		LLSD& exp = cur->second;
+		++it;
 
-			if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now)
+		if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now)
+		{
+            if(!exp.has(EXPERIENCE_ID))
 			{
-                if(!exp.has(EXPERIENCE_ID))
+                LL_WARNS("ExperienceCache") << "Removing experience with no id " << LL_ENDL ;
+                sCache.erase(cur);
+				}
+            else
+            {
+                LLUUID id = exp[EXPERIENCE_ID].asUUID();
+                LLUUID private_key = exp.has(LLExperienceCache::PRIVATE_KEY) ? exp[LLExperienceCache::PRIVATE_KEY].asUUID():LLUUID::null;
+                if(private_key.notNull() || !exp.has("DoesNotExist"))
 				{
-                    LL_WARNS("ExperienceCache") << "Removing experience with no id " << LL_ENDL ;
-                    sCache.erase(cur);
-					}
-                else
-                {
-                    LLUUID id = exp[EXPERIENCE_ID].asUUID();
-                    LLUUID private_key = exp.has(LLExperienceCache::PRIVATE_KEY) ? exp[LLExperienceCache::PRIVATE_KEY].asUUID():LLUUID::null;
-                    if(private_key.notNull() || !exp.has("DoesNotExist"))
-					{
-						fetch(id, true);
-					}
-					else
-					{
-                        LL_WARNS("ExperienceCache") << "Removing invalid experience " << id << LL_ENDL ;
-						sCache.erase(cur);
-					}
+					fetch(id, true);
+				}
+				else
+				{
+                    LL_WARNS("ExperienceCache") << "Removing invalid experience " << id << LL_ENDL ;
+					sCache.erase(cur);
 				}
 			}
 		}
 	}
+}
 
 	
-	bool fetch( const LLUUID& key, bool refresh/* = true*/ ) 
+bool LLExperienceCache::fetch(const LLUUID& key, bool refresh/* = true*/)
+{
+	if(!key.isNull() && !isRequestPending(key) && (refresh || sCache.find(key)==sCache.end()))
 	{
-		if(!key.isNull() && !isRequestPending(key) && (refresh || sCache.find(key)==sCache.end()))
-		{
-			LL_DEBUGS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL ;
-			sAskQueue[key]=EXPERIENCE_ID;
+		LL_DEBUGS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL ;
+		sAskQueue[key]=EXPERIENCE_ID;
 
-			return true;
-		}
-		return false;
+		return true;
 	}
+	return false;
+}
 
-	void insert(const LLSD& experience_data )
+void LLExperienceCache::insert(const LLSD& experience_data)
+{
+	if(experience_data.has(EXPERIENCE_ID))
 	{
-		if(experience_data.has(EXPERIENCE_ID))
-		{
-            processExperience(experience_data[EXPERIENCE_ID].asUUID(), experience_data);
-		}
-		else
-		{
-			LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL;
-		}
+        processExperience(experience_data[EXPERIENCE_ID].asUUID(), experience_data);
 	}
-	static LLSD empty;
-	const LLSD& get(const LLUUID& key)
+	else
 	{
-		if(key.isNull()) return empty;
-		cache_t::const_iterator it = sCache.find(key);
-
-		if (it != sCache.end())
-		{
-			return it->second;
-		}
-
-		fetch(key);
-
-		return empty;
+		LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL;
 	}
-	void get( const LLUUID& key, callback_slot_t slot )
-	{
-		if(key.isNull()) return;
-
-		cache_t::const_iterator it = sCache.find(key);
-		if (it != sCache.end())
-		{
-			// ...name already exists in cache, fire callback now
-			callback_signal_t signal;
-			signal.connect(slot);
-			
-			signal(it->second);
-			return;
-		}
+}
 
-		fetch(key);
+static LLSD empty;
+const LLSD& LLExperienceCache::get(const LLUUID& key)
+{
+	if(key.isNull()) return empty;
+	cache_t::const_iterator it = sCache.find(key);
 
-		// always store additional callback, even if request is pending
-		signal_map_t::iterator sig_it = sSignalMap.find(key);
-		if (sig_it == sSignalMap.end())
-		{
-			// ...new callback for this id
-			callback_signal_t* signal = new callback_signal_t();
-			signal->connect(slot);
-			sSignalMap[key] = signal;
-		}
-		else
-		{
-			// ...existing callback, bind additional slot
-			callback_signal_t* signal = sig_it->second;
-			signal->connect(slot);
-		}
+	if (it != sCache.end())
+	{
+		return it->second;
 	}
 
-}
-
+	fetch(key);
 
-void LLExperienceCache::mapKeys( const LLSD& legacyKeys )
+	return empty;
+}
+void LLExperienceCache::get(const LLUUID& key, callback_slot_t slot)
 {
-	LLSD::array_const_iterator exp = legacyKeys.beginArray();
-	for(/**/ ; exp != legacyKeys.endArray() ; ++exp)
+	if(key.isNull()) return;
+
+	cache_t::const_iterator it = sCache.find(key);
+	if (it != sCache.end())
 	{
-		if(exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY))
-		{
-            privateToPublicKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()]=(*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID();
-		}
+		// ...name already exists in cache, fire callback now
+		callback_signal_t signal;
+		signal.connect(slot);
+			
+		signal(it->second);
+		return;
 	}
-}
 
+	fetch(key);
 
-LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found)
-{
-	if (private_key.isNull())
-		return LLUUID::null;
-
-    KeyMap::const_iterator it=privateToPublicKeyMap.find(private_key);
-    if(it == privateToPublicKeyMap.end())
+	// always store additional callback, even if request is pending
+	signal_map_t::iterator sig_it = sSignalMap.find(key);
+	if (sig_it == sSignalMap.end())
 	{
-		if(null_if_not_found)
-		{
-			return LLUUID::null;
-		}
-		return private_key;
+		// ...new callback for this id
+		callback_signal_t* signal = new callback_signal_t();
+		signal->connect(slot);
+		sSignalMap[key] = signal;
+	}
+	else
+	{
+		// ...existing callback, bind additional slot
+		callback_signal_t* signal = sig_it->second;
+		signal->connect(slot);
 	}
-	LL_WARNS("LLExperience") << "converted private key " << private_key << " to experience_id " << it->second << LL_ENDL;
-	return it->second;
 }
+
+
diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h
index e669ee888e..8a55719443 100644
--- a/indra/llmessage/llexperiencecache.h
+++ b/indra/llmessage/llexperiencecache.h
@@ -30,15 +30,47 @@
 #define LL_LLEXPERIENCECACHE_H
 
 #include "linden_common.h"
+#include "llsingleton.h"
+#include "llsd.h"
 #include <boost/signals2.hpp>
+#include <boost/function.hpp>
 
 class LLSD;
 class LLUUID;
 
 
-
-namespace LLExperienceCache
+class LLExperienceCache: public LLSingleton < LLExperienceCache >
 {
+    friend class LLSingleton < LLExperienceCache > ;
+
+public:
+    typedef boost::function<void(const LLSD &)> Callback_t;
+
+    void erase(const LLUUID& key);
+    bool fetch(const LLUUID& key, bool refresh = false);
+    void insert(const LLSD& experience_data);
+    const LLSD& get(const LLUUID& key);
+
+    // If name information is in cache, callback will be called immediately.
+    void get(const LLUUID& key, Callback_t slot);
+
+private:
+    // Callback types for get() 
+//  typedef boost::signals2::signal < void(const LLSD &) > callback_signal_t;
+    typedef boost::signals2::signal < Callback_t > callback_signal_t;
+    typedef std::map<LLUUID, LLSD> cache_t;
+
+//--------------------------------------------
+    LLExperienceCache();
+    virtual ~LLExperienceCache();
+
+    void exportFile(std::ostream& ostr) const;
+    void importFile(std::istream& istr);
+
+//--------------------------------------------
+    void processExperience(const LLUUID& public_key, const LLSD& experience);
+
+
 	const std::string PRIVATE_KEY	= "private_id";
     const std::string MISSING       = "DoesNotExist";
 
@@ -61,19 +93,12 @@ namespace LLExperienceCache
 	const int PROPERTY_GRID			= 1 << 4;
 	const int PROPERTY_PRIVATE		= 1 << 5;
 	const int PROPERTY_DISABLED		= 1 << 6;  
-	const int PROPERTY_SUSPENDED	    = 1 << 7;
-
+	const int PROPERTY_SUSPENDED	= 1 << 7;
 
 	// default values
 	const static F64 DEFAULT_EXPIRATION = 600.0;
 	const static S32 DEFAULT_QUOTA = 128; // this is megabytes
 
-	// Callback types for get() below
-	typedef boost::signals2::signal<void (const LLSD& experience)>
-		callback_signal_t;
-	typedef callback_signal_t::slot_type callback_slot_t;
-	typedef std::map<LLUUID, LLSD> cache_t;
-
 
 	void setLookupURL(const std::string& lookup_url);
 	bool hasLookupURL();
@@ -81,24 +106,26 @@ namespace LLExperienceCache
 	void setMaximumLookups(int maximumLookups);
 
 	void idle();
-	void exportFile(std::ostream& ostr);
-	void importFile(std::istream& istr);
-	void initClass();
 	void bootstrap(const LLSD& legacyKeys, int initialExpiration);
 	
-	void erase(const LLUUID& key);
-	bool fetch(const LLUUID& key, bool refresh=false);
-	void insert(const LLSD& experience_data);
-	const LLSD& get(const LLUUID& key);
-
-	// If name information is in cache, callback will be called immediately.
-	void get(const LLUUID& key, callback_slot_t slot);
 
 	const cache_t& getCached();
 
 	// maps an experience private key to the experience id
 	LLUUID getExperienceId(const LLUUID& private_key, bool null_if_not_found=false);
 
+    //=====================================================================
+    inline friend std::ostream &operator << (std::ostream &os, const LLExperienceCache &cache)
+    {
+        cache.exportFile(os);
+        return os;
+    }
+
+    inline friend std::istream &operator >> (std::istream &is, LLExperienceCache &cache)
+    {
+        cache.importFile(is);
+        return is;
+    }
 };
 
 #endif // LL_LLEXPERIENCECACHE_H
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index c74890a4e9..46f75c4f57 100755
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -2820,9 +2820,10 @@ void LLStartUp::initNameCache()
 
 
 void LLStartUp::initExperiences()
-{
+{   
+    // just a get instance here.  Should trigger loading the cache.
+    LLExperienceCache::getInstance();
 	LLAppViewer::instance()->loadExperienceCache();
-	LLExperienceCache::initClass();
 	LLExperienceLog::instance().initialize();
 }
 
-- 
cgit v1.2.3


From 4b3269c94d8b68c977598d2444ae04f7e1f9062c Mon Sep 17 00:00:00 2001
From: Rider Linden <rider@lindenlab.com>
Date: Mon, 31 Aug 2015 07:27:13 -0700
Subject: Some initial changes to convert the experience cache to a singleton

--HG--
branch : MAINT-5575
---
 indra/llmessage/llexperiencecache.cpp | 214 +++++++++++++++++++---------------
 indra/llmessage/llexperiencecache.h   |  80 +++++++------
 2 files changed, 168 insertions(+), 126 deletions(-)

(limited to 'indra')

diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp
index d196d7da93..34c4210359 100644
--- a/indra/llmessage/llexperiencecache.cpp
+++ b/indra/llmessage/llexperiencecache.cpp
@@ -32,22 +32,18 @@
 #include <set>
 #include <map>
 #include "boost/tokenizer.hpp"
+#include <boost/concept_check.hpp>
 
 
 typedef std::map<LLUUID, LLUUID> KeyMap;
 KeyMap privateToPublicKeyMap;
 
-void mapKeys(const LLSD& legacyKeys);
 
 std::string sLookupURL;
 
-typedef std::map<LLUUID, std::string> ask_queue_t;
-ask_queue_t sAskQueue;
-
 typedef std::map<LLUUID, F64> pending_queue_t;
 pending_queue_t sPendingQueue;
 
-LLExperienceCache::cache_t sCache;
 int sMaximumLookups = 10;
 
 LLFrameTimer sRequestTimer;
@@ -55,16 +51,41 @@ LLFrameTimer sRequestTimer;
 // Periodically clean out expired entries from the cache
 LLFrameTimer sEraseExpiredTimer;
 
-// May have multiple callbacks for a single ID, which are
-// represented as multiple slots bound to the signal.
-// Avoid copying signals via pointers.
-typedef std::map<LLUUID, callback_signal_t*> signal_map_t;
-signal_map_t sSignalMap;
-
 
-bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age);
-void eraseExpired();
+//=========================================================================
+namespace LLExperienceCacheImpl
+{
+	bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age);
+	void mapKeys(const LLSD& legacyKeys);
+}
 
+//=========================================================================
+const std::string LLExperienceCache::PRIVATE_KEY	= "private_id";
+const std::string LLExperienceCache::MISSING       	= "DoesNotExist";
+
+const std::string LLExperienceCache::AGENT_ID      	= "agent_id";
+const std::string LLExperienceCache::GROUP_ID      	= "group_id";
+const std::string LLExperienceCache::EXPERIENCE_ID	= "public_id";
+const std::string LLExperienceCache::NAME			= "name";
+const std::string LLExperienceCache::PROPERTIES		= "properties";
+const std::string LLExperienceCache::EXPIRES		= "expiration";  
+const std::string LLExperienceCache::DESCRIPTION	= "description";
+const std::string LLExperienceCache::QUOTA         	= "quota";
+const std::string LLExperienceCache::MATURITY      	= "maturity";
+const std::string LLExperienceCache::METADATA      	= "extended_metadata";
+const std::string LLExperienceCache::SLURL         	= "slurl";
+
+// should be in sync with experience-api/experiences/models.py
+const int LLExperienceCache::PROPERTY_INVALID		= 1 << 0;
+const int LLExperienceCache::PROPERTY_PRIVILEGED	= 1 << 3;
+const int LLExperienceCache::PROPERTY_GRID			= 1 << 4;
+const int LLExperienceCache::PROPERTY_PRIVATE		= 1 << 5;
+const int LLExperienceCache::PROPERTY_DISABLED		= 1 << 6;  
+const int LLExperienceCache::PROPERTY_SUSPENDED		= 1 << 7;
+
+// default values
+const F64 LLExperienceCache::DEFAULT_EXPIRATION		= 600.0;
+const S32 LLExperienceCache::DEFAULT_QUOTA			= 128; // this is megabytes
 
 //=========================================================================
 LLExperienceCache::LLExperienceCache()
@@ -118,7 +139,7 @@ void LLExperienceCache::exportFile(std::ostream& ostr) const
 // *TODO$: Rider: These three functions not seem to be used... it may be useful in testing.
 void LLExperienceCache::bootstrap(const LLSD& legacyKeys, int initialExpiration)
 {
-    mapKeys(legacyKeys);
+	LLExperienceCacheImpl::mapKeys(legacyKeys);
     LLSD::array_const_iterator it = legacyKeys.beginArray();
     for (/**/; it != legacyKeys.endArray(); ++it)
     {
@@ -140,18 +161,6 @@ void LLExperienceCache::bootstrap(const LLSD& legacyKeys, int initialExpiration)
     }
 }
 
-void LLExperienceCache::mapKeys(const LLSD& legacyKeys)
-{
-    LLSD::array_const_iterator exp = legacyKeys.beginArray();
-    for (/**/; exp != legacyKeys.endArray(); ++exp)
-    {
-        if (exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY))
-        {
-            privateToPublicKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()] = (*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID();
-        }
-    }
-}
-
 LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found)
 {
     if (private_key.isNull())
@@ -190,12 +199,10 @@ void LLExperienceCache::processExperience(const LLUUID& public_key, const LLSD&
 	signal_map_t::iterator sig_it =	sSignalMap.find(public_key);
 	if (sig_it != sSignalMap.end())
 	{
-		callback_signal_t* signal = sig_it->second;
+		signal_ptr signal = sig_it->second;
 		(*signal)(experience);
 
 		sSignalMap.erase(public_key);
-
-		delete signal;
 	}
 }
 
@@ -236,60 +243,6 @@ static const std::string MAX_AGE("max-age");
 static const boost::char_separator<char> EQUALS_SEPARATOR("=");
 static const boost::char_separator<char> COMMA_SEPARATOR(",");
 
-bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age)
-{
-	// Split the string on "," to get a list of directives
-	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
-	tokenizer directives(cache_control, COMMA_SEPARATOR);
-
-	tokenizer::iterator token_it = directives.begin();
-	for ( ; token_it != directives.end(); ++token_it)
-	{
-		// Tokens may have leading or trailing whitespace
-		std::string token = *token_it;
-		LLStringUtil::trim(token);
-
-		if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
-		{
-			// ...this token starts with max-age, so let's chop it up by "="
-			tokenizer subtokens(token, EQUALS_SEPARATOR);
-			tokenizer::iterator subtoken_it = subtokens.begin();
-
-			// Must have a token
-			if (subtoken_it == subtokens.end()) return false;
-			std::string subtoken = *subtoken_it;
-
-			// Must exactly equal "max-age"
-			LLStringUtil::trim(subtoken);
-			if (subtoken != MAX_AGE) return false;
-
-			// Must have another token
-			++subtoken_it;
-			if (subtoken_it == subtokens.end()) return false;
-			subtoken = *subtoken_it;
-
-			// Must be a valid integer
-			// *NOTE: atoi() returns 0 for invalid values, so we have to
-			// check the string first.
-			// *TODO: Do servers ever send "0000" for zero?  We don't handle it
-			LLStringUtil::trim(subtoken);
-			if (subtoken == "0")
-			{
-				*max_age = 0;
-				return true;
-			}
-			S32 val = atoi( subtoken.c_str() );
-			if (val > 0 && val < S32_MAX)
-			{
-				*max_age = val;
-				return true;
-			}
-			return false;
-		}
-	}
-	return false;
-}
-
 
 class LLExperienceResponder : public LLHTTPClient::Responder
 {
@@ -435,7 +388,6 @@ void LLExperienceCache::requestExperiences()
 
 	ostr << sLookupURL << "?page_size=" << PAGE_SIZE;
 
-
 	int request_count = 0;
 	while(!sAskQueue.empty() && request_count < sMaximumLookups)
 	{
@@ -503,7 +455,6 @@ bool LLExperienceCache::hasLookupURL()
 
 void LLExperienceCache::idle()
 {
-
 	const F32 SECS_BETWEEN_REQUESTS = 0.1f;
 	if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS))
 	{
@@ -517,7 +468,6 @@ void LLExperienceCache::idle()
 		eraseExpired();
 	}
 
-
 	if(!sAskQueue.empty())
 	{
 		requestExperiences();
@@ -568,7 +518,6 @@ void LLExperienceCache::eraseExpired()
 		}
 	}
 }
-
 	
 bool LLExperienceCache::fetch(const LLUUID& key, bool refresh/* = true*/)
 {
@@ -594,10 +543,12 @@ void LLExperienceCache::insert(const LLSD& experience_data)
 	}
 }
 
-static LLSD empty;
 const LLSD& LLExperienceCache::get(const LLUUID& key)
 {
-	if(key.isNull()) return empty;
+	static const LLSD empty;
+	
+	if(key.isNull()) 
+		return empty;
 	cache_t::const_iterator it = sCache.find(key);
 
 	if (it != sCache.end())
@@ -609,9 +560,10 @@ const LLSD& LLExperienceCache::get(const LLUUID& key)
 
 	return empty;
 }
-void LLExperienceCache::get(const LLUUID& key, callback_slot_t slot)
+void LLExperienceCache::get(const LLUUID& key, LLExperienceCache::Callback_t slot)
 {
-	if(key.isNull()) return;
+	if(key.isNull()) 
+		return;
 
 	cache_t::const_iterator it = sCache.find(key);
 	if (it != sCache.end())
@@ -626,12 +578,20 @@ void LLExperienceCache::get(const LLUUID& key, callback_slot_t slot)
 
 	fetch(key);
 
+	signal_ptr signal = signal_ptr(new callback_signal_t());
+	
+	std::pair<signal_map_t::iterator, bool> result = sSignalMap.insert(signal_map_t::value_type(key, signal));
+	if (!result.second)
+		signal = result.first.second;
+	signal->connect(slot);
+	
+#if 0
 	// always store additional callback, even if request is pending
 	signal_map_t::iterator sig_it = sSignalMap.find(key);
 	if (sig_it == sSignalMap.end())
 	{
 		// ...new callback for this id
-		callback_signal_t* signal = new callback_signal_t();
+		signal_ptr signal = signal_ptr(new callback_signal_t());
 		signal->connect(slot);
 		sSignalMap[key] = signal;
 	}
@@ -641,6 +601,76 @@ void LLExperienceCache::get(const LLUUID& key, callback_slot_t slot)
 		callback_signal_t* signal = sig_it->second;
 		signal->connect(slot);
 	}
+#endif
+}
+
+//=========================================================================
+void LLExperienceCacheImpl::mapKeys(const LLSD& legacyKeys)
+{
+	LLSD::array_const_iterator exp = legacyKeys.beginArray();
+	for (/**/; exp != legacyKeys.endArray(); ++exp)
+	{
+		if (exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY))
+		{
+			privateToPublicKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()] = (*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID();
+		}
+	}
 }
 
+bool LLExperienceCacheImpl::max_age_from_cache_control(const std::string& cache_control, S32 *max_age)
+{
+	// Split the string on "," to get a list of directives
+	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+	tokenizer directives(cache_control, COMMA_SEPARATOR);
+	
+	tokenizer::iterator token_it = directives.begin();
+	for ( ; token_it != directives.end(); ++token_it)
+	{
+		// Tokens may have leading or trailing whitespace
+		std::string token = *token_it;
+		LLStringUtil::trim(token);
+		
+		if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
+		{
+			// ...this token starts with max-age, so let's chop it up by "="
+			tokenizer subtokens(token, EQUALS_SEPARATOR);
+			tokenizer::iterator subtoken_it = subtokens.begin();
+			
+			// Must have a token
+			if (subtoken_it == subtokens.end()) return false;
+			std::string subtoken = *subtoken_it;
+			
+			// Must exactly equal "max-age"
+			LLStringUtil::trim(subtoken);
+			if (subtoken != MAX_AGE) return false;
+			
+			// Must have another token
+			++subtoken_it;
+			if (subtoken_it == subtokens.end()) return false;
+			subtoken = *subtoken_it;
+			
+			// Must be a valid integer
+			// *NOTE: atoi() returns 0 for invalid values, so we have to
+			// check the string first.
+			// *TODO: Do servers ever send "0000" for zero?  We don't handle it
+			LLStringUtil::trim(subtoken);
+			if (subtoken == "0")
+			{
+				*max_age = 0;
+				return true;
+			}
+			S32 val = atoi( subtoken.c_str() );
+			if (val > 0 && val < S32_MAX)
+			{
+				*max_age = val;
+				return true;
+			}
+			return false;
+		}
+	}
+	return false;
+}
+
+
+
 
diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h
index 8a55719443..8da038a8c3 100644
--- a/indra/llmessage/llexperiencecache.h
+++ b/indra/llmessage/llexperiencecache.h
@@ -56,10 +56,45 @@ public:
 
 private:
     // Callback types for get() 
-//  typedef boost::signals2::signal < void(const LLSD &) > callback_signal_t;
-    typedef boost::signals2::signal < Callback_t > callback_signal_t;
-    typedef std::map<LLUUID, LLSD> cache_t;
-
+    typedef boost::signals2::signal < void(const LLSD &) > callback_signal_t;
+	typedef boost::shared_ptr<callback_signal_t> signal_ptr;
+	// May have multiple callbacks for a single ID, which are
+	// represented as multiple slots bound to the signal.
+	// Avoid copying signals via pointers.
+	typedef std::map<LLUUID, signal_ptr> signal_map_t;
+	typedef std::map<LLUUID, LLSD> cache_t;
+	
+	typedef std::set<LLUUID> ask_queue_t;
+	
+	
+	//--------------------------------------------
+	static const std::string PRIVATE_KEY;	// "private_id"
+	static const std::string MISSING;       // "DoesNotExist"
+	
+	static const std::string AGENT_ID;      // "agent_id"
+	static const std::string GROUP_ID;      // "group_id"
+	static const std::string EXPERIENCE_ID;	// "public_id"
+	static const std::string NAME;			// "name"
+	static const std::string PROPERTIES;	// "properties"
+	static const std::string EXPIRES;		// "expiration"  
+	static const std::string DESCRIPTION;	// "description"
+	static const std::string QUOTA;         // "quota"
+	static const std::string MATURITY;      // "maturity"
+	static const std::string METADATA;      // "extended_metadata"
+	static const std::string SLURL;         // "slurl"
+	
+	// should be in sync with experience-api/experiences/models.py
+	static const int PROPERTY_INVALID;		// 1 << 0
+	static const int PROPERTY_PRIVILEGED;	// 1 << 3
+	static const int PROPERTY_GRID;			// 1 << 4
+	static const int PROPERTY_PRIVATE;		// 1 << 5
+	static const int PROPERTY_DISABLED;		// 1 << 6  
+	static const int PROPERTY_SUSPENDED;	// 1 << 7
+	
+	// default values
+	static const F64 DEFAULT_EXPIRATION; 	// 600.0
+	static const S32 DEFAULT_QUOTA; 		// 128 this is megabytes
+	
 //--------------------------------------------
     LLExperienceCache();
     virtual ~LLExperienceCache();
@@ -70,36 +105,13 @@ private:
 //--------------------------------------------
     void processExperience(const LLUUID& public_key, const LLSD& experience);
 
-
-	const std::string PRIVATE_KEY	= "private_id";
-    const std::string MISSING       = "DoesNotExist";
-
-    const std::string AGENT_ID      = "agent_id";
-    const std::string GROUP_ID      = "group_id";
-	const std::string EXPERIENCE_ID	= "public_id";
-	const std::string NAME			= "name";
-	const std::string PROPERTIES	= "properties";
-	const std::string EXPIRES		= "expiration";  
-    const std::string DESCRIPTION	= "description";
-    const std::string QUOTA         = "quota";
-    const std::string MATURITY      = "maturity";
-    const std::string METADATA      = "extended_metadata";
-    const std::string SLURL         = "slurl";
-
-
-	// should be in sync with experience-api/experiences/models.py
-	const int PROPERTY_INVALID		= 1 << 0;
-	const int PROPERTY_PRIVILEGED	= 1 << 3;
-	const int PROPERTY_GRID			= 1 << 4;
-	const int PROPERTY_PRIVATE		= 1 << 5;
-	const int PROPERTY_DISABLED		= 1 << 6;  
-	const int PROPERTY_SUSPENDED	= 1 << 7;
-
-	// default values
-	const static F64 DEFAULT_EXPIRATION = 600.0;
-	const static S32 DEFAULT_QUOTA = 128; // this is megabytes
-
-
+//--------------------------------------------
+	cache_t			sCache;
+	signal_map_t	sSignalMap;	
+	ask_queue_t		sAskQueue;
+	
+	void eraseExpired();
+	
 	void setLookupURL(const std::string& lookup_url);
 	bool hasLookupURL();
 
-- 
cgit v1.2.3


From 96e343b49b0b5a0951ffab0beb2e1d09c37bbdc5 Mon Sep 17 00:00:00 2001
From: Rider Linden <rider@lindenlab.com>
Date: Tue, 1 Sep 2015 16:13:52 -0700
Subject: MAINT-5575: Convert the Experience cache into a coro based singleton.

--HG--
branch : MAINT-5575
---
 indra/llmessage/CMakeLists.txt                     |   2 +
 indra/llmessage/llcoproceduremanager.cpp           | 414 +++++++++++++++
 indra/llmessage/llcoproceduremanager.h             |  98 ++++
 indra/llmessage/llexperiencecache.cpp              | 569 ++++++++++-----------
 indra/llmessage/llexperiencecache.h                |  99 ++--
 indra/llui/CMakeLists.txt                          |   2 +
 indra/llui/llurlentry.cpp                          |   4 +-
 indra/newview/CMakeLists.txt                       |   2 -
 indra/newview/llagent.cpp                          |   9 +
 indra/newview/llagent.h                            |   3 +
 indra/newview/llappviewer.cpp                      |  57 +--
 indra/newview/llappviewer.h                        |   4 -
 indra/newview/llcoproceduremanager.cpp             | 406 ---------------
 indra/newview/llcoproceduremanager.h               |  87 ----
 indra/newview/llexperienceassociationresponder.cpp |   2 +-
 indra/newview/llfloaterexperienceprofile.cpp       |  10 +-
 indra/newview/llfloaterreporter.cpp                |   2 +-
 indra/newview/llpanelexperiencelisteditor.cpp      |   2 +-
 indra/newview/llpanelexperiencelog.cpp             |   4 +-
 indra/newview/llpanelexperiencepicker.cpp          |   2 +-
 indra/newview/llpreviewscript.cpp                  |   6 +-
 indra/newview/llstartup.cpp                        |   7 +-
 indra/newview/llviewermessage.cpp                  |   2 +-
 23 files changed, 897 insertions(+), 896 deletions(-)
 create mode 100644 indra/llmessage/llcoproceduremanager.cpp
 create mode 100644 indra/llmessage/llcoproceduremanager.h
 delete mode 100644 indra/newview/llcoproceduremanager.cpp
 delete mode 100644 indra/newview/llcoproceduremanager.h

(limited to 'indra')

diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt
index 9739f7c607..12fc1bbcfc 100755
--- a/indra/llmessage/CMakeLists.txt
+++ b/indra/llmessage/CMakeLists.txt
@@ -40,6 +40,7 @@ set(llmessage_SOURCE_FILES
     llchainio.cpp
     llcircuit.cpp
     llclassifiedflags.cpp
+    llcoproceduremanager.cpp
     llcorehttputil.cpp
     llcurl.cpp
     lldatapacker.cpp
@@ -128,6 +129,7 @@ set(llmessage_HEADER_FILES
     llcipher.h
     llcircuit.h
     llclassifiedflags.h
+    llcoproceduremanager.h
     llcorehttputil.h
     llcurl.h
     lldatapacker.h
diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp
new file mode 100644
index 0000000000..062f2e6e42
--- /dev/null
+++ b/indra/llmessage/llcoproceduremanager.cpp
@@ -0,0 +1,414 @@
+/**
+* @file LLCoprocedurePool.cpp
+* @author Rider Linden
+* @brief Singleton class for managing asset uploads to the sim.
+*
+* $LicenseInfo:firstyear=2015&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2015, 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 "llcoproceduremanager.h"
+
+//=========================================================================
+// Map of pool sizes for known pools
+static std::map<std::string, U32> DefaultPoolSizes;
+
+// *TODO$: When C++11 this can be initialized here as follows:
+// = {{"AIS", 25}, {"Upload", 1}}
+
+#define DEFAULT_POOL_SIZE 5
+
+//=========================================================================
+class LLCoprocedurePool: private boost::noncopyable
+{
+public:
+    typedef LLCoprocedureManager::CoProcedure_t CoProcedure_t;
+
+    LLCoprocedurePool(const std::string &name, size_t size);
+    virtual ~LLCoprocedurePool();
+
+    /// Places the coprocedure on the queue for processing. 
+    /// 
+    /// @param name Is used for debugging and should identify this coroutine.
+    /// @param proc Is a bound function to be executed 
+    /// 
+    /// @return This method returns a UUID that can be used later to cancel execution.
+    LLUUID enqueueCoprocedure(const std::string &name, CoProcedure_t proc);
+
+    /// Cancel a coprocedure. If the coprocedure is already being actively executed 
+    /// this method calls cancelYieldingOperation() on the associated HttpAdapter
+    /// If it has not yet been dequeued it is simply removed from the queue.
+    bool cancelCoprocedure(const LLUUID &id);
+
+    /// Requests a shutdown of the upload manager. Passing 'true' will perform 
+    /// an immediate kill on the upload coroutine.
+    void shutdown(bool hardShutdown = false);
+
+    /// Returns the number of coprocedures in the queue awaiting processing.
+    ///
+    inline size_t countPending() const
+    {
+        return mPendingCoprocs.size();
+    }
+
+    /// Returns the number of coprocedures actively being processed.
+    ///
+    inline size_t countActive() const
+    {
+        return mActiveCoprocs.size();
+    }
+
+    /// Returns the total number of coprocedures either queued or in active processing.
+    ///
+    inline size_t count() const
+    {
+        return countPending() + countActive();
+    }
+
+private:
+    struct QueuedCoproc
+    {
+        typedef boost::shared_ptr<QueuedCoproc> ptr_t;
+
+        QueuedCoproc(const std::string &name, const LLUUID &id, CoProcedure_t proc) :
+            mName(name),
+            mId(id),
+            mProc(proc)
+        {}
+
+        std::string mName;
+        LLUUID mId;
+        CoProcedure_t mProc;
+    };
+
+    // we use a deque here rather than std::queue since we want to be able to 
+    // iterate through the queue and potentially erase an entry from the middle.
+    typedef std::deque<QueuedCoproc::ptr_t>  CoprocQueue_t;
+    typedef std::map<LLUUID, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> ActiveCoproc_t;
+
+    std::string     mPoolName;
+    size_t          mPoolSize;
+    CoprocQueue_t   mPendingCoprocs;
+    ActiveCoproc_t  mActiveCoprocs;
+    bool            mShutdown;
+    LLEventStream   mWakeupTrigger;
+
+    typedef std::map<std::string, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> CoroAdapterMap_t;
+    LLCore::HttpRequest::policy_t mHTTPPolicy;
+
+    CoroAdapterMap_t mCoroMapping;
+
+    void coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter);
+
+};
+
+//=========================================================================
+LLCoprocedureManager::LLCoprocedureManager()
+{
+    DefaultPoolSizes.insert(std::map<std::string, U32>::value_type("Upload", 1));
+    DefaultPoolSizes.insert(std::map<std::string, U32>::value_type("AIS", 25));
+}
+
+LLCoprocedureManager::~LLCoprocedureManager()
+{
+
+}
+
+LLCoprocedureManager::poolPtr_t LLCoprocedureManager::initializePool(const std::string &poolName)
+{
+    // Attempt to look up a pool size in the configuration.  If found use that
+    std::string keyName = "PoolSize" + poolName;
+    int size = 0;
+
+    if (mPropertyQueryFn && !mPropertyQueryFn.empty())
+    {
+        size = mPropertyQueryFn(keyName);
+    }
+
+    if (size == 0)
+    {   // if not found grab the know default... if there is no known 
+        // default use a reasonable number like 5.
+        std::map<std::string, U32>::iterator it = DefaultPoolSizes.find(poolName);
+        if (it == DefaultPoolSizes.end())
+            size = DEFAULT_POOL_SIZE;
+        else
+            size = (*it).second;
+
+        if (mPropertyDefineFn && !mPropertyDefineFn.empty())
+            mPropertyDefineFn(keyName, size, "Coroutine Pool size for " + poolName);
+        LL_WARNS() << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL;
+    }
+
+    poolPtr_t pool = poolPtr_t(new LLCoprocedurePool(poolName, size));
+    mPoolMap.insert(poolMap_t::value_type(poolName, pool));
+
+    return pool;
+}
+
+//-------------------------------------------------------------------------
+LLUUID LLCoprocedureManager::enqueueCoprocedure(const std::string &pool, const std::string &name, CoProcedure_t proc)
+{
+    // Attempt to find the pool and enqueue the procedure.  If the pool does 
+    // not exist, create it.
+    poolPtr_t targetPool;
+    poolMap_t::iterator it = mPoolMap.find(pool);
+
+    if (it == mPoolMap.end())
+    {
+        targetPool = initializePool(pool);
+    }
+    else
+    {
+        targetPool = (*it).second;
+    }
+
+    if (!targetPool)
+    {
+        LL_WARNS() << "LLCoprocedureManager unable to create coprocedure pool named \"" << pool << "\"" << LL_ENDL;
+        return LLUUID::null;
+    }
+
+    return targetPool->enqueueCoprocedure(name, proc);
+}
+
+void LLCoprocedureManager::cancelCoprocedure(const LLUUID &id)
+{
+    for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
+    {
+        if ((*it).second->cancelCoprocedure(id))
+            return;
+    }
+    LL_INFOS() << "Coprocedure not found." << LL_ENDL;
+}
+
+void LLCoprocedureManager::shutdown(bool hardShutdown)
+{
+    for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
+    {
+        (*it).second->shutdown(hardShutdown);
+    }
+    mPoolMap.clear();
+}
+
+void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn)
+{
+    mPropertyQueryFn = queryfn;
+    mPropertyDefineFn = updatefn;
+}
+
+//-------------------------------------------------------------------------
+size_t LLCoprocedureManager::countPending() const
+{
+    size_t count = 0;
+    for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
+    {
+        count += (*it).second->countPending();
+    }
+    return count;
+}
+
+size_t LLCoprocedureManager::countPending(const std::string &pool) const
+{
+    poolMap_t::const_iterator it = mPoolMap.find(pool);
+
+    if (it == mPoolMap.end())
+        return 0;
+    return (*it).second->countPending();
+}
+
+size_t LLCoprocedureManager::countActive() const
+{
+    size_t count = 0;
+    for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
+    {
+        count += (*it).second->countActive();
+    }
+    return count;
+}
+
+size_t LLCoprocedureManager::countActive(const std::string &pool) const
+{
+    poolMap_t::const_iterator it = mPoolMap.find(pool);
+
+    if (it == mPoolMap.end())
+        return 0;
+    return (*it).second->countActive();
+}
+
+size_t LLCoprocedureManager::count() const
+{
+    size_t count = 0;
+    for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
+    {
+        count += (*it).second->count();
+    }
+    return count;
+}
+
+size_t LLCoprocedureManager::count(const std::string &pool) const
+{
+    poolMap_t::const_iterator it = mPoolMap.find(pool);
+
+    if (it == mPoolMap.end())
+        return 0;
+    return (*it).second->count();
+}
+
+//=========================================================================
+LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size):
+    mPoolName(poolName),
+    mPoolSize(size),
+    mPendingCoprocs(),
+    mShutdown(false),
+    mWakeupTrigger("CoprocedurePool" + poolName, true),
+    mCoroMapping(),
+    mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID)
+{
+    for (size_t count = 0; count < mPoolSize; ++count)
+    {
+        LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter =
+            LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t(
+            new LLCoreHttpUtil::HttpCoroutineAdapter( mPoolName + "Adapter", mHTTPPolicy));
+
+        std::string uploadCoro = LLCoros::instance().launch("LLCoprocedurePool("+mPoolName+")::coprocedureInvokerCoro",
+            boost::bind(&LLCoprocedurePool::coprocedureInvokerCoro, this, httpAdapter));
+
+        mCoroMapping.insert(CoroAdapterMap_t::value_type(uploadCoro, httpAdapter));
+    }
+
+    LL_INFOS() << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items." << LL_ENDL;
+
+    mWakeupTrigger.post(LLSD());
+}
+
+LLCoprocedurePool::~LLCoprocedurePool() 
+{
+    shutdown();
+}
+
+//-------------------------------------------------------------------------
+void LLCoprocedurePool::shutdown(bool hardShutdown)
+{
+    CoroAdapterMap_t::iterator it;
+
+    for (it = mCoroMapping.begin(); it != mCoroMapping.end(); ++it)
+    {
+        if (!(*it).first.empty())
+        {
+            if (hardShutdown)
+            {
+                LLCoros::instance().kill((*it).first);
+            }
+        }
+        if ((*it).second)
+        {
+            (*it).second->cancelYieldingOperation();
+        }
+    }
+
+    mShutdown = true;
+    mCoroMapping.clear();
+    mPendingCoprocs.clear();
+}
+
+//-------------------------------------------------------------------------
+LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoprocedurePool::CoProcedure_t proc)
+{
+    LLUUID id(LLUUID::generateNewID());
+
+    mPendingCoprocs.push_back(QueuedCoproc::ptr_t(new QueuedCoproc(name, id, proc)));
+    LL_INFOS() << "Coprocedure(" << name << ") enqueued with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
+
+    mWakeupTrigger.post(LLSD());
+
+    return id;
+}
+
+bool LLCoprocedurePool::cancelCoprocedure(const LLUUID &id)
+{
+    // first check the active coroutines.  If there, remove it and return.
+    ActiveCoproc_t::iterator itActive = mActiveCoprocs.find(id);
+    if (itActive != mActiveCoprocs.end())
+    {
+        LL_INFOS() << "Found and canceling active coprocedure with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
+        (*itActive).second->cancelYieldingOperation();
+        mActiveCoprocs.erase(itActive);
+        return true;
+    }
+
+    for (CoprocQueue_t::iterator it = mPendingCoprocs.begin(); it != mPendingCoprocs.end(); ++it)
+    {
+        if ((*it)->mId == id)
+        {
+            LL_INFOS() << "Found and removing queued coroutine(" << (*it)->mName << ") with Id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
+            mPendingCoprocs.erase(it);
+            return true;
+        }
+    }
+
+    LL_INFOS() << "Coprocedure with Id=" << id.asString() << " was not found." << " in pool \"" << mPoolName << "\"" << LL_ENDL;
+    return false;
+}
+
+//-------------------------------------------------------------------------
+void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter)
+{
+    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+
+    while (!mShutdown)
+    {
+        llcoro::waitForEventOn(mWakeupTrigger);
+        if (mShutdown)
+            break;
+        
+        while (!mPendingCoprocs.empty())
+        {
+            QueuedCoproc::ptr_t coproc = mPendingCoprocs.front();
+            mPendingCoprocs.pop_front();
+            mActiveCoprocs.insert(ActiveCoproc_t::value_type(coproc->mId, httpAdapter));
+
+            LL_INFOS() << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
+
+            try
+            {
+                coproc->mProc(httpAdapter, coproc->mId);
+            }
+            catch (std::exception &e)
+            {
+                LL_WARNS() << "Coprocedure(" << coproc->mName << ") id=" << coproc->mId.asString() <<
+                    " threw an exception! Message=\"" << e.what() << "\"" << LL_ENDL;
+            }
+            catch (...)
+            {
+                LL_WARNS() << "A non std::exception was thrown from " << coproc->mName << " with id=" << coproc->mId << "." << " in pool \"" << mPoolName << "\"" << LL_ENDL;
+            }
+
+            LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL;
+
+            ActiveCoproc_t::iterator itActive = mActiveCoprocs.find(coproc->mId);
+            if (itActive != mActiveCoprocs.end())
+            {
+                mActiveCoprocs.erase(itActive);
+            }
+        }
+    }
+}
diff --git a/indra/llmessage/llcoproceduremanager.h b/indra/llmessage/llcoproceduremanager.h
new file mode 100644
index 0000000000..497367b80c
--- /dev/null
+++ b/indra/llmessage/llcoproceduremanager.h
@@ -0,0 +1,98 @@
+/**
+* @file llcoproceduremanager.h
+* @author Rider Linden
+* @brief Singleton class for managing asset uploads to the sim.
+*
+* $LicenseInfo:firstyear=2015&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2015, 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_COPROCEDURE_MANAGER_H
+#define LL_COPROCEDURE_MANAGER_H
+
+#include "lleventcoro.h"
+#include "llcoros.h"
+#include "llcorehttputil.h"
+#include "lluuid.h"
+
+class LLCoprocedurePool;
+
+class LLCoprocedureManager : public LLSingleton < LLCoprocedureManager >
+{
+    friend class LLSingleton < LLCoprocedureManager > ;
+
+public:
+    typedef boost::function<U32(const std::string &)> SettingQuery_t;
+    typedef boost::function<void(const std::string &, U32, const std::string &)> SettingUpdate_t;
+
+    typedef boost::function<void(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, const LLUUID &id)> CoProcedure_t;
+
+    LLCoprocedureManager();
+    virtual ~LLCoprocedureManager();
+
+    /// Places the coprocedure on the queue for processing. 
+    /// 
+    /// @param name Is used for debugging and should identify this coroutine.
+    /// @param proc Is a bound function to be executed 
+    /// 
+    /// @return This method returns a UUID that can be used later to cancel execution.
+    LLUUID enqueueCoprocedure(const std::string &pool, const std::string &name, CoProcedure_t proc);
+
+    /// Cancel a coprocedure. If the coprocedure is already being actively executed 
+    /// this method calls cancelYieldingOperation() on the associated HttpAdapter
+    /// If it has not yet been dequeued it is simply removed from the queue.
+    void cancelCoprocedure(const LLUUID &id);
+
+    /// Requests a shutdown of the upload manager. Passing 'true' will perform 
+    /// an immediate kill on the upload coroutine.
+    void shutdown(bool hardShutdown = false);
+
+    void setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn);
+
+    /// Returns the number of coprocedures in the queue awaiting processing.
+    ///
+    size_t countPending() const;
+    size_t countPending(const std::string &pool) const;
+
+    /// Returns the number of coprocedures actively being processed.
+    ///
+    size_t countActive() const;
+    size_t countActive(const std::string &pool) const;
+
+    /// Returns the total number of coprocedures either queued or in active processing.
+    ///
+    size_t count() const;
+    size_t count(const std::string &pool) const;
+
+private:
+
+    typedef boost::shared_ptr<LLCoprocedurePool> poolPtr_t;
+    typedef std::map<std::string, poolPtr_t> poolMap_t;
+
+    poolMap_t mPoolMap;
+
+    poolPtr_t initializePool(const std::string &poolName);
+
+    SettingQuery_t mPropertyQueryFn;
+    SettingUpdate_t mPropertyDefineFn;
+};
+
+#endif
diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp
index 34c4210359..36a4fc8823 100644
--- a/indra/llmessage/llexperiencecache.cpp
+++ b/indra/llmessage/llexperiencecache.cpp
@@ -26,53 +26,51 @@
 #include "llexperiencecache.h"
 
 #include "llavatarname.h"
-#include "llframetimer.h"
 #include "llhttpclient.h"
 #include "llsdserialize.h"
+#include "llcoros.h"
+#include "lleventcoro.h"
+#include "lleventfilter.h"
+#include "llcoproceduremanager.h"
+#include "lldir.h"
 #include <set>
 #include <map>
-#include "boost/tokenizer.hpp"
+#include <boost/tokenizer.hpp>
 #include <boost/concept_check.hpp>
 
-
-typedef std::map<LLUUID, LLUUID> KeyMap;
-KeyMap privateToPublicKeyMap;
-
-
-std::string sLookupURL;
-
-typedef std::map<LLUUID, F64> pending_queue_t;
-pending_queue_t sPendingQueue;
-
-int sMaximumLookups = 10;
-
-LLFrameTimer sRequestTimer;
-
-// Periodically clean out expired entries from the cache
-LLFrameTimer sEraseExpiredTimer;
-
-
 //=========================================================================
 namespace LLExperienceCacheImpl
 {
-	bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age);
 	void mapKeys(const LLSD& legacyKeys);
+    F64 getErrorRetryDeltaTime(S32 status, LLSD headers);
+    bool maxAgeFromCacheControl(const std::string& cache_control, S32 *max_age);
+
+    static const std::string PRIVATE_KEY    = "private_id";
+    static const std::string EXPERIENCE_ID  = "public_id";
+
+    static const std::string MAX_AGE("max-age");
+    static const boost::char_separator<char> EQUALS_SEPARATOR("=");
+    static const boost::char_separator<char> COMMA_SEPARATOR(",");
+
+    // *TODO$: this seems to be tied to mapKeys which is used by bootstrap.... but I don't think that bootstrap is used.
+    typedef std::map<LLUUID, LLUUID> KeyMap;
+    KeyMap privateToPublicKeyMap;
 }
 
 //=========================================================================
 const std::string LLExperienceCache::PRIVATE_KEY	= "private_id";
 const std::string LLExperienceCache::MISSING       	= "DoesNotExist";
 
-const std::string LLExperienceCache::AGENT_ID      	= "agent_id";
-const std::string LLExperienceCache::GROUP_ID      	= "group_id";
+const std::string LLExperienceCache::AGENT_ID      = "agent_id";
+const std::string LLExperienceCache::GROUP_ID      = "group_id";
 const std::string LLExperienceCache::EXPERIENCE_ID	= "public_id";
 const std::string LLExperienceCache::NAME			= "name";
 const std::string LLExperienceCache::PROPERTIES		= "properties";
 const std::string LLExperienceCache::EXPIRES		= "expiration";  
 const std::string LLExperienceCache::DESCRIPTION	= "description";
 const std::string LLExperienceCache::QUOTA         	= "quota";
-const std::string LLExperienceCache::MATURITY      	= "maturity";
-const std::string LLExperienceCache::METADATA      	= "extended_metadata";
+const std::string LLExperienceCache::MATURITY      = "maturity";
+const std::string LLExperienceCache::METADATA      = "extended_metadata";
 const std::string LLExperienceCache::SLURL         	= "slurl";
 
 // should be in sync with experience-api/experiences/models.py
@@ -80,20 +78,51 @@ const int LLExperienceCache::PROPERTY_INVALID		= 1 << 0;
 const int LLExperienceCache::PROPERTY_PRIVILEGED	= 1 << 3;
 const int LLExperienceCache::PROPERTY_GRID			= 1 << 4;
 const int LLExperienceCache::PROPERTY_PRIVATE		= 1 << 5;
-const int LLExperienceCache::PROPERTY_DISABLED		= 1 << 6;  
-const int LLExperienceCache::PROPERTY_SUSPENDED		= 1 << 7;
+const int LLExperienceCache::PROPERTY_DISABLED	= 1 << 6;  
+const int LLExperienceCache::PROPERTY_SUSPENDED	= 1 << 7;
 
 // default values
-const F64 LLExperienceCache::DEFAULT_EXPIRATION		= 600.0;
+const F64 LLExperienceCache::DEFAULT_EXPIRATION	= 600.0;
 const S32 LLExperienceCache::DEFAULT_QUOTA			= 128; // this is megabytes
 
 //=========================================================================
-LLExperienceCache::LLExperienceCache()
+LLExperienceCache::LLExperienceCache():
+    mShutdown(false)
 {
 }
 
 LLExperienceCache::~LLExperienceCache()
 {
+
+}
+
+void LLExperienceCache::initSingleton()
+{
+    mCacheFileName = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml");
+
+    LL_INFOS("ExperienceCache") << "Loading " << mCacheFileName << LL_ENDL;
+    llifstream cache_stream(mCacheFileName.c_str());
+
+    if (cache_stream.is_open())
+    {
+        cache_stream >> (*this);
+    }
+
+    LLCoros::instance().launch("LLExperienceCache::idleCoro",
+        boost::bind(&LLExperienceCache::idleCoro, this));
+
+}
+
+void LLExperienceCache::cleanup()
+{
+    LL_INFOS("ExperienceCache") << "Saving " << mCacheFileName << LL_ENDL;
+
+    llofstream cache_stream(mCacheFileName.c_str());
+    if (cache_stream.is_open())
+    {
+        cache_stream << (*this);
+    }
+    mShutdown = true;
 }
 
 //-------------------------------------------------------------------------
@@ -110,18 +139,18 @@ void LLExperienceCache::importFile(std::istream& istr)
     for (; it != experiences.endMap(); ++it)
     {
         public_key.set(it->first);
-        sCache[public_key] = it->second;
+        mCache[public_key] = it->second;
     }
 
-    LL_DEBUGS("ExperienceCache") << "importFile() loaded " << sCache.size() << LL_ENDL;
+    LL_DEBUGS("ExperienceCache") << "importFile() loaded " << mCache.size() << LL_ENDL;
 }
 
 void LLExperienceCache::exportFile(std::ostream& ostr) const
 {
     LLSD experiences;
 
-    cache_t::const_iterator it = sCache.begin();
-    for (; it != sCache.end(); ++it)
+    cache_t::const_iterator it = mCache.begin();
+    for (; it != mCache.end(); ++it)
     {
         if (!it->second.has(EXPERIENCE_ID) || it->second[EXPERIENCE_ID].asUUID().isNull() ||
             it->second.has("DoesNotExist") || (it->second.has(PROPERTIES) && it->second[PROPERTIES].asInteger() & PROPERTY_INVALID))
@@ -136,7 +165,7 @@ void LLExperienceCache::exportFile(std::ostream& ostr) const
     LLSDSerialize::toPrettyXML(data, ostr);
 }
 
-// *TODO$: Rider: These three functions not seem to be used... it may be useful in testing.
+// *TODO$: Rider: This method does not seem to be used... it may be useful in testing.
 void LLExperienceCache::bootstrap(const LLSD& legacyKeys, int initialExpiration)
 {
 	LLExperienceCacheImpl::mapKeys(legacyKeys);
@@ -166,8 +195,8 @@ LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_i
     if (private_key.isNull())
         return LLUUID::null;
 
-    KeyMap::const_iterator it = privateToPublicKeyMap.find(private_key);
-    if (it == privateToPublicKeyMap.end())
+    LLExperienceCacheImpl::KeyMap::const_iterator it = LLExperienceCacheImpl::privateToPublicKeyMap.find(private_key);
+    if (it == LLExperienceCacheImpl::privateToPublicKeyMap.end())
     {
         if (null_if_not_found)
         {
@@ -182,8 +211,10 @@ LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_i
 //=========================================================================
 void LLExperienceCache::processExperience(const LLUUID& public_key, const LLSD& experience)
 {
-	sCache[public_key]=experience;
-	LLSD & row = sCache[public_key];
+    LL_INFOS("ExperienceCache") << "Processing experience \"" << experience[NAME] << "\" with key " << public_key.asString() << LL_ENDL;
+
+	mCache[public_key]=experience;
+	LLSD & row = mCache[public_key];
 
 	if(row.has(EXPIRES))
 	{
@@ -192,233 +223,148 @@ void LLExperienceCache::processExperience(const LLUUID& public_key, const LLSD&
 
 	if(row.has(EXPERIENCE_ID))
 	{
-		sPendingQueue.erase(row[EXPERIENCE_ID].asUUID());
+		mPendingQueue.erase(row[EXPERIENCE_ID].asUUID());
 	}
 
 	//signal
-	signal_map_t::iterator sig_it =	sSignalMap.find(public_key);
-	if (sig_it != sSignalMap.end())
+	signal_map_t::iterator sig_it =	mSignalMap.find(public_key);
+	if (sig_it != mSignalMap.end())
 	{
 		signal_ptr signal = sig_it->second;
 		(*signal)(experience);
 
-		sSignalMap.erase(public_key);
+		mSignalMap.erase(public_key);
 	}
 }
 
 const LLExperienceCache::cache_t& LLExperienceCache::getCached()
 {
-	return sCache;
-}
-
-void LLExperienceCache::setMaximumLookups(int maximumLookups)
-{
-	sMaximumLookups = maximumLookups;
+	return mCache;
 }
 
-
-bool LLExperienceCache::expirationFromCacheControl(LLSD headers, F64 *expires)
+void LLExperienceCache::requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, std::string url, RequestQueue_t requests)
 {
-	// Allow the header to override the default
-	LLSD cache_control_header = headers["cache-control"];
-	if (cache_control_header.isDefined())
-	{
-		S32 max_age = 0;
-		std::string cache_control = cache_control_header.asString();
-		if (max_age_from_cache_control(cache_control, &max_age))
-		{
-			LL_WARNS("ExperienceCache") 
-				<< "got EXPIRES from headers, max_age " << max_age 
-				<< LL_ENDL;
-			F64 now = LLFrameTimer::getTotalSeconds();
-			*expires = now + (F64)max_age;
-			return true;
-		}
-	}
-	return false;
-}
+    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest());
 
+    //LL_INFOS("requestExperiencesCoro") << "url: " << url << LL_ENDL;
 
-static const std::string MAX_AGE("max-age");
-static const boost::char_separator<char> EQUALS_SEPARATOR("=");
-static const boost::char_separator<char> COMMA_SEPARATOR(",");
+    LLSD result = httpAdapter->getAndYield(httpRequest, url);
+        
+    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
 
+    if (!status)
+    {
+        F64 now = LLFrameTimer::getTotalSeconds();
 
-class LLExperienceResponder : public LLHTTPClient::Responder
-{
-public:
-	LLExperienceResponder(const ask_queue_t& keys)
-		:mKeys(keys)
-	{
-
-	}
-
-	/*virtual*/ void httpCompleted()
-	{
-		LLSD experiences = getContent()["experience_keys"];
-		LLSD::array_const_iterator it = experiences.beginArray();
-		for( /**/ ; it != experiences.endArray(); ++it)
-		{
-			const LLSD& row = *it;
-			LLUUID public_key = row[EXPERIENCE_ID].asUUID();
-
-
-			LL_DEBUGS("ExperienceCache") << "Received result for " << public_key 
-				<< " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL ;
-
-			processExperience(public_key, row);
-		}
-
-		LLSD error_ids = getContent()["error_ids"];
-		LLSD::array_const_iterator errIt = error_ids.beginArray();
-		for( /**/ ; errIt != error_ids.endArray() ; ++errIt )
-		{
-			LLUUID id = errIt->asUUID();		
-			LLSD exp;
-			exp[EXPIRES]=DEFAULT_EXPIRATION;
-			exp[EXPERIENCE_ID] = id;
-			exp[PROPERTIES]=PROPERTY_INVALID;
-			exp[MISSING]=true;
-            exp[QUOTA] = DEFAULT_QUOTA;
-
-			processExperience(id, exp);
-			LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL ;
-		}
-
-		LL_DEBUGS("ExperienceCache") << sCache.size() << " cached experiences" << LL_ENDL;
-	}
-
-	/*virtual*/ void httpFailure()
-	{
- 		LL_WARNS("ExperienceCache") << "Request failed "<<getStatus()<<" "<<getReason()<< LL_ENDL;
- 		// We're going to construct a dummy record and cache it for a while,
- 		// either briefly for a 503 Service Unavailable, or longer for other
- 		// errors.
- 		F64 retry_timestamp = errorRetryTimestamp(getStatus());
- 
- 
- 		// Add dummy records for all agent IDs in this request
- 		ask_queue_t::const_iterator it = mKeys.begin();
- 		for ( ; it != mKeys.end(); ++it)
-		{
-
-			LLSD exp = get(it->first);
+        LLSD headers = httpResults[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_HEADERS];
+        // build dummy entries for the failed requests
+        for (RequestQueue_t::const_iterator it = requests.begin(); it != requests.end(); ++it)
+        {
+            LLSD exp = get(*it);
             //leave the properties alone if we already have a cache entry for this xp
-            if(exp.isUndefined())
+            if (exp.isUndefined())
             {
-                exp[PROPERTIES]=PROPERTY_INVALID;
+                exp[PROPERTIES] = PROPERTY_INVALID;
             }
-			exp[EXPIRES]=retry_timestamp;
-			exp[EXPERIENCE_ID] = it->first;
-			exp["key_type"] = it->second;
-			exp["uuid"] = it->first;
-			exp["error"] = (LLSD::Integer)getStatus();
+            exp[EXPIRES] = now + LLExperienceCacheImpl::getErrorRetryDeltaTime(status, headers);
+            exp[EXPERIENCE_ID] = *it;
+            exp["key_type"] = EXPERIENCE_ID;
+            exp["uuid"] = *it;
+            exp["error"] = (LLSD::Integer)status.getType();
             exp[QUOTA] = DEFAULT_QUOTA;
 
- 			LLExperienceCache::processExperience(it->first, exp);
- 		}
-
-	}
-
-	// Return time to retry a request that generated an error, based on
-	// error type and headers.  Return value is seconds-since-epoch.
-	F64 errorRetryTimestamp(S32 status)
-	{
+            processExperience(*it, exp);
+        }
+        return;
+    }
 
-		// Retry-After takes priority
-		LLSD retry_after = getResponseHeaders()["retry-after"];
-		if (retry_after.isDefined())
-		{
-			// We only support the delta-seconds type
-			S32 delta_seconds = retry_after.asInteger();
-			if (delta_seconds > 0)
-			{
-				// ...valid delta-seconds
-				return F64(delta_seconds);
-			}
-		}
+    LLSD experiences = result["experience_keys"];
+    
+    for (LLSD::array_const_iterator it = experiences.beginArray(); 
+        it != experiences.endArray(); ++it)
+    {
+        const LLSD& row = *it;
+        LLUUID public_key = row[EXPERIENCE_ID].asUUID();
 
-		// If no Retry-After, look for Cache-Control max-age
-		F64 expires = 0.0;
-		if (LLExperienceCache::expirationFromCacheControl(getResponseHeaders(), &expires))
-		{
-			return expires;
-		}
+        LL_DEBUGS("ExperienceCache") << "Received result for " << public_key
+            << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL;
 
-		// No information in header, make a guess
-		if (status == 503)
-		{
-			// ...service unavailable, retry soon
-			const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min
-			return SERVICE_UNAVAILABLE_DELAY;
-		}
-		else if (status == 499)
-		{
-			// ...we were probably too busy, retry quickly
-			const F64 BUSY_DELAY = 10.0; // 10 seconds
-			return BUSY_DELAY;
+        processExperience(public_key, row);
+    }
 
-		}
-		else
-		{
-			// ...other unexpected error
-			const F64 DEFAULT_DELAY = 3600.0; // 1 hour
-			return DEFAULT_DELAY;
-		}
-	}
+    LLSD error_ids = result["error_ids"];
+    
+    for (LLSD::array_const_iterator errIt = error_ids.beginArray(); 
+        errIt != error_ids.endArray(); ++errIt)
+    {
+        LLUUID id = errIt->asUUID();
+        LLSD exp;
+        exp[EXPIRES] = DEFAULT_EXPIRATION;
+        exp[EXPERIENCE_ID] = id;
+        exp[PROPERTIES] = PROPERTY_INVALID;
+        exp[MISSING] = true;
+        exp[QUOTA] = DEFAULT_QUOTA;
+
+        processExperience(id, exp);
+        LL_WARNS("ExperienceCache") << "LLExperienceResponder::result() error result for " << id << LL_ENDL;
+    }
 
-private:
-	ask_queue_t mKeys;
-};
+}
 
 
 void LLExperienceCache::requestExperiences()
 {
-	if(sAskQueue.empty() || sLookupURL.empty())
-		return;
-
-	F64 now = LLFrameTimer::getTotalSeconds();
+    if (mCapability.empty())
+    {
+        LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL;
+        return;
+    }
 
-	const U32 EXP_URL_SEND_THRESHOLD = 3000;
-	const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD/UUID_STR_LENGTH;
+    std::string urlBase = mCapability("GetExperienceInfo");
+    if (urlBase.empty())
+    {
+        LL_WARNS("ExperienceCache") << "No Experience capability." << LL_ENDL;
+        return;
+    }
 
-	std::ostringstream ostr;
+    if (*urlBase.rbegin() != '/')
+    {
+        urlBase += "/";
+    }
+    urlBase += "id/";
 
-	ask_queue_t keys;
 
-	ostr << sLookupURL << "?page_size=" << PAGE_SIZE;
+	F64 now = LLFrameTimer::getTotalSeconds();
 
-	int request_count = 0;
-	while(!sAskQueue.empty() && request_count < sMaximumLookups)
-	{
-		ask_queue_t::iterator it = sAskQueue.begin();
-		const LLUUID& key = it->first;
-		const std::string& key_type = it->second;
+    const U32 EXP_URL_SEND_THRESHOLD = 3000;
+    const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD / UUID_STR_LENGTH;
 
-		ostr << '&' << key_type << '=' << key.asString() ;
-		
-		keys[key]=key_type;
-		request_count++;
+    std::ostringstream ostr;
+    ostr << urlBase << "?page_size=" << PAGE_SIZE;
+    RequestQueue_t  requests;
 
-		sPendingQueue[key] = now;
-			
-		if(ostr.tellp() > EXP_URL_SEND_THRESHOLD)
-		{
-			LL_DEBUGS("ExperienceCache") <<  "requestExperiences() query: " << ostr.str() << LL_ENDL;
-			LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys));
-			ostr.clear();
-			ostr.str(sLookupURL);
-			ostr << "?page_size=" << PAGE_SIZE;
-			keys.clear();
-		}
-		sAskQueue.erase(it);
-	}
+    while (!mRequestQueue.empty())
+    {
+        RequestQueue_t::iterator it = mRequestQueue.begin();
+        LLUUID key = (*it);
+        mRequestQueue.erase(it);
+        requests.insert(key);
+
+        ostr << "&" << EXPERIENCE_ID << "=" << key.asString();
+        mPendingQueue[key] = now;
+        
+        if (mRequestQueue.empty() || (ostr.tellp() > EXP_URL_SEND_THRESHOLD))
+        {   // request is placed in the coprocedure pool for the ExpCache cache.  Throttling is done by the pool itself.
+            LLCoprocedureManager::getInstance()->enqueueCoprocedure("ExpCache", "Request",
+                boost::bind(&LLExperienceCache::requestExperiencesCoro, this, _1, ostr.str(), requests) );
+
+            ostr.str(std::string());
+            ostr << urlBase << "?page_size=" << PAGE_SIZE;
+            requests.clear();
+        }
+    }
 
-	if(ostr.tellp() > sLookupURL.size())
-	{
-		LL_DEBUGS("ExperienceCache") <<  "requestExperiences() query 2: " << ostr.str() << LL_ENDL;
-		LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys));
-	}
 }
 
 
@@ -427,9 +373,9 @@ bool LLExperienceCache::isRequestPending(const LLUUID& public_key)
 	bool isPending = false;
 	const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0;
 
-	pending_queue_t::const_iterator it = sPendingQueue.find(public_key);
+    PendingQueue_t::const_iterator it = mPendingQueue.find(public_key);
 
-	if(it != sPendingQueue.end())
+	if(it != mPendingQueue.end())
 	{
 		F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS;
 		isPending = (it->second > expire_time);
@@ -438,69 +384,70 @@ bool LLExperienceCache::isRequestPending(const LLUUID& public_key)
 	return isPending;
 }
 
-
-void LLExperienceCache::setLookupURL(const std::string& lookup_url)
+void LLExperienceCache::setCapabilityQuery(LLExperienceCache::CapabilityQuery_t queryfn)
 {
-	sLookupURL = lookup_url;
-	if(!sLookupURL.empty())
-	{
-		sLookupURL += "id/";
-	}
+    mCapability = queryfn;
 }
 
-bool LLExperienceCache::hasLookupURL()
-{
-	return !sLookupURL.empty();
-}
 
-void LLExperienceCache::idle()
+void LLExperienceCache::idleCoro()
 {
-	const F32 SECS_BETWEEN_REQUESTS = 0.1f;
-	if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS))
-	{
-		return;
-	}
+    const F32 SECS_BETWEEN_REQUESTS = 0.5f;
+    const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds
 
-	// Must be large relative to above
-	const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds
-	if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT))
-	{
-		eraseExpired();
-	}
+    LL_INFOS("ExperienceCache") << "Launching Experience cache idle coro." << LL_ENDL;
+    LLEventTimeout timeout;
 
-	if(!sAskQueue.empty())
-	{
-		requestExperiences();
-	}
+    do 
+    {
+        timeout.eventAfter(SECS_BETWEEN_REQUESTS, LLSD());
+        llcoro::waitForEventOn(timeout);
+
+        if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT))
+        {
+            eraseExpired();
+        }
+
+        if (!mRequestQueue.empty())
+        {
+            requestExperiences();
+        }
+
+    } while (!mShutdown);
+
+    // The coroutine system will likely be shut down by the time we get to this point
+    // (or at least no further cycling will occur on it since the user has decided to quit.)
 }
 
 void LLExperienceCache::erase(const LLUUID& key)
 {
-	cache_t::iterator it = sCache.find(key);
+	cache_t::iterator it = mCache.find(key);
 				
-	if(it != sCache.end())
+	if(it != mCache.end())
 	{
-		sCache.erase(it);
+		mCache.erase(it);
 	}
 }
 
 void LLExperienceCache::eraseExpired()
 {
 	F64 now = LLFrameTimer::getTotalSeconds();
-	cache_t::iterator it = sCache.begin();
-	while (it != sCache.end())
+	cache_t::iterator it = mCache.begin();
+	while (it != mCache.end())
 	{
 		cache_t::iterator cur = it;
 		LLSD& exp = cur->second;
 		++it;
 
+        //LL_INFOS("ExperienceCache") << "Testing experience \"" << exp[NAME] << "\" with exp time " << exp[EXPIRES].asReal() << "(now = " << now << ")" << LL_ENDL;
+
 		if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now)
 		{
             if(!exp.has(EXPERIENCE_ID))
 			{
                 LL_WARNS("ExperienceCache") << "Removing experience with no id " << LL_ENDL ;
-                sCache.erase(cur);
-				}
+                mCache.erase(cur);
+			}
             else
             {
                 LLUUID id = exp[EXPERIENCE_ID].asUUID();
@@ -512,7 +459,7 @@ void LLExperienceCache::eraseExpired()
 				else
 				{
                     LL_WARNS("ExperienceCache") << "Removing invalid experience " << id << LL_ENDL ;
-					sCache.erase(cur);
+					mCache.erase(cur);
 				}
 			}
 		}
@@ -521,11 +468,11 @@ void LLExperienceCache::eraseExpired()
 	
 bool LLExperienceCache::fetch(const LLUUID& key, bool refresh/* = true*/)
 {
-	if(!key.isNull() && !isRequestPending(key) && (refresh || sCache.find(key)==sCache.end()))
+	if(!key.isNull() && !isRequestPending(key) && (refresh || mCache.find(key)==mCache.end()))
 	{
-		LL_DEBUGS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL ;
-		sAskQueue[key]=EXPERIENCE_ID;
+		LL_DEBUGS("ExperienceCache") << " queue request for " << EXPERIENCE_ID << " " << key << LL_ENDL;
 
+        mRequestQueue.insert(key);
 		return true;
 	}
 	return false;
@@ -549,13 +496,12 @@ const LLSD& LLExperienceCache::get(const LLUUID& key)
 	
 	if(key.isNull()) 
 		return empty;
-	cache_t::const_iterator it = sCache.find(key);
+	cache_t::const_iterator it = mCache.find(key);
 
-	if (it != sCache.end())
+	if (it != mCache.end())
 	{
 		return it->second;
 	}
-
 	fetch(key);
 
 	return empty;
@@ -565,8 +511,8 @@ void LLExperienceCache::get(const LLUUID& key, LLExperienceCache::Callback_t slo
 	if(key.isNull()) 
 		return;
 
-	cache_t::const_iterator it = sCache.find(key);
-	if (it != sCache.end())
+	cache_t::const_iterator it = mCache.find(key);
+	if (it != mCache.end())
 	{
 		// ...name already exists in cache, fire callback now
 		callback_signal_t signal;
@@ -580,28 +526,10 @@ void LLExperienceCache::get(const LLUUID& key, LLExperienceCache::Callback_t slo
 
 	signal_ptr signal = signal_ptr(new callback_signal_t());
 	
-	std::pair<signal_map_t::iterator, bool> result = sSignalMap.insert(signal_map_t::value_type(key, signal));
+	std::pair<signal_map_t::iterator, bool> result = mSignalMap.insert(signal_map_t::value_type(key, signal));
 	if (!result.second)
-		signal = result.first.second;
+		signal = (*result.first).second;
 	signal->connect(slot);
-	
-#if 0
-	// always store additional callback, even if request is pending
-	signal_map_t::iterator sig_it = sSignalMap.find(key);
-	if (sig_it == sSignalMap.end())
-	{
-		// ...new callback for this id
-		signal_ptr signal = signal_ptr(new callback_signal_t());
-		signal->connect(slot);
-		sSignalMap[key] = signal;
-	}
-	else
-	{
-		// ...existing callback, bind additional slot
-		callback_signal_t* signal = sig_it->second;
-		signal->connect(slot);
-	}
-#endif
 }
 
 //=========================================================================
@@ -610,14 +538,71 @@ void LLExperienceCacheImpl::mapKeys(const LLSD& legacyKeys)
 	LLSD::array_const_iterator exp = legacyKeys.beginArray();
 	for (/**/; exp != legacyKeys.endArray(); ++exp)
 	{
-		if (exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY))
+        if (exp->has(LLExperienceCacheImpl::EXPERIENCE_ID) && exp->has(LLExperienceCacheImpl::PRIVATE_KEY))
 		{
-			privateToPublicKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()] = (*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID();
+            LLExperienceCacheImpl::privateToPublicKeyMap[(*exp)[LLExperienceCacheImpl::PRIVATE_KEY].asUUID()] = 
+                (*exp)[LLExperienceCacheImpl::EXPERIENCE_ID].asUUID();
 		}
 	}
 }
 
-bool LLExperienceCacheImpl::max_age_from_cache_control(const std::string& cache_control, S32 *max_age)
+// Return time to retry a request that generated an error, based on
+// error type and headers.  Return value is seconds-since-epoch.
+F64 LLExperienceCacheImpl::getErrorRetryDeltaTime(S32 status, LLSD headers)
+{
+
+    // Retry-After takes priority
+    LLSD retry_after = headers["retry-after"];
+    if (retry_after.isDefined())
+    {
+        // We only support the delta-seconds type
+        S32 delta_seconds = retry_after.asInteger();
+        if (delta_seconds > 0)
+        {
+            // ...valid delta-seconds
+            return F64(delta_seconds);
+        }
+    }
+
+    // If no Retry-After, look for Cache-Control max-age
+    // Allow the header to override the default
+    LLSD cache_control_header = headers["cache-control"];
+    if (cache_control_header.isDefined())
+    {
+        S32 max_age = 0;
+        std::string cache_control = cache_control_header.asString();
+        if (LLExperienceCacheImpl::maxAgeFromCacheControl(cache_control, &max_age))
+        {
+            LL_WARNS("ExperienceCache")
+                << "got EXPIRES from headers, max_age " << max_age
+                << LL_ENDL;
+            return (F64)max_age;
+        }
+    }
+
+    // No information in header, make a guess
+    if (status == 503)
+    {
+        // ...service unavailable, retry soon
+        const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min
+        return SERVICE_UNAVAILABLE_DELAY;
+    }
+    else if (status == 499)
+    {
+        // ...we were probably too busy, retry quickly
+        const F64 BUSY_DELAY = 10.0; // 10 seconds
+        return BUSY_DELAY;
+
+    }
+    else
+    {
+        // ...other unexpected error
+        const F64 DEFAULT_DELAY = 3600.0; // 1 hour
+        return DEFAULT_DELAY;
+    }
+}
+
+bool LLExperienceCacheImpl::maxAgeFromCacheControl(const std::string& cache_control, S32 *max_age)
 {
 	// Split the string on "," to get a list of directives
 	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h
index 8da038a8c3..937225a80a 100644
--- a/indra/llmessage/llexperiencecache.h
+++ b/indra/llmessage/llexperiencecache.h
@@ -31,7 +31,9 @@
 
 #include "linden_common.h"
 #include "llsingleton.h"
+#include "llframetimer.h"
 #include "llsd.h"
+#include "llcorehttputil.h"
 #include <boost/signals2.hpp>
 #include <boost/function.hpp>
 
@@ -44,8 +46,11 @@ class LLExperienceCache: public LLSingleton < LLExperienceCache >
     friend class LLSingleton < LLExperienceCache > ;
 
 public:
+    typedef boost::function<std::string(const std::string &)> CapabilityQuery_t;
     typedef boost::function<void(const LLSD &)> Callback_t;
 
+    void cleanup();
+
     void erase(const LLUUID& key);
     bool fetch(const LLUUID& key, bool refresh = false);
     void insert(const LLSD& experience_data);
@@ -54,7 +59,39 @@ public:
     // If name information is in cache, callback will be called immediately.
     void get(const LLUUID& key, Callback_t slot);
 
+    bool isRequestPending(const LLUUID& public_key);
+
+    void setCapabilityQuery(CapabilityQuery_t queryfn);
+
+    static const std::string NAME;			// "name"
+    static const std::string EXPERIENCE_ID;	// "public_id"
+    static const std::string AGENT_ID;      // "agent_id"
+    static const std::string GROUP_ID;      // "group_id"
+    static const std::string PROPERTIES;	// "properties"
+    static const std::string EXPIRES;		// "expiration"  
+    static const std::string DESCRIPTION;	// "description"
+    static const std::string QUOTA;         // "quota"
+    static const std::string MATURITY;      // "maturity"
+    static const std::string METADATA;      // "extended_metadata"
+    static const std::string SLURL;         // "slurl"
+
+    static const std::string MISSING;       // "DoesNotExist"
+
+    // should be in sync with experience-api/experiences/models.py
+    static const int PROPERTY_INVALID;		// 1 << 0
+    static const int PROPERTY_PRIVILEGED;	// 1 << 3
+    static const int PROPERTY_GRID;			// 1 << 4
+    static const int PROPERTY_PRIVATE;		// 1 << 5
+    static const int PROPERTY_DISABLED;		// 1 << 6  
+    static const int PROPERTY_SUSPENDED;	// 1 << 7
+
 private:
+    LLExperienceCache();
+    virtual ~LLExperienceCache();
+
+    virtual void initSingleton();
+
+
     // Callback types for get() 
     typedef boost::signals2::signal < void(const LLSD &) > callback_signal_t;
 	typedef boost::shared_ptr<callback_signal_t> signal_ptr;
@@ -64,63 +101,43 @@ private:
 	typedef std::map<LLUUID, signal_ptr> signal_map_t;
 	typedef std::map<LLUUID, LLSD> cache_t;
 	
-	typedef std::set<LLUUID> ask_queue_t;
-	
-	
+	typedef std::set<LLUUID> RequestQueue_t;
+    typedef std::map<LLUUID, F64> PendingQueue_t;
+
 	//--------------------------------------------
 	static const std::string PRIVATE_KEY;	// "private_id"
-	static const std::string MISSING;       // "DoesNotExist"
-	
-	static const std::string AGENT_ID;      // "agent_id"
-	static const std::string GROUP_ID;      // "group_id"
-	static const std::string EXPERIENCE_ID;	// "public_id"
-	static const std::string NAME;			// "name"
-	static const std::string PROPERTIES;	// "properties"
-	static const std::string EXPIRES;		// "expiration"  
-	static const std::string DESCRIPTION;	// "description"
-	static const std::string QUOTA;         // "quota"
-	static const std::string MATURITY;      // "maturity"
-	static const std::string METADATA;      // "extended_metadata"
-	static const std::string SLURL;         // "slurl"
-	
-	// should be in sync with experience-api/experiences/models.py
-	static const int PROPERTY_INVALID;		// 1 << 0
-	static const int PROPERTY_PRIVILEGED;	// 1 << 3
-	static const int PROPERTY_GRID;			// 1 << 4
-	static const int PROPERTY_PRIVATE;		// 1 << 5
-	static const int PROPERTY_DISABLED;		// 1 << 6  
-	static const int PROPERTY_SUSPENDED;	// 1 << 7
 	
 	// default values
 	static const F64 DEFAULT_EXPIRATION; 	// 600.0
 	static const S32 DEFAULT_QUOTA; 		// 128 this is megabytes
 	
-//--------------------------------------------
-    LLExperienceCache();
-    virtual ~LLExperienceCache();
-
-    void exportFile(std::ostream& ostr) const;
-    void importFile(std::istream& istr);
-
 //--------------------------------------------
     void processExperience(const LLUUID& public_key, const LLSD& experience);
 
 //--------------------------------------------
-	cache_t			sCache;
-	signal_map_t	sSignalMap;	
-	ask_queue_t		sAskQueue;
-	
+	cache_t			mCache;
+	signal_map_t	mSignalMap;	
+	RequestQueue_t	mRequestQueue;
+    PendingQueue_t  mPendingQueue;
+
+    LLFrameTimer    mRequestTimer;
+    LLFrameTimer    mEraseExpiredTimer;    // Periodically clean out expired entries from the cache
+    CapabilityQuery_t mCapability;
+    std::string     mCacheFileName;
+    bool            mShutdown;
+
+    void idleCoro();
 	void eraseExpired();
-	
-	void setLookupURL(const std::string& lookup_url);
-	bool hasLookupURL();
+    void requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, std::string, RequestQueue_t);
+    void requestExperiences();
 
 	void setMaximumLookups(int maximumLookups);
 
-	void idle();
-	void bootstrap(const LLSD& legacyKeys, int initialExpiration);
-	
+    void bootstrap(const LLSD& legacyKeys, int initialExpiration);
+    void exportFile(std::ostream& ostr) const;
+    void importFile(std::istream& istr);
 
+    // 
 	const cache_t& getCached();
 
 	// maps an experience private key to the experience id
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 259fbde806..d192aac448 100755
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -11,11 +11,13 @@ include(LLMessage)
 include(LLCoreHttp)
 include(LLRender)
 include(LLWindow)
+include(LLCoreHttp)
 include(LLVFS)
 include(LLXML)
 
 include_directories(
     ${LLCOMMON_INCLUDE_DIRS}
+    ${LLCOREHTTP_INCLUDE_DIRS}
     ${LLIMAGE_INCLUDE_DIRS}
     ${LLINVENTORY_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index eb7f98e618..e76f2a1550 100755
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -1430,7 +1430,7 @@ std::string LLUrlEntryExperienceProfile::getLabel( const std::string &url, const
         return LLTrans::getString("ExperienceNameNull");
     }
 
-    const LLSD& experience_details = LLExperienceCache::get(experience_id);
+    const LLSD& experience_details = LLExperienceCache::getInstance()->get(experience_id);
     if(!experience_details.isUndefined())
     {
 		std::string experience_name_string = experience_details[LLExperienceCache::NAME].asString();
@@ -1438,7 +1438,7 @@ std::string LLUrlEntryExperienceProfile::getLabel( const std::string &url, const
     }
 
     addObserver(experience_id_string, url, cb);
-    LLExperienceCache::get(experience_id, boost::bind(&LLUrlEntryExperienceProfile::onExperienceDetails, this, _1));
+    LLExperienceCache::getInstance()->get(experience_id, boost::bind(&LLUrlEntryExperienceProfile::onExperienceDetails, this, _1));
     return LLTrans::getString("LoadingData");
 
 }
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index fe9c7c0fc0..090879e372 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -161,7 +161,6 @@ set(viewer_SOURCE_FILES
     llconversationloglistitem.cpp
     llconversationmodel.cpp
     llconversationview.cpp
-    llcoproceduremanager.cpp
     llcurrencyuimanager.cpp
     llcylinder.cpp
     lldateutil.cpp
@@ -771,7 +770,6 @@ set(viewer_HEADER_FILES
     llconversationloglistitem.h
     llconversationmodel.h
     llconversationview.h
-    llcoproceduremanager.h
     llcurrencyuimanager.h
     llcylinder.h
     lldateutil.h
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index e7dd378edd..3316f1e654 100755
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -953,6 +953,15 @@ BOOL LLAgent::inPrelude()
 }
 
 
+std::string LLAgent::getRegionCapability(const std::string &name)
+{
+    if (!mRegionp)
+        return std::string();
+    
+    return mRegionp->getCapability(name);
+}
+
+
 //-----------------------------------------------------------------------------
 // canManageEstate()
 //-----------------------------------------------------------------------------
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index 0ba3dea427..5731f4db89 100755
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -262,6 +262,9 @@ public:
 	LLHost			getRegionHost() const;
 	BOOL			inPrelude();
 
+    // Capability 
+    std::string     getRegionCapability(const std::string &name); // short hand for if (getRegion()) { getRegion()->getCapability(name) }
+
 	/**
 	 * Register a boost callback to be called when the agent changes regions
 	 * Note that if you need to access a capability for the region, you may need to wait
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 9b9b591cd1..e13a9d96c7 100755
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -228,7 +228,7 @@
 #include "llmachineid.h"
 #include "llmainlooprepeater.h"
 
-
+#include "llcoproceduremanager.h"
 #include "llviewereventrecorder.h"
 
 
@@ -755,8 +755,11 @@ void fast_exit(int rc)
 {
 	_exit(rc);
 }
+
+
 }
 
+
 bool LLAppViewer::init()
 {	
 	setupErrorHandling(mSecondInstance);
@@ -1216,6 +1219,12 @@ bool LLAppViewer::init()
 
 	LLAgentLanguage::init();
 
+    /// Tell the Coprocedure manager how to discover and store the pool sizes
+    // what I wanted
+    LLCoprocedureManager::getInstance()->setPropertyMethods(
+        boost::bind(&LLControlGroup::getU32, boost::ref(gSavedSettings), _1),
+        boost::bind(&LLControlGroup::declareU32, boost::ref(gSavedSettings), _1, _2, _3, LLControlVariable::PERSIST_ALWAYS));
+
 	return true;
 }
 
@@ -4700,31 +4709,6 @@ void LLAppViewer::saveNameCache()
 }
 
 
-void LLAppViewer::saveExperienceCache()
-{
-	std::string filename =
-		gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml");
-	LL_INFOS("ExperienceCache") << "Saving " << filename << LL_ENDL;
-	llofstream cache_stream(filename.c_str());
-	if(cache_stream.is_open())
-	{
-		LLExperienceCache::exportFile(cache_stream);
-	}
-}
-
-void LLAppViewer::loadExperienceCache()
-{
-	std::string filename =
-		gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "experience_cache.xml");
-	LL_INFOS("ExperienceCache") << "Loading " << filename << LL_ENDL;
-	llifstream cache_stream(filename.c_str());
-	if(cache_stream.is_open())
-	{
-		LLExperienceCache::importFile(cache_stream);
-	}
-}
-
-
 /*!	@brief		This class is an LLFrameTimer that can be created with
 				an elapsed time that starts counting up from the given value
 				rather than 0.0.
@@ -4920,7 +4904,6 @@ void LLAppViewer::idle()
 	    // floating throughout the various object lists.
 	    //
 		idleNameCache();
-		idleExperienceCache();
 		idleNetwork();
 	    	        
 
@@ -5350,22 +5333,6 @@ void LLAppViewer::idleNameCache()
 	LLAvatarNameCache::idle();
 }
 
-void LLAppViewer::idleExperienceCache()
-{
-	LLViewerRegion* region = gAgent.getRegion();
-	if (!region) return;
-	
-	std::string lookup_url=region->getCapability("GetExperienceInfo"); 
-	if(!lookup_url.empty() && *lookup_url.rbegin() != '/')
-	{
-		lookup_url += '/';
-	}
-	
-	LLExperienceCache::setLookupURL(lookup_url);
-
-	LLExperienceCache::idle();
-}
-
 //
 // Handle messages, and all message related stuff
 //
@@ -5528,7 +5495,9 @@ void LLAppViewer::disconnectViewer()
 	}
 
 	saveNameCache();
-	saveExperienceCache();
+    LLExperienceCache *expCache = LLExperienceCache::getIfExists();
+    if (expCache)
+        expCache->cleanup();
 
 	// close inventory interface, close all windows
 	LLFloaterInventory::cleanup();
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 718871138e..e8a1ca036b 100755
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -122,9 +122,6 @@ public:
     void loadNameCache();
     void saveNameCache();
 
-	void loadExperienceCache();
-	void saveExperienceCache();
-
 	void removeMarkerFiles();
 	
 	void removeDumpDir();
@@ -233,7 +230,6 @@ private:
     void idle(); 
     void idleShutdown();
 	// update avatar SLID and display name caches
-	void idleExperienceCache();
 	void idleNameCache();
     void idleNetwork();
 
diff --git a/indra/newview/llcoproceduremanager.cpp b/indra/newview/llcoproceduremanager.cpp
deleted file mode 100644
index db01c13079..0000000000
--- a/indra/newview/llcoproceduremanager.cpp
+++ /dev/null
@@ -1,406 +0,0 @@
-/**
-* @file LLCoprocedurePool.cpp
-* @author Rider Linden
-* @brief Singleton class for managing asset uploads to the sim.
-*
-* $LicenseInfo:firstyear=2015&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2015, 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 "llviewerprecompiledheaders.h"
-#include "linden_common.h" 
-
-#include "llviewercontrol.h"
-
-#include "llcoproceduremanager.h"
-
-//=========================================================================
-// Map of pool sizes for known pools
-static std::map<std::string, U32> DefaultPoolSizes;
-
-// *TODO$: When C++11 this can be initialized here as follows:
-// = {{"AIS", 25}, {"Upload", 1}}
-
-#define DEFAULT_POOL_SIZE 5
-
-//=========================================================================
-class LLCoprocedurePool: private boost::noncopyable
-{
-public:
-    typedef LLCoprocedureManager::CoProcedure_t CoProcedure_t;
-
-    LLCoprocedurePool(const std::string &name, size_t size);
-    virtual ~LLCoprocedurePool();
-
-    /// Places the coprocedure on the queue for processing. 
-    /// 
-    /// @param name Is used for debugging and should identify this coroutine.
-    /// @param proc Is a bound function to be executed 
-    /// 
-    /// @return This method returns a UUID that can be used later to cancel execution.
-    LLUUID enqueueCoprocedure(const std::string &name, CoProcedure_t proc);
-
-    /// Cancel a coprocedure. If the coprocedure is already being actively executed 
-    /// this method calls cancelYieldingOperation() on the associated HttpAdapter
-    /// If it has not yet been dequeued it is simply removed from the queue.
-    bool cancelCoprocedure(const LLUUID &id);
-
-    /// Requests a shutdown of the upload manager. Passing 'true' will perform 
-    /// an immediate kill on the upload coroutine.
-    void shutdown(bool hardShutdown = false);
-
-    /// Returns the number of coprocedures in the queue awaiting processing.
-    ///
-    inline size_t countPending() const
-    {
-        return mPendingCoprocs.size();
-    }
-
-    /// Returns the number of coprocedures actively being processed.
-    ///
-    inline size_t countActive() const
-    {
-        return mActiveCoprocs.size();
-    }
-
-    /// Returns the total number of coprocedures either queued or in active processing.
-    ///
-    inline size_t count() const
-    {
-        return countPending() + countActive();
-    }
-
-private:
-    struct QueuedCoproc
-    {
-        typedef boost::shared_ptr<QueuedCoproc> ptr_t;
-
-        QueuedCoproc(const std::string &name, const LLUUID &id, CoProcedure_t proc) :
-            mName(name),
-            mId(id),
-            mProc(proc)
-        {}
-
-        std::string mName;
-        LLUUID mId;
-        CoProcedure_t mProc;
-    };
-
-    // we use a deque here rather than std::queue since we want to be able to 
-    // iterate through the queue and potentially erase an entry from the middle.
-    typedef std::deque<QueuedCoproc::ptr_t>  CoprocQueue_t;
-    typedef std::map<LLUUID, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> ActiveCoproc_t;
-
-    std::string     mPoolName;
-    size_t          mPoolSize;
-    CoprocQueue_t   mPendingCoprocs;
-    ActiveCoproc_t  mActiveCoprocs;
-    bool            mShutdown;
-    LLEventStream   mWakeupTrigger;
-
-    typedef std::map<std::string, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> CoroAdapterMap_t;
-    LLCore::HttpRequest::policy_t mHTTPPolicy;
-
-    CoroAdapterMap_t mCoroMapping;
-
-    void coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter);
-
-};
-
-//=========================================================================
-LLCoprocedureManager::LLCoprocedureManager()
-{
-    DefaultPoolSizes.insert(std::map<std::string, U32>::value_type("Upload", 1));
-    DefaultPoolSizes.insert(std::map<std::string, U32>::value_type("AIS", 25));
-}
-
-LLCoprocedureManager::~LLCoprocedureManager()
-{
-
-}
-
-LLCoprocedureManager::poolPtr_t LLCoprocedureManager::initializePool(const std::string &poolName)
-{
-    // Attempt to look up a pool size in the configuration.  If found use that
-    std::string keyName = "PoolSize" + poolName;
-    int size = 5;
-
-    size = gSavedSettings.getU32(keyName);
-    if (size == 0)
-    {   // if not found grab the know default... if there is no known 
-        // default use a reasonable number like 5.
-        std::map<std::string, U32>::iterator it = DefaultPoolSizes.find(poolName);
-        if (it == DefaultPoolSizes.end())
-            size = DEFAULT_POOL_SIZE;
-        else
-            size = (*it).second;
-        gSavedSettings.declareU32(keyName, size, "Coroutine Pool size for " + poolName, LLControlVariable::PERSIST_ALWAYS);
-        LL_WARNS() << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL;
-    }
-
-    poolPtr_t pool = poolPtr_t(new LLCoprocedurePool(poolName, size));
-    mPoolMap.insert(poolMap_t::value_type(poolName, pool));
-
-    return pool;
-}
-
-//-------------------------------------------------------------------------
-LLUUID LLCoprocedureManager::enqueueCoprocedure(const std::string &pool, const std::string &name, CoProcedure_t proc)
-{
-    // Attempt to find the pool and enqueue the procedure.  If the pool does 
-    // not exist, create it.
-    poolPtr_t targetPool;
-    poolMap_t::iterator it = mPoolMap.find(pool);
-
-    if (it == mPoolMap.end())
-    {
-        targetPool = initializePool(pool);
-    }
-    else
-    {
-        targetPool = (*it).second;
-    }
-
-    if (!targetPool)
-    {
-        LL_WARNS() << "LLCoprocedureManager unable to create coprocedure pool named \"" << pool << "\"" << LL_ENDL;
-        return LLUUID::null;
-    }
-
-    return targetPool->enqueueCoprocedure(name, proc);
-}
-
-void LLCoprocedureManager::cancelCoprocedure(const LLUUID &id)
-{
-    for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
-    {
-        if ((*it).second->cancelCoprocedure(id))
-            return;
-    }
-    LL_INFOS() << "Coprocedure not found." << LL_ENDL;
-}
-
-void LLCoprocedureManager::shutdown(bool hardShutdown)
-{
-    for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
-    {
-        (*it).second->shutdown(hardShutdown);
-    }
-    mPoolMap.clear();
-}
-
-//-------------------------------------------------------------------------
-size_t LLCoprocedureManager::countPending() const
-{
-    size_t count = 0;
-    for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
-    {
-        count += (*it).second->countPending();
-    }
-    return count;
-}
-
-size_t LLCoprocedureManager::countPending(const std::string &pool) const
-{
-    poolMap_t::const_iterator it = mPoolMap.find(pool);
-
-    if (it == mPoolMap.end())
-        return 0;
-    return (*it).second->countPending();
-}
-
-size_t LLCoprocedureManager::countActive() const
-{
-    size_t count = 0;
-    for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
-    {
-        count += (*it).second->countActive();
-    }
-    return count;
-}
-
-size_t LLCoprocedureManager::countActive(const std::string &pool) const
-{
-    poolMap_t::const_iterator it = mPoolMap.find(pool);
-
-    if (it == mPoolMap.end())
-        return 0;
-    return (*it).second->countActive();
-}
-
-size_t LLCoprocedureManager::count() const
-{
-    size_t count = 0;
-    for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it)
-    {
-        count += (*it).second->count();
-    }
-    return count;
-}
-
-size_t LLCoprocedureManager::count(const std::string &pool) const
-{
-    poolMap_t::const_iterator it = mPoolMap.find(pool);
-
-    if (it == mPoolMap.end())
-        return 0;
-    return (*it).second->count();
-}
-
-//=========================================================================
-LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size):
-    mPoolName(poolName),
-    mPoolSize(size),
-    mPendingCoprocs(),
-    mShutdown(false),
-    mWakeupTrigger("CoprocedurePool" + poolName, true),
-    mCoroMapping(),
-    mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID)
-{
-    for (size_t count = 0; count < mPoolSize; ++count)
-    {
-        LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter =
-            LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t(
-            new LLCoreHttpUtil::HttpCoroutineAdapter( mPoolName + "Adapter", mHTTPPolicy));
-
-        std::string uploadCoro = LLCoros::instance().launch("LLCoprocedurePool("+mPoolName+")::coprocedureInvokerCoro",
-            boost::bind(&LLCoprocedurePool::coprocedureInvokerCoro, this, httpAdapter));
-
-        mCoroMapping.insert(CoroAdapterMap_t::value_type(uploadCoro, httpAdapter));
-    }
-
-    LL_INFOS() << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items." << LL_ENDL;
-
-    mWakeupTrigger.post(LLSD());
-}
-
-LLCoprocedurePool::~LLCoprocedurePool() 
-{
-    shutdown();
-}
-
-//-------------------------------------------------------------------------
-void LLCoprocedurePool::shutdown(bool hardShutdown)
-{
-    CoroAdapterMap_t::iterator it;
-
-    for (it = mCoroMapping.begin(); it != mCoroMapping.end(); ++it)
-    {
-        if (!(*it).first.empty())
-        {
-            if (hardShutdown)
-            {
-                LLCoros::instance().kill((*it).first);
-            }
-        }
-        if ((*it).second)
-        {
-            (*it).second->cancelYieldingOperation();
-        }
-    }
-
-    mShutdown = true;
-    mCoroMapping.clear();
-    mPendingCoprocs.clear();
-}
-
-//-------------------------------------------------------------------------
-LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoprocedurePool::CoProcedure_t proc)
-{
-    LLUUID id(LLUUID::generateNewID());
-
-    mPendingCoprocs.push_back(QueuedCoproc::ptr_t(new QueuedCoproc(name, id, proc)));
-    LL_INFOS() << "Coprocedure(" << name << ") enqueued with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
-
-    mWakeupTrigger.post(LLSD());
-
-    return id;
-}
-
-bool LLCoprocedurePool::cancelCoprocedure(const LLUUID &id)
-{
-    // first check the active coroutines.  If there, remove it and return.
-    ActiveCoproc_t::iterator itActive = mActiveCoprocs.find(id);
-    if (itActive != mActiveCoprocs.end())
-    {
-        LL_INFOS() << "Found and canceling active coprocedure with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
-        (*itActive).second->cancelYieldingOperation();
-        mActiveCoprocs.erase(itActive);
-        return true;
-    }
-
-    for (CoprocQueue_t::iterator it = mPendingCoprocs.begin(); it != mPendingCoprocs.end(); ++it)
-    {
-        if ((*it)->mId == id)
-        {
-            LL_INFOS() << "Found and removing queued coroutine(" << (*it)->mName << ") with Id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
-            mPendingCoprocs.erase(it);
-            return true;
-        }
-    }
-
-    LL_INFOS() << "Coprocedure with Id=" << id.asString() << " was not found." << " in pool \"" << mPoolName << "\"" << LL_ENDL;
-    return false;
-}
-
-//-------------------------------------------------------------------------
-void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter)
-{
-    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
-
-    while (!mShutdown)
-    {
-        llcoro::waitForEventOn(mWakeupTrigger);
-        if (mShutdown)
-            break;
-        
-        while (!mPendingCoprocs.empty())
-        {
-            QueuedCoproc::ptr_t coproc = mPendingCoprocs.front();
-            mPendingCoprocs.pop_front();
-            mActiveCoprocs.insert(ActiveCoproc_t::value_type(coproc->mId, httpAdapter));
-
-            LL_INFOS() << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL;
-
-            try
-            {
-                coproc->mProc(httpAdapter, coproc->mId);
-            }
-            catch (std::exception &e)
-            {
-                LL_WARNS() << "Coprocedure(" << coproc->mName << ") id=" << coproc->mId.asString() <<
-                    " threw an exception! Message=\"" << e.what() << "\"" << LL_ENDL;
-            }
-            catch (...)
-            {
-                LL_WARNS() << "A non std::exception was thrown from " << coproc->mName << " with id=" << coproc->mId << "." << " in pool \"" << mPoolName << "\"" << LL_ENDL;
-            }
-
-            LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL;
-
-            ActiveCoproc_t::iterator itActive = mActiveCoprocs.find(coproc->mId);
-            if (itActive != mActiveCoprocs.end())
-            {
-                mActiveCoprocs.erase(itActive);
-            }
-        }
-    }
-}
diff --git a/indra/newview/llcoproceduremanager.h b/indra/newview/llcoproceduremanager.h
deleted file mode 100644
index d7f74af76b..0000000000
--- a/indra/newview/llcoproceduremanager.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
-* @file llcoproceduremanager.h
-* @author Rider Linden
-* @brief Singleton class for managing asset uploads to the sim.
-*
-* $LicenseInfo:firstyear=2015&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2015, 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_COPROCEDURE_MANAGER_H
-#define LL_COPROCEDURE_MANAGER_H
-
-#include "lleventcoro.h"
-#include "llcoros.h"
-#include "llcorehttputil.h"
-#include "lluuid.h"
-
-class LLCoprocedurePool;
-
-class LLCoprocedureManager : public LLSingleton < LLCoprocedureManager >
-{
-public:
-    typedef boost::function<void(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, const LLUUID &id)> CoProcedure_t;
-
-    LLCoprocedureManager();
-    virtual ~LLCoprocedureManager();
-
-    /// Places the coprocedure on the queue for processing. 
-    /// 
-    /// @param name Is used for debugging and should identify this coroutine.
-    /// @param proc Is a bound function to be executed 
-    /// 
-    /// @return This method returns a UUID that can be used later to cancel execution.
-    LLUUID enqueueCoprocedure(const std::string &pool, const std::string &name, CoProcedure_t proc);
-
-    /// Cancel a coprocedure. If the coprocedure is already being actively executed 
-    /// this method calls cancelYieldingOperation() on the associated HttpAdapter
-    /// If it has not yet been dequeued it is simply removed from the queue.
-    void cancelCoprocedure(const LLUUID &id);
-
-    /// Requests a shutdown of the upload manager. Passing 'true' will perform 
-    /// an immediate kill on the upload coroutine.
-    void shutdown(bool hardShutdown = false);
-
-    /// Returns the number of coprocedures in the queue awaiting processing.
-    ///
-    size_t countPending() const;
-    size_t countPending(const std::string &pool) const;
-
-    /// Returns the number of coprocedures actively being processed.
-    ///
-    size_t countActive() const;
-    size_t countActive(const std::string &pool) const;
-
-    /// Returns the total number of coprocedures either queued or in active processing.
-    ///
-    size_t count() const;
-    size_t count(const std::string &pool) const;
-
-private:
-    typedef boost::shared_ptr<LLCoprocedurePool> poolPtr_t;
-    typedef std::map<std::string, poolPtr_t> poolMap_t;
-
-    poolMap_t mPoolMap;
-
-    poolPtr_t initializePool(const std::string &poolName);
-};
-
-#endif
diff --git a/indra/newview/llexperienceassociationresponder.cpp b/indra/newview/llexperienceassociationresponder.cpp
index b50c81eedc..cd4a7516b1 100644
--- a/indra/newview/llexperienceassociationresponder.cpp
+++ b/indra/newview/llexperienceassociationresponder.cpp
@@ -83,7 +83,7 @@ void ExperienceAssociationResponder::httpSuccess()
         return;
     }
 
-    LLExperienceCache::get(getContent()["experience"].asUUID(), boost::bind(&ExperienceAssociationResponder::sendResult, this, _1));
+    LLExperienceCache::getInstance()->get(getContent()["experience"].asUUID(), boost::bind(&ExperienceAssociationResponder::sendResult, this, _1));
 
 }
 
diff --git a/indra/newview/llfloaterexperienceprofile.cpp b/indra/newview/llfloaterexperienceprofile.cpp
index 197162487d..8a04f9e4cc 100644
--- a/indra/newview/llfloaterexperienceprofile.cpp
+++ b/indra/newview/llfloaterexperienceprofile.cpp
@@ -99,7 +99,7 @@ public:
         if(params.size() != 2 || params[1].asString() != "profile")
             return false;
 
-        LLExperienceCache::get(params[0].asUUID(), boost::bind(&LLExperienceHandler::experienceCallback, this, _1));
+        LLExperienceCache::getInstance()->get(params[0].asUUID(), boost::bind(&LLExperienceHandler::experienceCallback, this, _1));
         return true;
     }
 
@@ -288,8 +288,8 @@ BOOL LLFloaterExperienceProfile::postBuild()
 
     if (mExperienceId.notNull())
     {
-        LLExperienceCache::fetch(mExperienceId, true);
-        LLExperienceCache::get(mExperienceId, boost::bind(&LLFloaterExperienceProfile::experienceCallback, 
+        LLExperienceCache::getInstance()->fetch(mExperienceId, true);
+        LLExperienceCache::getInstance()->get(mExperienceId, boost::bind(&LLFloaterExperienceProfile::experienceCallback,
             getDerivedHandle<LLFloaterExperienceProfile>(), _1)); 
         
         LLViewerRegion* region = gAgent.getRegion();
@@ -799,8 +799,8 @@ void LLFloaterExperienceProfile::onSaveComplete( const LLSD& content )
     }
  
     refreshExperience(*it);
-    LLExperienceCache::insert(*it);
-    LLExperienceCache::fetch(id, true);
+    LLExperienceCache::getInstance()->insert(*it);
+    LLExperienceCache::getInstance()->fetch(id, true);
 
     if(mSaveCompleteAction==VIEW)
     {
diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp
index 370d0f4f1b..714d8d0e8f 100755
--- a/indra/newview/llfloaterreporter.cpp
+++ b/indra/newview/llfloaterreporter.cpp
@@ -268,7 +268,7 @@ void LLFloaterReporter::getExperienceInfo(const LLUUID& experience_id)
 
 	if (LLUUID::null != mExperienceID)
 	{
-		const LLSD& experience = LLExperienceCache::get(mExperienceID);
+        const LLSD& experience = LLExperienceCache::getInstance()->get(mExperienceID);
 		std::stringstream desc;
 
 		if(experience.isDefined())
diff --git a/indra/newview/llpanelexperiencelisteditor.cpp b/indra/newview/llpanelexperiencelisteditor.cpp
index fc4ee9862e..20fe0c52fa 100644
--- a/indra/newview/llpanelexperiencelisteditor.cpp
+++ b/indra/newview/llpanelexperiencelisteditor.cpp
@@ -183,7 +183,7 @@ void LLPanelExperienceListEditor::onItems()
 		columns[0]["value"] = getString("loading");
 		mItems->addElement(item);
 
-		LLExperienceCache::get(experience, boost::bind(&LLPanelExperienceListEditor::experienceDetailsCallback, 
+        LLExperienceCache::getInstance()->get(experience, boost::bind(&LLPanelExperienceListEditor::experienceDetailsCallback,
 			getDerivedHandle<LLPanelExperienceListEditor>(), _1));
 	}
 
diff --git a/indra/newview/llpanelexperiencelog.cpp b/indra/newview/llpanelexperiencelog.cpp
index df03ef7526..9329d900b1 100644
--- a/indra/newview/llpanelexperiencelog.cpp
+++ b/indra/newview/llpanelexperiencelog.cpp
@@ -140,7 +140,7 @@ void LLPanelExperienceLog::refresh()
 				}
 				const LLSD event = dayArray[i];
 				LLUUID id = event[LLExperienceCache::EXPERIENCE_ID].asUUID();
-				const LLSD& experience = LLExperienceCache::get(id);
+                const LLSD& experience = LLExperienceCache::getInstance()->get(id);
 				if(experience.isUndefined()){
 					waiting = true;
 					waiting_id = id;
@@ -168,7 +168,7 @@ void LLPanelExperienceLog::refresh()
 	{
 		mEventList->deleteAllItems();
 		mEventList->setCommentText(getString("loading"));
-		LLExperienceCache::get(waiting_id, boost::bind(&LLPanelExperienceLog::refresh, this));
+        LLExperienceCache::getInstance()->get(waiting_id, boost::bind(&LLPanelExperienceLog::refresh, this));
 	}
 	else
 	{
diff --git a/indra/newview/llpanelexperiencepicker.cpp b/indra/newview/llpanelexperiencepicker.cpp
index c7a353a6af..7c19e32e7e 100644
--- a/indra/newview/llpanelexperiencepicker.cpp
+++ b/indra/newview/llpanelexperiencepicker.cpp
@@ -238,7 +238,7 @@ void LLPanelExperiencePicker::processResponse( const LLUUID& query_id, const LLS
 	LLSD::array_const_iterator it = experiences.beginArray();
 	for ( ; it != experiences.endArray(); ++it)
 	{
-		LLExperienceCache::insert(*it);
+        LLExperienceCache::getInstance()->insert(*it);
 	}
 
 	getChildView(BTN_RIGHT)->setEnabled(content.has("next_page_url"));
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
index a548d7b705..4f5d21b6be 100755
--- a/indra/newview/llpreviewscript.cpp
+++ b/indra/newview/llpreviewscript.cpp
@@ -1326,7 +1326,7 @@ void LLLiveLSLEditor::buildExperienceList()
 			position = ADD_TOP;
 		}
 		
-		const LLSD& experience = LLExperienceCache::get(id);
+        const LLSD& experience = LLExperienceCache::getInstance()->get(id);
 		if(experience.isUndefined())
 		{
 			mExperiences->add(getString("loading"), id, position);
@@ -1345,7 +1345,7 @@ void LLLiveLSLEditor::buildExperienceList()
 
 	if(!foundAssociated )
 	{
-		const LLSD& experience = LLExperienceCache::get(associated);
+        const LLSD& experience = LLExperienceCache::getInstance()->get(associated);
 		if(experience.isDefined())
 		{
 			std::string experience_name_string = experience[LLExperienceCache::NAME].asString();
@@ -1366,7 +1366,7 @@ void LLLiveLSLEditor::buildExperienceList()
 	if(last.notNull())
 	{
 		mExperiences->setEnabled(FALSE);
-		LLExperienceCache::get(last, boost::bind(&LLLiveLSLEditor::buildExperienceList, this));  
+        LLExperienceCache::getInstance()->get(last, boost::bind(&LLLiveLSLEditor::buildExperienceList, this));
 	}
 	else
 	{
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 46f75c4f57..361cc6c48b 100755
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -2821,9 +2821,10 @@ void LLStartUp::initNameCache()
 
 void LLStartUp::initExperiences()
 {   
-    // just a get instance here.  Should trigger loading the cache.
-    LLExperienceCache::getInstance();
-	LLAppViewer::instance()->loadExperienceCache();
+    // Should trigger loading the cache.
+    LLExperienceCache::getInstance()->setCapabilityQuery(
+        boost::bind(&LLAgent::getRegionCapability, &gAgent, _1));
+
 	LLExperienceLog::instance().initialize();
 }
 
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 886725be79..4e1a86bb71 100755
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -6653,7 +6653,7 @@ void process_script_question(LLMessageSystem *msg, void **user_data)
 			else if(experienceid.notNull())
 			{
 				payload["experience"]=experienceid;
-				LLExperienceCache::get(experienceid, boost::bind(process_script_experience_details, _1, args, payload));
+                LLExperienceCache::getInstance()->get(experienceid, boost::bind(process_script_experience_details, _1, args, payload));
 				return;
 			}
 
-- 
cgit v1.2.3