diff options
21 files changed, 709 insertions, 633 deletions
| 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/newview/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index db01c13079..062f2e6e42 100644 --- a/indra/newview/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -25,11 +25,7 @@  * $/LicenseInfo$  */ -#include "llviewerprecompiledheaders.h"  #include "linden_common.h"  - -#include "llviewercontrol.h" -  #include "llcoproceduremanager.h"  //========================================================================= @@ -141,9 +137,13 @@ LLCoprocedureManager::poolPtr_t LLCoprocedureManager::initializePool(const std::  {      // Attempt to look up a pool size in the configuration.  If found use that      std::string keyName = "PoolSize" + poolName; -    int size = 5; +    int size = 0; + +    if (mPropertyQueryFn && !mPropertyQueryFn.empty()) +    { +        size = mPropertyQueryFn(keyName); +    } -    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. @@ -152,7 +152,9 @@ LLCoprocedureManager::poolPtr_t LLCoprocedureManager::initializePool(const std::              size = DEFAULT_POOL_SIZE;          else              size = (*it).second; -        gSavedSettings.declareU32(keyName, size, "Coroutine Pool size for " + poolName, LLControlVariable::PERSIST_ALWAYS); + +        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;      } @@ -207,6 +209,12 @@ void LLCoprocedureManager::shutdown(bool hardShutdown)      mPoolMap.clear();  } +void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn) +{ +    mPropertyQueryFn = queryfn; +    mPropertyDefineFn = updatefn; +} +  //-------------------------------------------------------------------------  size_t LLCoprocedureManager::countPending() const  { diff --git a/indra/newview/llcoproceduremanager.h b/indra/llmessage/llcoproceduremanager.h index d7f74af76b..497367b80c 100644 --- a/indra/newview/llcoproceduremanager.h +++ b/indra/llmessage/llcoproceduremanager.h @@ -37,7 +37,12 @@ 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(); @@ -60,6 +65,8 @@ public:      /// 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; @@ -76,12 +83,16 @@ public:      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 52b60a176e..36a4fc8823 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -26,616 +26,636 @@  #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> - -namespace LLExperienceCache +//========================================================================= +namespace LLExperienceCacheImpl  { +	void mapKeys(const LLSD& legacyKeys); +    F64 getErrorRetryDeltaTime(S32 status, LLSD headers); +    bool maxAgeFromCacheControl(const std::string& cache_control, S32 *max_age); -    typedef std::map<LLUUID, LLUUID> KeyMap; -    KeyMap privateToPublicKeyMap; - -    void mapKeys(const LLSD& legacyKeys); +    static const std::string PRIVATE_KEY    = "private_id"; +    static const std::string EXPERIENCE_ID  = "public_id"; -	std::string sLookupURL; +    static const std::string MAX_AGE("max-age"); +    static const boost::char_separator<char> EQUALS_SEPARATOR("="); +    static const boost::char_separator<char> COMMA_SEPARATOR(","); -	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; +    // *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; +} -	cache_t sCache; -	int sMaximumLookups = 10; +//========================================================================= +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(): +    mShutdown(false) +{ +} -	LLFrameTimer sRequestTimer; +LLExperienceCache::~LLExperienceCache() +{ -	// 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; +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); +    } -	bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age); -	void eraseExpired(); +    LLCoros::instance().launch("LLExperienceCache::idleCoro", +        boost::bind(&LLExperienceCache::idleCoro, this)); -	void processExperience( const LLUUID& public_key, const LLSD& experience )  -	{ -		sCache[public_key]=experience; -		LLSD & row = sCache[public_key]; +} -		if(row.has(EXPIRES)) -		{ -			row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds(); -		} +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; +} -		if(row.has(EXPERIENCE_ID)) -		{ -			sPendingQueue.erase(row[EXPERIENCE_ID].asUUID()); -		} +//------------------------------------------------------------------------- +void LLExperienceCache::importFile(std::istream& istr) +{ +    LLSD data; +    S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr); +    if (parse_count < 1) return; -		//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); +    LLSD experiences = data["experiences"]; -			sSignalMap.erase(public_key); +    LLUUID public_key; +    LLSD::map_const_iterator it = experiences.beginMap(); +    for (; it != experiences.endMap(); ++it) +    { +        public_key.set(it->first); +        mCache[public_key] = it->second; +    } -			delete signal; -		} -	} +    LL_DEBUGS("ExperienceCache") << "importFile() loaded " << mCache.size() << LL_ENDL; +} -	void initClass( ) -	{ -	} +void LLExperienceCache::exportFile(std::ostream& ostr) const +{ +    LLSD experiences; -	const cache_t& getCached() -	{ -		return sCache; -	} +    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)) +            continue; -	void setMaximumLookups( int maximumLookups) -	{ -		sMaximumLookups = maximumLookups; -	} +        experiences[it->first.asString()] = it->second; +    } -	void 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; -			} -		}		 -	} +    LLSD data; +    data["experiences"] = experiences; +    LLSDSerialize::toPrettyXML(data, ostr); +} +// *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); +    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; +        } +    } +} -	bool expirationFromCacheControl(LLSD headers, F64 *expires) -	{ -		// 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; -	} +LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found) +{ +    if (private_key.isNull()) +        return LLUUID::null; + +    LLExperienceCacheImpl::KeyMap::const_iterator it = LLExperienceCacheImpl::privateToPublicKeyMap.find(private_key); +    if (it == LLExperienceCacheImpl::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) +{ +    LL_INFOS("ExperienceCache") << "Processing experience \"" << experience[NAME] << "\" with key " << public_key.asString() << 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(","); +	mCache[public_key]=experience; +	LLSD & row = mCache[public_key]; -	bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age) +	if(row.has(EXPIRES))  	{ -		// 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; +		row[EXPIRES] = row[EXPIRES].asReal() + LLFrameTimer::getTotalSeconds();  	} - -	void importFile(std::istream& istr) +	if(row.has(EXPERIENCE_ID))  	{ -		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; +		mPendingQueue.erase(row[EXPERIENCE_ID].asUUID());  	} -	void exportFile(std::ostream& ostr) +	//signal +	signal_map_t::iterator sig_it =	mSignalMap.find(public_key); +	if (sig_it != mSignalMap.end())  	{ -		LLSD experiences; +		signal_ptr signal = sig_it->second; +		(*signal)(experience); -		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; - -			experiences[it->first.asString()] = it->second; -		} - -		LLSD data; -		data["experiences"] = experiences; - -		LLSDSerialize::toPrettyXML(data, ostr); +		mSignalMap.erase(public_key);  	} +} -	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; -		} +const LLExperienceCache::cache_t& LLExperienceCache::getCached() +{ +	return mCache; +} -		/*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) -			{ +void LLExperienceCache::requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &httpAdapter, std::string url, RequestQueue_t requests) +{ +    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); + +    //LL_INFOS("requestExperiencesCoro") << "url: " << url << LL_ENDL; + +    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(); + +        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()) +            { +                exp[PROPERTIES] = PROPERTY_INVALID; +            } +            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; + +            processExperience(*it, exp); +        } +        return; +    } + +    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(); + +        LL_DEBUGS("ExperienceCache") << "Received result for " << public_key +            << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL; + +        processExperience(public_key, row); +    } + +    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; +    } -				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 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) -		{ - -			// 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); -				} -			} +void LLExperienceCache::requestExperiences() +{ +    if (mCapability.empty()) +    { +        LL_WARNS("ExperienceCache") << "Capability query method not set." << LL_ENDL; +        return; +    } + +    std::string urlBase = mCapability("GetExperienceInfo"); +    if (urlBase.empty()) +    { +        LL_WARNS("ExperienceCache") << "No Experience capability." << LL_ENDL; +        return; +    } + +    if (*urlBase.rbegin() != '/') +    { +        urlBase += "/"; +    } +    urlBase += "id/"; + + +	F64 now = LLFrameTimer::getTotalSeconds(); + +    const U32 EXP_URL_SEND_THRESHOLD = 3000; +    const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD / UUID_STR_LENGTH; + +    std::ostringstream ostr; +    ostr << urlBase << "?page_size=" << PAGE_SIZE; +    RequestQueue_t  requests; + +    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 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; -			} -		} +bool LLExperienceCache::isRequestPending(const LLUUID& public_key) +{ +	bool isPending = false; +	const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; -	private: -		ask_queue_t mKeys; -	}; +    PendingQueue_t::const_iterator it = mPendingQueue.find(public_key); -	void requestExperiences()  +	if(it != mPendingQueue.end())  	{ -		if(sAskQueue.empty() || sLookupURL.empty()) -			return; - -		F64 now = LLFrameTimer::getTotalSeconds(); - -		const U32 EXP_URL_SEND_THRESHOLD = 3000; -		const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD/UUID_STR_LENGTH; - -		std::ostringstream ostr; - -		ask_queue_t keys; - -		ostr << sLookupURL << "?page_size=" << PAGE_SIZE; +		F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS; +		isPending = (it->second > expire_time); +	} +	return isPending; +} -		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; +void LLExperienceCache::setCapabilityQuery(LLExperienceCache::CapabilityQuery_t queryfn) +{ +    mCapability = queryfn; +} -			ostr << '&' << key_type << '=' << key.asString() ; -		 -			keys[key]=key_type; -			request_count++; -			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()) -		{ -			LL_DEBUGS("ExperienceCache") <<  "requestExperiences() query 2: " << ostr.str() << LL_ENDL; -			LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys)); -		} -	} +void LLExperienceCache::idleCoro() +{ +    const F32 SECS_BETWEEN_REQUESTS = 0.5f; +    const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds -	bool isRequestPending(const LLUUID& public_key) -	{ -		bool isPending = false; -		const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; +    LL_INFOS("ExperienceCache") << "Launching Experience cache idle coro." << LL_ENDL; +    LLEventTimeout timeout; -		pending_queue_t::const_iterator it = sPendingQueue.find(public_key); +    do  +    { +        timeout.eventAfter(SECS_BETWEEN_REQUESTS, LLSD()); +        llcoro::waitForEventOn(timeout); -		if(it != sPendingQueue.end()) -		{ -			F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS; -			isPending = (it->second > expire_time); -		} +        if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) +        { +            eraseExpired(); +        } -		return isPending; -	} +        if (!mRequestQueue.empty()) +        { +            requestExperiences(); +        } +    } while (!mShutdown); -	void setLookupURL( const std::string& lookup_url ) -	{ -		sLookupURL = lookup_url; -		if(!sLookupURL.empty()) -		{ -			sLookupURL += "id/"; -		} -	} +    // 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.) +} -	bool hasLookupURL() +void LLExperienceCache::erase(const LLUUID& key) +{ +	cache_t::iterator it = mCache.find(key); +				 +	if(it != mCache.end())  	{ -		return !sLookupURL.empty(); +		mCache.erase(it);  	} +} -	void idle() +void LLExperienceCache::eraseExpired() +{ +	F64 now = LLFrameTimer::getTotalSeconds(); +	cache_t::iterator it = mCache.begin(); +	while (it != mCache.end())  	{ +		cache_t::iterator cur = it; +		LLSD& exp = cur->second; +		++it; -		const F32 SECS_BETWEEN_REQUESTS = 0.1f; -		if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS)) -		{ -			return; -		} +        //LL_INFOS("ExperienceCache") << "Testing experience \"" << exp[NAME] << "\" with exp time " << exp[EXPIRES].asReal() << "(now = " << now << ")" << LL_ENDL; -		// Must be large relative to above -		const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds -		if (sEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) +		if(exp.has(EXPIRES) && exp[EXPIRES].asReal() < now)  		{ -			eraseExpired(); -		} - - -		if(!sAskQueue.empty()) -		{ -			requestExperiences(); -		} -	} - -	void erase( const LLUUID& key ) -	{ -		cache_t::iterator it = sCache.find(key); -				 -		if(it != sCache.end()) -		{ -			sCache.erase(it); -		} -	} - -	void eraseExpired() -	{ -		F64 now = LLFrameTimer::getTotalSeconds(); -		cache_t::iterator it = sCache.begin(); -		while (it != sCache.end()) -		{ -			cache_t::iterator cur = it; -			LLSD& exp = cur->second; -			++it; - -			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 ; +                mCache.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 ; +					mCache.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 || mCache.find(key)==mCache.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; -			return true; -		} -		return false; +        mRequestQueue.insert(key); +		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); +		LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << EXPERIENCE_ID << LL_ENDL; +	} +} +const LLSD& LLExperienceCache::get(const LLUUID& key) +{ +	static const LLSD empty; +	 +	if(key.isNull())   		return empty; -	} -	void get( const LLUUID& key, callback_slot_t slot ) -	{ -		if(key.isNull()) return; +	cache_t::const_iterator it = mCache.find(key); -		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; -		} +	if (it != mCache.end()) +	{ +		return it->second; +	} +	fetch(key); -		fetch(key); +	return empty; +} +void LLExperienceCache::get(const LLUUID& key, LLExperienceCache::Callback_t slot) +{ +	if(key.isNull())  +		return; -		// 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); -		} +	cache_t::const_iterator it = mCache.find(key); +	if (it != mCache.end()) +	{ +		// ...name already exists in cache, fire callback now +		callback_signal_t signal; +		signal.connect(slot); +			 +		signal(it->second); +		return;  	} -} +	fetch(key); +	signal_ptr signal = signal_ptr(new callback_signal_t()); +	 +	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->connect(slot); +} -void LLExperienceCache::mapKeys( const LLSD& legacyKeys ) +//========================================================================= +void LLExperienceCacheImpl::mapKeys(const LLSD& legacyKeys)  {  	LLSD::array_const_iterator exp = legacyKeys.beginArray(); -	for(/**/ ; exp != legacyKeys.endArray() ; ++exp) +	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();  		}  	}  } - -LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found) +// 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)  { -	if (private_key.isNull()) -		return LLUUID::null; -    KeyMap::const_iterator it=privateToPublicKeyMap.find(private_key); -    if(it == privateToPublicKeyMap.end()) +    // 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; +	tokenizer directives(cache_control, COMMA_SEPARATOR); +	 +	tokenizer::iterator token_it = directives.begin(); +	for ( ; token_it != directives.end(); ++token_it)  	{ -		if(null_if_not_found) +		// 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)  		{ -			return LLUUID::null; +			// ...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 private_key;  	} -	LL_WARNS("LLExperience") << "converted private key " << private_key << " to experience_id " << it->second << LL_ENDL; -	return it->second; +	return false;  } + + + + diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index e669ee888e..937225a80a 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -30,75 +30,131 @@  #define LL_LLEXPERIENCECACHE_H  #include "linden_common.h" +#include "llsingleton.h" +#include "llframetimer.h" +#include "llsd.h" +#include "llcorehttputil.h"  #include <boost/signals2.hpp> +#include <boost/function.hpp>  class LLSD;  class LLUUID; - -namespace LLExperienceCache +class LLExperienceCache: public LLSingleton < LLExperienceCache >  { -	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 - -	// 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; +    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); +    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); + +    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; +	// 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> RequestQueue_t; +    typedef std::map<LLUUID, F64> PendingQueue_t; - -	void setLookupURL(const std::string& lookup_url); -	bool hasLookupURL(); +	//-------------------------------------------- +	static const std::string PRIVATE_KEY;	// "private_id" +	 +	// default values +	static const F64 DEFAULT_EXPIRATION; 	// 600.0 +	static const S32 DEFAULT_QUOTA; 		// 128 this is megabytes +	 +//-------------------------------------------- +    void processExperience(const LLUUID& public_key, const LLSD& experience); + +//-------------------------------------------- +	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 requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, std::string, RequestQueue_t); +    void requestExperiences();  	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); +    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  	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/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/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 c74890a4e9..361cc6c48b 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2820,9 +2820,11 @@ void LLStartUp::initNameCache()  void LLStartUp::initExperiences() -{ -	LLAppViewer::instance()->loadExperienceCache(); -	LLExperienceCache::initClass(); +{    +    // 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;  			} | 
