diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp
index d196d7da93f507e86a9ffc9519ebcc7c4f8f6561..34c42103593d20e600774b6cd52e097caaf9610d 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 8a557194437da578f508823b934aebf3fa2efe39..8da038a8c34f3ef3c6c265c3e45f30b8cebaee13 100644
--- a/indra/llmessage/llexperiencecache.h
+++ b/indra/llmessage/llexperiencecache.h
@@ -56,10 +56,45 @@ class LLExperienceCache: public LLSingleton < LLExperienceCache >
 
 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 @@ class LLExperienceCache: public LLSingleton < LLExperienceCache >
 //--------------------------------------------
     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();