diff options
Diffstat (limited to 'indra/llmessage')
37 files changed, 1190 insertions, 685 deletions
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index e0922c0667..2f99ca069e 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -217,7 +217,7 @@ target_link_libraries( ${NGHTTP2_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${LLCOREHTTP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} rt @@ -235,7 +235,7 @@ target_link_libraries( ${NGHTTP2_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${LLCOREHTTP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ) @@ -244,6 +244,7 @@ endif(LINUX) # tests if (LL_TESTS) SET(llmessage_TEST_SOURCE_FILES + llcoproceduremanager.cpp llnamevalue.cpp lltrustedmessageservice.cpp lltemplatemessagedispatcher.cpp @@ -264,7 +265,7 @@ if (LINUX) ${LLMESSAGE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${JSONCPP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} rt ${GOOGLEMOCK_LIBRARIES} @@ -280,7 +281,7 @@ else (LINUX) ${LLMESSAGE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${JSONCPP_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} + ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${GOOGLEMOCK_LIBRARIES} ) diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index 596d57c7b7..d7801b6ddc 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -62,6 +62,42 @@ const LLUUID CATEGORIZE_LOST_AND_FOUND_ID(std::string("00000000-0000-0000-0000-0 const U64 TOXIC_ASSET_LIFETIME = (120 * 1000000); // microseconds +namespace +{ + bool operator == (const LLAssetStorage::LLGetAssetCallback &lhs, const LLAssetStorage::LLGetAssetCallback &rhs) + { + auto fnPtrLhs = lhs.target<LLAssetStorage::LLGetAssetCallback>(); + auto fnPtrRhs = rhs.target<LLAssetStorage::LLGetAssetCallback>(); + if (fnPtrLhs && fnPtrRhs) + return (*fnPtrLhs == *fnPtrRhs); + else if (!fnPtrLhs && !fnPtrRhs) + return true; + return false; + } + +// Rider: This is the general case of the operator declared above. The code compares the callback +// passed into the LLAssetStorage functions to determine if there are duplicated requests for an +// asset. Unfortunately std::function does not provide a direct way to compare two variables so +// we define the operator here. +// XCode is not very happy with the variadic temples in use below so we will just define the specific +// case of comparing two LLGetAssetCallback objects since that is all we really use. +// +// template<typename T, typename... U> +// bool operator == (const std::function<T(U...)> &a, const std::function <T(U...)> &b) +// { +// typedef T(fnType)(U...); +// +// auto fnPtrA = a.target<T(*)(U...)>(); +// auto fnPtrB = b.target<T(*)(U...)>(); +// if (fnPtrA && fnPtrB) +// return (*fnPtrA == *fnPtrB); +// else if (!fnPtrA && !fnPtrB) +// return true; +// return false; +// } + +} + ///---------------------------------------------------------------------------- /// LLAssetInfo ///---------------------------------------------------------------------------- @@ -160,7 +196,7 @@ void LLAssetInfo::setFromNameValue( const LLNameValue& nv ) LLBaseDownloadRequest::LLBaseDownloadRequest(const LLUUID &uuid, const LLAssetType::EType type) : mUUID(uuid), mType(type), - mDownCallback(NULL), + mDownCallback(), mUserData(NULL), mHost(), mIsTemp(FALSE), @@ -191,7 +227,7 @@ LLBaseDownloadRequest* LLBaseDownloadRequest::getCopy() LLAssetRequest::LLAssetRequest(const LLUUID &uuid, const LLAssetType::EType type) : LLBaseDownloadRequest(uuid, type), - mUpCallback( NULL ), + mUpCallback(), mInfoCallback( NULL ), mIsLocal(FALSE), mIsUserWaiting(FALSE), @@ -390,11 +426,11 @@ void LLAssetStorage::_cleanupRequests(BOOL all, S32 error) LLAssetRequest* tmp = *curiter; if (tmp->mUpCallback) { - tmp->mUpCallback(tmp->getUUID(), tmp->mUserData, error, LL_EXSTAT_NONE); + tmp->mUpCallback(tmp->getUUID(), tmp->mUserData, error, LLExtStat::NONE); } if (tmp->mDownCallback) { - tmp->mDownCallback(mVFS, tmp->getUUID(), tmp->getType(), tmp->mUserData, error, LL_EXSTAT_NONE); + tmp->mDownCallback(mVFS, tmp->getUUID(), tmp->getType(), tmp->mUserData, error, LLExtStat::NONE); } if (tmp->mInfoCallback) { @@ -429,7 +465,7 @@ bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAsse // we've already got the file if (callback) { - callback(mStaticVFS, uuid, type, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); + callback(mStaticVFS, uuid, type, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); } return true; } @@ -449,7 +485,7 @@ bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAsse // IW - uuid is passed by value to avoid side effects, please don't re-add & void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, - LLGetAssetCallback callback, + LLAssetStorage::LLGetAssetCallback callback, void *user_data, BOOL is_priority) { @@ -470,7 +506,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_FAILED, LL_EXSTAT_NONE); + callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_FAILED, LLExtStat::NONE); } return; } @@ -481,7 +517,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LL_EXSTAT_NULL_UUID); + callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LLExtStat::NULL_UUID); } return; } @@ -496,7 +532,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, BOOL exists = mVFS->getExists(uuid, type); LLVFile file(mVFS, uuid, type); U32 size = exists ? file.getSize() : 0; - + if (size > 0) { // we've already got the file @@ -504,7 +540,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, // unless there's a weird error if (callback) { - callback(mVFS, uuid, type, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); + callback(mVFS, uuid, type, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); } LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in VFS" << LL_ENDL; @@ -658,7 +694,7 @@ void LLAssetStorage::downloadCompleteCallback( } } - removeAndCallbackPendingDownloads(file_id, file_type, callback_id, callback_type, ext_status, result); + removeAndCallbackPendingDownloads(file_id, file_type, callback_id, callback_type, result, ext_status); } void LLAssetStorage::getEstateAsset( @@ -683,7 +719,7 @@ void LLAssetStorage::getEstateAsset( if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LL_EXSTAT_NULL_UUID); + callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LLExtStat::NULL_UUID); } return; } @@ -705,7 +741,7 @@ void LLAssetStorage::getEstateAsset( // unless there's a weird error if (callback) { - callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); + callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); } } else @@ -756,7 +792,7 @@ void LLAssetStorage::getEstateAsset( if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); + callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LLExtStat::NO_UPSTREAM); } } } @@ -849,7 +885,7 @@ void LLAssetStorage::getInvItemAsset( // unless there's a weird error if (callback) { - callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); + callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); } } else @@ -900,7 +936,7 @@ void LLAssetStorage::getInvItemAsset( if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); + callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LLExtStat::NO_UPSTREAM); } } } @@ -998,7 +1034,7 @@ void LLAssetStorage::processUploadComplete(LLMessageSystem *msg, void **user_dat msg->getBOOLFast(_PREHASH_AssetBlock, _PREHASH_Success, success); asset_type = (LLAssetType::EType)asset_type_s8; - this_ptr->_callUploadCallbacks(uuid, asset_type, success, LL_EXSTAT_NONE); + this_ptr->_callUploadCallbacks(uuid, asset_type, success, LLExtStat::NONE); } void LLAssetStorage::_callUploadCallbacks(const LLUUID &uuid, LLAssetType::EType asset_type, BOOL success, LLExtStat ext_status ) @@ -1252,12 +1288,12 @@ bool LLAssetStorage::deletePendingRequestImpl(LLAssetStorage::request_list_t* re // Run callbacks. if (req->mUpCallback) { - req->mUpCallback(req->getUUID(), req->mUserData, error, LL_EXSTAT_REQUEST_DROPPED); + req->mUpCallback(req->getUUID(), req->mUserData, error, LLExtStat::REQUEST_DROPPED); } if (req->mDownCallback) { add(sFailedDownloadCount, 1); - req->mDownCallback(mVFS, req->getUUID(), req->getType(), req->mUserData, error, LL_EXSTAT_REQUEST_DROPPED); + req->mDownCallback(mVFS, req->getUUID(), req->getType(), req->mUserData, error, LLExtStat::REQUEST_DROPPED); } if (req->mInfoCallback) { @@ -1326,9 +1362,13 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, iter != mPendingDownloads.end(); ) { LLAssetRequest* tmp = *iter++; + + //void(*const* cbptr)(LLVFS *, const LLUUID &, LLAssetType::EType, void *, S32, LLExtStat) + auto cbptr = tmp->mDownCallback.target<void(*)(LLVFS *, const LLUUID &, LLAssetType::EType, void *, S32, LLExtStat)>(); + if (type == tmp->getType() && uuid == tmp->getUUID() && - legacyGetDataCallback == tmp->mDownCallback && + (cbptr && (*cbptr == legacyGetDataCallback)) && callback == ((LLLegacyAssetRequest *)tmp->mUserData)->mDownCallback && user_data == ((LLLegacyAssetRequest *)tmp->mUserData)->mUserData) { diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index 33b88473b9..7e4572f50f 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -28,6 +28,7 @@ #ifndef LL_LLASSETSTORAGE_H #define LL_LLASSETSTORAGE_H #include <string> +#include <functional> #include "lluuid.h" #include "lltimer.h" @@ -59,6 +60,14 @@ const int LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4; const int LL_ERR_INSUFFICIENT_PERMISSIONS = -5; const int LL_ERR_PRICE_MISMATCH = -23018; +// *TODO: these typedefs are passed into the VFS via a legacy C function pointer +// future project would be to convert these to C++ callables (std::function<>) so that +// we can use bind and remove the userData parameter. +// +typedef std::function<void(LLVFS *vfs, const LLUUID &asset_id, LLAssetType::EType asset_type, void *user_data, S32 status, LLExtStat ext_status)> LLGetAssetCallback; +typedef std::function<void(const LLUUID &asset_id, void *user_data, S32 status, LLExtStat ext_status)> LLStoreAssetCallback; + + class LLAssetInfo { protected: @@ -110,7 +119,8 @@ protected: LLAssetType::EType mType; public: - void(*mDownCallback)(LLVFS*, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat); + LLGetAssetCallback mDownCallback; +// void(*mDownCallback)(LLVFS*, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat); void *mUserData; LLHost mHost; @@ -131,7 +141,8 @@ public: virtual LLBaseDownloadRequest* getCopy(); - void (*mUpCallback)(const LLUUID&, void *, S32, LLExtStat); + LLStoreAssetCallback mUpCallback; +// void (*mUpCallback)(const LLUUID&, void *, S32, LLExtStat); void (*mInfoCallback)(LLAssetInfo *, void *, S32); BOOL mIsLocal; @@ -182,12 +193,7 @@ protected: // Map of known bad assets typedef std::map<LLUUID,U64,lluuid_less> toxic_asset_map_t; -// *TODO: these typedefs are passed into the VFS via a legacy C function pointer -// future project would be to convert these to C++ callables (std::function<>) so that -// we can use bind and remove the userData parameter. -// -typedef void (*LLGetAssetCallback)(LLVFS *vfs, const LLUUID &asset_id, - LLAssetType::EType asset_type, void *user_data, S32 status, LLExtStat ext_status); + class LLAssetStorage { @@ -195,7 +201,8 @@ public: // VFS member is public because static child methods need it :( LLVFS *mVFS; LLVFS *mStaticVFS; - typedef void (*LLStoreAssetCallback)(const LLUUID &asset_id, void *user_data, S32 status, LLExtStat ext_status); + typedef ::LLStoreAssetCallback LLStoreAssetCallback; + typedef ::LLGetAssetCallback LLGetAssetCallback; enum ERequestType { @@ -255,7 +262,7 @@ public: virtual void logAssetStorageInfo() = 0; - void checkForTimeouts(); + virtual void checkForTimeouts(); void getEstateAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id, const LLUUID &asset_id, LLAssetType::EType atype, EstateAssetType etype, @@ -377,8 +384,8 @@ protected: void _cleanupRequests(BOOL all, S32 error); void _callUploadCallbacks(const LLUUID &uuid, const LLAssetType::EType asset_type, BOOL success, LLExtStat ext_status); - virtual void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, - void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), + virtual void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, LLGetAssetCallback callback, +// void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), void *user_data, BOOL duplicate, BOOL is_priority) = 0; @@ -424,7 +431,7 @@ class LLLegacyAssetRequest { public: void (*mDownCallback)(const char *, const LLUUID&, void *, S32, LLExtStat); - LLAssetStorage::LLStoreAssetCallback mUpCallback; + LLStoreAssetCallback mUpCallback; void *mUserData; }; diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index ba1a2a035e..c67f59bc0c 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -49,101 +49,22 @@ #include <map> #include <set> -namespace LLAvatarNameCache -{ - use_display_name_signal_t mUseDisplayNamesSignal; - // Cache starts in a paused state until we can determine if the - // current region supports display names. - bool sRunning = false; - - // Use the People API (modern) for fetching name if true. Use the old legacy protocol if false. - // For testing, there's a UsePeopleAPI setting that can be flipped (must restart viewer). - bool sUsePeopleAPI = true; - - // Base lookup URL for name service. - // On simulator, loaded from indra.xml - // On viewer, usually a simulator capability (at People API team's request) - // Includes the trailing slash, like "http://pdp60.lindenlab.com:8000/agents/" - std::string sNameLookupURL; - - // Accumulated agent IDs for next query against service - typedef std::set<LLUUID> ask_queue_t; - ask_queue_t sAskQueue; - - // Agent IDs that have been requested, but with no reply. - // Maps agent ID to frame time request was made. - typedef std::map<LLUUID, F64> pending_queue_t; - pending_queue_t sPendingQueue; - - // Callbacks to fire when we received a name. - // 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; - - // The cache at last, i.e. avatar names we know about. - typedef std::map<LLUUID, LLAvatarName> cache_t; - cache_t sCache; - - // Send bulk lookup requests a few times a second at most. - // Only need per-frame timing resolution. - LLFrameTimer sRequestTimer; - - // Maximum time an unrefreshed cache entry is allowed. - const F64 MAX_UNREFRESHED_TIME = 20.0 * 60.0; - - // Time when unrefreshed cached names were checked last. - static F64 sLastExpireCheck; - - // Time-to-live for a temp cache entry. - const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0; - - LLCore::HttpRequest::ptr_t sHttpRequest; - LLCore::HttpHeaders::ptr_t sHttpHeaders; - LLCore::HttpOptions::ptr_t sHttpOptions; - LLCore::HttpRequest::policy_t sHttpPolicy; - LLCore::HttpRequest::priority_t sHttpPriority; - - //----------------------------------------------------------------------- - // Internal methods - //----------------------------------------------------------------------- - - // Handle name response off network. - void processName(const LLUUID& agent_id, - const LLAvatarName& av_name); - - void requestNamesViaCapability(); - - // Legacy name system callbacks - void legacyNameCallback(const LLUUID& agent_id, - const std::string& full_name, - bool is_group); - void legacyNameFetch(const LLUUID& agent_id, - const std::string& full_name, - bool is_group); - - void requestNamesViaLegacy(); +// Time-to-live for a temp cache entry. +const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0; +// Maximum time an unrefreshed cache entry is allowed. +const F64 MAX_UNREFRESHED_TIME = 20.0 * 60.0; - // Do a single callback to a given slot - void fireSignal(const LLUUID& agent_id, - const callback_slot_t& slot, - const LLAvatarName& av_name); - - // Is a request in-flight over the network? - bool isRequestPending(const LLUUID& agent_id); +// Send bulk lookup requests a few times a second at most. +// Only need per-frame timing resolution. +static LLFrameTimer sRequestTimer; - // Erase expired names from cache - void eraseUnrefreshed(); - - bool expirationFromCacheControl(const LLSD& headers, F64 *expires); - - // This is a coroutine. - void requestAvatarNameCache_(std::string url, std::vector<LLUUID> agentIds); - - void handleAvNameCacheSuccess(const LLSD &data, const LLSD &httpResult); -} +// static to avoid unnessesary dependencies +LLCore::HttpRequest::ptr_t sHttpRequest; +LLCore::HttpHeaders::ptr_t sHttpHeaders; +LLCore::HttpOptions::ptr_t sHttpOptions; +LLCore::HttpRequest::policy_t sHttpPolicy; +LLCore::HttpRequest::priority_t sHttpPriority; /* Sample response: <?xml version="1.0"?> @@ -187,9 +108,33 @@ namespace LLAvatarNameCache // Coroutine for sending and processing avatar name cache requests. // Do not call directly. See documentation in lleventcoro.h and llcoro.h for // further explanation. + +LLAvatarNameCache::LLAvatarNameCache() +{ + // Will be set to running later + // For now fail immediate lookups and query async ones. + mRunning = false; + + mUsePeopleAPI = true; + + sHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest()); + sHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()); + sHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()); + sHttpPolicy = LLCore::HttpRequest::DEFAULT_POLICY_ID; + sHttpPriority = 0; +} + +LLAvatarNameCache::~LLAvatarNameCache() +{ + sHttpRequest.reset(); + sHttpHeaders.reset(); + sHttpOptions.reset(); + mCache.clear(); +} + void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLUUID> agentIds) { - LL_DEBUGS("AvNameCache") << "Entering coroutine " << LLCoros::instance().getName() + LL_DEBUGS("AvNameCache") << "Entering coroutine " << LLCoros::getName() << " with url '" << url << "', requesting " << agentIds.size() << " Agent Ids" << LL_ENDL; // Check pointer that can be cleaned up by cleanupClass() @@ -200,12 +145,12 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLU } LLSD httpResults; + bool success = true; try { - bool success = true; - LLCoreHttpUtil::HttpCoroutineAdapter httpAdapter("NameCache", LLAvatarNameCache::sHttpPolicy); + LLCoreHttpUtil::HttpCoroutineAdapter httpAdapter("NameCache", sHttpPolicy); LLSD results = httpAdapter.getAndSuspend(sHttpRequest, url); LL_DEBUGS() << results << LL_ENDL; @@ -218,35 +163,47 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLU else { httpResults = results["http_result"]; - success = httpResults["success"].asBoolean(); - if (!success) + if (!httpResults.isMap()) { - LL_WARNS("AvNameCache") << "Error result from LLCoreHttpUtil::HttpCoroHandler. Code " - << httpResults["status"] << ": '" << httpResults["message"] << "'" << LL_ENDL; + success = false; + LL_WARNS("AvNameCache") << " Invalid http_result returned from LLCoreHttpUtil::HttpCoroHandler." << LL_ENDL; } - } - - if (!success) - { // on any sort of failure add dummy records for any agent IDs - // in this request that we do not have cached already - std::vector<LLUUID>::const_iterator it = agentIds.begin(); - for ( ; it != agentIds.end(); ++it) + else { - const LLUUID& agent_id = *it; - LLAvatarNameCache::handleAgentError(agent_id); + success = httpResults["success"].asBoolean(); + if (!success) + { + LL_WARNS("AvNameCache") << "Error result from LLCoreHttpUtil::HttpCoroHandler. Code " + << httpResults["status"] << ": '" << httpResults["message"] << "'" << LL_ENDL; + } } - return; } - LLAvatarNameCache::handleAvNameCacheSuccess(results, httpResults); + if (LLAvatarNameCache::instanceExists()) + { + if (!success) + { // on any sort of failure add dummy records for any agent IDs + // in this request that we do not have cached already + std::vector<LLUUID>::const_iterator it = agentIds.begin(); + for (; it != agentIds.end(); ++it) + { + const LLUUID& agent_id = *it; + LLAvatarNameCache::getInstance()->handleAgentError(agent_id); + } + return; + } + LLAvatarNameCache::getInstance()->handleAvNameCacheSuccess(results, httpResults); + } } catch (...) { - LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName() - << "('" << url << "', " << agentIds.size() - << " http result: " << httpResults.asString() - << " Agent Ids)")); + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::getName() + << "('" << url << "', " + << agentIds.size() << "Agent Ids," + << " http result: " << S32(success) + << " has response: " << S32(httpResults.size()) + << ")")); throw; } } @@ -300,15 +257,15 @@ void LLAvatarNameCache::handleAvNameCacheSuccess(const LLSD &data, const LLSD &h } } LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result " - << LLAvatarNameCache::sCache.size() << " cached names" + << LLAvatarNameCache::mCache.size() << " cached names" << LL_ENDL; } // Provide some fallback for agents that return errors void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) { - std::map<LLUUID,LLAvatarName>::iterator existing = sCache.find(agent_id); - if (existing == sCache.end()) + std::map<LLUUID,LLAvatarName>::iterator existing = mCache.find(agent_id); + if (existing == mCache.end()) { // there is no existing cache entry, so make a temporary name from legacy LL_WARNS("AvNameCache") << "LLAvatarNameCache get legacy for agent " @@ -322,7 +279,7 @@ void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) // been returned by the get method, there is no need to signal anyone // Clear this agent from the pending list - LLAvatarNameCache::sPendingQueue.erase(agent_id); + LLAvatarNameCache::mPendingQueue.erase(agent_id); LLAvatarName& av_name = existing->second; LL_DEBUGS("AvNameCache") << "LLAvatarNameCache use cache for agent " << agent_id << LL_ENDL; @@ -340,24 +297,41 @@ void LLAvatarNameCache::processName(const LLUUID& agent_id, const LLAvatarName& return; } + bool updated_account = true; // assume obsolete value for new arrivals by default + + std::map<LLUUID, LLAvatarName>::iterator it = mCache.find(agent_id); + if (it != mCache.end() + && (*it).second.getAccountName() == av_name.getAccountName()) + { + updated_account = false; + } + // Add to the cache - sCache[agent_id] = av_name; + mCache[agent_id] = av_name; // Suppress request from the queue - sPendingQueue.erase(agent_id); + mPendingQueue.erase(agent_id); + + // notify mute list about changes + if (updated_account && mAccountNameChangedCallback) + { + mAccountNameChangedCallback(agent_id, av_name); + } // Signal everyone waiting on this name - signal_map_t::iterator sig_it = sSignalMap.find(agent_id); - if (sig_it != sSignalMap.end()) + signal_map_t::iterator sig_it = mSignalMap.find(agent_id); + if (sig_it != mSignalMap.end()) { callback_signal_t* signal = sig_it->second; (*signal)(agent_id, av_name); - sSignalMap.erase(agent_id); + mSignalMap.erase(agent_id); delete signal; signal = NULL; } + + } void LLAvatarNameCache::requestNamesViaCapability() @@ -379,16 +353,16 @@ void LLAvatarNameCache::requestNamesViaCapability() U32 ids = 0; ask_queue_t::const_iterator it; - while(!sAskQueue.empty()) + while(!mAskQueue.empty()) { - it = sAskQueue.begin(); + it = mAskQueue.begin(); LLUUID agent_id = *it; - sAskQueue.erase(it); + mAskQueue.erase(it); if (url.empty()) { // ...starting new request - url += sNameLookupURL; + url += mNameLookupURL; url += "?ids="; ids = 1; } @@ -402,7 +376,7 @@ void LLAvatarNameCache::requestNamesViaCapability() agent_ids.push_back(agent_id); // mark request as pending - sPendingQueue[agent_id] = now; + mPendingQueue[agent_id] = now; if (url.size() > NAME_URL_SEND_THRESHOLD) { @@ -432,7 +406,7 @@ void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id, // Retrieve the name and set it to never (or almost never...) expire: when we are using the legacy // protocol, we do not get an expiration date for each name and there's no reason to ask the // data again and again so we set the expiration time to the largest value admissible. - std::map<LLUUID,LLAvatarName>::iterator av_record = sCache.find(agent_id); + std::map<LLUUID,LLAvatarName>::iterator av_record = LLAvatarNameCache::getInstance()->mCache.find(agent_id); LLAvatarName& av_name = av_record->second; av_name.setExpires(MAX_UNREFRESHED_TIME); } @@ -451,7 +425,7 @@ void LLAvatarNameCache::legacyNameFetch(const LLUUID& agent_id, av_name.fromString(full_name); // Add to cache: we're still using the new cache even if we're using the old (legacy) protocol. - processName(agent_id, av_name); + LLAvatarNameCache::getInstance()->processName(agent_id, av_name); } void LLAvatarNameCache::requestNamesViaLegacy() @@ -460,15 +434,15 @@ void LLAvatarNameCache::requestNamesViaLegacy() F64 now = LLFrameTimer::getTotalSeconds(); std::string full_name; ask_queue_t::const_iterator it; - for (S32 requests = 0; !sAskQueue.empty() && requests < MAX_REQUESTS; ++requests) + for (S32 requests = 0; !mAskQueue.empty() && requests < MAX_REQUESTS; ++requests) { - it = sAskQueue.begin(); + it = mAskQueue.begin(); LLUUID agent_id = *it; - sAskQueue.erase(it); + mAskQueue.erase(it); // Mark as pending first, just in case the callback is immediately // invoked below. This should never happen in practice. - sPendingQueue[agent_id] = now; + mPendingQueue[agent_id] = now; LL_DEBUGS("AvNameCache") << "agent " << agent_id << LL_ENDL; @@ -477,26 +451,6 @@ void LLAvatarNameCache::requestNamesViaLegacy() } } -void LLAvatarNameCache::initClass(bool running, bool usePeopleAPI) -{ - sRunning = running; - sUsePeopleAPI = usePeopleAPI; - - sHttpRequest = LLCore::HttpRequest::ptr_t(new LLCore::HttpRequest()); - sHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders()); - sHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions()); - sHttpPolicy = LLCore::HttpRequest::DEFAULT_POLICY_ID; - sHttpPriority = 0; -} - -void LLAvatarNameCache::cleanupClass() -{ - sHttpRequest.reset(); - sHttpHeaders.reset(); - sHttpOptions.reset(); - sCache.clear(); -} - bool LLAvatarNameCache::importFile(std::istream& istr) { LLSD data; @@ -517,9 +471,9 @@ bool LLAvatarNameCache::importFile(std::istream& istr) { agent_id.set(it->first); av_name.fromLLSD( it->second ); - sCache[agent_id] = av_name; + mCache[agent_id] = av_name; } - LL_INFOS("AvNameCache") << "LLAvatarNameCache loaded " << sCache.size() << LL_ENDL; + LL_INFOS("AvNameCache") << "LLAvatarNameCache loaded " << mCache.size() << LL_ENDL; // Some entries may have expired since the cache was stored, // but they will be flushed in the first call to eraseUnrefreshed // from LLAvatarNameResponder::idle @@ -531,9 +485,9 @@ void LLAvatarNameCache::exportFile(std::ostream& ostr) { LLSD agents; F64 max_unrefreshed = LLFrameTimer::getTotalSeconds() - MAX_UNREFRESHED_TIME; - LL_INFOS("AvNameCache") << "LLAvatarNameCache at exit cache has " << sCache.size() << LL_ENDL; - cache_t::const_iterator it = sCache.begin(); - for ( ; it != sCache.end(); ++it) + LL_INFOS("AvNameCache") << "LLAvatarNameCache at exit cache has " << mCache.size() << LL_ENDL; + cache_t::const_iterator it = mCache.begin(); + for ( ; it != mCache.end(); ++it) { const LLUUID& agent_id = it->first; const LLAvatarName& av_name = it->second; @@ -552,23 +506,28 @@ void LLAvatarNameCache::exportFile(std::ostream& ostr) void LLAvatarNameCache::setNameLookupURL(const std::string& name_lookup_url) { - sNameLookupURL = name_lookup_url; + mNameLookupURL = name_lookup_url; } bool LLAvatarNameCache::hasNameLookupURL() { - return !sNameLookupURL.empty(); + return !mNameLookupURL.empty(); +} + +void LLAvatarNameCache::setUsePeopleAPI(bool use_api) +{ + mUsePeopleAPI = use_api; } bool LLAvatarNameCache::usePeopleAPI() { - return hasNameLookupURL() && sUsePeopleAPI; + return hasNameLookupURL() && mUsePeopleAPI; } void LLAvatarNameCache::idle() { // By convention, start running at first idle() call - sRunning = true; + mRunning = true; // *TODO: Possibly re-enabled this based on People API load measurements // 100 ms is the threshold for "user speed" operations, so we can @@ -579,7 +538,7 @@ void LLAvatarNameCache::idle() return; } - if (!sAskQueue.empty()) + if (!mAskQueue.empty()) { if (usePeopleAPI()) { @@ -592,7 +551,7 @@ void LLAvatarNameCache::idle() } } - if (sAskQueue.empty()) + if (mAskQueue.empty()) { // cleared the list, reset the request timer. sRequestTimer.resetWithExpiry(SECS_BETWEEN_REQUESTS); @@ -607,8 +566,8 @@ bool LLAvatarNameCache::isRequestPending(const LLUUID& agent_id) bool isPending = false; const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0; - pending_queue_t::const_iterator it = sPendingQueue.find(agent_id); - if (it != sPendingQueue.end()) + pending_queue_t::const_iterator it = mPendingQueue.find(agent_id); + if (it != mPendingQueue.end()) { // in the list of requests in flight, retry if too old F64 expire_time = LLFrameTimer::getTotalSeconds() - PENDING_TIMEOUT_SECS; @@ -622,11 +581,11 @@ void LLAvatarNameCache::eraseUnrefreshed() F64 now = LLFrameTimer::getTotalSeconds(); F64 max_unrefreshed = now - MAX_UNREFRESHED_TIME; - if (!sLastExpireCheck || sLastExpireCheck < max_unrefreshed) + if (!mLastExpireCheck || mLastExpireCheck < max_unrefreshed) { - sLastExpireCheck = now; + mLastExpireCheck = now; S32 expired = 0; - for (cache_t::iterator it = sCache.begin(); it != sCache.end();) + for (cache_t::iterator it = mCache.begin(); it != mCache.end();) { const LLAvatarName& av_name = it->second; if (av_name.mExpires < max_unrefreshed) @@ -635,7 +594,7 @@ void LLAvatarNameCache::eraseUnrefreshed() << " user '" << av_name.getAccountName() << "' " << "expired " << now - av_name.mExpires << " secs ago" << LL_ENDL; - sCache.erase(it++); + mCache.erase(it++); expired++; } else @@ -644,19 +603,24 @@ void LLAvatarNameCache::eraseUnrefreshed() } } LL_INFOS("AvNameCache") << "LLAvatarNameCache expired " << expired << " cached avatar names, " - << sCache.size() << " remaining" << LL_ENDL; + << mCache.size() << " remaining" << LL_ENDL; } } +//static, wrapper +bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name) +{ + return LLAvatarNameCache::getInstance()->getName(agent_id, av_name); +} // fills in av_name if it has it in the cache, even if expired (can check expiry time) // returns bool specifying if av_name was filled, false otherwise -bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name) +bool LLAvatarNameCache::getName(const LLUUID& agent_id, LLAvatarName *av_name) { - if (sRunning) + if (mRunning) { // ...only do immediate lookups when cache is running - std::map<LLUUID,LLAvatarName>::iterator it = sCache.find(agent_id); - if (it != sCache.end()) + std::map<LLUUID,LLAvatarName>::iterator it = mCache.find(agent_id); + if (it != mCache.end()) { *av_name = it->second; @@ -667,7 +631,7 @@ bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name) { LL_DEBUGS("AvNameCache") << "LLAvatarNameCache refresh agent " << agent_id << LL_ENDL; - sAskQueue.insert(agent_id); + mAskQueue.insert(agent_id); } } @@ -678,7 +642,7 @@ bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name) if (!isRequestPending(agent_id)) { LL_DEBUGS("AvNameCache") << "LLAvatarNameCache queue request for agent " << agent_id << LL_ENDL; - sAskQueue.insert(agent_id); + mAskQueue.insert(agent_id); } return false; @@ -693,15 +657,21 @@ void LLAvatarNameCache::fireSignal(const LLUUID& agent_id, signal(agent_id, av_name); } +// static, wrapper LLAvatarNameCache::callback_connection_t LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot) { + return LLAvatarNameCache::getInstance()->getNameCallback(agent_id, slot); +} + +LLAvatarNameCache::callback_connection_t LLAvatarNameCache::getNameCallback(const LLUUID& agent_id, callback_slot_t slot) +{ callback_connection_t connection; - if (sRunning) + if (mRunning) { // ...only do immediate lookups when cache is running - std::map<LLUUID,LLAvatarName>::iterator it = sCache.find(agent_id); - if (it != sCache.end()) + std::map<LLUUID,LLAvatarName>::iterator it = mCache.find(agent_id); + if (it != mCache.end()) { const LLAvatarName& av_name = it->second; @@ -717,17 +687,17 @@ LLAvatarNameCache::callback_connection_t LLAvatarNameCache::get(const LLUUID& ag // schedule a request if (!isRequestPending(agent_id)) { - sAskQueue.insert(agent_id); + mAskQueue.insert(agent_id); } // always store additional callback, even if request is pending - signal_map_t::iterator sig_it = sSignalMap.find(agent_id); - if (sig_it == sSignalMap.end()) + signal_map_t::iterator sig_it = mSignalMap.find(agent_id); + if (sig_it == mSignalMap.end()) { // ...new callback for this id callback_signal_t* signal = new callback_signal_t(); connection = signal->connect(slot); - sSignalMap[agent_id] = signal; + mSignalMap[agent_id] = signal; } else { @@ -760,20 +730,20 @@ void LLAvatarNameCache::setUseUsernames(bool use) void LLAvatarNameCache::erase(const LLUUID& agent_id) { - sCache.erase(agent_id); + mCache.erase(agent_id); } void LLAvatarNameCache::insert(const LLUUID& agent_id, const LLAvatarName& av_name) { // *TODO: update timestamp if zero? - sCache[agent_id] = av_name; + mCache[agent_id] = av_name; } LLUUID LLAvatarNameCache::findIdByName(const std::string& name) { std::map<LLUUID, LLAvatarName>::iterator it; - std::map<LLUUID, LLAvatarName>::iterator end = sCache.end(); - for (it = sCache.begin(); it != end; ++it) + std::map<LLUUID, LLAvatarName>::iterator end = mCache.end(); + for (it = mCache.begin(); it != end; ++it) { if (it->second.getUserName() == name) { diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h index 63e067c939..549d1703fa 100644 --- a/indra/llmessage/llavatarnamecache.h +++ b/indra/llmessage/llavatarnamecache.h @@ -29,20 +29,20 @@ #define LLAVATARNAMECACHE_H #include "llavatarname.h" // for convenience +#include "llsingleton.h" #include <boost/signals2.hpp> +#include <set> class LLSD; class LLUUID; -namespace LLAvatarNameCache +class LLAvatarNameCache : public LLSingleton<LLAvatarNameCache> { + LLSINGLETON(LLAvatarNameCache); + ~LLAvatarNameCache(); +public: typedef boost::signals2::signal<void (void)> use_display_name_signal_t; - - // Until the cache is set running, immediate lookups will fail and - // async lookups will be queued. This allows us to block requests - // until we know if the first region supports display names. - void initClass(bool running, bool usePeopleAPI); - void cleanupClass(); + typedef boost::function<void (const LLUUID id, const LLAvatarName& av_name)> account_name_changed_callback_t; // Import/export the name cache to file. bool importFile(std::istream& istr); @@ -55,6 +55,7 @@ namespace LLAvatarNameCache // Do we have a valid lookup URL, i.e. are we trying to use the // more recent display name lookup system? bool hasNameLookupURL(); + void setUsePeopleAPI(bool use_api); bool usePeopleAPI(); // Periodically makes a batch request for display names not already in @@ -63,7 +64,8 @@ namespace LLAvatarNameCache // If name is in cache, returns true and fills in provided LLAvatarName // otherwise returns false. - bool get(const LLUUID& agent_id, LLAvatarName *av_name); + static bool get(const LLUUID& agent_id, LLAvatarName *av_name); + bool getName(const LLUUID& agent_id, LLAvatarName *av_name); // Callback types for get() below typedef boost::signals2::signal< @@ -74,7 +76,8 @@ namespace LLAvatarNameCache // Fetches name information and calls callbacks. // If name information is in cache, callbacks will be called immediately. - callback_connection_t get(const LLUUID& agent_id, callback_slot_t slot); + static callback_connection_t get(const LLUUID& agent_id, callback_slot_t slot); + callback_connection_t getNameCallback(const LLUUID& agent_id, callback_slot_t slot); // Set display name: flips the switch and triggers the callbacks. void setUseDisplayNames(bool use); @@ -100,7 +103,86 @@ namespace LLAvatarNameCache F64 nameExpirationFromHeaders(const LLSD& headers); void addUseDisplayNamesCallback(const use_display_name_signal_t::slot_type& cb); -} + + void setAccountNameChangedCallback(const account_name_changed_callback_t& cb) { mAccountNameChangedCallback = cb; } + +private: + // Handle name response off network. + void processName(const LLUUID& agent_id, + const LLAvatarName& av_name); + + void requestNamesViaCapability(); + + // Legacy name system callbacks + static void legacyNameCallback(const LLUUID& agent_id, + const std::string& full_name, + bool is_group); + static void legacyNameFetch(const LLUUID& agent_id, + const std::string& full_name, + bool is_group); + + void requestNamesViaLegacy(); + + // Do a single callback to a given slot + void fireSignal(const LLUUID& agent_id, + const callback_slot_t& slot, + const LLAvatarName& av_name); + + // Is a request in-flight over the network? + bool isRequestPending(const LLUUID& agent_id); + + // Erase expired names from cache + void eraseUnrefreshed(); + + bool expirationFromCacheControl(const LLSD& headers, F64 *expires); + + // This is a coroutine. + static void requestAvatarNameCache_(std::string url, std::vector<LLUUID> agentIds); + + void handleAvNameCacheSuccess(const LLSD &data, const LLSD &httpResult); + +private: + + use_display_name_signal_t mUseDisplayNamesSignal; + account_name_changed_callback_t mAccountNameChangedCallback; + + // Cache starts in a paused state until we can determine if the + // current region supports display names. + bool mRunning; + + // Use the People API (modern) for fetching name if true. Use the old legacy protocol if false. + // For testing, there's a UsePeopleAPI setting that can be flipped (must restart viewer). + bool mUsePeopleAPI; + + // Base lookup URL for name service. + // On simulator, loaded from indra.xml + // On viewer, usually a simulator capability (at People API team's request) + // Includes the trailing slash, like "http://pdp60.lindenlab.com:8000/agents/" + std::string mNameLookupURL; + + // Accumulated agent IDs for next query against service + typedef std::set<LLUUID> ask_queue_t; + ask_queue_t mAskQueue; + + // Agent IDs that have been requested, but with no reply. + // Maps agent ID to frame time request was made. + typedef std::map<LLUUID, F64> pending_queue_t; + pending_queue_t mPendingQueue; + + // Callbacks to fire when we received a name. + // 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 mSignalMap; + + // The cache at last, i.e. avatar names we know about. + typedef std::map<LLUUID, LLAvatarName> cache_t; + cache_t mCache; + + // Time when unrefreshed cached names were checked last. + F64 mLastExpireCheck; +}; // Parse a cache-control header to get the max-age delta-seconds. // Returns true if header has max-age param and it parses correctly. diff --git a/indra/llmessage/llblowfishcipher.cpp b/indra/llmessage/llblowfishcipher.cpp index 0b5025a422..949d4cc0c7 100644 --- a/indra/llmessage/llblowfishcipher.cpp +++ b/indra/llmessage/llblowfishcipher.cpp @@ -52,24 +52,28 @@ U32 LLBlowfishCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) if (src_len > dst_len) return 0; // OpenSSL uses "cipher contexts" to hold encryption parameters. - EVP_CIPHER_CTX context; - EVP_CIPHER_CTX_init(&context); + EVP_CIPHER_CTX *context = EVP_CIPHER_CTX_new(); + if (!context) + { + LL_WARNS() << "LLBlowfishCipher::encrypt EVP_CIPHER_CTX initiation failure" << LL_ENDL; + return 0; + } // We want a blowfish cyclic block chain cipher, but need to set // the key length before we pass in a key, so call EncryptInit // first with NULLs. - EVP_EncryptInit_ex(&context, EVP_bf_cbc(), NULL, NULL, NULL); - EVP_CIPHER_CTX_set_key_length(&context, (int)mSecretSize); + EVP_EncryptInit_ex(context, EVP_bf_cbc(), NULL, NULL, NULL); + EVP_CIPHER_CTX_set_key_length(context, (int)mSecretSize); // Complete initialization. Per EVP_EncryptInit man page, the // cipher pointer must be NULL. Apparently initial_vector must // be 8 bytes for blowfish, as this is the block size. unsigned char initial_vector[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - EVP_EncryptInit_ex(&context, NULL, NULL, mSecret, initial_vector); + EVP_EncryptInit_ex(context, NULL, NULL, mSecret, initial_vector); - int blocksize = EVP_CIPHER_CTX_block_size(&context); - int keylen = EVP_CIPHER_CTX_key_length(&context); - int iv_length = EVP_CIPHER_CTX_iv_length(&context); + int blocksize = EVP_CIPHER_CTX_block_size(context); + int keylen = EVP_CIPHER_CTX_key_length(context); + int iv_length = EVP_CIPHER_CTX_iv_length(context); LL_DEBUGS() << "LLBlowfishCipher blocksize " << blocksize << " keylen " << keylen << " iv_len " << iv_length @@ -77,7 +81,7 @@ U32 LLBlowfishCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) int output_len = 0; int temp_len = 0; - if (!EVP_EncryptUpdate(&context, + if (!EVP_EncryptUpdate(context, dst, &output_len, src, @@ -89,18 +93,18 @@ U32 LLBlowfishCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len) // There may be some final data left to encrypt if the input is // not an exact multiple of the block size. - if (!EVP_EncryptFinal_ex(&context, (unsigned char*)(dst + output_len), &temp_len)) + if (!EVP_EncryptFinal_ex(context, (unsigned char*)(dst + output_len), &temp_len)) { LL_WARNS() << "LLBlowfishCipher::encrypt EVP_EncryptFinal failure" << LL_ENDL; goto ERROR; } output_len += temp_len; - EVP_CIPHER_CTX_cleanup(&context); + EVP_CIPHER_CTX_free(context); return output_len; ERROR: - EVP_CIPHER_CTX_cleanup(&context); + EVP_CIPHER_CTX_free(context); return 0; } diff --git a/indra/llmessage/llbuffer.cpp b/indra/llmessage/llbuffer.cpp index 1a0eceba0f..cfe38605ad 100644 --- a/indra/llmessage/llbuffer.cpp +++ b/indra/llmessage/llbuffer.cpp @@ -32,6 +32,7 @@ #include "llmath.h" #include "llstl.h" #include "llthread.h" +#include "llmutex.h" #include <iterator> #define ASSERT_LLBUFFERARRAY_MUTEX_LOCKED() llassert(!mMutexp || mMutexp->isSelfLocked()) diff --git a/indra/llmessage/llbufferstream.cpp b/indra/llmessage/llbufferstream.cpp index ff1c9993cc..39508c1c52 100644 --- a/indra/llmessage/llbufferstream.cpp +++ b/indra/llmessage/llbufferstream.cpp @@ -31,6 +31,7 @@ #include "llbuffer.h" #include "llthread.h" +#include "llmutex.h" static const S32 DEFAULT_OUTPUT_SEGMENT_SIZE = 1024 * 4; diff --git a/indra/llmessage/llcircuit.cpp b/indra/llmessage/llcircuit.cpp index 8dbe2f8411..8baa2e328b 100644 --- a/indra/llmessage/llcircuit.cpp +++ b/indra/llmessage/llcircuit.cpp @@ -543,7 +543,7 @@ void LLCircuitData::checkPeriodTime() mBytesOutLastPeriod = mBytesOutThisPeriod; mBytesInThisPeriod = S32Bytes(0); mBytesOutThisPeriod = S32Bytes(0); - mLastPeriodLength = period_length; + mLastPeriodLength = F32Seconds::convert(period_length); mPeriodTime = mt_sec; } @@ -1378,8 +1378,8 @@ F32Milliseconds LLCircuitData::getPingInTransitTime() if (mPingsInTransit) { - time_since_ping_was_sent = ((mPingsInTransit*mHeartbeatInterval - F32Seconds(1)) - + (LLMessageSystem::getMessageTimeSeconds() - mPingTime)); + time_since_ping_was_sent = F32Milliseconds::convert(((mPingsInTransit*mHeartbeatInterval - F32Seconds(1)) + + (LLMessageSystem::getMessageTimeSeconds() - mPingTime))); } return time_since_ping_was_sent; diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index 74cdff2b00..be014e7b1e 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -25,23 +25,29 @@ * $/LicenseInfo$ */ -#include "linden_common.h" +#include "llwin32headers.h" + +#include "linden_common.h" + #include "llcoproceduremanager.h" + +#include <chrono> + +#include <boost/fiber/buffered_channel.hpp> + #include "llexception.h" #include "stringize.h" -#include <boost/assign.hpp> //========================================================================= // Map of pool sizes for known pools -// *TODO$: When C++11 this can be initialized here as follows: -// = {{"AIS", 25}, {"Upload", 1}} -static std::map<std::string, U32> DefaultPoolSizes = - boost::assign::map_list_of - (std::string("Upload"), 1) - (std::string("AIS"), 1); - // *TODO: Rider for the moment keep AIS calls serialized otherwise the COF will tend to get out of sync. +static const std::map<std::string, U32> DefaultPoolSizes{ + {std::string("Upload"), 1}, + {std::string("AIS"), 1}, + // *TODO: Rider for the moment keep AIS calls serialized otherwise the COF will tend to get out of sync. +}; -#define DEFAULT_POOL_SIZE 5 +static const U32 DEFAULT_POOL_SIZE = 5; +const U32 LLCoprocedureManager::DEFAULT_QUEUE_SIZE = 4096; //========================================================================= class LLCoprocedurePool: private boost::noncopyable @@ -50,7 +56,7 @@ public: typedef LLCoprocedureManager::CoProcedure_t CoProcedure_t; LLCoprocedurePool(const std::string &name, size_t size); - virtual ~LLCoprocedurePool(); + ~LLCoprocedurePool(); /// Places the coprocedure on the queue for processing. /// @@ -60,36 +66,29 @@ public: /// @return This method returns a UUID that can be used later to cancel execution. LLUUID enqueueCoprocedure(const std::string &name, CoProcedure_t proc); - /// Cancel a coprocedure. If the coprocedure is already being actively executed - /// this method calls cancelSuspendedOperation() on the associated HttpAdapter - /// If it has not yet been dequeued it is simply removed from the queue. - bool cancelCoprocedure(const LLUUID &id); - - /// Requests a shutdown of the upload manager. Passing 'true' will perform - /// an immediate kill on the upload coroutine. - void shutdown(bool hardShutdown = false); - /// Returns the number of coprocedures in the queue awaiting processing. /// inline size_t countPending() const { - return mPendingCoprocs.size(); + return mPending; } /// Returns the number of coprocedures actively being processed. /// inline size_t countActive() const { - return mActiveCoprocs.size(); + return mActiveCoprocsCount; } /// Returns the total number of coprocedures either queued or in active processing. /// - inline size_t count() const + inline S32 count() const { return countPending() + countActive(); } + void close(); + private: struct QueuedCoproc { @@ -106,25 +105,27 @@ private: CoProcedure_t mProc; }; - // we use a deque here rather than std::queue since we want to be able to - // iterate through the queue and potentially erase an entry from the middle. - typedef std::deque<QueuedCoproc::ptr_t> CoprocQueue_t; - typedef std::map<LLUUID, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> ActiveCoproc_t; + // we use a buffered_channel here rather than unbuffered_channel since we want to be able to + // push values without blocking,even if there's currently no one calling a pop operation (due to + // fiber running right now) + typedef boost::fibers::buffered_channel<QueuedCoproc::ptr_t> CoprocQueue_t; + // Use shared_ptr to control the lifespan of our CoprocQueue_t instance + // because the consuming coroutine might outlive this LLCoprocedurePool + // instance. + typedef boost::shared_ptr<CoprocQueue_t> CoprocQueuePtr; std::string mPoolName; - size_t mPoolSize; - CoprocQueue_t mPendingCoprocs; - ActiveCoproc_t mActiveCoprocs; - bool mShutdown; - LLEventStream mWakeupTrigger; + size_t mPoolSize, mActiveCoprocsCount, mPending; + CoprocQueuePtr mPendingCoprocs; + LLTempBoundListener mStatusListener; typedef std::map<std::string, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> CoroAdapterMap_t; LLCore::HttpRequest::policy_t mHTTPPolicy; CoroAdapterMap_t mCoroMapping; - void coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter); - + void coprocedureInvokerCoro(CoprocQueuePtr pendingCoprocs, + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter); }; //========================================================================= @@ -134,43 +135,53 @@ LLCoprocedureManager::LLCoprocedureManager() LLCoprocedureManager::~LLCoprocedureManager() { - + close(); } -LLCoprocedureManager::poolPtr_t LLCoprocedureManager::initializePool(const std::string &poolName) +void LLCoprocedureManager::initializePool(const std::string &poolName) { + poolMap_t::iterator it = mPoolMap.find(poolName); + + if (it != mPoolMap.end()) + { + // Pools are not supposed to be initialized twice + // Todo: ideally restrict init to STATE_FIRST + LL_ERRS() << "Pool is already present " << poolName << LL_ENDL; + return; + } + // Attempt to look up a pool size in the configuration. If found use that std::string keyName = "PoolSize" + poolName; int size = 0; - if (poolName.empty()) - LL_ERRS("CoprocedureManager") << "Poolname must not be empty" << LL_ENDL; + LL_ERRS_IF(poolName.empty(), "CoprocedureManager") << "Poolname must not be empty" << LL_ENDL; + LL_INFOS("CoprocedureManager") << "Initializing pool " << poolName << LL_ENDL; - if (mPropertyQueryFn && !mPropertyQueryFn.empty()) + if (mPropertyQueryFn) { size = mPropertyQueryFn(keyName); } if (size == 0) - { // if not found grab the know default... if there is no known + { + // if not found grab the know default... if there is no known // default use a reasonable number like 5. - std::map<std::string, U32>::iterator it = DefaultPoolSizes.find(poolName); - if (it == DefaultPoolSizes.end()) - size = DEFAULT_POOL_SIZE; - else - size = (*it).second; + auto it = DefaultPoolSizes.find(poolName); + size = (it != DefaultPoolSizes.end()) ? it->second : DEFAULT_POOL_SIZE; - if (mPropertyDefineFn && !mPropertyDefineFn.empty()) + if (mPropertyDefineFn) + { mPropertyDefineFn(keyName, size, "Coroutine Pool size for " + poolName); - LL_WARNS() << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL; + } + + LL_WARNS("CoProcMgr") << "LLCoprocedureManager: No setting for \"" << keyName << "\" setting pool size to default of " << size << LL_ENDL; } poolPtr_t pool(new LLCoprocedurePool(poolName, size)); - mPoolMap.insert(poolMap_t::value_type(poolName, pool)); + LL_ERRS_IF(!pool, "CoprocedureManager") << "Unable to create pool named \"" << poolName << "\" FATAL!" << LL_ENDL; - if (!pool) - LL_ERRS("CoprocedureManager") << "Unable to create pool named \"" << poolName << "\" FATAL!" << LL_ENDL; - return pool; + bool inserted = mPoolMap.emplace(poolName, pool).second; + LL_ERRS_IF(!inserted, "CoprocedureManager") << "Unable to add pool named \"" << poolName << "\" to map. FATAL!" << LL_ENDL; } //------------------------------------------------------------------------- @@ -178,53 +189,39 @@ LLUUID LLCoprocedureManager::enqueueCoprocedure(const std::string &pool, const s { // Attempt to find the pool and enqueue the procedure. If the pool does // not exist, create it. - poolPtr_t targetPool; poolMap_t::iterator it = mPoolMap.find(pool); if (it == mPoolMap.end()) { - targetPool = initializePool(pool); - } - else - { - targetPool = (*it).second; + // initializing pools in enqueueCoprocedure is not thread safe, + // at the moment pools need to be initialized manually + LL_ERRS() << "Uninitialized pool " << pool << LL_ENDL; } + poolPtr_t targetPool = it->second; return targetPool->enqueueCoprocedure(name, proc); } -void LLCoprocedureManager::cancelCoprocedure(const LLUUID &id) -{ - for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) - { - if ((*it).second->cancelCoprocedure(id)) - return; - } - LL_INFOS() << "Coprocedure not found." << LL_ENDL; -} - -void LLCoprocedureManager::shutdown(bool hardShutdown) -{ - for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) - { - (*it).second->shutdown(hardShutdown); - } - mPoolMap.clear(); -} - void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn) { + // functions to discover and store the pool sizes + // Might be a better idea to make an initializePool(name, size) to init everything externally mPropertyQueryFn = queryfn; mPropertyDefineFn = updatefn; + + initializePool("Upload"); + initializePool("AIS"); // it might be better to have some kind of on-demand initialization for AIS + // "ExpCache" pool gets initialized in LLExperienceCache + // asset storage pool gets initialized in LLViewerAssetStorage } //------------------------------------------------------------------------- size_t LLCoprocedureManager::countPending() const { size_t count = 0; - for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + for (const auto& pair : mPoolMap) { - count += (*it).second->countPending(); + count += pair.second->countPending(); } return count; } @@ -235,7 +232,7 @@ size_t LLCoprocedureManager::countPending(const std::string &pool) const if (it == mPoolMap.end()) return 0; - return (*it).second->countPending(); + return it->second->countPending(); } size_t LLCoprocedureManager::countActive() const @@ -243,7 +240,7 @@ size_t LLCoprocedureManager::countActive() const size_t count = 0; for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) { - count += (*it).second->countActive(); + count += it->second->countActive(); } return count; } @@ -253,16 +250,18 @@ size_t LLCoprocedureManager::countActive(const std::string &pool) const poolMap_t::const_iterator it = mPoolMap.find(pool); if (it == mPoolMap.end()) + { return 0; - return (*it).second->countActive(); + } + return it->second->countActive(); } size_t LLCoprocedureManager::count() const { size_t count = 0; - for (poolMap_t::const_iterator it = mPoolMap.begin(); it != mPoolMap.end(); ++it) + for (const auto& pair : mPoolMap) { - count += (*it).second->count(); + count += pair.second->count(); } return count; } @@ -273,59 +272,88 @@ size_t LLCoprocedureManager::count(const std::string &pool) const if (it == mPoolMap.end()) return 0; - return (*it).second->count(); + return it->second->count(); +} + +void LLCoprocedureManager::close() +{ + for(auto & poolEntry : mPoolMap) + { + poolEntry.second->close(); + } +} + +void LLCoprocedureManager::close(const std::string &pool) +{ + poolMap_t::iterator it = mPoolMap.find(pool); + if (it != mPoolMap.end()) + { + it->second->close(); + } } //========================================================================= LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size): mPoolName(poolName), mPoolSize(size), - mPendingCoprocs(), - mShutdown(false), - mWakeupTrigger("CoprocedurePool" + poolName, true), - mCoroMapping(), - mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID) + mActiveCoprocsCount(0), + mPending(0), + mPendingCoprocs(boost::make_shared<CoprocQueue_t>(LLCoprocedureManager::DEFAULT_QUEUE_SIZE)), + mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mCoroMapping() { + try + { + // store in our LLTempBoundListener so that when the LLCoprocedurePool is + // destroyed, we implicitly disconnect from this LLEventPump + // Monitores application status + mStatusListener = LLEventPumps::instance().obtain("LLApp").listen( + poolName + "_pool", // Make sure it won't repeat names from lleventcoro + [pendingCoprocs = mPendingCoprocs, poolName](const LLSD& status) + { + auto& statsd = status["status"]; + if (statsd.asString() != "running") + { + LL_INFOS("CoProcMgr") << "Pool " << poolName + << " closing queue because status " << statsd + << LL_ENDL; + // This should ensure that all waiting coprocedures in this + // pool will wake up and terminate. + pendingCoprocs->close(); + } + return false; + }); + } + catch (const LLEventPump::DupListenerName &) + { + // This shounldn't be possible since LLCoprocedurePool is supposed to have unique names, + // yet it somehow did happen, as result pools got '_pool' suffix and this catch. + // + // If this somehow happens again it is better to crash later on shutdown due to pump + // not stopping coroutine and see warning in logs than on startup or during login. + LL_WARNS("CoProcMgr") << "Attempted to register dupplicate listener name: " << poolName + << "_pool. Failed to start listener." << LL_ENDL; + + llassert(0); // Fix Me! Ignoring missing listener! + } + for (size_t count = 0; count < mPoolSize; ++count) { LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter( mPoolName + "Adapter", mHTTPPolicy)); - std::string pooledCoro = LLCoros::instance().launch("LLCoprocedurePool("+mPoolName+")::coprocedureInvokerCoro", - boost::bind(&LLCoprocedurePool::coprocedureInvokerCoro, this, httpAdapter)); + std::string pooledCoro = LLCoros::instance().launch( + "LLCoprocedurePool("+mPoolName+")::coprocedureInvokerCoro", + boost::bind(&LLCoprocedurePool::coprocedureInvokerCoro, this, + mPendingCoprocs, httpAdapter)); mCoroMapping.insert(CoroAdapterMap_t::value_type(pooledCoro, httpAdapter)); } - LL_INFOS() << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items." << LL_ENDL; - - mWakeupTrigger.post(LLSD()); + LL_INFOS("CoProcMgr") << "Created coprocedure pool named \"" << mPoolName << "\" with " << size << " items, queue max " << LLCoprocedureManager::DEFAULT_QUEUE_SIZE << LL_ENDL; } LLCoprocedurePool::~LLCoprocedurePool() { - shutdown(); -} - -//------------------------------------------------------------------------- -void LLCoprocedurePool::shutdown(bool hardShutdown) -{ - CoroAdapterMap_t::iterator it; - - for (it = mCoroMapping.begin(); it != mCoroMapping.end(); ++it) - { - if (hardShutdown) - { - LLCoros::instance().kill((*it).first); - } - if ((*it).second) - { - (*it).second->cancelSuspendedOperation(); - } - } - - mShutdown = true; - mCoroMapping.clear(); - mPendingCoprocs.clear(); } //------------------------------------------------------------------------- @@ -333,76 +361,99 @@ LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoproced { LLUUID id(LLUUID::generateNewID()); - mPendingCoprocs.push_back(QueuedCoproc::ptr_t(new QueuedCoproc(name, id, proc))); - LL_INFOS() << "Coprocedure(" << name << ") enqueued with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; - - mWakeupTrigger.post(LLSD()); - - return id; -} - -bool LLCoprocedurePool::cancelCoprocedure(const LLUUID &id) -{ - // first check the active coroutines. If there, remove it and return. - ActiveCoproc_t::iterator itActive = mActiveCoprocs.find(id); - if (itActive != mActiveCoprocs.end()) + LL_INFOS("CoProcMgr") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName << "\" at " << mPending << LL_ENDL; + auto pushed = mPendingCoprocs->try_push(boost::make_shared<QueuedCoproc>(name, id, proc)); + if (pushed == boost::fibers::channel_op_status::success) { - LL_INFOS() << "Found and canceling active coprocedure with id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; - (*itActive).second->cancelSuspendedOperation(); - mActiveCoprocs.erase(itActive); - return true; + ++mPending; + return id; } - for (CoprocQueue_t::iterator it = mPendingCoprocs.begin(); it != mPendingCoprocs.end(); ++it) + // Here we didn't succeed in pushing. Shutdown could be the reason. + if (pushed == boost::fibers::channel_op_status::closed) { - if ((*it)->mId == id) - { - LL_INFOS() << "Found and removing queued coroutine(" << (*it)->mName << ") with Id=" << id.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; - mPendingCoprocs.erase(it); - return true; - } + LL_WARNS("CoProcMgr") << "Discarding coprocedure '" << name << "' because shutdown" << LL_ENDL; + return {}; } - LL_INFOS() << "Coprocedure with Id=" << id.asString() << " was not found in pool \"" << mPoolName << "\"" << LL_ENDL; - return false; + // The queue should never fill up. + LL_ERRS("CoProcMgr") << "Enqueue into '" << name << "' failed (" << unsigned(pushed) << ")" << LL_ENDL; + return {}; // never executed, pacify the compiler } //------------------------------------------------------------------------- -void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter) +void LLCoprocedurePool::coprocedureInvokerCoro( + CoprocQueuePtr pendingCoprocs, + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter) { - LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - - while (!mShutdown) + for (;;) { - llcoro::suspendUntilEventOn(mWakeupTrigger); - if (mShutdown) + // It is VERY IMPORTANT that we instantiate a new ptr_t just before + // the pop_wait_for() call below. When this ptr_t was declared at + // function scope (outside the for loop), NickyD correctly diagnosed a + // mysterious hang condition due to: + // - the second time through the loop, the ptr_t held the last pointer + // to the previous QueuedCoproc, which indirectly held the last + // LLPointer to an LLInventoryCallback instance + // - while holding the lock on pendingCoprocs, pop_wait_for() assigned + // the popped value to the ptr_t variable + // - assignment destroyed the previous value of that variable, which + // indirectly destroyed the LLInventoryCallback + // - whose destructor called ~LLRequestServerAppearanceUpdateOnDestroy() + // - which called LLAppearanceMgr::requestServerAppearanceUpdate() + // - which called enqueueCoprocedure() + // - which tried to acquire the lock on pendingCoprocs... alas. + // Using a fresh, clean ptr_t ensures that no previous value is + // destroyed during pop_wait_for(). + QueuedCoproc::ptr_t coproc; + boost::fibers::channel_op_status status; + { + LLCoros::TempStatus st("waiting for work for 10s"); + status = pendingCoprocs->pop_wait_for(coproc, std::chrono::seconds(10)); + } + if (status == boost::fibers::channel_op_status::closed) + { break; - - while (!mPendingCoprocs.empty()) + } + + if(status == boost::fibers::channel_op_status::timeout) { - QueuedCoproc::ptr_t coproc = mPendingCoprocs.front(); - mPendingCoprocs.pop_front(); - ActiveCoproc_t::iterator itActive = mActiveCoprocs.insert(ActiveCoproc_t::value_type(coproc->mId, httpAdapter)).first; + LL_DEBUGS_ONCE("CoProcMgr") << "pool '" << mPoolName << "' waiting." << LL_ENDL; + continue; + } + // we actually popped an item + --mPending; + mActiveCoprocsCount++; - LL_INFOS() << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\"" << LL_ENDL; + LL_DEBUGS("CoProcMgr") << "Dequeued and invoking coprocedure(" << coproc->mName << ") with id=" << coproc->mId.asString() << " in pool \"" << mPoolName << "\" (" << mPending << " left)" << LL_ENDL; - try - { - coproc->mProc(httpAdapter, coproc->mId); - } - catch (...) - { - LOG_UNHANDLED_EXCEPTION(STRINGIZE("Coprocedure('" << coproc->mName - << "', id=" << coproc->mId.asString() - << ") in pool '" << mPoolName << "'")); - // must NOT omit this or we deplete the pool - mActiveCoprocs.erase(itActive); - throw; - } + try + { + coproc->mProc(httpAdapter, coproc->mId); + } + catch (const LLCoros::Stop &e) + { + LL_INFOS("LLCoros") << "coprocedureInvokerCoro terminating because " + << e.what() << LL_ENDL; + throw; // let toplevel handle this as LLContinueError + } + catch (...) + { + LOG_UNHANDLED_EXCEPTION(STRINGIZE("Coprocedure('" << coproc->mName + << "', id=" << coproc->mId.asString() + << ") in pool '" << mPoolName << "'")); + // must NOT omit this or we deplete the pool + mActiveCoprocsCount--; + continue; + } - LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL; + LL_DEBUGS("CoProcMgr") << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL; - mActiveCoprocs.erase(itActive); - } + mActiveCoprocsCount--; } } + +void LLCoprocedurePool::close() +{ + mPendingCoprocs->close(); +} diff --git a/indra/llmessage/llcoproceduremanager.h b/indra/llmessage/llcoproceduremanager.h index 7d0e83180c..2d460826ff 100644 --- a/indra/llmessage/llcoproceduremanager.h +++ b/indra/llmessage/llcoproceduremanager.h @@ -32,6 +32,7 @@ #include "llcoros.h" #include "llcorehttputil.h" #include "lluuid.h" +#include <boost/smart_ptr/shared_ptr.hpp> class LLCoprocedurePool; @@ -57,11 +58,7 @@ public: /// Cancel a coprocedure. If the coprocedure is already being actively executed /// this method calls cancelYieldingOperation() on the associated HttpAdapter /// If it has not yet been dequeued it is simply removed from the queue. - void cancelCoprocedure(const LLUUID &id); - - /// Requests a shutdown of the upload manager. Passing 'true' will perform - /// an immediate kill on the upload coroutine. - void shutdown(bool hardShutdown = false); + //void cancelCoprocedure(const LLUUID &id); void setPropertyMethods(SettingQuery_t queryfn, SettingUpdate_t updatefn); @@ -80,6 +77,11 @@ public: size_t count() const; size_t count(const std::string &pool) const; + void close(); + void close(const std::string &pool); + + void initializePool(const std::string &poolName); + private: typedef boost::shared_ptr<LLCoprocedurePool> poolPtr_t; @@ -87,10 +89,11 @@ private: poolMap_t mPoolMap; - poolPtr_t initializePool(const std::string &poolName); - SettingQuery_t mPropertyQueryFn; SettingUpdate_t mPropertyDefineFn; + +public: + static const U32 DEFAULT_QUEUE_SIZE; }; #endif diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index 24387fbffd..0eae6d9826 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -597,7 +597,7 @@ LLSD HttpCoroJSONHandler::handleSuccess(LLCore::HttpResponse * response, LLCore: { bas >> jsonRoot; } - catch (std::runtime_error e) + catch (std::runtime_error& e) { // deserialization failed. Record the reason and pass back an empty map for markup. status = LLCore::HttpStatus(499, std::string(e.what())); return result; @@ -625,7 +625,7 @@ LLSD HttpCoroJSONHandler::parseBody(LLCore::HttpResponse *response, bool &succes { bas >> jsonRoot; } - catch (std::runtime_error e) + catch (std::runtime_error&) { success = false; return LLSD(); diff --git a/indra/llmessage/lldatapacker.cpp b/indra/llmessage/lldatapacker.cpp index 3510f93805..6cf6af6437 100644 --- a/indra/llmessage/lldatapacker.cpp +++ b/indra/llmessage/lldatapacker.cpp @@ -186,7 +186,7 @@ BOOL LLDataPackerBinaryBuffer::packString(const std::string& value, const char * if (mWriteEnabled) { - htonmemcpy(mCurBufferp, value.c_str(), MVT_VARIABLE, length); + htolememcpy(mCurBufferp, value.c_str(), MVT_VARIABLE, length); } mCurBufferp += length; return success; @@ -213,12 +213,12 @@ BOOL LLDataPackerBinaryBuffer::packBinaryData(const U8 *value, S32 size, const c if (mWriteEnabled) { - htonmemcpy(mCurBufferp, &size, MVT_S32, 4); + htolememcpy(mCurBufferp, &size, MVT_S32, 4); } mCurBufferp += 4; if (mWriteEnabled) { - htonmemcpy(mCurBufferp, value, MVT_VARIABLE, size); + htolememcpy(mCurBufferp, value, MVT_VARIABLE, size); } mCurBufferp += size; return success; @@ -229,12 +229,12 @@ BOOL LLDataPackerBinaryBuffer::unpackBinaryData(U8 *value, S32 &size, const char { BOOL success = TRUE; success &= verifyLength(4, name); - htonmemcpy(&size, mCurBufferp, MVT_S32, 4); + htolememcpy(&size, mCurBufferp, MVT_S32, 4); mCurBufferp += 4; success &= verifyLength(size, name); if (success) { - htonmemcpy(value, mCurBufferp, MVT_VARIABLE, size); + htolememcpy(value, mCurBufferp, MVT_VARIABLE, size); mCurBufferp += size; } else @@ -253,7 +253,7 @@ BOOL LLDataPackerBinaryBuffer::packBinaryDataFixed(const U8 *value, S32 size, co if (mWriteEnabled) { - htonmemcpy(mCurBufferp, value, MVT_VARIABLE, size); + htolememcpy(mCurBufferp, value, MVT_VARIABLE, size); } mCurBufferp += size; return success; @@ -264,7 +264,7 @@ BOOL LLDataPackerBinaryBuffer::unpackBinaryDataFixed(U8 *value, S32 size, const { BOOL success = TRUE; success &= verifyLength(size, name); - htonmemcpy(value, mCurBufferp, MVT_VARIABLE, size); + htolememcpy(value, mCurBufferp, MVT_VARIABLE, size); mCurBufferp += size; return success; } @@ -302,7 +302,7 @@ BOOL LLDataPackerBinaryBuffer::packU16(const U16 value, const char *name) if (mWriteEnabled) { - htonmemcpy(mCurBufferp, &value, MVT_U16, 2); + htolememcpy(mCurBufferp, &value, MVT_U16, 2); } mCurBufferp += 2; return success; @@ -314,7 +314,7 @@ BOOL LLDataPackerBinaryBuffer::unpackU16(U16 &value, const char *name) BOOL success = TRUE; success &= verifyLength(sizeof(U16), name); - htonmemcpy(&value, mCurBufferp, MVT_U16, 2); + htolememcpy(&value, mCurBufferp, MVT_U16, 2); mCurBufferp += 2; return success; } @@ -327,7 +327,7 @@ BOOL LLDataPackerBinaryBuffer::packU32(const U32 value, const char *name) if (mWriteEnabled) { - htonmemcpy(mCurBufferp, &value, MVT_U32, 4); + htolememcpy(mCurBufferp, &value, MVT_U32, 4); } mCurBufferp += 4; return success; @@ -339,7 +339,7 @@ BOOL LLDataPackerBinaryBuffer::unpackU32(U32 &value, const char *name) BOOL success = TRUE; success &= verifyLength(sizeof(U32), name); - htonmemcpy(&value, mCurBufferp, MVT_U32, 4); + htolememcpy(&value, mCurBufferp, MVT_U32, 4); mCurBufferp += 4; return success; } @@ -352,7 +352,7 @@ BOOL LLDataPackerBinaryBuffer::packS32(const S32 value, const char *name) if (mWriteEnabled) { - htonmemcpy(mCurBufferp, &value, MVT_S32, 4); + htolememcpy(mCurBufferp, &value, MVT_S32, 4); } mCurBufferp += 4; return success; @@ -364,7 +364,7 @@ BOOL LLDataPackerBinaryBuffer::unpackS32(S32 &value, const char *name) BOOL success = TRUE; success &= verifyLength(sizeof(S32), name); - htonmemcpy(&value, mCurBufferp, MVT_S32, 4); + htolememcpy(&value, mCurBufferp, MVT_S32, 4); mCurBufferp += 4; return success; } @@ -377,7 +377,7 @@ BOOL LLDataPackerBinaryBuffer::packF32(const F32 value, const char *name) if (mWriteEnabled) { - htonmemcpy(mCurBufferp, &value, MVT_F32, 4); + htolememcpy(mCurBufferp, &value, MVT_F32, 4); } mCurBufferp += 4; return success; @@ -389,7 +389,7 @@ BOOL LLDataPackerBinaryBuffer::unpackF32(F32 &value, const char *name) BOOL success = TRUE; success &= verifyLength(sizeof(F32), name); - htonmemcpy(&value, mCurBufferp, MVT_F32, 4); + htolememcpy(&value, mCurBufferp, MVT_F32, 4); mCurBufferp += 4; return success; } @@ -402,7 +402,7 @@ BOOL LLDataPackerBinaryBuffer::packColor4(const LLColor4 &value, const char *nam if (mWriteEnabled) { - htonmemcpy(mCurBufferp, value.mV, MVT_LLVector4, 16); + htolememcpy(mCurBufferp, value.mV, MVT_LLVector4, 16); } mCurBufferp += 16; return success; @@ -414,7 +414,7 @@ BOOL LLDataPackerBinaryBuffer::unpackColor4(LLColor4 &value, const char *name) BOOL success = TRUE; success &= verifyLength(16, name); - htonmemcpy(value.mV, mCurBufferp, MVT_LLVector4, 16); + htolememcpy(value.mV, mCurBufferp, MVT_LLVector4, 16); mCurBufferp += 16; return success; } @@ -427,7 +427,7 @@ BOOL LLDataPackerBinaryBuffer::packColor4U(const LLColor4U &value, const char *n if (mWriteEnabled) { - htonmemcpy(mCurBufferp, value.mV, MVT_VARIABLE, 4); + htolememcpy(mCurBufferp, value.mV, MVT_VARIABLE, 4); } mCurBufferp += 4; return success; @@ -439,7 +439,7 @@ BOOL LLDataPackerBinaryBuffer::unpackColor4U(LLColor4U &value, const char *name) BOOL success = TRUE; success &= verifyLength(4, name); - htonmemcpy(value.mV, mCurBufferp, MVT_VARIABLE, 4); + htolememcpy(value.mV, mCurBufferp, MVT_VARIABLE, 4); mCurBufferp += 4; return success; } @@ -453,8 +453,8 @@ BOOL LLDataPackerBinaryBuffer::packVector2(const LLVector2 &value, const char *n if (mWriteEnabled) { - htonmemcpy(mCurBufferp, &value.mV[0], MVT_F32, 4); - htonmemcpy(mCurBufferp+4, &value.mV[1], MVT_F32, 4); + htolememcpy(mCurBufferp, &value.mV[0], MVT_F32, 4); + htolememcpy(mCurBufferp+4, &value.mV[1], MVT_F32, 4); } mCurBufferp += 8; return success; @@ -466,8 +466,8 @@ BOOL LLDataPackerBinaryBuffer::unpackVector2(LLVector2 &value, const char *name) BOOL success = TRUE; success &= verifyLength(8, name); - htonmemcpy(&value.mV[0], mCurBufferp, MVT_F32, 4); - htonmemcpy(&value.mV[1], mCurBufferp+4, MVT_F32, 4); + htolememcpy(&value.mV[0], mCurBufferp, MVT_F32, 4); + htolememcpy(&value.mV[1], mCurBufferp+4, MVT_F32, 4); mCurBufferp += 8; return success; } @@ -480,7 +480,7 @@ BOOL LLDataPackerBinaryBuffer::packVector3(const LLVector3 &value, const char *n if (mWriteEnabled) { - htonmemcpy(mCurBufferp, value.mV, MVT_LLVector3, 12); + htolememcpy(mCurBufferp, value.mV, MVT_LLVector3, 12); } mCurBufferp += 12; return success; @@ -492,7 +492,7 @@ BOOL LLDataPackerBinaryBuffer::unpackVector3(LLVector3 &value, const char *name) BOOL success = TRUE; success &= verifyLength(12, name); - htonmemcpy(value.mV, mCurBufferp, MVT_LLVector3, 12); + htolememcpy(value.mV, mCurBufferp, MVT_LLVector3, 12); mCurBufferp += 12; return success; } @@ -504,7 +504,7 @@ BOOL LLDataPackerBinaryBuffer::packVector4(const LLVector4 &value, const char *n if (mWriteEnabled) { - htonmemcpy(mCurBufferp, value.mV, MVT_LLVector4, 16); + htolememcpy(mCurBufferp, value.mV, MVT_LLVector4, 16); } mCurBufferp += 16; return success; @@ -516,7 +516,7 @@ BOOL LLDataPackerBinaryBuffer::unpackVector4(LLVector4 &value, const char *name) BOOL success = TRUE; success &= verifyLength(16, name); - htonmemcpy(value.mV, mCurBufferp, MVT_LLVector4, 16); + htolememcpy(value.mV, mCurBufferp, MVT_LLVector4, 16); mCurBufferp += 16; return success; } @@ -528,7 +528,7 @@ BOOL LLDataPackerBinaryBuffer::packUUID(const LLUUID &value, const char *name) if (mWriteEnabled) { - htonmemcpy(mCurBufferp, value.mData, MVT_LLUUID, 16); + htolememcpy(mCurBufferp, value.mData, MVT_LLUUID, 16); } mCurBufferp += 16; return success; @@ -540,7 +540,7 @@ BOOL LLDataPackerBinaryBuffer::unpackUUID(LLUUID &value, const char *name) BOOL success = TRUE; success &= verifyLength(16, name); - htonmemcpy(value.mData, mCurBufferp, MVT_LLUUID, 16); + htolememcpy(value.mData, mCurBufferp, MVT_LLUUID, 16); mCurBufferp += 16; return success; } diff --git a/indra/llmessage/lldispatcher.cpp b/indra/llmessage/lldispatcher.cpp index c40fe0d389..717ef10f70 100644 --- a/indra/llmessage/lldispatcher.cpp +++ b/indra/llmessage/lldispatcher.cpp @@ -101,48 +101,70 @@ LLDispatchHandler* LLDispatcher::addHandler( // static bool LLDispatcher::unpackMessage( - LLMessageSystem* msg, - LLDispatcher::key_t& method, - LLUUID& invoice, - LLDispatcher::sparam_t& parameters) + LLMessageSystem* msg, + LLDispatcher::key_t& method, + LLUUID& invoice, + LLDispatcher::sparam_t& parameters) { - char buf[MAX_STRING]; /*Flawfinder: ignore*/ - msg->getStringFast(_PREHASH_MethodData, _PREHASH_Method, method); - msg->getUUIDFast(_PREHASH_MethodData, _PREHASH_Invoice, invoice); - S32 size; - S32 count = msg->getNumberOfBlocksFast(_PREHASH_ParamList); - for (S32 i = 0; i < count; ++i) - { - // we treat the SParam as binary data (since it might be an - // LLUUID in compressed form which may have embedded \0's,) - size = msg->getSizeFast(_PREHASH_ParamList, i, _PREHASH_Parameter); - if (size >= 0) - { - msg->getBinaryDataFast( - _PREHASH_ParamList, _PREHASH_Parameter, - buf, size, i, MAX_STRING-1); + char buf[MAX_STRING]; /*Flawfinder: ignore*/ + msg->getStringFast(_PREHASH_MethodData, _PREHASH_Method, method); + msg->getUUIDFast(_PREHASH_MethodData, _PREHASH_Invoice, invoice); + S32 size; + S32 count = msg->getNumberOfBlocksFast(_PREHASH_ParamList); + for (S32 i = 0; i < count; ++i) + { + // we treat the SParam as binary data (since it might be an + // LLUUID in compressed form which may have embedded \0's,) + size = msg->getSizeFast(_PREHASH_ParamList, i, _PREHASH_Parameter); + if (size >= 0) + { + msg->getBinaryDataFast( + _PREHASH_ParamList, _PREHASH_Parameter, + buf, size, i, MAX_STRING - 1); - // If the last byte of the data is 0x0, this is either a normally - // packed string, or a binary packed UUID (which for these messages - // are packed with a 17th byte 0x0). Unpack into a std::string - // without the trailing \0, so "abc\0" becomes std::string("abc", 3) - // which matches const char* "abc". - if (size > 0 - && buf[size-1] == 0x0) - { - // special char*/size constructor because UUIDs may have embedded - // 0x0 bytes. - std::string binary_data(buf, size-1); - parameters.push_back(binary_data); - } - else - { - // This is either a NULL string, or a string that was packed - // incorrectly as binary data, without the usual trailing '\0'. - std::string string_data(buf, size); - parameters.push_back(string_data); - } - } - } - return true; + // If the last byte of the data is 0x0, this is either a normally + // packed string, or a binary packed UUID (which for these messages + // are packed with a 17th byte 0x0). Unpack into a std::string + // without the trailing \0, so "abc\0" becomes std::string("abc", 3) + // which matches const char* "abc". + if (size > 0 + && buf[size - 1] == 0x0) + { + // special char*/size constructor because UUIDs may have embedded + // 0x0 bytes. + std::string binary_data(buf, size - 1); + parameters.push_back(binary_data); + } + else + { + // This is either a NULL string, or a string that was packed + // incorrectly as binary data, without the usual trailing '\0'. + std::string string_data(buf, size); + parameters.push_back(string_data); + } + } + } + return true; +} + +// static +bool LLDispatcher::unpackLargeMessage( + LLMessageSystem* msg, + LLDispatcher::key_t& method, + LLUUID& invoice, + LLDispatcher::sparam_t& parameters) +{ + msg->getStringFast(_PREHASH_MethodData, _PREHASH_Method, method); + msg->getUUIDFast(_PREHASH_MethodData, _PREHASH_Invoice, invoice); + S32 count = msg->getNumberOfBlocksFast(_PREHASH_ParamList); + for (S32 i = 0; i < count; ++i) + { + // This method treats all Parameter List params as strings and unpacks + // them regardless of length. If there is binary data it is the callers + // responsibility to decode it. + std::string param; + msg->getStringFast(_PREHASH_ParamList, _PREHASH_Parameter, param, i); + parameters.push_back(param); + } + return true; } diff --git a/indra/llmessage/lldispatcher.h b/indra/llmessage/lldispatcher.h index 9d1751f588..43c63ac4df 100644 --- a/indra/llmessage/lldispatcher.h +++ b/indra/llmessage/lldispatcher.h @@ -105,6 +105,12 @@ public: LLUUID& invoice, sparam_t& parameters); + static bool unpackLargeMessage( + LLMessageSystem* msg, + key_t& method, + LLUUID& invoice, + sparam_t& parameters); + protected: typedef std::map<key_t, LLDispatchHandler*> dispatch_map_t; dispatch_map_t mHandlers; diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index aa7b3c1260..db22ad2ea3 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -85,15 +85,15 @@ const F64 LLExperienceCache::DEFAULT_EXPIRATION = 600.0; const S32 LLExperienceCache::DEFAULT_QUOTA = 128; // this is megabytes const int LLExperienceCache::SEARCH_PAGE_SIZE = 30; +bool LLExperienceCache::sShutdown = false; + //========================================================================= -LLExperienceCache::LLExperienceCache(): - mShutdown(false) +LLExperienceCache::LLExperienceCache() { } LLExperienceCache::~LLExperienceCache() { - } void LLExperienceCache::initSingleton() @@ -108,6 +108,8 @@ void LLExperienceCache::initSingleton() cache_stream >> (*this); } + LLCoprocedureManager::instance().initializePool("ExpCache"); + LLCoros::instance().launch("LLExperienceCache::idleCoro", boost::bind(&LLExperienceCache::idleCoro, this)); @@ -122,7 +124,7 @@ void LLExperienceCache::cleanup() { cache_stream << (*this); } - mShutdown = true; + sShutdown = true; } //------------------------------------------------------------------------- @@ -338,13 +340,13 @@ void LLExperienceCache::requestExperiences() F64 now = LLFrameTimer::getTotalSeconds(); const U32 EXP_URL_SEND_THRESHOLD = 3000; - const U32 PAGE_SIZE = EXP_URL_SEND_THRESHOLD / UUID_STR_LENGTH; + const U32 PAGE_SIZE1 = EXP_URL_SEND_THRESHOLD / UUID_STR_LENGTH; std::ostringstream ostr; - ostr << urlBase << "?page_size=" << PAGE_SIZE; + ostr << urlBase << "?page_size=" << PAGE_SIZE1; RequestQueue_t requests; - while (!mRequestQueue.empty()) + while (!mRequestQueue.empty() && !sShutdown) { RequestQueue_t::iterator it = mRequestQueue.begin(); LLUUID key = (*it); @@ -360,7 +362,7 @@ void LLExperienceCache::requestExperiences() boost::bind(&LLExperienceCache::requestExperiencesCoro, this, _1, ostr.str(), requests) ); ostr.str(std::string()); - ostr << urlBase << "?page_size=" << PAGE_SIZE; + ostr << urlBase << "?page_size=" << PAGE_SIZE1; requests.clear(); } } @@ -398,8 +400,6 @@ void LLExperienceCache::idleCoro() LL_INFOS("ExperienceCache") << "Launching Experience cache idle coro." << LL_ENDL; do { - llcoro::suspendUntilTimeout(SECS_BETWEEN_REQUESTS); - if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) { eraseExpired(); @@ -410,7 +410,9 @@ void LLExperienceCache::idleCoro() requestExperiences(); } - } while (!mShutdown); + llcoro::suspendUntilTimeout(SECS_BETWEEN_REQUESTS); + + } while (!sShutdown); // 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.) diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index f9ff69c2b6..1c97133723 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -142,7 +142,7 @@ private: LLFrameTimer mEraseExpiredTimer; // Periodically clean out expired entries from the cache CapabilityQuery_t mCapability; std::string mCacheFileName; - bool mShutdown; + static bool sShutdown; // control for coroutines, they exist out of LLExperienceCache's scope, so they need a static control void idleCoro(); void eraseExpired(); diff --git a/indra/llmessage/llextendedstatus.h b/indra/llmessage/llextendedstatus.h index 8ce173d1ff..9923d73c1a 100644 --- a/indra/llmessage/llextendedstatus.h +++ b/indra/llmessage/llextendedstatus.h @@ -28,40 +28,36 @@ #ifndef LL_LLEXTENDEDSTATUS_H #define LL_LLEXTENDEDSTATUS_H - -typedef S32 LLExtStat; - - -// Status provider groups - Top bits indicate which status type it is -// Zero is common status code (next section) -const LLExtStat LL_EXSTAT_CURL_RESULT = 1L<<30; // serviced by curl - use 1L if we really implement the below -const LLExtStat LL_EXSTAT_RES_RESULT = 2L<<30; // serviced by resident copy -const LLExtStat LL_EXSTAT_VFS_RESULT = 3L<<30; // serviced by vfs - - -// Common Status Codes -// -const LLExtStat LL_EXSTAT_NONE = 0x00000; // No extra info here - sorry! -const LLExtStat LL_EXSTAT_NULL_UUID = 0x10001; // null asset ID -const LLExtStat LL_EXSTAT_NO_UPSTREAM = 0x10002; // attempt to upload without a valid upstream method/provider -const LLExtStat LL_EXSTAT_REQUEST_DROPPED = 0x10003; // request was dropped unserviced -const LLExtStat LL_EXSTAT_NONEXISTENT_FILE = 0x10004; // trying to upload a file that doesn't exist -const LLExtStat LL_EXSTAT_BLOCKED_FILE = 0x10005; // trying to upload a file that we can't open - - -// curl status codes: -// -// Mask off LL_EXSTAT_CURL_RESULT for original result and -// see: libraries/include/curl/curl.h - - -// Memory-Resident status codes: -// None at present - - -// VFS status codes: -const LLExtStat LL_EXSTAT_VFS_CACHED = LL_EXSTAT_VFS_RESULT | 0x0001; -const LLExtStat LL_EXSTAT_VFS_CORRUPT = LL_EXSTAT_VFS_RESULT | 0x0002; +enum class LLExtStat: uint32_t +{ + // Status provider groups - Top bits indicate which status type it is + // Zero is common status code (next section) + CURL_RESULT = 1UL<<30, // serviced by curl - use 1L if we really implement the below + RES_RESULT = 2UL<<30, // serviced by resident copy + VFS_RESULT = 3UL<<30, // serviced by vfs + + + // Common Status Codes + // + NONE = 0x00000, // No extra info here - sorry! + NULL_UUID = 0x10001, // null asset ID + NO_UPSTREAM = 0x10002, // attempt to upload without a valid upstream method/provider + REQUEST_DROPPED = 0x10003, // request was dropped unserviced + NONEXISTENT_FILE= 0x10004, // trying to upload a file that doesn't exist + BLOCKED_FILE = 0x10005, // trying to upload a file that we can't open + + // curl status codes: + // + // Mask off CURL_RESULT for original result and + // see: libraries/include/curl/curl.h + + // Memory-Resident status codes: + // None at present + + // VFS status codes: + VFS_CACHED = VFS_RESULT | 0x0001, + VFS_CORRUPT = VFS_RESULT | 0x0002, +}; #endif // LL_LLEXTENDEDSTATUS_H diff --git a/indra/llmessage/llhttpnode.cpp b/indra/llmessage/llhttpnode.cpp index 04b34a296c..6fd17c9154 100644 --- a/indra/llmessage/llhttpnode.cpp +++ b/indra/llmessage/llhttpnode.cpp @@ -125,7 +125,7 @@ void LLHTTPNode::get(LLHTTPNode::ResponsePtr response, const LLSD& context) cons { response->result(simpleGet()); } - catch (NotImplemented) + catch (NotImplemented&) { response->methodNotAllowed(); } @@ -138,7 +138,7 @@ void LLHTTPNode::put(LLHTTPNode::ResponsePtr response, const LLSD& context, cons { response->result(simplePut(input)); } - catch (NotImplemented) + catch (NotImplemented&) { response->methodNotAllowed(); } @@ -151,7 +151,7 @@ void LLHTTPNode::post(LLHTTPNode::ResponsePtr response, const LLSD& context, con { response->result(simplePost(input)); } - catch (NotImplemented) + catch (NotImplemented&) { response->methodNotAllowed(); } @@ -164,7 +164,7 @@ void LLHTTPNode::del(LLHTTPNode::ResponsePtr response, const LLSD& context) cons { response->result(simpleDel(context)); } - catch (NotImplemented) + catch (NotImplemented&) { response->methodNotAllowed(); } diff --git a/indra/llmessage/lliosocket.cpp b/indra/llmessage/lliosocket.cpp index 7caf0766b7..a9cc71c365 100644 --- a/indra/llmessage/lliosocket.cpp +++ b/indra/llmessage/lliosocket.cpp @@ -62,9 +62,9 @@ bool is_addr_in_use(apr_status_t status) #endif } -#if LL_LINUX +#if ! LL_WINDOWS // Define this to see the actual file descriptors being tossed around. -//#define LL_DEBUG_SOCKET_FILE_DESCRIPTORS 1 +#define LL_DEBUG_SOCKET_FILE_DESCRIPTORS 1 #if LL_DEBUG_SOCKET_FILE_DESCRIPTORS #include "apr_portable.h" #endif @@ -77,7 +77,7 @@ void ll_debug_socket(const char* msg, apr_socket_t* apr_sock) #if LL_DEBUG_SOCKET_FILE_DESCRIPTORS if(!apr_sock) { - LL_DEBUGS() << "Socket -- " << (msg?msg:"") << ": no socket." << LL_ENDL; + LL_DEBUGS("Socket") << "Socket -- " << (msg?msg:"") << ": no socket." << LL_ENDL; return; } // *TODO: Why doesn't this work? @@ -85,12 +85,12 @@ void ll_debug_socket(const char* msg, apr_socket_t* apr_sock) int os_sock; if(APR_SUCCESS == apr_os_sock_get(&os_sock, apr_sock)) { - LL_DEBUGS() << "Socket -- " << (msg?msg:"") << " on fd " << os_sock + LL_DEBUGS("Socket") << "Socket -- " << (msg?msg:"") << " on fd " << os_sock << " at " << apr_sock << LL_ENDL; } else { - LL_DEBUGS() << "Socket -- " << (msg?msg:"") << " no fd " + LL_DEBUGS("Socket") << "Socket -- " << (msg?msg:"") << " no fd " << " at " << apr_sock << LL_ENDL; } #endif @@ -144,6 +144,9 @@ LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port, const c if(new_pool) apr_pool_destroy(new_pool); return rv; } + // At this point, the new LLSocket instance takes ownership of new_pool, + // which is why no early return below this call explicitly destroys it: it + // is instead cleaned up by ~LLSocket(). rv = ptr_t(new LLSocket(socket, new_pool)); if(port > 0) { @@ -186,7 +189,7 @@ LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port, const c } } } - else + else // port <= 0 { // we need to indicate that we have an ephemeral port if the // previous calls were successful. It will diff --git a/indra/llmessage/llmail.cpp b/indra/llmessage/llmail.cpp index 134154aa6c..fcda2a85f6 100644 --- a/indra/llmessage/llmail.cpp +++ b/indra/llmessage/llmail.cpp @@ -373,7 +373,7 @@ std::string LLMail::encryptIMEmailAddress(const LLUUID& from_agent_id, // Convert input data into a binary blob std::vector<U8> data; data.resize(data_size); - // *NOTE: This may suffer from endian issues. Could be htonmemcpy. + // *NOTE: This may suffer from endian issues. Could be htolememcpy. memcpy(&data[0], &time, 4); memcpy(&data[4], &from_agent_id.mData[0], UUID_BYTES); memcpy(&data[4 + UUID_BYTES], &to_agent_id.mData[0], UUID_BYTES); diff --git a/indra/llmessage/llmessagetemplate.cpp b/indra/llmessage/llmessagetemplate.cpp index c4c7e66703..e70e259436 100644 --- a/indra/llmessage/llmessagetemplate.cpp +++ b/indra/llmessage/llmessagetemplate.cpp @@ -47,7 +47,7 @@ void LLMsgVarData::addData(const void *data, S32 size, EMsgVariableType type, S3 { delete[] mData; // Delete it if it already exists mData = new U8[size]; - htonmemcpy(mData, data, mType, size); + htolememcpy(mData, data, mType, size); } } diff --git a/indra/llmessage/llproxy.cpp b/indra/llmessage/llproxy.cpp index dea03aab85..749e599c66 100644 --- a/indra/llmessage/llproxy.cpp +++ b/indra/llmessage/llproxy.cpp @@ -40,6 +40,7 @@ // incoming packet just to do a simple bool test. The getter for this // member is also static bool LLProxy::sUDPProxyEnabled = false; +LLProxy* LLProxy::sProxyInstance = NULL; // Some helpful TCP static functions. static apr_status_t tcp_blocking_handshake(LLSocket::ptr_t handle, char * dataout, apr_size_t outlen, char * datain, apr_size_t maxinlen); // Do a TCP data handshake @@ -60,11 +61,21 @@ LLProxy::LLProxy(): LLProxy::~LLProxy() { - if (ll_apr_is_initialized()) - { - stopSOCKSProxy(); - disableHTTPProxy(); - } + if (ll_apr_is_initialized()) + { + // locks mutex + stopSOCKSProxy(); + disableHTTPProxy(); + } + // The primary safety of sProxyInstance is the fact that by the + // point SUBSYSTEM_CLEANUP(LLProxy) gets called, nothing should + // be capable of using proxy + sProxyInstance = NULL; +} + +void LLProxy::initSingleton() +{ + sProxyInstance = this; } /** @@ -115,9 +126,9 @@ S32 LLProxy::proxyHandshake(LLHost proxy) U32 request_size = socks_username.size() + socks_password.size() + 3; char * password_auth = new char[request_size]; password_auth[0] = 0x01; - password_auth[1] = socks_username.size(); + password_auth[1] = (char)(socks_username.size()); memcpy(&password_auth[2], socks_username.c_str(), socks_username.size()); - password_auth[socks_username.size() + 2] = socks_password.size(); + password_auth[socks_username.size() + 2] = (char)(socks_password.size()); memcpy(&password_auth[socks_username.size() + 3], socks_password.c_str(), socks_password.size()); authmethod_password_reply_t password_reply; @@ -403,8 +414,11 @@ LLSocks5AuthType LLProxy::getSelectedAuthMethod() const //static void LLProxy::cleanupClass() { - getInstance()->stopSOCKSProxy(); - deleteSingleton(); + if (instanceExists()) + { + getInstance()->stopSOCKSProxy(); + deleteSingleton(); + } } /** @@ -421,28 +435,28 @@ void LLProxy::cleanupClass() void LLProxy::applyProxySettings(CURL* handle) { // Do a faster unlocked check to see if we are supposed to proxy. - if (mHTTPProxyEnabled) + if (sProxyInstance && sProxyInstance->mHTTPProxyEnabled) { - // We think we should proxy, lock the proxy mutex. - LLMutexLock lock(&mProxyMutex); + // We think we should proxy, lock the proxy mutex. sProxyInstance is not protected by mutex + LLMutexLock lock(&sProxyInstance->mProxyMutex); // Now test again to verify that the proxy wasn't disabled between the first check and the lock. - if (mHTTPProxyEnabled) + if (sProxyInstance->mHTTPProxyEnabled) { - LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXY, mHTTPProxy.getIPString().c_str()), CURLOPT_PROXY); - LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, mHTTPProxy.getPort()), CURLOPT_PROXYPORT); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXY, sProxyInstance->mHTTPProxy.getIPString().c_str()), CURLOPT_PROXY); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYPORT, sProxyInstance->mHTTPProxy.getPort()), CURLOPT_PROXYPORT); - if (mProxyType == LLPROXY_SOCKS) + if (sProxyInstance->mProxyType == LLPROXY_SOCKS) { - LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5), CURLOPT_PROXYTYPE); - if (mAuthMethodSelected == METHOD_PASSWORD) + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5), CURLOPT_PROXYTYPE); + if (sProxyInstance->mAuthMethodSelected == METHOD_PASSWORD) { - std::string auth_string = mSocksUsername + ":" + mSocksPassword; - LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str()), CURLOPT_PROXYUSERPWD); + std::string auth_string = sProxyInstance->mSocksUsername + ":" + sProxyInstance->mSocksPassword; + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, auth_string.c_str()), CURLOPT_PROXYUSERPWD); } } else { - LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP), CURLOPT_PROXYTYPE); + LLCore::LLHttp::check_curl_code(curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP), CURLOPT_PROXYTYPE); } } } diff --git a/indra/llmessage/llproxy.h b/indra/llmessage/llproxy.h index 87891901ad..25f6977e14 100644 --- a/indra/llmessage/llproxy.h +++ b/indra/llmessage/llproxy.h @@ -32,6 +32,7 @@ #include "llmemory.h" #include "llsingleton.h" #include "llthread.h" +#include "llmutex.h" #include <curl/curl.h> #include <string> @@ -225,6 +226,8 @@ class LLProxy: public LLSingleton<LLProxy> LLSINGLETON(LLProxy); LOG_CLASS(LLProxy); + /*virtual*/ void initSingleton(); + public: // Static check for enabled status for UDP packets. Call from main thread only. static bool isSOCKSProxyEnabled() { return sUDPProxyEnabled; } @@ -250,7 +253,7 @@ public: // Apply the current proxy settings to a curl request. Doesn't do anything if mHTTPProxyEnabled is false. // Safe to call from any thread. - void applyProxySettings(CURL* handle); + static void applyProxySettings(CURL* handle); // Start a connection to the SOCKS 5 proxy. Call from main thread only. S32 startSOCKSProxy(LLHost host); @@ -343,6 +346,10 @@ private: /*########################################################################################### END OF SHARED MEMBERS ###########################################################################################*/ + + // A hack to get arround getInstance() and capture_dependency() which are unsafe to use inside threads + // set/reset on init/cleanup, strictly for use in applyProxySettings + static LLProxy* sProxyInstance; }; #endif diff --git a/indra/llmessage/llregionflags.h b/indra/llmessage/llregionflags.h index e1ccd333f1..c13f39df9b 100644 --- a/indra/llmessage/llregionflags.h +++ b/indra/llmessage/llregionflags.h @@ -54,6 +54,9 @@ const U64 REGION_FLAGS_BLOCK_LAND_RESELL = (1 << 7); // All content wiped once per night const U64 REGION_FLAGS_SANDBOX = (1 << 8); + +const U64 REGION_FLAGS_ALLOW_ENVIRONMENT_OVERRIDE = (1 << 9); + const U64 REGION_FLAGS_SKIP_COLLISIONS = (1 << 12); // Pin all non agent rigid bodies const U64 REGION_FLAGS_SKIP_SCRIPTS = (1 << 13); const U64 REGION_FLAGS_SKIP_PHYSICS = (1 << 14); // Skip all physics diff --git a/indra/llmessage/llteleportflags.h b/indra/llmessage/llteleportflags.h index b3fcad036e..fd1e702832 100644 --- a/indra/llmessage/llteleportflags.h +++ b/indra/llmessage/llteleportflags.h @@ -44,6 +44,8 @@ const U32 TELEPORT_FLAGS_VIA_REGION_ID = 1 << 12; const U32 TELEPORT_FLAGS_IS_FLYING = 1 << 13; const U32 TELEPORT_FLAGS_SHOW_RESET_HOME = 1 << 14; const U32 TELEPORT_FLAGS_FORCE_REDIRECT = 1 << 15; // used to force a redirect to some random location - used when kicking someone from land. +const U32 TELEPORT_FLAGS_VIA_GLOBAL_COORDS = 1 << 16; +const U32 TELEPORT_FLAGS_WITHIN_REGION = 1 << 17; const U32 TELEPORT_FLAGS_MASK_VIA = TELEPORT_FLAGS_VIA_LURE | TELEPORT_FLAGS_VIA_LANDMARK diff --git a/indra/llmessage/lltemplatemessagebuilder.cpp b/indra/llmessage/lltemplatemessagebuilder.cpp index 8d7c4c0282..5ac5f6c580 100644 --- a/indra/llmessage/lltemplatemessagebuilder.cpp +++ b/indra/llmessage/lltemplatemessagebuilder.cpp @@ -689,14 +689,14 @@ static S32 buildBlock(U8* buffer, S32 buffer_size, const LLMessageBlock* templat { case 1: sizeb = size; - htonmemcpy(&buffer[result], &sizeb, MVT_U8, 1); + htolememcpy(&buffer[result], &sizeb, MVT_U8, 1); break; case 2: sizeh = size; - htonmemcpy(&buffer[result], &sizeh, MVT_U16, 2); + htolememcpy(&buffer[result], &sizeh, MVT_U16, 2); break; case 4: - htonmemcpy(&buffer[result], &size, MVT_S32, 4); + htolememcpy(&buffer[result], &size, MVT_S32, 4); break; default: LL_ERRS() << "Attempting to build variable field with unknown size of " << size << LL_ENDL; diff --git a/indra/llmessage/lltemplatemessagereader.cpp b/indra/llmessage/lltemplatemessagereader.cpp index 4e0c53c37e..6d5ad0ba08 100644 --- a/indra/llmessage/lltemplatemessagereader.cpp +++ b/indra/llmessage/lltemplatemessagereader.cpp @@ -645,15 +645,15 @@ BOOL LLTemplateMessageReader::decodeData(const U8* buffer, const LLHost& sender switch(data_size) { case 1: - htonmemcpy(&tsizeb, &buffer[decode_pos], MVT_U8, 1); + htolememcpy(&tsizeb, &buffer[decode_pos], MVT_U8, 1); tsize = tsizeb; break; case 2: - htonmemcpy(&tsizeh, &buffer[decode_pos], MVT_U16, 2); + htolememcpy(&tsizeh, &buffer[decode_pos], MVT_U16, 2); tsize = tsizeh; break; case 4: - htonmemcpy(&tsize, &buffer[decode_pos], MVT_U32, 4); + htolememcpy(&tsize, &buffer[decode_pos], MVT_U32, 4); break; default: LL_ERRS() << "Attempting to read variable field with unknown size of " << data_size << LL_ENDL; diff --git a/indra/llmessage/lltransfertargetvfile.cpp b/indra/llmessage/lltransfertargetvfile.cpp index a572c68a7f..b27f0881e0 100644 --- a/indra/llmessage/lltransfertargetvfile.cpp +++ b/indra/llmessage/lltransfertargetvfile.cpp @@ -227,7 +227,7 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status) mParams.getAssetID(), mParams.getAssetType(), mParams.mRequestDatap, - LL_EXSTAT_NONE); + LLExtStat::NONE); } delete mParams.mRequestDatap; mParams.mRequestDatap = NULL; diff --git a/indra/llmessage/llxfer.cpp b/indra/llmessage/llxfer.cpp index c8b9d5d19f..93d5cfc131 100644 --- a/indra/llmessage/llxfer.cpp +++ b/indra/llmessage/llxfer.cpp @@ -245,7 +245,7 @@ void LLXfer::sendPacket(S32 packet_num) num_copy); } fdata_size += sizeof(S32); - htonmemcpy(fdata_buf,&mXferSize, MVT_S32, sizeof(S32)); + htolememcpy(fdata_buf,&mXferSize, MVT_S32, sizeof(S32)); } S32 encoded_packetnum = encodePacketNum(packet_num,last_packet); @@ -319,7 +319,7 @@ S32 LLXfer::processEOF() if (mCallback) { - mCallback(mCallbackDataHandle,mCallbackResult,LL_EXSTAT_NONE); + mCallback(mCallbackDataHandle,mCallbackResult, LLExtStat::NONE); } return(retval); diff --git a/indra/llmessage/llxfer_mem.cpp b/indra/llmessage/llxfer_mem.cpp index 78a3e4f558..da8534ecdc 100644 --- a/indra/llmessage/llxfer_mem.cpp +++ b/indra/llmessage/llxfer_mem.cpp @@ -112,7 +112,7 @@ S32 LLXfer_Mem::processEOF() if (mCallback) { - mCallback((void *)mBuffer,mBufferLength,mCallbackDataHandle,mCallbackResult,LL_EXSTAT_NONE); + mCallback((void *)mBuffer,mBufferLength,mCallbackDataHandle,mCallbackResult, LLExtStat::NONE); } return(retval); diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 6ef4025ab1..da62bb12e8 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -117,8 +117,8 @@ void LLMessageHandlerBridge::post(LLHTTPNode::ResponsePtr response, gMessageSystem->mLastSender = LLHost(input["sender"].asString()); gMessageSystem->mPacketsIn += 1; gMessageSystem->mLLSDMessageReader->setMessage(namePtr, input["body"]); - gMessageSystem->mMessageReader = gMessageSystem->mLLSDMessageReader; - + LockMessageReader rdr(gMessageSystem->mMessageReader, gMessageSystem->mLLSDMessageReader); + if(gMessageSystem->callHandler(namePtr, false, gMessageSystem)) { response->result(LLSD()); @@ -189,7 +189,7 @@ void LLMessageSystem::init() mTimingCallbackData = NULL; mMessageBuilder = NULL; - mMessageReader = NULL; + LockMessageReader(mMessageReader, NULL); } // Read file and build message templates @@ -230,7 +230,6 @@ LLMessageSystem::LLMessageSystem(const std::string& filename, U32 port, mTemplateMessageReader = new LLTemplateMessageReader(mMessageNumbers); mLLSDMessageReader = new LLSDMessageReader(); - mMessageReader = NULL; // initialize various bits of net info mSocket = 0; @@ -330,7 +329,6 @@ LLMessageSystem::~LLMessageSystem() delete mTemplateMessageReader; mTemplateMessageReader = NULL; - mMessageReader = NULL; delete mTemplateMessageBuilder; mTemplateMessageBuilder = NULL; @@ -480,11 +478,12 @@ LLCircuitData* LLMessageSystem::findCircuit(const LLHost& host, } // Returns TRUE if a valid, on-circuit message has been received. -BOOL LLMessageSystem::checkMessages( S64 frame_count ) +// Requiring a non-const LockMessageChecker reference ensures that +// mMessageReader has been set to mTemplateMessageReader. +BOOL LLMessageSystem::checkMessages(LockMessageChecker&, S64 frame_count ) { // Pump BOOL valid_packet = FALSE; - mMessageReader = mTemplateMessageReader; LLTransferTargetVFile::updateQueue(); @@ -748,7 +747,7 @@ S32 LLMessageSystem::getReceiveBytes() const } -void LLMessageSystem::processAcks(F32 collect_time) +void LLMessageSystem::processAcks(LockMessageChecker&, F32 collect_time) { F64Seconds mt_sec = getMessageTimeSeconds(); { @@ -2062,8 +2061,9 @@ void LLMessageSystem::dispatch( return; } // enable this for output of message names - //LL_INFOS("Messaging") << "< \"" << msg_name << "\"" << LL_ENDL; - //LL_DEBUGS() << "data: " << LLSDNotationStreamer(message) << LL_ENDL; + LL_DEBUGS("Messaging") << "< \"" << msg_name << "\"" << LL_ENDL; + LL_DEBUGS("Messaging") << "context: " << context << LL_ENDL; + LL_DEBUGS("Messaging") << "message: " << message << LL_ENDL; handler->post(responsep, context, message); } @@ -3268,6 +3268,8 @@ void null_message_callback(LLMessageSystem *msg, void **data) // up, and then sending auth messages. void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_count ) { + LockMessageChecker lmc(this); + std::string shared_secret = get_shared_secret(); if(shared_secret.empty()) { @@ -3287,7 +3289,7 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_ addU8Fast(_PREHASH_PingID, 0); addU32Fast(_PREHASH_OldestUnacked, 0); sendMessage(host); - if (checkMessages( frame_count )) + if (lmc.checkMessages( frame_count )) { if (isMessageFast(_PREHASH_CompletePingCheck) && (getSender() == host)) @@ -3295,7 +3297,7 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_ break; } } - processAcks(); + lmc.processAcks(); ms_sleep(1); } @@ -3314,8 +3316,8 @@ void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_ cdp = mCircuitInfo.findCircuit(host); if(!cdp) break; // no circuit anymore, no point continuing. if(cdp->getTrusted()) break; // circuit is trusted. - checkMessages(frame_count); - processAcks(); + lmc.checkMessages(frame_count); + lmc.processAcks(); ms_sleep(1); } } @@ -3973,11 +3975,18 @@ void LLMessageSystem::setTimeDecodesSpamThreshold( F32 seconds ) LLMessageReader::setTimeDecodesSpamThreshold(seconds); } +LockMessageChecker::LockMessageChecker(LLMessageSystem* msgsystem): + // for the lifespan of this LockMessageChecker instance, use + // LLTemplateMessageReader as msgsystem's mMessageReader + LockMessageReader(msgsystem->mMessageReader, msgsystem->mTemplateMessageReader), + mMessageSystem(msgsystem) +{} + // HACK! babbage: return true if message rxed via either UDP or HTTP // TODO: babbage: move gServicePump in to LLMessageSystem? -bool LLMessageSystem::checkAllMessages(S64 frame_count, LLPumpIO* http_pump) +bool LLMessageSystem::checkAllMessages(LockMessageChecker& lmc, S64 frame_count, LLPumpIO* http_pump) { - if(checkMessages(frame_count)) + if(lmc.checkMessages(frame_count)) { return true; } diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index f6c5d9e228..e25a9ea7ef 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -35,10 +35,6 @@ #include <netinet/in.h> #endif -#if LL_SOLARIS -#include <netinet/in.h> -#endif - #if LL_WINDOWS #include "winsock2.h" // htons etc. #endif @@ -61,6 +57,8 @@ #include "llstoredmessage.h" #include "boost/function.hpp" #include "llpounceable.h" +#include "llcoros.h" +#include LLCOROS_MUTEX_HEADER const U32 MESSAGE_MAX_STRINGS_LENGTH = 64; const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192; @@ -199,6 +197,91 @@ public: virtual void complete(const LLHost& host, const LLUUID& agent) const = 0; }; +/** + * SL-12204: We've observed crashes when consumer code sets + * LLMessageSystem::mMessageReader, assuming that all subsequent processing of + * the current message will use the same mMessageReader value -- only to have + * a different coroutine sneak in and replace mMessageReader before + * completion. This is a limitation of sharing a stateful global resource for + * message parsing; instead code receiving a new message should instantiate a + * (trivially constructed) local message parser and use that. + * + * Until then, when one coroutine sets a particular LLMessageReader subclass + * as the current message reader, ensure that no other coroutine can replace + * it until the first coroutine has finished with its message. + * + * This is achieved with two helper classes. LLMessageSystem::mMessageReader + * is now an LLMessageReaderPointer instance, which can efficiently compare or + * dereference its contained LLMessageReader* but which cannot be directly + * assigned. To change the value of LLMessageReaderPointer, you must + * instantiate LockMessageReader with the LLMessageReader* you wish to make + * current. mMessageReader will have that value for the lifetime of the + * LockMessageReader instance, then revert to nullptr. Moreover, as its name + * implies, LockMessageReader locks the mutex in LLMessageReaderPointer so + * that any other coroutine instantiating LockMessageReader will block until + * the first coroutine has destroyed its instance. + */ +class LLMessageReaderPointer +{ +public: + LLMessageReaderPointer(): mPtr(nullptr) {} + // It is essential that comparison and dereferencing must be fast, which + // is why we don't check for nullptr when dereferencing. + LLMessageReader* operator->() const { return mPtr; } + bool operator==(const LLMessageReader* other) const { return mPtr == other; } + bool operator!=(const LLMessageReader* other) const { return ! (*this == other); } +private: + // Only LockMessageReader can set mPtr. + friend class LockMessageReader; + LLMessageReader* mPtr; + LLCoros::Mutex mMutex; +}; + +/** + * To set mMessageReader to nullptr: + * + * @code + * // use an anonymous instance that is destroyed immediately + * LockMessageReader(gMessageSystem->mMessageReader, nullptr); + * @endcode + * + * Why do we still require going through LockMessageReader at all? Because it + * would be Bad if any coroutine set mMessageReader to nullptr while another + * coroutine was still parsing a message. + */ +class LockMessageReader +{ +public: + LockMessageReader(LLMessageReaderPointer& var, LLMessageReader* instance): + mVar(var.mPtr), + mLock(var.mMutex) + { + mVar = instance; + } + // Some compilers reportedly fail to suppress generating implicit copy + // operations even though we have a move-only LockType data member. + LockMessageReader(const LockMessageReader&) = delete; + LockMessageReader& operator=(const LockMessageReader&) = delete; + ~LockMessageReader() + { + mVar = nullptr; + } +private: + // capture a reference to LLMessageReaderPointer::mPtr + decltype(LLMessageReaderPointer::mPtr)& mVar; + // while holding a lock on LLMessageReaderPointer::mMutex + LLCoros::LockType mLock; +}; + +/** + * LockMessageReader is great as long as you only need mMessageReader locked + * during a single LLMessageSystem function call. However, empirically the + * sequence from checkAllMessages() through processAcks() need mMessageReader + * locked to LLTemplateMessageReader. Enforce that by making them require an + * instance of LockMessageChecker. + */ +class LockMessageChecker; + class LLMessageSystem : public LLMessageSenderInterface { private: @@ -331,8 +414,8 @@ public: bool addCircuitCode(U32 code, const LLUUID& session_id); BOOL poll(F32 seconds); // Number of seconds that we want to block waiting for data, returns if data was received - BOOL checkMessages( S64 frame_count = 0 ); - void processAcks(F32 collect_time = 0.f); + BOOL checkMessages(LockMessageChecker&, S64 frame_count = 0 ); + void processAcks(LockMessageChecker&, F32 collect_time = 0.f); BOOL isMessageFast(const char *msg); BOOL isMessage(const char *msg) @@ -730,7 +813,7 @@ public: const LLSD& data); // Check UDP messages and pump http_pump to receive HTTP messages. - bool checkAllMessages(S64 frame_count, LLPumpIO* http_pump); + bool checkAllMessages(LockMessageChecker&, S64 frame_count, LLPumpIO* http_pump); // Moved to allow access from LLTemplateMessageDispatcher void clearReceiveState(); @@ -817,12 +900,13 @@ private: LLMessageBuilder* mMessageBuilder; LLTemplateMessageBuilder* mTemplateMessageBuilder; LLSDMessageBuilder* mLLSDMessageBuilder; - LLMessageReader* mMessageReader; + LLMessageReaderPointer mMessageReader; LLTemplateMessageReader* mTemplateMessageReader; LLSDMessageReader* mLLSDMessageReader; friend class LLMessageHandlerBridge; - + friend class LockMessageChecker; + bool callHandler(const char *name, bool trustedSource, LLMessageSystem* msg); @@ -835,6 +919,40 @@ private: // external hook into messaging system extern LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem; +// Implementation of LockMessageChecker depends on definition of +// LLMessageSystem, hence must follow it. +class LockMessageChecker: public LockMessageReader +{ +public: + LockMessageChecker(LLMessageSystem* msgsystem); + + // For convenience, provide forwarding wrappers so you can call (e.g.) + // checkAllMessages() on your LockMessageChecker instance instead of + // passing the instance to LLMessageSystem::checkAllMessages(). Use + // perfect forwarding to avoid having to maintain these wrappers in sync + // with the target methods. + template <typename... ARGS> + bool checkAllMessages(ARGS&&... args) + { + return mMessageSystem->checkAllMessages(*this, std::forward<ARGS>(args)...); + } + + template <typename... ARGS> + bool checkMessages(ARGS&&... args) + { + return mMessageSystem->checkMessages(*this, std::forward<ARGS>(args)...); + } + + template <typename... ARGS> + void processAcks(ARGS&&... args) + { + return mMessageSystem->processAcks(*this, std::forward<ARGS>(args)...); + } + +private: + LLMessageSystem* mMessageSystem; +}; + // Must specific overall system version, which is used to determine // if a patch is available in the message template checksum verification. // Return true if able to initialize system. @@ -860,10 +978,10 @@ void null_message_callback(LLMessageSystem *msg, void **data); // #if !defined( LL_BIG_ENDIAN ) && !defined( LL_LITTLE_ENDIAN ) -#error Unknown endianness for htonmemcpy. Did you miss a common include? +#error Unknown endianness for htolememcpy. Did you miss a common include? #endif -static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, size_t n) +static inline void *htolememcpy(void *vs, const void *vct, EMsgVariableType type, size_t n) { char *s = (char *)vs; const char *ct = (const char *)vct; @@ -886,7 +1004,7 @@ static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, case MVT_S16: if (n != 2) { - LL_ERRS() << "Size argument passed to htonmemcpy doesn't match swizzle type size" << LL_ENDL; + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN *(s + 1) = *(ct); @@ -901,7 +1019,7 @@ static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, case MVT_F32: if (n != 4) { - LL_ERRS() << "Size argument passed to htonmemcpy doesn't match swizzle type size" << LL_ENDL; + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN *(s + 3) = *(ct); @@ -918,7 +1036,7 @@ static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, case MVT_F64: if (n != 8) { - LL_ERRS() << "Size argument passed to htonmemcpy doesn't match swizzle type size" << LL_ENDL; + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN *(s + 7) = *(ct); @@ -938,12 +1056,12 @@ static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, case MVT_LLQuaternion: // We only send x, y, z and infer w (we set x, y, z to ensure that w >= 0) if (n != 12) { - LL_ERRS() << "Size argument passed to htonmemcpy doesn't match swizzle type size" << LL_ENDL; + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN - htonmemcpy(s + 8, ct + 8, MVT_F32, 4); - htonmemcpy(s + 4, ct + 4, MVT_F32, 4); - return(htonmemcpy(s, ct, MVT_F32, 4)); + htolememcpy(s + 8, ct + 8, MVT_F32, 4); + htolememcpy(s + 4, ct + 4, MVT_F32, 4); + return(htolememcpy(s, ct, MVT_F32, 4)); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif @@ -951,12 +1069,12 @@ static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, case MVT_LLVector3d: if (n != 24) { - LL_ERRS() << "Size argument passed to htonmemcpy doesn't match swizzle type size" << LL_ENDL; + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN - htonmemcpy(s + 16, ct + 16, MVT_F64, 8); - htonmemcpy(s + 8, ct + 8, MVT_F64, 8); - return(htonmemcpy(s, ct, MVT_F64, 8)); + htolememcpy(s + 16, ct + 16, MVT_F64, 8); + htolememcpy(s + 8, ct + 8, MVT_F64, 8); + return(htolememcpy(s, ct, MVT_F64, 8)); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif @@ -964,13 +1082,13 @@ static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, case MVT_LLVector4: if (n != 16) { - LL_ERRS() << "Size argument passed to htonmemcpy doesn't match swizzle type size" << LL_ENDL; + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN - htonmemcpy(s + 12, ct + 12, MVT_F32, 4); - htonmemcpy(s + 8, ct + 8, MVT_F32, 4); - htonmemcpy(s + 4, ct + 4, MVT_F32, 4); - return(htonmemcpy(s, ct, MVT_F32, 4)); + htolememcpy(s + 12, ct + 12, MVT_F32, 4); + htolememcpy(s + 8, ct + 8, MVT_F32, 4); + htolememcpy(s + 4, ct + 4, MVT_F32, 4); + return(htolememcpy(s, ct, MVT_F32, 4)); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif @@ -978,12 +1096,12 @@ static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, case MVT_U16Vec3: if (n != 6) { - LL_ERRS() << "Size argument passed to htonmemcpy doesn't match swizzle type size" << LL_ENDL; + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN - htonmemcpy(s + 4, ct + 4, MVT_U16, 2); - htonmemcpy(s + 2, ct + 2, MVT_U16, 2); - return(htonmemcpy(s, ct, MVT_U16, 2)); + htolememcpy(s + 4, ct + 4, MVT_U16, 2); + htolememcpy(s + 2, ct + 2, MVT_U16, 2); + return(htolememcpy(s, ct, MVT_U16, 2)); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif @@ -991,13 +1109,13 @@ static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, case MVT_U16Quat: if (n != 8) { - LL_ERRS() << "Size argument passed to htonmemcpy doesn't match swizzle type size" << LL_ENDL; + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN - htonmemcpy(s + 6, ct + 6, MVT_U16, 2); - htonmemcpy(s + 4, ct + 4, MVT_U16, 2); - htonmemcpy(s + 2, ct + 2, MVT_U16, 2); - return(htonmemcpy(s, ct, MVT_U16, 2)); + htolememcpy(s + 6, ct + 6, MVT_U16, 2); + htolememcpy(s + 4, ct + 4, MVT_U16, 2); + htolememcpy(s + 2, ct + 2, MVT_U16, 2); + return(htolememcpy(s, ct, MVT_U16, 2)); #else return(memcpy(s,ct,n)); /* Flawfinder: ignore */ #endif @@ -1005,15 +1123,15 @@ static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, case MVT_S16Array: if (n % 2) { - LL_ERRS() << "Size argument passed to htonmemcpy doesn't match swizzle type size" << LL_ENDL; + LL_ERRS() << "Size argument passed to htolememcpy doesn't match swizzle type size" << LL_ENDL; } #ifdef LL_BIG_ENDIAN length = n % 2; for (i = 1; i < length; i++) { - htonmemcpy(s + i*2, ct + i*2, MVT_S16, 2); + htolememcpy(s + i*2, ct + i*2, MVT_S16, 2); } - return(htonmemcpy(s, ct, MVT_S16, 2)); + return(htolememcpy(s, ct, MVT_S16, 2)); #else return(memcpy(s,ct,n)); #endif @@ -1025,7 +1143,7 @@ static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, inline void *ntohmemcpy(void *s, const void *ct, EMsgVariableType type, size_t n) { - return(htonmemcpy(s,ct,type, n)); + return(htolememcpy(s,ct,type, n)); } inline const LLHost& LLMessageSystem::getReceivingInterface() const {return mLastReceivingIF;} diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index f8e11e324e..fba5b7453d 100644 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -1376,7 +1376,9 @@ char const* const _PREHASH_RegionDenyAgeUnverified = LLMessageStringTable::getIn char const* const _PREHASH_AgeVerificationBlock = LLMessageStringTable::getInstance()->getString("AgeVerificationBlock"); char const* const _PREHASH_RegionAllowAccessBlock = LLMessageStringTable::getInstance()->getString("RegionAllowAccessBlock"); char const* const _PREHASH_RegionAllowAccessOverride = LLMessageStringTable::getInstance()->getString("RegionAllowAccessOverride"); - +char const* const _PREHASH_ParcelEnvironmentBlock = LLMessageStringTable::getInstance()->getString("ParcelEnvironmentBlock"); +char const* const _PREHASH_ParcelEnvironmentVersion = LLMessageStringTable::getInstance()->getString("ParcelEnvironmentVersion"); +char const* const _PREHASH_RegionAllowEnvironmentOverride = LLMessageStringTable::getInstance()->getString("RegionAllowEnvironmentOverride"); char const* const _PREHASH_UCoord = LLMessageStringTable::getInstance()->getString("UCoord"); char const* const _PREHASH_VCoord = LLMessageStringTable::getInstance()->getString("VCoord"); char const* const _PREHASH_FaceIndex = LLMessageStringTable::getInstance()->getString("FaceIndex"); @@ -1392,3 +1394,4 @@ char const* const _PREHASH_AppearanceHover = LLMessageStringTable::getInstance() char const* const _PREHASH_HoverHeight = LLMessageStringTable::getInstance()->getString("HoverHeight"); char const* const _PREHASH_Experience = LLMessageStringTable::getInstance()->getString("Experience"); char const* const _PREHASH_ExperienceID = LLMessageStringTable::getInstance()->getString("ExperienceID"); +char const* const _PREHASH_LargeGenericMessage = LLMessageStringTable::getInstance()->getString("LargeGenericMessage"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 334236fb25..4f72c01ddf 100644 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -1376,6 +1376,9 @@ extern char const* const _PREHASH_RegionDenyAgeUnverified; extern char const* const _PREHASH_AgeVerificationBlock; extern char const* const _PREHASH_RegionAllowAccessBlock; extern char const* const _PREHASH_RegionAllowAccessOverride; +extern char const* const _PREHASH_ParcelEnvironmentBlock; +extern char const* const _PREHASH_ParcelEnvironmentVersion; +extern char const* const _PREHASH_RegionAllowEnvironmentOverride; extern char const* const _PREHASH_UCoord; extern char const* const _PREHASH_VCoord; extern char const* const _PREHASH_FaceIndex; @@ -1391,4 +1394,6 @@ extern char const* const _PREHASH_AppearanceHover; extern char const* const _PREHASH_HoverHeight; extern char const* const _PREHASH_Experience; extern char const* const _PREHASH_ExperienceID; +extern char const* const _PREHASH_LargeGenericMessage; + #endif diff --git a/indra/llmessage/tests/llareslistener_test.cpp b/indra/llmessage/tests/llareslistener_test.cpp index c04696c86b..254185cbd0 100644 --- a/indra/llmessage/tests/llareslistener_test.cpp +++ b/indra/llmessage/tests/llareslistener_test.cpp @@ -138,15 +138,9 @@ namespace tut WrapLLErrs capture; LLSD request; request["op"] = "foo"; - std::string threw; - try - { - LLEventPumps::instance().obtain("LLAres").post(request); - } - catch (const WrapLLErrs::FatalException& e) - { - threw = e.what(); - } + std::string threw = capture.catch_llerrs([&request](){ + LLEventPumps::instance().obtain("LLAres").post(request); + }); ensure_contains("LLAresListener bad op", threw, "bad"); } @@ -157,15 +151,9 @@ namespace tut WrapLLErrs capture; LLSD request; request["op"] = "rewriteURI"; - std::string threw; - try - { - LLEventPumps::instance().obtain("LLAres").post(request); - } - catch (const WrapLLErrs::FatalException& e) - { - threw = e.what(); - } + std::string threw = capture.catch_llerrs([&request](){ + LLEventPumps::instance().obtain("LLAres").post(request); + }); ensure_contains("LLAresListener bad req", threw, "missing"); ensure_contains("LLAresListener bad req", threw, "reply"); ensure_contains("LLAresListener bad req", threw, "uri"); @@ -179,15 +167,9 @@ namespace tut LLSD request; request["op"] = "rewriteURI"; request["reply"] = "nonexistent"; - std::string threw; - try - { - LLEventPumps::instance().obtain("LLAres").post(request); - } - catch (const WrapLLErrs::FatalException& e) - { - threw = e.what(); - } + std::string threw = capture.catch_llerrs([&request](){ + LLEventPumps::instance().obtain("LLAres").post(request); + }); ensure_contains("LLAresListener bad req", threw, "missing"); ensure_contains("LLAresListener bad req", threw, "uri"); ensure_does_not_contain("LLAresListener bad req", threw, "reply"); @@ -201,15 +183,9 @@ namespace tut LLSD request; request["op"] = "rewriteURI"; request["uri"] = "foo.bar.com"; - std::string threw; - try - { - LLEventPumps::instance().obtain("LLAres").post(request); - } - catch (const WrapLLErrs::FatalException& e) - { - threw = e.what(); - } + std::string threw = capture.catch_llerrs([&request](){ + LLEventPumps::instance().obtain("LLAres").post(request); + }); ensure_contains("LLAresListener bad req", threw, "missing"); ensure_contains("LLAresListener bad req", threw, "reply"); ensure_does_not_contain("LLAresListener bad req", threw, "uri"); diff --git a/indra/llmessage/tests/llcoproceduremanager_test.cpp b/indra/llmessage/tests/llcoproceduremanager_test.cpp new file mode 100644 index 0000000000..6424117ef3 --- /dev/null +++ b/indra/llmessage/tests/llcoproceduremanager_test.cpp @@ -0,0 +1,179 @@ +/** + * @file llcoproceduremanager_test.cpp + * @author Brad + * @date 2019-02 + * @brief LLCoprocedureManager unit test + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llwin32headers.h" + +#include "linden_common.h" +#include "llsdserialize.h" + +#include "../llcoproceduremanager.h" + +#include <functional> + +#include <boost/fiber/fiber.hpp> +#include <boost/fiber/buffered_channel.hpp> +#include <boost/fiber/unbuffered_channel.hpp> + +#include "../test/lltut.h" +#include "../test/sync.h" + + +#if LL_WINDOWS +// disable unreachable code warnings +#pragma warning(disable: 4702) +#endif + +LLCoreHttpUtil::HttpCoroutineAdapter::HttpCoroutineAdapter(std::string const&, unsigned int, unsigned int) +{ +} + +void LLCoreHttpUtil::HttpCoroutineAdapter::cancelSuspendedOperation() +{ +} + +LLCoreHttpUtil::HttpCoroutineAdapter::~HttpCoroutineAdapter() +{ +} + +LLCore::HttpRequest::HttpRequest() +{ +} + +LLCore::HttpRequest::~HttpRequest() +{ +} + +namespace tut +{ + struct coproceduremanager_test + { + coproceduremanager_test() + { + } + + ~coproceduremanager_test() + { + LLCoprocedureManager::instance().close(); + } + }; + typedef test_group<coproceduremanager_test> coproceduremanager_t; + typedef coproceduremanager_t::object coproceduremanager_object_t; + tut::coproceduremanager_t tut_coproceduremanager("LLCoprocedureManager"); + + + template<> template<> + void coproceduremanager_object_t::test<1>() + { + Sync sync; + int foo = 0; + LLCoprocedureManager::instance().initializePool("PoolName"); + LLUUID queueId = LLCoprocedureManager::instance().enqueueCoprocedure("PoolName", "ProcName", + [&foo, &sync] (LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t & ptr, const LLUUID & id) { + sync.bump(); + foo = 1; + }); + + sync.yield(); + ensure_equals("coprocedure failed to update foo", foo, 1); + + LLCoprocedureManager::instance().close("PoolName"); + } + + template<> template<> + void coproceduremanager_object_t::test<2>() + { + const size_t capacity = 2; + boost::fibers::buffered_channel<std::function<void(void)>> chan(capacity); + + boost::fibers::fiber worker([&chan]() { + chan.value_pop()(); + }); + + chan.push([]() { + LL_INFOS("Test") << "test 1" << LL_ENDL; + }); + + worker.join(); + } + + template<> template<> + void coproceduremanager_object_t::test<3>() + { + boost::fibers::unbuffered_channel<std::function<void(void)>> chan; + + boost::fibers::fiber worker([&chan]() { + chan.value_pop()(); + }); + + chan.push([]() { + LL_INFOS("Test") << "test 1" << LL_ENDL; + }); + + worker.join(); + } + + template<> template<> + void coproceduremanager_object_t::test<4>() + { + boost::fibers::buffered_channel<std::function<void(void)>> chan(4); + + boost::fibers::fiber worker([&chan]() { + std::function<void(void)> f; + + // using namespace std::chrono_literals; + // const auto timeout = 5s; + // boost::fibers::channel_op_status status; + while (chan.pop(f) != boost::fibers::channel_op_status::closed) + { + LL_INFOS("CoWorker") << "got coproc" << LL_ENDL; + f(); + } + LL_INFOS("CoWorker") << "got closed" << LL_ENDL; + }); + + int counter = 0; + + for (int i = 0; i < 5; ++i) + { + LL_INFOS("CoMain") << "pushing coproc " << i << LL_ENDL; + chan.push([&counter]() { + LL_INFOS("CoProc") << "in coproc" << LL_ENDL; + ++counter; + }); + } + + LL_INFOS("CoMain") << "closing channel" << LL_ENDL; + chan.close(); + + LL_INFOS("CoMain") << "joining worker" << LL_ENDL; + worker.join(); + + LL_INFOS("CoMain") << "checking count" << LL_ENDL; + ensure_equals("coprocedure failed to update counter", counter, 5); + } +} // namespace tut |