summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRider Linden <rider@lindenlab.com>2015-08-28 16:55:33 -0700
committerRider Linden <rider@lindenlab.com>2015-08-28 16:55:33 -0700
commit99e56eedabfe34dbfbfd8105759403173de72d44 (patch)
tree32747c8f4d13a4ebf816917fa88e98ff1632cc7e
parent02c3262ac8e7f27b0effb546ad235e103c9581cf (diff)
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
-rw-r--r--indra/llmessage/llexperiencecache.cpp925
-rw-r--r--indra/llmessage/llexperiencecache.h67
-rwxr-xr-xindra/newview/llstartup.cpp5
3 files changed, 515 insertions, 482 deletions
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();
}