diff options
Diffstat (limited to 'indra/newview')
-rwxr-xr-x | indra/newview/CMakeLists.txt | 5 | ||||
-rwxr-xr-x | indra/newview/app_settings/settings.xml | 11 | ||||
-rw-r--r-- | indra/newview/llappcorehttp.cpp | 192 | ||||
-rw-r--r-- | indra/newview/llappcorehttp.h | 86 | ||||
-rw-r--r-- | indra/newview/llappviewer.cpp | 9 | ||||
-rw-r--r-- | indra/newview/llappviewer.h | 7 | ||||
-rw-r--r-- | indra/newview/lltexturefetch.cpp | 2106 | ||||
-rw-r--r-- | indra/newview/lltexturefetch.h | 263 | ||||
-rwxr-xr-x | indra/newview/lltextureview.cpp | 44 | ||||
-rw-r--r-- | indra/newview/viewer_manifest.py | 18 |
10 files changed, 2000 insertions, 741 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 0031362210..6a64b6af6b 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -18,6 +18,7 @@ include(JsonCpp) include(LLAudio) include(LLCharacter) include(LLCommon) +include(LLCoreHttp) include(LLImage) include(LLImageJ2COJ) include(LLInventory) @@ -57,6 +58,7 @@ include_directories( ${LLAUDIO_INCLUDE_DIRS} ${LLCHARACTER_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} ${LLPHYSICS_INCLUDE_DIRS} ${FMOD_INCLUDE_DIR} ${LLIMAGE_INCLUDE_DIRS} @@ -97,6 +99,7 @@ set(viewer_SOURCE_FILES llagentwearables.cpp llagentwearablesfetch.cpp llanimstatelabels.cpp + llappcorehttp.cpp llappearancemgr.cpp llappviewer.cpp llappviewerlistener.cpp @@ -672,6 +675,7 @@ set(viewer_HEADER_FILES llagentwearables.h llagentwearablesfetch.h llanimstatelabels.h + llappcorehttp.h llappearance.h llappearancemgr.h llappviewer.h @@ -1811,6 +1815,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} ${NDOF_LIBRARY} ${HUNSPELL_LIBRARY} diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 5e42fc29f7..190dd5e2d5 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10789,6 +10789,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>TextureFetchConcurrency</key> + <map> + <key>Comment</key> + <string>Maximum number of HTTP connections used for texture fetches</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>0</integer> + </map> <key>TextureFetchDebuggerEnabled</key> <map> <key>Comment</key> diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp new file mode 100644 index 0000000000..0d7d41304d --- /dev/null +++ b/indra/newview/llappcorehttp.cpp @@ -0,0 +1,192 @@ +/** + * @file llappcorehttp.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llappcorehttp.h" + +#include "llviewercontrol.h" + + +const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0); + +LLAppCoreHttp::LLAppCoreHttp() + : mRequest(NULL), + mStopHandle(LLCORE_HTTP_HANDLE_INVALID), + mStopRequested(0.0), + mStopped(false), + mPolicyDefault(-1) +{} + + +LLAppCoreHttp::~LLAppCoreHttp() +{ + delete mRequest; + mRequest = NULL; +} + + +void LLAppCoreHttp::init() +{ + LLCore::HttpStatus status = LLCore::HttpRequest::createService(); + if (! status) + { + LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Point to our certs or SSH/https: will fail on connect + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, + gDirUtilp->getCAFile()); + if (! status) + { + LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Establish HTTP Proxy. "LLProxy" is a special string which directs + // the code to use LLProxy::applyProxySettings() to establish any + // HTTP or SOCKS proxy for http operations. + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); + if (! status) + { + LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy): + // 0 - None + // 1 - Basic start, stop simple transitions + // 2 - libcurl CURLOPT_VERBOSE mode with brief lines + // 3 - with partial data content + static const std::string http_trace("QAModeHttpTrace"); + if (gSavedSettings.controlExists(http_trace)) + { + long trace_level(0L); + trace_level = long(gSavedSettings.getU32(http_trace)); + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level); + } + + // Setup default policy and constrain if directed to + mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID; + static const std::string texture_concur("TextureFetchConcurrency"); + if (gSavedSettings.controlExists(texture_concur)) + { + U32 concur(llmin(gSavedSettings.getU32(texture_concur), U32(12))); + + if (concur > 0) + { + LLCore::HttpStatus status; + status = LLCore::HttpRequest::setPolicyClassOption(mPolicyDefault, + LLCore::HttpRequest::CP_CONNECTION_LIMIT, + concur); + if (! status) + { + LL_WARNS("Init") << "Unable to set texture fetch concurrency. Reason: " + << status.toString() + << LL_ENDL; + } + else + { + LL_INFOS("Init") << "Application settings overriding default texture fetch concurrency. New value: " + << concur + << LL_ENDL; + } + } + } + + // Kick the thread + status = LLCore::HttpRequest::startThread(); + if (! status) + { + LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " + << status.toString() + << LL_ENDL; + } + + mRequest = new LLCore::HttpRequest; +} + + +void LLAppCoreHttp::requestStop() +{ + llassert_always(mRequest); + + mStopHandle = mRequest->requestStopThread(this); + if (LLCORE_HTTP_HANDLE_INVALID != mStopHandle) + { + mStopRequested = LLTimer::getTotalSeconds(); + } +} + + +void LLAppCoreHttp::cleanup() +{ + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + // Should have been started already... + requestStop(); + } + + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services without thread shutdown" + << LL_ENDL; + } + else + { + while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME)) + { + mRequest->update(200000); + ms_sleep(50); + } + if (! mStopped) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services with thread shutdown incomplete" + << LL_ENDL; + } + } + + delete mRequest; + mRequest = NULL; + + LLCore::HttpStatus status = LLCore::HttpRequest::destroyService(); + if (! status) + { + LL_WARNS("Cleanup") << "Failed to shutdown HTTP services, continuing. Reason: " + << status.toString() + << LL_ENDL; + } +} + + +void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) +{ + mStopped = true; +} diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h new file mode 100644 index 0000000000..241d73ad52 --- /dev/null +++ b/indra/newview/llappcorehttp.h @@ -0,0 +1,86 @@ +/** + * @file llappcorehttp.h + * @brief Singleton initialization/shutdown class for llcorehttp library + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LL_APP_COREHTTP_H_ +#define _LL_APP_COREHTTP_H_ + + +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" + + +// This class manages the lifecyle of the core http library. +// Slightly different style than traditional code but reflects +// the use of handler classes and light-weight interface +// object instances of the new libraries. To be used +// as a singleton and static construction is fine. +class LLAppCoreHttp : public LLCore::HttpHandler +{ +public: + LLAppCoreHttp(); + ~LLAppCoreHttp(); + + // Initialize the LLCore::HTTP library creating service classes + // and starting the servicing thread. Caller is expected to do + // other initializations (SSL mutex, thread hash function) appropriate + // for the application. + void init(); + + // Request that the servicing thread stop servicing requests, + // release resource references and stop. Request is asynchronous + // and @see cleanup() will perform a limited wait loop for this + // request to stop the thread. + void requestStop(); + + // Terminate LLCore::HTTP library services. Caller is expected + // to have made a best-effort to shutdown the servicing thread + // by issuing a requestThreadStop() and waiting for completion + // notification that the stop has completed. + void cleanup(); + + // Notification when the stop request is complete. + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + // Retrieve the policy class for default operations. + int getPolicyDefault() const + { + return mPolicyDefault; + } + +private: + static const F64 MAX_THREAD_WAIT_TIME; + +private: + LLCore::HttpRequest * mRequest; + LLCore::HttpHandle mStopHandle; + F64 mStopRequested; + bool mStopped; + int mPolicyDefault; +}; + + +#endif // _LL_APP_COREHTTP_H_ diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c9458857d1..777008d920 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -219,6 +219,7 @@ #include "llmachineid.h" #include "llmainlooprepeater.h" + // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -734,6 +735,10 @@ bool LLAppViewer::init() LLViewerStatsRecorder::initClass(); #endif + // Initialize the non-LLCurl libcurl library. Should be called + // before consumers (LLTextureFetch). + mAppCoreHttp.init(); + // *NOTE:Mani - LLCurl::initClass is not thread safe. // Called before threads are created. LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut"), @@ -1884,6 +1889,7 @@ bool LLAppViewer::cleanup() // Delete workers first // shotdown all worker threads before deleting them in case of co-dependencies + mAppCoreHttp.requestStop(); sTextureFetch->shutdown(); sTextureCache->shutdown(); sImageDecodeThread->shutdown(); @@ -1967,6 +1973,9 @@ bool LLAppViewer::cleanup() // *NOTE:Mani - The following call is not thread safe. LLCurl::cleanupClass(); + // Non-LLCurl libcurl library + mAppCoreHttp.cleanup(); + // If we're exiting to launch an URL, do that here so the screen // is at the right resolution before we launch IE. if (!gLaunchFileOnQuit.empty()) diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index ae3c795d1e..0d9c420ff8 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -31,6 +31,7 @@ #include "llcontrol.h" #include "llsys.h" // for LLOSInfo #include "lltimer.h" +#include "llappcorehttp.h" class LLCommandLineParser; class LLFrameTimer; @@ -173,6 +174,9 @@ public: // Metrics policy helper statics. static void metricsUpdateRegion(U64 region_handle); static void metricsSend(bool enable_reporting); + + // llcorehttp init/shutdown/config information. + LLAppCoreHttp & getAppCoreHttp() { return mAppCoreHttp; } protected: virtual bool initWindow(); // Initialize the viewer's window. @@ -271,6 +275,9 @@ private: boost::scoped_ptr<LLUpdaterService> mUpdater; + // llcorehttp library init/shutdown helper + LLAppCoreHttp mAppCoreHttp; + //--------------------------------------------- //*NOTE: Mani - legacy updater stuff // Still useable? diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 7adf5212c2..43198d3725 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, 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 @@ -28,12 +28,12 @@ #include <iostream> #include <map> +#include <algorithm> #include "llstl.h" #include "lltexturefetch.h" -#include "llcurl.h" #include "lldir.h" #include "llhttpclient.h" #include "llhttpstatuscodes.h" @@ -54,28 +54,213 @@ #include "llworld.h" #include "llsdutil.h" #include "llstartup.h" -#include "llviewerstats.h" +#include "llsdserialize.h" + +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" +#include "bufferstream.h" bool LLTextureFetchDebugger::sDebuggerEnabled = false ; LLStat LLTextureFetch::sCacheHitRate("texture_cache_hits", 128); LLStat LLTextureFetch::sCacheReadLatency("texture_cache_read_latency", 128); + +////////////////////////////////////////////////////////////////////////////// +// +// Introduction +// +// This is an attempt to document what's going on in here after-the-fact. +// It's a sincere attempt to be accurate but there will be mistakes. +// +// +// Purpose +// +// What is this module trying to do? It accepts requests to load textures +// at a given priority and discard level and notifies the caller when done +// (successfully or not). Additional constraints are: +// +// * Support a local texture cache. Don't hit network when possible +// to avoid it. +// * Use UDP or HTTP as directed or as fallback. HTTP is tried when +// not disabled and a URL is available. UDP when a URL isn't +// available or HTTP attempts fail. +// * Asynchronous (using threads). Main thread is not to be blocked or +// burdened. +// * High concurrency. Many requests need to be in-flight and at various +// stages of completion. +// * Tolerate frequent re-prioritizations of requests. Priority is +// a reflection of a camera's viewpoint and as that viewpoint changes, +// objects and textures become more and less relevant and that is +// expressed at this level by priority changes and request cancelations. +// +// The caller interfaces that fall out of the above and shape the +// implementation are: +// * createRequest - Load j2c image via UDP or HTTP at given discard level and priority +// * deleteRequest - Request removal of prior request +// * getRequestFinished - Test if request is finished returning data to caller +// * updateRequestPriority - Change priority of existing request +// * getFetchState - Retrieve progress on existing request +// +// Everything else in here is mostly plumbing, metrics and debug. +// +// +// The Work Queue +// +// The two central classes are LLTextureFetch and LLTextureFetchWorker. +// LLTextureFetch combines threading with a priority queue of work +// requests. The priority queue is sorted by a U32 priority derived +// from the F32 priority in the APIs. The *only* work request that +// receives service time by this thread is the highest priority +// request. All others wait until it is complete or a dynamic priority +// change has re-ordered work. +// +// LLTextureFetchWorker implements the work request and is 1:1 with +// texture fetch requests. Embedded in each is a state machine that +// walks it through the cache, HTTP, UDP, image decode and retry +// steps of texture acquisition. +// +// +// Threads +// +// Several threads are actively invoking code in this module. They +// include: +// +// 1. Tmain Main thread of execution +// 2. Ttf LLTextureFetch's worker thread provided by LLQueuedThread +// 3. Tcurl LLCurl's worker thread (should disappear over time) +// 4. Ttc LLTextureCache's worker thread +// 5. Tid Image decoder's worker thread +// 6. Thl HTTP library's worker thread +// +// +// Mutexes/Condition Variables +// +// 1. Mt Mutex defined for LLThread's condition variable (base class of +// LLTextureFetch) +// 2. Ct Condition variable for LLThread and used by lock/unlockData(). +// 3. Mwtd Special LLWorkerThread mutex used for request deletion +// operations (base class of LLTextureFetch) +// 4. Mfq LLTextureFetch's mutex covering request and command queue +// data. +// 5. Mfnq LLTextureFetch's mutex covering udp and http request +// queue data. +// 6. Mwc Mutex covering LLWorkerClass's members (base class of +// LLTextureFetchWorker). One per request. +// 7. Mw LLTextureFetchWorker's mutex. One per request. +// +// +// Lock Ordering Rules +// +// Not an exhaustive list but shows the order of lock acquisition +// needed to prevent deadlocks. 'A < B' means acquire 'A' before +// acquiring 'B'. +// +// 1. Mw < Mfnq +// (there are many more...) +// +// +// Method and Member Definitions +// +// With the above, we'll try to document what threads can call what +// methods (using T* for any), what locks must be held on entry and +// are taken out during execution and what data is covered by which +// lock (if any). This latter category will be especially prone to +// error so be skeptical. +// +// A line like: "// Locks: M<xxx>" indicates a method that must +// be invoked by a caller holding the 'M<xxx>' lock. Similarly, +// "// Threads: T<xxx>" means that a caller should be running in +// the indicated thread. +// +// For data members, a trailing comment like "// M<xxx>" means that +// the data member is covered by the specified lock. Absence of a +// comment can mean the member is unlocked or that I didn't bother +// to do the archaeology. In the case of LLTextureFetchWorker, +// most data members added by the leaf class are actually covered +// by the Mw lock. You may also see "// T<xxx>" which means that +// the member's usage is restricted to one thread (except for +// perhaps construction and destruction) and so explicit locking +// isn't used. +// +// In code, a trailing comment like "// [-+]M<xxx>" indicates a +// lock acquision or release point. +// +// +// Worker Lifecycle +// +// The threading and responder model makes it very likely that +// other components are holding on to a pointer to a worker request. +// So, uncoordinated deletions of requests is a guarantee of memory +// corruption in a short time. So destroying a request involves +// invocations's of LLQueuedThread/LLWorkerThread's abort/stop +// logic that removes workers and puts them ona delete queue for +// 2-phase destruction. That second phase is deferrable by calls +// to deleteOK() which only allow final destruction (via dtor) +// once deleteOK has determined that the request is in a safe +// state. +// +// +// Worker State Machine +// +// (ASCII art needed) +// +// +// Priority Scheme +// +// [PRIORITY_LOW, PRIORITY_NORMAL) - for WAIT_HTTP_RESOURCE state +// and other wait states +// [PRIORITY_HIGH, PRIORITY_URGENT) - External event delivered, +// rapidly transitioning through states, +// no waiting allowed +// +// By itself, the above work queue model would fail the concurrency +// and liveness requirements of the interface. A high priority +// request could find itself on the head and stalled for external +// reasons (see VWR-28996). So a few additional constraints are +// required to keep things running: +// * Anything that can make forward progress must be kept at a +// higher priority than anything that can't. +// * On completion of external events, the associated request +// needs to be elevated beyond the normal range to handle +// any data delivery and release any external resource. +// +// This effort is made to keep higher-priority entities moving +// forward in their state machines at every possible step of +// processing. It's not entirely proven that this produces the +// experiencial benefits promised. +// + ////////////////////////////////////////////////////////////////////////////// -class LLTextureFetchWorker : public LLWorkerClass + +// Tuning/Parameterization Constants + +static const S32 HTTP_REQUESTS_IN_QUEUE_HIGH_WATER = 40; // Maximum requests to have active in HTTP +static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20; // Active level at which to refill + + +////////////////////////////////////////////////////////////////////////////// + +class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler + { friend class LLTextureFetch; - friend class HTTPGetResponder; friend class LLTextureFetchDebugger; private: class CacheReadResponder : public LLTextureCache::ReadResponder { public: + + // Threads: Ttf CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image) : mFetcher(fetcher), mID(id) { setImage(image); } + + // Threads: Ttc virtual void completed(bool success) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -92,10 +277,14 @@ private: class CacheWriteResponder : public LLTextureCache::WriteResponder { public: + + // Threads: Ttf CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id) : mFetcher(fetcher), mID(id) { } + + // Threads: Ttc virtual void completed(bool success) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -112,10 +301,14 @@ private: class DecodeResponder : public LLImageDecodeThread::Responder { public: + + // Threads: Ttf DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker) : mFetcher(fetcher), mID(id), mWorker(worker) { } + + // Threads: Tid virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -148,22 +341,35 @@ private: }; public: + + // Threads: Ttf /*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest() + + // Threads: Ttf /*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) - /*virtual*/ bool deleteOK(); // called from update() (WORK THREAD) - + + // Threads: Tmain + /*virtual*/ bool deleteOK(); // called from update() + ~LLTextureFetchWorker(); - // void relese() { --mActiveCount; } - S32 callbackHttpGet(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success); + // Threads: Ttf + // Locks: Mw + S32 callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success); + + // Threads: Ttc void callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); + + // Threads: Ttc void callbackCacheWrite(bool success); + + // Threads: Tid void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux); - void setGetStatus(U32 status, const std::string& reason) + // Threads: T* + void setGetStatus(LLCore::HttpStatus status, const std::string& reason) { LLMutexLock lock(&mWorkMutex); @@ -175,35 +381,93 @@ public: bool getCanUseHTTP() const { return mCanUseHTTP; } LLTextureFetch & getFetcher() { return *mFetcher; } + + // Inherited from LLCore::HttpHandler + // Threads: Ttf + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); protected: LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 discard, S32 size); private: + + // Threads: Tmain /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD) + + // Threads: Tmain /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) + // Locks: Mw void resetFormattedData(); + // Locks: Mw void setImagePriority(F32 priority); + + // Locks: Mw (ctor invokes without lock) void setDesiredDiscard(S32 discard, S32 size); + + // Threads: T* + // Locks: Mw bool insertPacket(S32 index, U8* data, S32 size); + + // Locks: Mw void clearPackets(); + + // Locks: Mw void setupPacketData(); + + // Locks: Mw (ctor invokes without lock) U32 calcWorkPriority(); + + // Locks: Mw void removeFromCache(); + + // Threads: Ttf + // Locks: Mw bool processSimulatorPackets(); + + // Threads: Ttf bool writeToCacheComplete(); - void removeFromHTTPQueue(); + // Threads: Ttf + void recordTextureStart(bool is_http); + + // Threads: Ttf + void recordTextureDone(bool is_http); void lockWorkMutex() { mWorkMutex.lock(); } void unlockWorkMutex() { mWorkMutex.unlock(); } + // Threads: Ttf + // Locks: Mw + bool acquireHttpSemaphore() + { + llassert(! mHttpHasResource); + if (mFetcher->mHttpSemaphore <= 0) + { + return false; + } + mHttpHasResource = true; + mFetcher->mHttpSemaphore--; + return true; + } + + // Threads: Ttf + // Locks: Mw + void releaseHttpSemaphore() + { + llassert(mHttpHasResource); + mHttpHasResource = false; + mFetcher->mHttpSemaphore++; + } + private: enum e_state // mState { + // *NOTE: Do not change the order/value of state variables, some code + // depends upon specific ordering/adjacency. + // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack) INVALID = 0, INIT, @@ -211,8 +475,10 @@ private: CACHE_POST, LOAD_FROM_NETWORK, LOAD_FROM_SIMULATOR, - SEND_HTTP_REQ, - WAIT_HTTP_REQ, + WAIT_HTTP_RESOURCE, // Waiting for HTTP resources + WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources + SEND_HTTP_REQ, // Commit to sending as HTTP + WAIT_HTTP_REQ, // Request sent, wait for completion DECODE_IMAGE, DECODE_IMAGE_UPDATE, WRITE_TO_CACHE, @@ -256,9 +522,8 @@ private: F32 mCacheReadTime; LLTextureCache::handle_t mCacheReadHandle; LLTextureCache::handle_t mCacheWriteHandle; - U8* mBuffer; - S32 mBufferSize; S32 mRequestedSize; + S32 mRequestedOffset; S32 mDesiredSize; S32 mFileSize; S32 mCachedSize; @@ -273,12 +538,9 @@ private: BOOL mInCache; bool mCanUseHTTP ; bool mCanUseNET ; //can get from asset server. - S32 mHTTPFailCount; S32 mRetryAttempt; S32 mActiveCount; - U32 mGetStatus; - U32 mHTTPHandle; - F32 mDelay; + LLCore::HttpStatus mGetStatus; std::string mGetReason; // Work Data @@ -298,105 +560,19 @@ private: U8 mImageCodec; LLViewerAssetStats::duration_t mMetricsStartTime; -}; - -////////////////////////////////////////////////////////////////////////////// - -class HTTPGetResponder : public LLCurl::Responder -{ - LOG_CLASS(HTTPGetResponder); -public: - HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir) - : mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir) - { - } - ~HTTPGetResponder() - { - } - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); - static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); - static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; - - if (log_to_viewer_log || log_to_sim) - { - mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime); - U64 timeNow = LLTimer::getTotalTime(); - mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); - mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); - mFetcher->mTextureInfo.setRequestOffset(mID, mOffset); - mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); - } - - S32 data_size = 0; - lldebugs << "HTTP COMPLETE: " << mID << llendl; - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - bool success = false; - bool partial = false; - if (HTTP_OK <= status && status < HTTP_MULTIPLE_CHOICES) - { - success = true; - if (HTTP_PARTIAL_CONTENT == status) // partial information - { - partial = true; - } - } - - if (!success) - { - worker->setGetStatus(status, reason); -// llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl; - } - - data_size = worker->callbackHttpGet(channels, buffer, partial, success); - - if(log_texture_traffic && data_size > 0) - { - LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ; - if(tex) - { - gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; - } - } - - if (worker->mMetricsStartTime) - { - LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == worker->mType, - LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime); - worker->mMetricsStartTime = 0; - } - LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == worker->mType); - } - else - { - llwarns << "Worker not found: " << mID << llendl; - } - - mFetcher->getCurlRequest().completeRequest(data_size); - } - - virtual bool followRedir() - { - return mFollowRedir; - } - -private: - LLTextureFetch* mFetcher; - LLUUID mID; - U64 mStartTime; - S32 mRequestedSize; - U32 mOffset; - bool mFollowRedir; + LLCore::HttpHandle mHttpHandle; // Handle of any active request + LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data + int mHttpPolicyClass; + bool mHttpActive; // Active request to http library + unsigned int mHttpReplySize; // Actual received data size + unsigned int mHttpReplyOffset; // Actual received data offset + bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore + + // State history + U32 mCacheReadCount; + U32 mCacheWriteCount; + U32 mResourceWaitCount; // Requests entering WAIT_HTTP_RESOURCE2 }; ////////////////////////////////////////////////////////////////////////////// @@ -632,13 +808,15 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "CACHE_POST", "LOAD_FROM_NETWORK", "LOAD_FROM_SIMULATOR", + "WAIT_HTTP_RESOURCE", + "WAIT_HTTP_RESOURCE2", "SEND_HTTP_REQ", "WAIT_HTTP_REQ", "DECODE_IMAGE", "DECODE_IMAGE_UPDATE", "WRITE_TO_CACHE", "WAIT_ON_WRITE", - "DONE", + "DONE" }; // static @@ -654,6 +832,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, S32 discard, // Desired discard S32 size) // Desired size : LLWorkerClass(fetcher, "TextureFetch"), + LLCore::HttpHandler(), mState(INIT), mWriteToCacheState(NOT_WRITE), mFetcher(fetcher), @@ -671,9 +850,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mCacheReadTime(0.f), mCacheReadHandle(LLTextureCache::nullHandle()), mCacheWriteHandle(LLTextureCache::nullHandle()), - mBuffer(NULL), - mBufferSize(0), mRequestedSize(0), + mRequestedOffset(0), mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE), mFileSize(0), mCachedSize(0), @@ -687,18 +865,24 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mInLocalCache(FALSE), mInCache(FALSE), mCanUseHTTP(true), - mHTTPFailCount(0), mRetryAttempt(0), mActiveCount(0), - mGetStatus(0), mWorkMutex(NULL), mFirstPacket(0), mLastPacket(-1), mTotalPackets(0), mImageCodec(IMG_CODEC_INVALID), mMetricsStartTime(0), - mHTTPHandle(0), - mDelay(-1.f) + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), + mHttpBufferArray(NULL), + mHttpPolicyClass(mFetcher->mHttpPolicyClass), + mHttpActive(false), + mHttpReplySize(0U), + mHttpReplyOffset(0U), + mHttpHasResource(false), + mCacheReadCount(0U), + mCacheWriteCount(0U), + mResourceWaitCount(0U) { mCanUseNET = mUrl.empty() ; @@ -720,7 +904,20 @@ LLTextureFetchWorker::~LLTextureFetchWorker() // << " Requested=" << mRequestedDiscard // << " Desired=" << mDesiredDiscard << llendl; llassert_always(!haveWork()); - lockWorkMutex(); + + lockWorkMutex(); // +Mw (should be useless) + if (mHttpHasResource) + { + // Last-chance catchall to recover the resource. Using an + // atomic datatype solely because this can be running in + // another thread. + releaseHttpSemaphore(); + } + if (mHttpActive) + { + // Issue a cancel on a live request... + mFetcher->getHttpRequest().requestCancel(mHttpHandle, NULL); + } if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) { mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); @@ -731,22 +928,18 @@ LLTextureFetchWorker::~LLTextureFetchWorker() } mFormattedImage = NULL; clearPackets(); - unlockWorkMutex(); - - removeFromHTTPQueue(); -} - -void LLTextureFetchWorker::removeFromHTTPQueue() -{ - if(mHTTPHandle > 0) + if (mHttpBufferArray) { - llassert_always(mState == WAIT_HTTP_REQ); - - mFetcher->getCurlRequest().removeRequest(mHTTPHandle); - mHTTPHandle = 0; + mHttpBufferArray->release(); + mHttpBufferArray = NULL; } + unlockWorkMutex(); // -Mw + mFetcher->removeFromHTTPQueue(mID, 0); + mFetcher->removeHttpWaiter(mID); + mFetcher->updateStateStats(mCacheReadCount, mCacheWriteCount, mResourceWaitCount); } +// Locks: Mw void LLTextureFetchWorker::clearPackets() { for_each(mPackets.begin(), mPackets.end(), DeletePointer()); @@ -756,6 +949,7 @@ void LLTextureFetchWorker::clearPackets() mFirstPacket = 0; } +// Locks: Mw void LLTextureFetchWorker::setupPacketData() { S32 data_size = 0; @@ -788,6 +982,7 @@ void LLTextureFetchWorker::setupPacketData() } } +// Locks: Mw (ctor invokes without lock) U32 LLTextureFetchWorker::calcWorkPriority() { //llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerFetchedTexture::maxDecodePriority()); @@ -797,7 +992,7 @@ U32 LLTextureFetchWorker::calcWorkPriority() return mWorkPriority; } -// mWorkMutex is locked +// Locks: Mw (ctor invokes without lock) void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) { bool prioritize = false; @@ -833,6 +1028,7 @@ void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) } } +// Locks: Mw void LLTextureFetchWorker::setImagePriority(F32 priority) { // llassert_always(priority >= 0 && priority <= LLViewerTexture::maxDecodePriority()); @@ -842,35 +1038,43 @@ void LLTextureFetchWorker::setImagePriority(F32 priority) mImagePriority = priority; calcWorkPriority(); U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS); - mFetcher->getCurlRequest().updatePriority(mHTTPHandle, mWorkPriority); setPriority(work_priority); } } +// Locks: Mw void LLTextureFetchWorker::resetFormattedData() { - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; - mBufferSize = 0; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } if (mFormattedImage.notNull()) { mFormattedImage->deleteData(); } + mHttpReplySize = 0; + mHttpReplyOffset = 0; mHaveAllData = FALSE; } -// Called from MAIN thread +// Threads: Tmain void LLTextureFetchWorker::startWork(S32 param) { llassert(mFormattedImage.isNull()); } -#include "llviewertexturelist.h" // debug - -// Called from LLWorkerThread::processRequest() +// Threads: Ttf bool LLTextureFetchWorker::doWork(S32 param) { - LLMutexLock lock(&mWorkMutex); + static const F32 FETCHING_TIMEOUT = 120.f;//seconds + + static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); // 404 + static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); // 503 + static const LLCore::HttpStatus http_not_sat(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE); // 416; + + LLMutexLock lock(&mWorkMutex); // +Mw if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) { @@ -914,22 +1118,26 @@ bool LLTextureFetchWorker::doWork(S32 param) mLoadedDiscard = -1; mDecodedDiscard = -1; mRequestedSize = 0; + mRequestedOffset = 0; mFileSize = 0; mCachedSize = 0; mLoaded = FALSE; mSentRequest = UNSENT; mDecoded = FALSE; mWritten = FALSE; - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; - mBufferSize = 0; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } + mHttpReplySize = 0; + mHttpReplyOffset = 0; mHaveAllData = FALSE; clearPackets(); // TODO: Shouldn't be necessary mCacheReadHandle = LLTextureCache::nullHandle(); mCacheWriteHandle = LLTextureCache::nullHandle(); mState = LOAD_FROM_TEXTURE_CACHE; mInCache = FALSE; - mDelay = -1.f; mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority) << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL; @@ -945,7 +1153,7 @@ bool LLTextureFetchWorker::doWork(S32 param) S32 size = mDesiredSize - offset; if (size <= 0) { - mState = CACHE_POST; //have enough data, will fall to decode + mState = CACHE_POST; return false; } mFileSize = 0; @@ -956,6 +1164,7 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it // read file from local disk + ++mCacheReadCount; std::string filename = mUrl.substr(7, std::string::npos); CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority, @@ -966,6 +1175,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + ++mCacheReadCount; CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, offset, size, responder); @@ -979,7 +1189,7 @@ bool LLTextureFetchWorker::doWork(S32 param) llwarns << "Unknown URL Type: " << mUrl << llendl; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; } else { @@ -1082,7 +1292,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } if (mCanUseHTTP && !mUrl.empty()) { - mState = SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); if(mWriteToCacheState != NOT_WRITE) { @@ -1099,13 +1309,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedDiscard = mDesiredDiscard; mSentRequest = QUEUED; mFetcher->addToNetworkQueue(this); - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); + recordTextureStart(false); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; @@ -1116,12 +1320,7 @@ bool LLTextureFetchWorker::doWork(S32 param) //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end()); // Make certain this is in the network queue //mFetcher->addToNetworkQueue(this); - //if (! mMetricsStartTime) - //{ - // mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - //} - //LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false, - // LLImageBase::TYPE_AVATAR_BAKE == mType); + //recordTextureStart(false); //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } @@ -1146,193 +1345,201 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = DECODE_IMAGE; mWriteToCacheState = SHOULD_WRITE; - - if (mMetricsStartTime) - { - LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType, - LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); - mMetricsStartTime = 0; - } - LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); + recordTextureDone(false); } else { mFetcher->addToNetworkQueue(this); // failsafe - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + recordTextureStart(false); + } + return false; + } + + if (mState == WAIT_HTTP_RESOURCE) + { + // NOTE: + // control the number of the http requests issued for: + // 1, not openning too many file descriptors at the same time; + // 2, control the traffic of http so udp gets bandwidth. + // + // If it looks like we're busy, keep this request here. + // Otherwise, advance into the HTTP states. + if (mFetcher->getHttpWaitersCount() || ! acquireHttpSemaphore()) + { + mState = WAIT_HTTP_RESOURCE2; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mFetcher->addHttpWaiter(this->mID); + ++mResourceWaitCount; + return false; } + + mState = SEND_HTTP_REQ; + // *NOTE: You must invoke releaseHttpSemaphore() if you transition + // to a state other than SEND_HTTP_REQ or WAIT_HTTP_REQ or abort + // the request. + } + + if (mState == WAIT_HTTP_RESOURCE2) + { + // Just idle it if we make it to the head... return false; } if (mState == SEND_HTTP_REQ) { - if(mCanUseHTTP) + if (! mCanUseHTTP) { - mFetcher->removeFromNetworkQueue(this, false); + releaseHttpSemaphore(); + return true; // abort + } + + mFetcher->removeFromNetworkQueue(this, false); - S32 cur_size = 0; - if (mFormattedImage.notNull()) + S32 cur_size = 0; + if (mFormattedImage.notNull()) + { + cur_size = mFormattedImage->getDataSize(); // amount of data we already have + if (mFormattedImage->getDiscardLevel() == 0) { - cur_size = mFormattedImage->getDataSize(); // amount of data we already have - if (mFormattedImage->getDiscardLevel() == 0) + if (cur_size > 0) { - if(cur_size > 0) - { - // We already have all the data, just decode it - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = DECODE_IMAGE; - return false; - } - else - { - return true ; //abort. - } - } - } - mRequestedSize = mDesiredSize; - mRequestedDiscard = mDesiredDiscard; - mRequestedSize -= cur_size; - S32 offset = cur_size; - mBufferSize = cur_size; // This will get modified by callbackHttpGet() - - bool res = false; - if (!mUrl.empty()) - { - mRequestedTimer.reset(); - - mLoaded = FALSE; - mGetStatus = 0; - mGetReason.clear(); - LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset - << " Bytes: " << mRequestedSize - << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth - << LL_ENDL; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mState = WAIT_HTTP_REQ; - - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + // We already have all the data, just decode it + mLoadedDiscard = mFormattedImage->getDiscardLevel(); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + mState = DECODE_IMAGE; + releaseHttpSemaphore(); + return false; } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == mType); - - // Will call callbackHttpGet when curl request completes - std::vector<std::string> headers; - headers.push_back("Accept: image/x-j2c"); - // If we try to fetch the whole file, we set the size to 0 so that we generate the correct curl range request - // Note: it looks a bit hacky but we need to limit this (size==0) to mean "whole file" to HTTP only as it messes up UDP fetching - if ((offset+mRequestedSize) == MAX_IMAGE_DATA_SIZE) + else { - mRequestedSize = 0; + releaseHttpSemaphore(); + return true; // abort. } - mHTTPHandle = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, mWorkPriority, - new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true), mDelay); - mDelay = -1.f; //reset - res = true; } - if (!res) - { - llwarns << "HTTP GET request failed for " << mID << llendl; - resetFormattedData(); - ++mHTTPFailCount; - return true; // failed - } - // fall through } - else //can not use http fetch. + mRequestedSize = mDesiredSize; + mRequestedDiscard = mDesiredDiscard; + mRequestedSize -= cur_size; + mRequestedOffset = cur_size; + if (mRequestedOffset) { - return true ; //abort + // Texture fetching often issues 'speculative' loads that + // start beyond the end of the actual asset. Some cache/web + // systems, e.g. Varnish, will respond to this not with a + // 416 but with a 200 and the entire asset in the response + // body. By ensuring that we always have a partially + // satisfiable Range request, we avoid that hit to the network. + // We just have to deal with the overlapping data which is made + // somewhat harder by the fact that grid services don't necessarily + // return the Content-Range header on 206 responses. *Sigh* + mRequestedOffset -= 1; + mRequestedSize += 1; + } + + mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; + if (!mUrl.empty()) + { + mRequestedTimer.reset(); + mLoaded = FALSE; + mGetStatus = LLCore::HttpStatus(); + mGetReason.clear(); + LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset + << " Bytes: " << mRequestedSize + << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth + << LL_ENDL; + + // Will call callbackHttpGet when curl request completes + mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mWorkPriority, + mUrl, + mRequestedOffset, + mRequestedSize, + mFetcher->mHttpOptions, + mFetcher->mHttpHeaders, + this); + } + if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) + { + llwarns << "HTTP GET request failed for " << mID << llendl; + resetFormattedData(); + releaseHttpSemaphore(); + return true; // failed } + + mHttpActive = true; + mFetcher->addToHTTPQueue(mID); + recordTextureStart(true); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mState = WAIT_HTTP_REQ; + + // fall through } if (mState == WAIT_HTTP_REQ) { + // *NOTE: As stated above, all transitions out of this state should + // call releaseHttpSemaphore(). if (mLoaded) { S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; if (mRequestedSize < 0) { - S32 max_attempts; - if (mGetStatus == HTTP_NOT_FOUND) + if (http_not_found == mGetStatus) { if(mWriteToCacheState == NOT_WRITE) //map tiles { mState = DONE; + releaseHttpSemaphore(); return true; // failed, means no map tile on the empty region. } - mHTTPFailCount = max_attempts = 1; // Don't retry llwarns << "Texture missing from server (404): " << mUrl << llendl; - //roll back to try UDP - if(mCanUseNET) + // roll back to try UDP + if (mCanUseNET) { - mState = INIT ; - mCanUseHTTP = false ; + mState = INIT; + mCanUseHTTP = false; mUrl.clear(); setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - return false ; + releaseHttpSemaphore(); + return false; } } - else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE) + else if (http_service_unavail == mGetStatus) { - // *TODO: Should probably introduce a timer here to delay future HTTP requsts - // for a short time (~1s) to ease server load? Ideally the server would queue - // requests instead of returning 503... we already limit the number pending. - ++mHTTPFailCount; - max_attempts = mHTTPFailCount+1; // Keep retrying LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; - mDelay = 2.0f; //delay 2 second to re-issue the http request + } + else if (http_not_sat == mGetStatus) + { + // Allowed, we'll accept whatever data we have as complete. + mHaveAllData = TRUE; } else { - const S32 HTTP_MAX_RETRY_COUNT = 3; - max_attempts = HTTP_MAX_RETRY_COUNT + 1; - ++mHTTPFailCount; - mDelay = 2.0f; //delay 2 second to re-issue the http request - llinfos << "HTTP GET failed for: " << mUrl - << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" - << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; + << " Status: " << mGetStatus.toHex() + << " Reason: '" << mGetReason << "'" + << llendl; } - if (mHTTPFailCount >= max_attempts) - { - mUrl.clear(); - if (cur_size > 0) - { - // Use available data - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = DECODE_IMAGE; - return false; - } - else - { - resetFormattedData(); - mState = DONE; - return true; // failed - } - } - else + mUrl.clear(); + if (cur_size > 0) { + // Use available data + mLoadedDiscard = mFormattedImage->getDiscardLevel(); setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = SEND_HTTP_REQ; - return false; // retry + mState = DECODE_IMAGE; + releaseHttpSemaphore(); + return false; } + + // Fail harder + resetFormattedData(); + mState = DONE; + releaseHttpSemaphore(); + return true; // failed } // Clear the url since we're done with the fetch @@ -1342,18 +1549,46 @@ bool LLTextureFetchWorker::doWork(S32 param) { mUrl.clear(); } - - llassert_always(mBufferSize == cur_size + mRequestedSize); - if(!mBufferSize)//no data received. + + if (! mHttpBufferArray || ! mHttpBufferArray->size()) { - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; + // no data received. + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } - //abort. + // abort. mState = DONE; + releaseHttpSemaphore(); return true; } + S32 append_size(mHttpBufferArray->size()); + S32 total_size(cur_size + append_size); + S32 src_offset(0); + llassert_always(append_size == mRequestedSize); + if (mHttpReplyOffset && mHttpReplyOffset != cur_size) + { + // In case of a partial response, our offset may + // not be trivially contiguous with the data we have. + // Get back into alignment. + if (mHttpReplyOffset > cur_size) + { + LL_WARNS("Texture") << "Partial HTTP response produces break in image data for texture " + << mID << ". Aborting load." << LL_ENDL; + mState = DONE; + releaseHttpSemaphore(); + return true; + } + src_offset = cur_size - mHttpReplyOffset; + append_size -= src_offset; + total_size -= src_offset; + mRequestedSize -= src_offset; // Make requested values reflect useful part + mRequestedOffset += src_offset; + } + if (mFormattedImage.isNull()) { // For now, create formatted image based on extension @@ -1365,41 +1600,70 @@ bool LLTextureFetchWorker::doWork(S32 param) } } - if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded. + if (mHaveAllData) //the image file is fully loaded. { - mFileSize = mBufferSize; + mFileSize = total_size; } else //the file size is unknown. { - mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded. + mFileSize = total_size + 1 ; //flag the file is not fully loaded. } - U8* buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), mBufferSize); + U8 * buffer = (U8 *) ALLOCATE_MEM(LLImageBase::getPrivatePool(), total_size); if (cur_size > 0) { memcpy(buffer, mFormattedImage->getData(), cur_size); } - memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append + mHttpBufferArray->read(src_offset, (char *) buffer + cur_size, append_size); + // NOTE: setData releases current data and owns new data (buffer) - mFormattedImage->setData(buffer, mBufferSize); - // delete temp data - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); // Note: not 'buffer' (assigned in setData()) - mBuffer = NULL; - mBufferSize = 0; + mFormattedImage->setData(buffer, total_size); + + // Done with buffer array + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + mHttpReplySize = 0; + mHttpReplyOffset = 0; + mLoadedDiscard = mRequestedDiscard; mState = DECODE_IMAGE; - if(mWriteToCacheState != NOT_WRITE) + if (mWriteToCacheState != NOT_WRITE) { mWriteToCacheState = SHOULD_WRITE ; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + releaseHttpSemaphore(); return false; } else { - // - //No need to timeout, the responder should be triggered automatically. - // + // *FIXME: This auxiliary timeout logic appeared recently and then + // quickly disappeared. While I haven't seen it invoked, I'm leaving + // it active for now. + if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32()) + { + //timeout, abort. + LL_WARNS("Texture") << "Fetch of texture " << mID << " timed out after " + << mRequestedTimer.getElapsedTimeF32() + << " seconds. Canceling request." << LL_ENDL; + + if (LLCORE_HTTP_HANDLE_INVALID != mHttpHandle) + { + // Issue cancel on any outstanding request. Asynchronous + // so cancel may not actually take effect if operation is + // complete & queued. Either way, notification will + // complete and the request can be transitioned. + mFetcher->mHttpRequest->requestCancel(mHttpHandle, NULL); + } + else + { + // Shouldn't happen but if it does, cancel quickly. + mState = DONE; + releaseHttpSemaphore(); + return true; + } + } + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } @@ -1408,11 +1672,12 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DECODE_IMAGE) { static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); - if(textures_decode_disabled) + + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + if (textures_decode_disabled) { // for debug use, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } @@ -1420,7 +1685,6 @@ bool LLTextureFetchWorker::doWork(S32 param) { // We aborted, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } @@ -1430,7 +1694,6 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } if (mLoadedDiscard < 0) @@ -1439,10 +1702,9 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + mRawImage = NULL; mAuxImage = NULL; llassert_always(mFormattedImage.notNull()); @@ -1528,6 +1790,7 @@ bool LLTextureFetchWorker::doWork(S32 param) U32 cache_priority = mWorkPriority; mWritten = FALSE; mState = WAIT_ON_WRITE; + ++mCacheWriteCount; CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, mFormattedImage->getData(), datasize, @@ -1572,9 +1835,84 @@ bool LLTextureFetchWorker::doWork(S32 param) } return false; -} +} // -Mw + +// Threads: Ttf +// virtual +void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + static LLCachedControl<bool> log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog"); + static LLCachedControl<bool> log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator"); + static LLCachedControl<bool> log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ; + + LLMutexLock lock(&mWorkMutex); // +Mw -// Called from MAIN thread + mHttpActive = false; + + if (log_to_viewer_log || log_to_sim) + { + U64 timeNow = LLTimer::getTotalTime(); + mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime); + mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); + mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset); + mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); + } + + bool success = true; + bool partial = false; + LLCore::HttpStatus status(response->getStatus()); + + lldebugs << "HTTP COMPLETE: " << mID + << " status: " << status.toHex() + << " '" << status.toString() << "'" + << llendl; +// unsigned int offset(0), length(0), full_length(0); +// response->getRange(&offset, &length, &full_length); +// llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle +// << " status: " << status.toULong() << " '" << status.toString() << "'" +// << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize +// << " offset: " << offset << " length: " << length +// << llendl; + + if (! status) + { + success = false; + std::string reason(status.toString()); + setGetStatus(status, reason); + llwarns << "CURL GET FAILED, status: " << status.toHex() + << " reason: " << reason << llendl; + } + else + { + // A warning about partial (HTTP 206) data. Some grid services + // do *not* return a 'Content-Range' header in the response to + // Range requests with a 206 status. We're forced to assume + // we get what we asked for in these cases until we can fix + // the services. + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + + partial = (par_status == status); + } + + S32 data_size = callbackHttpGet(response, partial, success); + + if (log_texture_traffic && data_size > 0) + { + LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID); + if (tex) + { + gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; + } + } + + mFetcher->removeFromHTTPQueue(mID, data_size); + + recordTextureDone(true); +} // -Mw + + +// Threads: Tmain void LLTextureFetchWorker::endWork(S32 param, bool aborted) { if (mDecodeHandle != 0) @@ -1587,6 +1925,8 @@ void LLTextureFetchWorker::endWork(S32 param, bool aborted) ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf + // virtual void LLTextureFetchWorker::finishWork(S32 param, bool completed) { @@ -1603,10 +1943,37 @@ void LLTextureFetchWorker::finishWork(S32 param, bool completed) } } +// LLQueuedThread's update() method is asking if it's okay to +// delete this worker. You'll notice we're not locking in here +// which is a slight concern. Caller is expected to have made +// this request 'quiet' by whatever means... +// +// Threads: Tmain + // virtual bool LLTextureFetchWorker::deleteOK() { bool delete_ok = true; + + if (mHttpActive) + { + // HTTP library has a pointer to this worker + // and will dereference it to do notification. + delete_ok = false; + } + + if (WAIT_HTTP_RESOURCE2 == mState) + { + if (mFetcher->isHttpWaiter(mID)) + { + // Don't delete the worker out from under the releaseHttpWaiters() + // method. Keep the pointers valid, clean up after that method + // has recognized the cancelation and removed the UUID from the + // waiter list. + delete_ok = false; + } + } + // Allow any pending reads or writes to complete if (mCacheReadHandle != LLTextureCache::nullHandle()) { @@ -1641,6 +2008,7 @@ bool LLTextureFetchWorker::deleteOK() return delete_ok; } +// Threads: Ttf void LLTextureFetchWorker::removeFromCache() { if (!mInLocalCache) @@ -1652,6 +2020,8 @@ void LLTextureFetchWorker::removeFromCache() ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf +// Locks: Mw bool LLTextureFetchWorker::processSimulatorPackets() { if (mFormattedImage.isNull() || mRequestedSize < 0) @@ -1712,14 +2082,13 @@ bool LLTextureFetchWorker::processSimulatorPackets() ////////////////////////////////////////////////////////////////////////////// -S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success) +// Threads: Ttf +// Locks: Mw +S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success) { S32 data_size = 0 ; - LLMutexLock lock(&mWorkMutex); - mHTTPHandle = 0; if (mState != WAIT_HTTP_REQ) { llwarns << "callbackHttpGet for unrequested fetch worker: " << mID @@ -1734,27 +2103,68 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, if (success) { // get length of stream: - data_size = buffer->countAfter(channels.in(), NULL); - + LLCore::BufferArray * body(response->getBody()); + data_size = body ? body->size() : 0; + LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL; if (data_size > 0) { // *TODO: set the formatted image data here directly to avoid the copy - mBuffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size); - buffer->readAfter(channels.in(), NULL, mBuffer, data_size); - mBufferSize += data_size; - if (mRequestedSize == 0) + + // Hold on to body for later copy + llassert_always(NULL == mHttpBufferArray); + body->addRef(); + mHttpBufferArray = body; + + if (partial) + { + unsigned int offset(0), length(0), full_length(0); + response->getRange(&offset, &length, &full_length); + if (! offset && ! length) + { + // This is the case where we receive a 206 status but + // there wasn't a useful Content-Range header in the response. + // This could be because it was badly formatted but is more + // likely due to capabilities services which scrub headers + // from responses. Assume we got what we asked for... + mHttpReplySize = data_size; + mHttpReplyOffset = mRequestedOffset; + } + else + { + mHttpReplySize = length; + mHttpReplyOffset = offset; + } + } + + if (! partial) + { + // Response indicates this is the entire asset regardless + // of our asking for a byte range. Mark it so and drop + // any partial data we might have so that the current + // response body becomes the entire dataset. + if (data_size <= mRequestedOffset) + { + LL_WARNS("Texture") << "Fetched entire texture " << mID + << " when it was expected to be marked complete. mImageSize: " + << mFileSize << " datasize: " << mFormattedImage->getDataSize() + << LL_ENDL; + } + mHaveAllData = TRUE; + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; // discard any previous data we had + } + else if (data_size < mRequestedSize) { mHaveAllData = TRUE; } else if (data_size > mRequestedSize) { - // *TODO: This shouldn't be happening any more + // *TODO: This shouldn't be happening any more (REALLY don't expect this anymore) llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl; mHaveAllData = TRUE; llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; // discard any previous data we had - mBufferSize = data_size; } } else @@ -1778,10 +2188,11 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttc void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mState != LOAD_FROM_TEXTURE_CACHE) { // llwarns << "Read callback for " << mID << " with state = " << mState << llendl; @@ -1801,11 +2212,12 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima } mLoaded = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw +// Threads: Ttc void LLTextureFetchWorker::callbackCacheWrite(bool success) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mState != WAIT_ON_WRITE) { // llwarns << "Write callback for " << mID << " with state = " << mState << llendl; @@ -1813,13 +2225,14 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success) } mWritten = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw ////////////////////////////////////////////////////////////////////////////// +// Threads: Tid void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mDecodeHandle == 0) { return; // aborted, ignore @@ -1852,10 +2265,11 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag // llinfos << mID << " : DECODE COMPLETE " << llendl; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mCacheReadTime = mCacheReadTimer.getElapsedTimeF32(); -} +} // -Mw ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf bool LLTextureFetchWorker::writeToCacheComplete() { // Complete write to cache @@ -1878,6 +2292,36 @@ bool LLTextureFetchWorker::writeToCacheComplete() } +// Threads: Ttf +void LLTextureFetchWorker::recordTextureStart(bool is_http) +{ + if (! mMetricsStartTime) + { + mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); +} + + +// Threads: Ttf +void LLTextureFetchWorker::recordTextureDone(bool is_http) +{ + if (mMetricsStartTime) + { + LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType, + LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); + mMetricsStartTime = 0; + } + LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); +} + + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // public @@ -1893,14 +2337,23 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mTextureCache(cache), mImageDecodeThread(imagedecodethread), mTextureBandwidth(0), - mCurlGetRequest(NULL), + mHTTPTextureBits(0), + mTotalHTTPRequests(0), mQAMode(qa_mode), + mHttpRequest(NULL), + mHttpOptions(NULL), + mHttpHeaders(NULL), + mHttpMetricsHeaders(NULL), + mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER), + mTotalCacheReadCount(0U), + mTotalCacheWriteCount(0U), + mTotalResourceWaitCount(0U), mFetchDebugger(NULL), mFetchSource(LLTextureFetch::FROM_ALL), mOriginFetchSource(LLTextureFetch::FROM_ALL), mFetcherLocked(FALSE) { - mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); @@ -1916,11 +2369,19 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image } mOriginFetchSource = mFetchSource; } + + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = new LLCore::HttpOptions; + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); + mHttpMetricsHeaders = new LLCore::HttpHeaders; + mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault(); } LLTextureFetch::~LLTextureFetch() { - clearDeleteList() ; + clearDeleteList(); while (! mCommands.empty()) { @@ -1928,10 +2389,34 @@ LLTextureFetch::~LLTextureFetch() mCommands.erase(mCommands.begin()); delete req; } + + if (mHttpOptions) + { + mHttpOptions->release(); + mHttpOptions = NULL; + } + + if (mHttpHeaders) + { + mHttpHeaders->release(); + mHttpHeaders = NULL; + } + + if (mHttpMetricsHeaders) + { + mHttpMetricsHeaders->release(); + mHttpMetricsHeaders = NULL; + } + + mHttpWaitResource.clear(); - // ~LLQueuedThread() called here + delete mHttpRequest; + mHttpRequest = NULL; delete mFetchDebugger; + mFetchDebugger = NULL; + + // ~LLQueuedThread() called here } bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, @@ -1997,7 +2482,7 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con { return false; // need to wait for previous aborted request to complete } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->mActiveCount++; worker->mNeedsAux = needs_aux; worker->setImagePriority(priority); @@ -2006,41 +2491,44 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con if (!worker->haveWork()) { worker->mState = LLTextureFetchWorker::INIT; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); } else { - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } } else { worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size); - lockQueue() ; + lockQueue(); // +Mfq mRequestMap[id] = worker; - unlockQueue() ; + unlockQueue(); // -Mfq - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->mActiveCount++; worker->mNeedsAux = needs_aux; worker->setCanUseHTTP(can_use_http) ; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } // llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl; return true; } + +// Threads: T* (but Ttf in practice) + // protected void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { - lockQueue() ; + lockQueue(); // +Mfq bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ; - unlockQueue() ; + unlockQueue(); // -Mfq - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq if (in_request_map) { // only add to the queue if in the request map @@ -2052,27 +2540,51 @@ void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { iter1->second.erase(worker->mID); } -} +} // -Mfnq +// Threads: T* void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel) { - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq size_t erased = mNetworkQueue.erase(worker->mID); if (cancel && erased > 0) { mCancelQueue[worker->mHost].insert(worker->mID); } -} +} // -Mfnq + +// Threads: T* +// +// protected +void LLTextureFetch::addToHTTPQueue(const LLUUID& id) +{ + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + mHTTPTextureQueue.insert(id); + mTotalHTTPRequests++; +} // -Mfnq + +// Threads: T* +void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size) +{ + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + mHTTPTextureQueue.erase(id); + mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits +} // -Mfnq +// Threads: T* void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) { - lockQueue() ; + lockQueue(); // +Mfq LLTextureFetchWorker* worker = getWorkerAfterLock(id); - unlockQueue() ; + unlockQueue(); // -Mfq + // *TODO: Refactoring this code may have introduced a thread race + // here where other code can run between the lookup above and the + // removeRequest() below. removeRequest(worker, cancel); } +// Threads: T* void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) { if(!worker) @@ -2080,15 +2592,14 @@ void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) return; } - lockQueue() ; + lockQueue(); // +Mfq size_t erased_1 = mRequestMap.erase(worker->mID); - unlockQueue() ; + unlockQueue(); // -Mfq llassert_always(erased_1 > 0) ; removeFromNetworkQueue(worker, cancel); llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; - worker->removeFromHTTPQueue(); worker->scheduleDelete(); } @@ -2110,16 +2621,39 @@ void LLTextureFetch::deleteAllRequests() } } +// Threads: T* S32 LLTextureFetch::getNumRequests() { - lockQueue() ; + lockQueue(); // +Mfq S32 size = (S32)mRequestMap.size(); - unlockQueue() ; + unlockQueue(); // -Mfq - return size ; + return size; +} + +// Threads: T* +S32 LLTextureFetch::getNumHTTPRequests() +{ + mNetworkQueueMutex.lock(); // +Mfq + S32 size = (S32)mHTTPTextureQueue.size(); + mNetworkQueueMutex.unlock(); // -Mfq + + return size; +} + +// Threads: T* +U32 LLTextureFetch::getTotalNumHTTPRequests() +{ + mNetworkQueueMutex.lock(); // +Mfq + U32 size = mTotalHTTPRequests; + mNetworkQueueMutex.unlock(); // -Mfq + + return size; } // call lockQueue() first! +// Threads: T* +// Locks: Mfq LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) { LLTextureFetchWorker* res = NULL; @@ -2131,14 +2665,16 @@ LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) return res; } +// Threads: T* LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id) { - LLMutexLock lock(&mQueueMutex) ; + LLMutexLock lock(&mQueueMutex); // +Mfq - return getWorkerAfterLock(id) ; -} + return getWorkerAfterLock(id); +} // -Mfq +// Threads: T* bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux) { @@ -2161,7 +2697,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, } else if (worker->checkWork()) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw discard_level = worker->mDecodedDiscard; raw = worker->mRawImage; aux = worker->mAuxImage; @@ -2172,11 +2708,11 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, } res = true; LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } else { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw if ((worker->mDecodedDiscard >= 0) && (worker->mDecodedDiscard < discard_level || discard_level < 0) && (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE)) @@ -2186,7 +2722,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, raw = worker->mRawImage; aux = worker->mAuxImage; } - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } } else @@ -2196,15 +2732,16 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, return res; } +// Threads: T* bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) { bool res = false; LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->setImagePriority(priority); - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw res = true; } return res; @@ -2219,24 +2756,24 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) // in step, at least until this can be refactored and // the redundancy eliminated. // -// May be called from any thread +// Threads: T* //virtual S32 LLTextureFetch::getPending() { S32 res; - lockData(); + lockData(); // +Ct { - LLMutexLock lock(&mQueueMutex); + LLMutexLock lock(&mQueueMutex); // +Mfq res = mRequestQueue.size(); - res += mCurlPOSTRequestCount; res += mCommands.size(); - } - unlockData(); + } // -Mfq + unlockData(); // -Ct return res; } +// Locks: Ct // virtual bool LLTextureFetch::runCondition() { @@ -2251,42 +2788,53 @@ bool LLTextureFetch::runCondition() bool have_no_commands(false); { - LLMutexLock lock(&mQueueMutex); + LLMutexLock lock(&mQueueMutex); // +Mfq have_no_commands = mCommands.empty(); - } - - bool have_no_curl_requests(0 == mCurlPOSTRequestCount); + } // -Mfq return ! (have_no_commands - && have_no_curl_requests && (mRequestQueue.empty() && mIdleThread)); // From base class } ////////////////////////////////////////////////////////////////////////////// -// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs) +// Threads: Ttf void LLTextureFetch::commonUpdate() { + // Release waiters + releaseHttpWaiters(); + // Run a cross-thread command, if any. cmdDoWork(); - // Update Curl on same thread as mCurlGetRequest was constructed - S32 processed = mCurlGetRequest->process(); - if (processed > 0) + // Deliver all completion notifications + LLCore::HttpStatus status = mHttpRequest->update(0); + if (! status) { - lldebugs << "processed: " << processed << " messages." << llendl; + LL_INFOS_ONCE("Texture") << "Problem during HTTP servicing. Reason: " + << status.toString() + << LL_ENDL; } } -// MAIN THREAD +// Threads: Tmain + //virtual S32 LLTextureFetch::update(F32 max_time_ms) { static LLCachedControl<F32> band_width(gSavedSettings,"ThrottleBandwidthKBPS"); - mMaxBandwidth = band_width ; + { + mNetworkQueueMutex.lock(); // +Mfnq + mMaxBandwidth = band_width; + + gTextureList.sTextureBits += mHTTPTextureBits; + mHTTPTextureBits = 0; + + mNetworkQueueMutex.unlock(); // -Mfnq + } S32 res = LLWorkerThread::update(max_time_ms); @@ -2304,19 +2852,9 @@ S32 LLTextureFetch::update(F32 max_time_ms) if (!mThreaded) { commonUpdate(); - - if(mCurlGetRequest) - { - mCurlGetRequest->nextRequests(); - } - } - - if(mCurlGetRequest) - { - gTextureList.sTextureBits += mCurlGetRequest->getTotalReceivedBits(); } - if(mFetchDebugger) + if (mFetchDebugger) { mFetchDebugger->tryToStopDebug(); //check if need to stop debugger. } @@ -2324,7 +2862,9 @@ S32 LLTextureFetch::update(F32 max_time_ms) return res; } -//called in the MAIN thread after the TextureCacheThread shuts down. +// called in the MAIN thread after the TextureCacheThread shuts down. +// +// Threads: Tmain void LLTextureFetch::shutDownTextureCacheThread() { if(mTextureCache) @@ -2334,7 +2874,9 @@ void LLTextureFetch::shutDownTextureCacheThread() } } -//called in the MAIN thread after the ImageDecodeThread shuts down. +// called in the MAIN thread after the ImageDecodeThread shuts down. +// +// Threads: Tmain void LLTextureFetch::shutDownImageDecodeThread() { if(mImageDecodeThread) @@ -2344,37 +2886,27 @@ void LLTextureFetch::shutDownImageDecodeThread() } } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::startThread() { - // Construct mCurlGetRequest from Worker Thread - mCurlGetRequest = new LLCurlTextureRequest(8); - - if(mFetchDebugger) - { - mFetchDebugger->setCurlGetRequest(mCurlGetRequest); - } } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::endThread() { - // Destroy mCurlGetRequest from Worker Thread - delete mCurlGetRequest; - mCurlGetRequest = NULL; - if(mFetchDebugger) - { - mFetchDebugger->setCurlGetRequest(NULL); - } + LL_INFOS("Texture") << "CacheReads: " << mTotalCacheReadCount + << ", CacheWrites: " << mTotalCacheWriteCount + << ", ResWaits: " << mTotalResourceWaitCount + << ", TotalHTTPReq: " << getTotalNumHTTPRequests() + << LL_ENDL; } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::threadedUpdate() { - llassert_always(mCurlGetRequest); - - mCurlGetRequest->nextRequests(); + llassert_always(mHttpRequest); +#if 0 // Limit update frequency const F32 PROCESS_TIME = 0.05f; static LLFrameTimer process_timer; @@ -2383,9 +2915,10 @@ void LLTextureFetch::threadedUpdate() return; } process_timer.reset(); +#endif commonUpdate(); - + #if 0 const F32 INFO_TIME = 1.0f; static LLFrameTimer info_timer; @@ -2399,11 +2932,11 @@ void LLTextureFetch::threadedUpdate() } } #endif - } ////////////////////////////////////////////////////////////////////////////// +// Threads: Tmain void LLTextureFetch::sendRequestListToSimulators() { // All requests @@ -2429,48 +2962,48 @@ void LLTextureFetch::sendRequestListToSimulators() typedef std::map< LLHost, request_list_t > work_request_map_t; work_request_map_t requests; { - LLMutexLock lock2(&mNetworkQueueMutex); - for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) - { - queue_t::iterator curiter = iter++; - LLTextureFetchWorker* req = getWorker(*curiter); - if (!req) + LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq + for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) { - mNetworkQueue.erase(curiter); - continue; // paranoia - } - if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && - (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) - { - // We already received our URL, remove from the queue - llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; - mNetworkQueue.erase(curiter); - continue; - } - if (req->mID == mDebugID) - { - mDebugCount++; // for setting breakpoints - } - if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && - req->mTotalPackets > 0 && - req->mLastPacket >= req->mTotalPackets-1) - { - // We have all the packets... make sure this is high priority + queue_t::iterator curiter = iter++; + LLTextureFetchWorker* req = getWorker(*curiter); + if (!req) + { + mNetworkQueue.erase(curiter); + continue; // paranoia + } + if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && + (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) + { + // We already received our URL, remove from the queue + llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; + mNetworkQueue.erase(curiter); + continue; + } + if (req->mID == mDebugID) + { + mDebugCount++; // for setting breakpoints + } + if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && + req->mTotalPackets > 0 && + req->mLastPacket >= req->mTotalPackets-1) + { + // We have all the packets... make sure this is high priority // req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority); - continue; - } - F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); - { - F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); - if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || - (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || - (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + continue; + } + F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); { - requests[req->mHost].insert(req); + F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); + if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || + (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || + (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + { + requests[req->mHost].insert(req); + } } } - } - } + } // -Mfnq for (work_request_map_t::iterator iter1 = requests.begin(); iter1 != requests.end(); ++iter1) @@ -2493,9 +3026,9 @@ void LLTextureFetch::sendRequestListToSimulators() if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM) { // Initialize packet data based on data read from cache - req->lockWorkMutex(); + req->lockWorkMutex(); // +Mw req->setupPacketData(); - req->unlockWorkMutex(); + req->unlockWorkMutex(); // -Mw } if (0 == sim_request_count) { @@ -2524,12 +3057,12 @@ void LLTextureFetch::sendRequestListToSimulators() mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP); } - req->lockWorkMutex(); + req->lockWorkMutex(); // +Mw req->mSentRequest = LLTextureFetchWorker::SENT_SIM; req->mSimRequestedDiscard = req->mDesiredDiscard; req->mRequestedPriority = req->mImagePriority; req->mRequestedTimer.reset(); - req->unlockWorkMutex(); + req->unlockWorkMutex(); // -Mw sim_request_count++; if (sim_request_count >= IMAGES_PER_REQUEST) { @@ -2550,55 +3083,57 @@ void LLTextureFetch::sendRequestListToSimulators() // Send cancelations { - LLMutexLock lock2(&mNetworkQueueMutex); - if (gMessageSystem && !mCancelQueue.empty()) - { - for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); - iter1 != mCancelQueue.end(); ++iter1) + LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq + if (gMessageSystem && !mCancelQueue.empty()) { - LLHost host = iter1->first; - if (host == LLHost::invalid) - { - host = gAgent.getRegionHost(); - } - S32 request_count = 0; - for (queue_t::iterator iter2 = iter1->second.begin(); - iter2 != iter1->second.end(); ++iter2) + for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); + iter1 != mCancelQueue.end(); ++iter1) { - if (0 == request_count) + LLHost host = iter1->first; + if (host == LLHost::invalid) { - gMessageSystem->newMessageFast(_PREHASH_RequestImage); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + host = gAgent.getRegionHost(); } - gMessageSystem->nextBlockFast(_PREHASH_RequestImage); - gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); - gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); - gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); - gMessageSystem->addU32Fast(_PREHASH_Packet, 0); - gMessageSystem->addU8Fast(_PREHASH_Type, 0); + S32 request_count = 0; + for (queue_t::iterator iter2 = iter1->second.begin(); + iter2 != iter1->second.end(); ++iter2) + { + if (0 == request_count) + { + gMessageSystem->newMessageFast(_PREHASH_RequestImage); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + } + gMessageSystem->nextBlockFast(_PREHASH_RequestImage); + gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); + gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); + gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); + gMessageSystem->addU32Fast(_PREHASH_Packet, 0); + gMessageSystem->addU8Fast(_PREHASH_Type, 0); // llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl; - request_count++; - if (request_count >= IMAGES_PER_REQUEST) + request_count++; + if (request_count >= IMAGES_PER_REQUEST) + { + gMessageSystem->sendSemiReliable(host, NULL, NULL); + request_count = 0; + } + } + if (request_count > 0 && request_count < IMAGES_PER_REQUEST) { gMessageSystem->sendSemiReliable(host, NULL, NULL); - request_count = 0; } } - if (request_count > 0 && request_count < IMAGES_PER_REQUEST) - { - gMessageSystem->sendSemiReliable(host, NULL, NULL); - } + mCancelQueue.clear(); } - mCancelQueue.clear(); - } - } + } // -Mfnq } ////////////////////////////////////////////////////////////////////////////// +// Threads: T* +// Locks: Mw bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) { mRequestedTimer.reset(); @@ -2631,6 +3166,7 @@ bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) return true; } +// Threads: T* bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data) { @@ -2665,14 +3201,14 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 } if (!res) { + mNetworkQueueMutex.lock(); // +Mfnq ++mBadPacketCount; - mNetworkQueueMutex.lock() ; mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq return false; } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw // Copy header data into image object worker->mImageCodec = codec; @@ -2683,10 +3219,12 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 res = worker->insertPacket(0, data, data_size); worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw return res; } + +// Threads: T* bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data) { LLTextureFetchWorker* worker = getWorker(id); @@ -2711,14 +3249,14 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 } if (!res) { + mNetworkQueueMutex.lock(); // +Mfnq ++mBadPacketCount; - mNetworkQueueMutex.lock() ; mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq return false; } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw res = worker->insertPacket(packet_num, data, data_size); @@ -2735,7 +3273,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 removeFromNetworkQueue(worker, true); // failsafe } - if(packet_num >= (worker->mTotalPackets - 1)) + if (packet_num >= (worker->mTotalPackets - 1)) { static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); @@ -2747,12 +3285,14 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow); } } - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw return res; } ////////////////////////////////////////////////////////////////////////////// + +// Threads: T* BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) { BOOL from_cache = FALSE ; @@ -2760,14 +3300,15 @@ BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkMutex() ; - from_cache = worker->mInLocalCache ; - worker->unlockWorkMutex() ; + worker->lockWorkMutex(); // +Mw + from_cache = worker->mInLocalCache; + worker->unlockWorkMutex(); // -Mw } return from_cache ; } +// Threads: T* S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http) { @@ -2781,7 +3322,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r LLTextureFetchWorker* worker = getWorker(id); if (worker && worker->haveWork()) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw state = worker->mState; fetch_dtime = worker->mFetchTimer.getElapsedTimeF32(); request_dtime = worker->mRequestedTimer.getElapsedTimeF32(); @@ -2808,7 +3349,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r } fetch_priority = worker->getPriority(); can_use_http = worker->getCanUseHTTP() ; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } data_progress_p = data_progress; requested_priority_p = requested_priority; @@ -2832,12 +3373,219 @@ void LLTextureFetch::dump() << " STATE: " << worker->sStateDescs[worker->mState] << llendl; } + + llinfos << "LLTextureFetch ACTIVE_HTTP:" << llendl; + for (queue_t::const_iterator iter(mHTTPTextureQueue.begin()); + mHTTPTextureQueue.end() != iter; + ++iter) + { + llinfos << " ID: " << (*iter) << llendl; + } + + llinfos << "LLTextureFetch WAIT_HTTP_RESOURCE:" << llendl; + for (wait_http_res_queue_t::const_iterator iter(mHttpWaitResource.begin()); + mHttpWaitResource.end() != iter; + ++iter) + { + llinfos << " ID: " << (*iter) << llendl; + } +} + +////////////////////////////////////////////////////////////////////////////// + +// HTTP Resource Waiting Methods + +// Threads: Ttf +void LLTextureFetch::addHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.insert(tid); + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: Ttf +void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); + if (mHttpWaitResource.end() != iter) + { + mHttpWaitResource.erase(iter); + } + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: T* +bool LLTextureFetch::isHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); + const bool ret(mHttpWaitResource.end() != iter); + mNetworkQueueMutex.unlock(); // -Mfnq + return ret; +} + +// Release as many requests as permitted from the WAIT_HTTP_RESOURCE2 +// state to the SEND_HTTP_REQ state based on their current priority. +// +// This data structures and code associated with this looks a bit +// indirect and naive but it's done in the name of safety. An +// ordered container may become invalid from time to time due to +// priority changes caused by actions in other threads. State itself +// could also suffer the same fate with canceled operations. Even +// done this way, I'm not fully trusting we're truly safe. This +// module is due for a major refactoring and we'll deal with it then. +// +// Threads: Ttf +// Locks: -Mw (must not hold any worker when called) +void LLTextureFetch::releaseHttpWaiters() +{ + // Use mHttpSemaphore rather than mHTTPTextureQueue.size() + // to avoid a lock. + if (mHttpSemaphore < (HTTP_REQUESTS_IN_QUEUE_HIGH_WATER - HTTP_REQUESTS_IN_QUEUE_LOW_WATER)) + return; + + // Quickly make a copy of all the LLUIDs. Get off the + // mutex as early as possible. + typedef std::vector<LLUUID> uuid_vec_t; + uuid_vec_t tids; + + { + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + + if (mHttpWaitResource.empty()) + return; + tids.reserve(mHttpWaitResource.size()); + tids.assign(mHttpWaitResource.begin(), mHttpWaitResource.end()); + } // -Mfnq + + // Now lookup the UUUIDs to find valid requests and sort + // them in priority order, highest to lowest. We're going + // to modify priority later as a side-effect of releasing + // these objects. That, in turn, would violate the partial + // ordering assumption of std::set, std::map, etc. so we + // don't use those containers. We use a vector and an explicit + // sort to keep the containers valid later. + typedef std::vector<LLTextureFetchWorker *> worker_list_t; + worker_list_t tids2; + + tids2.reserve(tids.size()); + for (uuid_vec_t::iterator iter(tids.begin()); + tids.end() != iter; + ++iter) + { + LLTextureFetchWorker * worker(getWorker(* iter)); + if (worker) + { + tids2.push_back(worker); + } + else + { + // If worker isn't found, this should be due to a request + // for deletion. We signal our recognition that this + // uuid shouldn't be used for resource waiting anymore by + // erasing it from the resource waiter list. That allows + // deleteOK to do final deletion on the worker. + removeHttpWaiter(* iter); + } + } + tids.clear(); + + // Sort into priority order, if necessary and only as much as needed + if (tids2.size() > mHttpSemaphore) + { + LLTextureFetchWorker::Compare compare; + std::partial_sort(tids2.begin(), tids2.begin() + mHttpSemaphore, tids2.end(), compare); + } + + // Release workers up to the high water mark. Since we aren't + // holding any locks at this point, we can be in competition + // with other callers. Do defensive things like getting + // refreshed counts of requests and checking if someone else + // has moved any worker state around.... + for (worker_list_t::iterator iter2(tids2.begin()); tids2.end() != iter2; ++iter2) + { + LLTextureFetchWorker * worker(* iter2); + + worker->lockWorkMutex(); // +Mw + if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE2 != worker->mState) + { + // Not in expected state, remove it, try the next one + worker->unlockWorkMutex(); // -Mw + LL_WARNS("Texture") << "Resource-waited texture " << worker->mID + << " in unexpected state: " << worker->mState + << ". Removing from wait list." + << LL_ENDL; + removeHttpWaiter(worker->mID); + continue; + } + + if (! worker->acquireHttpSemaphore()) + { + // Out of active slots, quit + worker->unlockWorkMutex(); // -Mw + break; + } + + worker->mState = LLTextureFetchWorker::SEND_HTTP_REQ; + worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->unlockWorkMutex(); // -Mw + + removeHttpWaiter(worker->mID); + } +} + +// Threads: T* +void LLTextureFetch::cancelHttpWaiters() +{ + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.clear(); + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: T* +int LLTextureFetch::getHttpWaitersCount() +{ + mNetworkQueueMutex.lock(); // +Mfnq + int ret(mHttpWaitResource.size()); + mNetworkQueueMutex.unlock(); // -Mfnq + return ret; +} + + +// Threads: T* +void LLTextureFetch::updateStateStats(U32 cache_read, U32 cache_write, U32 res_wait) +{ + LLMutexLock lock(&mQueueMutex); // +Mfq + + mTotalCacheReadCount += cache_read; + mTotalCacheWriteCount += cache_write; + mTotalResourceWaitCount += res_wait; +} // -Mfq + + +// Threads: T* +void LLTextureFetch::getStateStats(U32 * cache_read, U32 * cache_write, U32 * res_wait) +{ + U32 ret1(0U), ret2(0U), ret3(0U); + + { + LLMutexLock lock(&mQueueMutex); // +Mfq + ret1 = mTotalCacheReadCount; + ret2 = mTotalCacheWriteCount; + ret3 = mTotalResourceWaitCount; + } // -Mfq + + *cache_read = ret1; + *cache_write = ret2; + *res_wait = ret3; } ////////////////////////////////////////////////////////////////////////////// // cross-thread command methods +// Threads: T* void LLTextureFetch::commandSetRegion(U64 region_handle) { TFReqSetRegion * req = new TFReqSetRegion(region_handle); @@ -2845,6 +3593,7 @@ void LLTextureFetch::commandSetRegion(U64 region_handle) cmdEnqueue(req); } +// Threads: T* void LLTextureFetch::commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, @@ -2855,6 +3604,7 @@ void LLTextureFetch::commandSendMetrics(const std::string & caps_url, cmdEnqueue(req); } +// Threads: T* void LLTextureFetch::commandDataBreak() { // The pedantically correct way to implement this is to create a command @@ -2865,30 +3615,33 @@ void LLTextureFetch::commandDataBreak() LLTextureFetch::svMetricsDataBreak = true; } +// Threads: T* void LLTextureFetch::cmdEnqueue(TFRequest * req) { - lockQueue(); + lockQueue(); // +Mfq mCommands.push_back(req); - unlockQueue(); + unlockQueue(); // -Mfq unpause(); } +// Threads: T* LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue() { TFRequest * ret = 0; - lockQueue(); + lockQueue(); // +Mfq if (! mCommands.empty()) { ret = mCommands.front(); mCommands.erase(mCommands.begin()); } - unlockQueue(); + unlockQueue(); // -Mfq return ret; } +// Threads: Ttf void LLTextureFetch::cmdDoWork() { if (mDebugPause) @@ -2912,6 +3665,37 @@ void LLTextureFetch::cmdDoWork() namespace { + +// Example of a simple notification handler for metrics +// delivery notification. Earlier versions of the code used +// a Responder that tried harder to detect delivery breaks +// but it really isn't that important. If someone wants to +// revisit that effort, here is a place to start. +class AssetReportHandler : public LLCore::HttpHandler +{ +public: + + // Threads: Ttf + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) + { + LLCore::HttpStatus status(response->getStatus()); + + if (status) + { + LL_WARNS("Texture") << "Successfully delivered asset metrics to grid." + << LL_ENDL; + } + else + { + LL_WARNS("Texture") << "Error delivering asset metrics to grid. Reason: " + << status.toString() << LL_ENDL; + } + } +}; // end class AssetReportHandler + +AssetReportHandler stats_handler; + + /** * Implements the 'Set Region' command. * @@ -2942,73 +3726,8 @@ TFReqSendMetrics::~TFReqSendMetrics() bool TFReqSendMetrics::doWork(LLTextureFetch * fetcher) { - /* - * HTTP POST responder. Doesn't do much but tries to - * detect simple breaks in recording the metrics stream. - * - * The 'volatile' modifiers don't indicate signals, - * mmap'd memory or threads, really. They indicate that - * the referenced data is part of a pseudo-closure for - * this responder rather than being required for correct - * operation. - * - * We don't try very hard with the POST request. We give - * it one shot and that's more-or-less it. With a proper - * refactoring of the LLQueuedThread usage, these POSTs - * could be put in a request object and made more reliable. - */ - class lcl_responder : public LLCurl::Responder - { - public: - lcl_responder(LLTextureFetch * fetcher, - S32 expected_sequence, - volatile const S32 & live_sequence, - volatile bool & reporting_break, - volatile bool & reporting_started) - : LLCurl::Responder(), - mFetcher(fetcher), - mExpectedSequence(expected_sequence), - mLiveSequence(live_sequence), - mReportingBreak(reporting_break), - mReportingStarted(reporting_started) - { - mFetcher->incrCurlPOSTCount(); - } - - ~lcl_responder() - { - mFetcher->decrCurlPOSTCount(); - } - - // virtual - void error(U32 status_num, const std::string & reason) - { - if (mLiveSequence == mExpectedSequence) - { - mReportingBreak = true; - } - LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service. Reason: " - << reason << LL_ENDL; - } - - // virtual - void result(const LLSD & content) - { - if (mLiveSequence == mExpectedSequence) - { - mReportingBreak = false; - mReportingStarted = true; - } - } - - private: - LLTextureFetch * mFetcher; - S32 mExpectedSequence; - volatile const S32 & mLiveSequence; - volatile bool & mReportingBreak; - volatile bool & mReportingStarted; - - }; // class lcl_responder + static const U32 report_priority(1); + static LLCore::HttpHandler * const handler(fetcher->isQAMode() || true ? &stats_handler : NULL); if (! gViewerAssetStatsThread1) return true; @@ -3036,21 +3755,26 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) // Update sequence number if (S32_MAX == ++report_sequence) report_sequence = 0; - + reporting_started = true; + // Limit the size of the stats report if necessary. merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd); if (! mCapsURL.empty()) { - LLCurlRequest::headers_t headers; - fetcher->getCurlRequest().post(mCapsURL, - headers, - merged_llsd, - new lcl_responder(fetcher, - report_sequence, - report_sequence, - LLTextureFetch::svMetricsDataBreak, - reporting_started)); + LLCore::BufferArray * ba = new LLCore::BufferArray; + LLCore::BufferArrayStream bas(ba); + LLSDSerialize::toXML(merged_llsd, bas); + + fetcher->getHttpRequest().requestPost(fetcher->getPolicyClass(), + report_priority, + mCapsURL, + ba, + NULL, + fetcher->getMetricsHeaders(), + handler); + ba->release(); + LLTextureFetch::svMetricsDataBreak = false; } else { @@ -3108,6 +3832,7 @@ truncate_viewer_metrics(int max_regions, LLSD & metrics) } // end of anonymous namespace + /////////////////////////////////////////////////////////////////////////////////////////// //Start LLTextureFetchDebugger /////////////////////////////////////////////////////////////////////////////////////////// @@ -3161,48 +3886,13 @@ private: S32 mID; }; -class LLDebuggerHTTPResponder : public LLCurl::Responder -{ -public: - LLDebuggerHTTPResponder(LLTextureFetchDebugger* debugger, S32 index) - : mDebugger(debugger), mIndex(index) - { - } - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - bool success = false; - bool partial = false; - if (HTTP_OK <= status && status < HTTP_MULTIPLE_CHOICES) - { - success = true; - if (HTTP_PARTIAL_CONTENT == status) // partial information - { - partial = true; - } - } - if (!success) - { - llinfos << "Fetch Debugger : CURL GET FAILED, index = " << mIndex << ", status:" << status << " reason:" << reason << llendl; - } - mDebugger->callbackHTTP(mIndex, channels, buffer, partial, success); - mDebugger->getCurlGetRequest()->completeRequest(0); - } - virtual bool followRedir() - { - return true; - } -private: - LLTextureFetchDebugger* mDebugger; - S32 mIndex; -}; LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextureCache* cache, LLImageDecodeThread* imagedecodethread) : mFetcher(fetcher), mTextureCache(cache), mImageDecodeThread(imagedecodethread), - mCurlGetRequest(NULL) + mHttpHeaders(NULL), + mHttpPolicyClass(fetcher->getPolicyClass()) { init(); } @@ -3212,6 +3902,11 @@ LLTextureFetchDebugger::~LLTextureFetchDebugger() mFetchingHistory.clear(); mStopDebug = TRUE; tryToStopDebug(); + if (mHttpHeaders) + { + mHttpHeaders->release(); + mHttpHeaders = NULL; + } } void LLTextureFetchDebugger::init() @@ -3249,6 +3944,12 @@ void LLTextureFetchDebugger::init() mFreezeHistory = FALSE; mStopDebug = FALSE; mClearHistory = FALSE; + + if (! mHttpHeaders) + { + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); + } } void LLTextureFetchDebugger::startWork(e_debug_state state) @@ -3386,7 +4087,7 @@ void LLTextureFetchDebugger::tryToStopDebug() { mTextureCache->readComplete(mFetchingHistory[i].mCacheHandle, true); } - } + } break; case WRITE_CACHE: for(S32 i = 0 ; i < size; i++) @@ -3428,6 +4129,7 @@ void LLTextureFetchDebugger::tryToStopDebug() if(mClearHistory) { mFetchingHistory.clear(); + mHandleToFetchIndex.clear(); init(); mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32(); //reset } @@ -3606,6 +4308,7 @@ void LLTextureFetchDebugger::debugHTTP() mFetchingHistory[i].mCurlState = FetchEntry::CURL_NOT_DONE; mFetchingHistory[i].mCurlReceivedSize = 0; mFetchingHistory[i].mHTTPFailCount = 0; + mFetchingHistory[i].mFormattedImage = NULL; } mNbCurlRequests = 0; mNbCurlCompleted = 0; @@ -3620,15 +4323,12 @@ S32 LLTextureFetchDebugger::fillCurlQueue() mNbCurlCompleted = mFetchingHistory.size(); return 0; } - S32 size = mFetchingHistory.size(); - - if (mNbCurlRequests == size) //all issued + if (mNbCurlRequests > HTTP_REQUESTS_IN_QUEUE_LOW_WATER) { - return 0; - } + return mNbCurlRequests; + } - S32 counter = 8; - mNbCurlRequests = 0; + S32 size = mFetchingHistory.size(); for (S32 i = 0 ; i < size ; i++) { mNbCurlRequests++; @@ -3643,13 +4343,28 @@ S32 LLTextureFetchDebugger::fillCurlQueue() requestedSize = llmax(0,requestedSize); // We request the whole file if the size was set to an absurdly high value (meaning all file) requestedSize = (requestedSize == 33554432 ? 0 : requestedSize); - std::vector<std::string> headers; - headers.push_back("Accept: image/x-j2c"); - mCurlGetRequest->getByteRange(texture_url, headers, 0, requestedSize, 0x10000, new LLDebuggerHTTPResponder(this, i)); - - mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS; - counter--; - if(counter < 1) + + LLCore::HttpHandle handle = mFetcher->getHttpRequest().requestGetByteRange(mHttpPolicyClass, + LLWorkerThread::PRIORITY_LOWBITS, + texture_url, + 0, + requestedSize, + NULL, + mHttpHeaders, + this); + if (LLCORE_HTTP_HANDLE_INVALID != handle) + { + mHandleToFetchIndex[handle] = i; + mFetchingHistory[i].mHttpHandle = handle; + mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS; + mNbCurlRequests++; + // Hack + if (mNbCurlRequests == HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) // emulate normal pipeline + { + break; + } + } + else { break; } @@ -3879,9 +4594,8 @@ bool LLTextureFetchDebugger::update(F32 max_time) } break; case HTTP_FETCHING: - mCurlGetRequest->process(); - mCurlGetRequest->nextRequests(); - LLCurl::getCurlThread()->update(1); + // Do some notifications... + mFetcher->getHttpRequest().update(10); if (!fillCurlQueue() && mNbCurlCompleted == mFetchingHistory.size()) { mHTTPTime = mTimer.getElapsedTimeF32() ; @@ -3952,6 +4666,28 @@ bool LLTextureFetchDebugger::update(F32 max_time) return mState == IDLE; } +void LLTextureFetchDebugger::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + handle_fetch_map_t::iterator iter(mHandleToFetchIndex.find(handle)); + if (mHandleToFetchIndex.end() == iter) + { + llinfos << "Fetch Debugger : Couldn't find handle " << handle << " in fetch list." << llendl; + return; + } + + S32 fetch_ind(iter->second); + mHandleToFetchIndex.erase(iter); + if (fetch_ind >= mFetchingHistory.size() || mFetchingHistory[fetch_ind].mHttpHandle != handle) + { + llinfos << "Fetch Debugger : Handle and fetch object in disagreement. Punting." << llendl; + } + else + { + callbackHTTP(mFetchingHistory[fetch_ind], response); + mFetchingHistory[fetch_ind].mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; // Not valid after notification + } +} + void LLTextureFetchDebugger::callbackCacheRead(S32 id, bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal) { @@ -3978,50 +4714,60 @@ void LLTextureFetchDebugger::callbackDecoded(S32 id, bool success, LLImageRaw* r } } -void LLTextureFetchDebugger::callbackHTTP(S32 id, const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success) +void LLTextureFetchDebugger::callbackHTTP(FetchEntry & fetch, LLCore::HttpResponse * response) { - if (success) + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + + LLCore::HttpStatus status(response->getStatus()); + mNbCurlRequests--; + if (status) { - mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE; + const bool partial(par_status == status); + LLCore::BufferArray * ba(response->getBody()); // *Not* holding reference to body + + fetch.mCurlState = FetchEntry::CURL_DONE; mNbCurlCompleted++; - S32 data_size = buffer->countAfter(channels.in(), NULL); - mFetchingHistory[id].mCurlReceivedSize += data_size; - //llinfos << "Fetch Debugger : got results for " << id << ", data_size = " << data_size << ", received = " << mFetchingHistory[id].mCurlReceivedSize << ", requested = " << mFetchingHistory[id].mRequestedSize << ", partial = " << partial << llendl; - if ((mFetchingHistory[id].mCurlReceivedSize >= mFetchingHistory[id].mRequestedSize) || !partial || (mFetchingHistory[id].mRequestedSize == 600)) + S32 data_size = ba ? ba->size() : 0; + fetch.mCurlReceivedSize += data_size; + //llinfos << "Fetch Debugger : got results for " << fetch.mID << ", data_size = " << data_size << ", received = " << fetch.mCurlReceivedSize << ", requested = " << fetch.mRequestedSize << ", partial = " << partial << llendl; + if ((fetch.mCurlReceivedSize >= fetch.mRequestedSize) || !partial || (fetch.mRequestedSize == 600)) { U8* d_buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size); - buffer->readAfter(channels.in(), NULL, d_buffer, data_size); + if (ba) + { + ba->read(0, d_buffer, data_size); + } - mFetchingHistory[id].mFormattedImage = NULL; + llassert_always(fetch.mFormattedImage.isNull()); { // For now, create formatted image based on extension - std::string texture_url = mHTTPUrl + "/?texture_id=" + mFetchingHistory[id].mID.asString().c_str(); + std::string texture_url = mHTTPUrl + "/?texture_id=" + fetch.mID.asString().c_str(); std::string extension = gDirUtilp->getExtension(texture_url); - mFetchingHistory[id].mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension)); - if (mFetchingHistory[id].mFormattedImage.isNull()) + fetch.mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension)); + if (fetch.mFormattedImage.isNull()) { - mFetchingHistory[id].mFormattedImage = new LLImageJ2C; // default + fetch.mFormattedImage = new LLImageJ2C; // default } } - mFetchingHistory[id].mFormattedImage->setData(d_buffer, data_size); + fetch.mFormattedImage->setData(d_buffer, data_size); } } else //failed { - mFetchingHistory[id].mHTTPFailCount++; - if(mFetchingHistory[id].mHTTPFailCount < 5) + llinfos << "Fetch Debugger : CURL GET FAILED, ID = " << fetch.mID + << ", status: " << status.toHex() + << " reason: " << status.toString() << llendl; + fetch.mHTTPFailCount++; + if(fetch.mHTTPFailCount < 5) { // Fetch will have to be redone - mFetchingHistory[id].mCurlState = FetchEntry::CURL_NOT_DONE; - mNbCurlRequests--; + fetch.mCurlState = FetchEntry::CURL_NOT_DONE; } else //skip { - mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE; + fetch.mCurlState = FetchEntry::CURL_DONE; mNbCurlCompleted++; } } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index f5072a79f1..3a99432b48 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, 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 @@ -27,19 +27,24 @@ #ifndef LL_LLTEXTUREFETCH_H #define LL_LLTEXTUREFETCH_H +#include <vector> +#include <map> + #include "lldir.h" #include "llimage.h" #include "lluuid.h" #include "llworkerthread.h" -#include "llcurl.h" #include "lltextureinfo.h" #include "llapr.h" #include "llimageworker.h" -//#include "lltexturecache.h" +#include "llcurl.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" +#include "httphandler.h" class LLViewerTexture; class LLTextureFetchWorker; -class HTTPGetResponder; class LLImageDecodeThread; class LLHost; class LLViewerAssetStats; @@ -47,10 +52,10 @@ class LLTextureFetchDebugger; class LLTextureCache; // Interface class + class LLTextureFetch : public LLWorkerThread { friend class LLTextureFetchWorker; - friend class HTTPGetResponder; public: LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode); @@ -58,70 +63,201 @@ public: class TFRequest; - /*virtual*/ S32 update(F32 max_time_ms); - void shutDownTextureCacheThread() ; //called in the main thread after the TextureCacheThread shuts down. - void shutDownImageDecodeThread() ; //called in the main thread after the ImageDecodeThread shuts down. + // Threads: Tmain + /*virtual*/ S32 update(F32 max_time_ms); + + // called in the main thread after the TextureCacheThread shuts down. + // Threads: Tmain + void shutDownTextureCacheThread(); + + //called in the main thread after the ImageDecodeThread shuts down. + // Threads: Tmain + void shutDownImageDecodeThread(); + // Threads: T* (but Tmain mostly) bool createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 w, S32 h, S32 c, S32 discard, bool needs_aux, bool can_use_http); + + // Requests that a fetch operation be deleted from the queue. + // If @cancel is true, also stops any I/O operations pending. + // Actual delete will be scheduled and performed later. + // + // Note: This *looks* like an override/variant of the + // base class's deleteRequest() but is functionally quite + // different. + // + // Threads: T* void deleteRequest(const LLUUID& id, bool cancel); + void deleteAllRequests(); + + // Threads: T* bool getRequestFinished(const LLUUID& id, S32& discard_level, LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux); + + // Threads: T* bool updateRequestPriority(const LLUUID& id, F32 priority); + // Threads: T* bool receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data); + + // Threads: T* bool receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data); + // Threads: T* (but not safe) void setTextureBandwidth(F32 bandwidth) { mTextureBandwidth = bandwidth; } + + // Threads: T* (but not safe) F32 getTextureBandwidth() { return mTextureBandwidth; } - // Debug + // Threads: T* BOOL isFromLocalCache(const LLUUID& id); + + // @return Magic number giving the internal state of the + // request. We should make these codes public if we're + // going to return them as a status value. + // + // Threads: T* S32 getFetchState(const LLUUID& id, F32& decode_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http); + + // Debug utility - generally not safe void dump(); - S32 getNumRequests() ; + + // Threads: T* + S32 getNumRequests(); + + // Threads: T* + S32 getNumHTTPRequests(); + + // Threads: T* + U32 getTotalNumHTTPRequests(); - // Public for access by callbacks + // Threads: T* S32 getPending(); + + // Threads: T* void lockQueue() { mQueueMutex.lock(); } + + // Threads: T* void unlockQueue() { mQueueMutex.unlock(); } + + // Threads: T* LLTextureFetchWorker* getWorker(const LLUUID& id); + + // Threads: T* + // Locks: Mfq LLTextureFetchWorker* getWorkerAfterLock(const LLUUID& id); - LLTextureInfo* getTextureInfo() { return &mTextureInfo; } - // Commands available to other threads to control metrics gathering operations. + + // Threads: T* void commandSetRegion(U64 region_handle); + + // Threads: T* void commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, LLViewerAssetStats * main_stats); + + // Threads: T* void commandDataBreak(); - LLCurlTextureRequest & getCurlRequest() { return *mCurlGetRequest; } + // Threads: T* + LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; } + + // Threads: T* + LLCore::HttpRequest::policy_t getPolicyClass() const { return mHttpPolicyClass; } + + // Return a pointer to the shared metrics headers definition. + // Does not increment the reference count, caller is required + // to do that to hold a reference for any length of time. + // + // Threads: T* + LLCore::HttpHeaders * getMetricsHeaders() const { return mHttpMetricsHeaders; } bool isQAMode() const { return mQAMode; } - // Curl POST counter maintenance - inline void incrCurlPOSTCount() { mCurlPOSTRequestCount++; } - inline void decrCurlPOSTCount() { mCurlPOSTRequestCount--; } + // ---------------------------------- + // HTTP resource waiting methods + + // Threads: T* + void addHttpWaiter(const LLUUID & tid); + + // Threads: T* + void removeHttpWaiter(const LLUUID & tid); + // Threads: T* + bool isHttpWaiter(const LLUUID & tid); + + // If there are slots, release one or more LLTextureFetchWorker + // requests from resource wait state (WAIT_HTTP_RESOURCE) to + // active (SEND_HTTP_REQ). + // + // Because this will modify state of many workers, you may not + // hold any Mw lock while calling. This makes it a little + // inconvenient to use but that's the rule. + // + // Threads: T* + // Locks: -Mw (must not hold any worker when called) + void releaseHttpWaiters(); + + // Threads: T* + void cancelHttpWaiters(); + + // Threads: T* + int getHttpWaitersCount(); + // ---------------------------------- + // Stats management + + // Add given counts to the global totals for the states/requests + // Threads: T* + void updateStateStats(U32 cache_read, U32 cache_write, U32 res_wait); + + // Return the global counts + // Threads: T* + void getStateStats(U32 * cache_read, U32 * cache_write, U32 * res_wait); + + // ---------------------------------- + protected: + // Threads: T* (but Ttf in practice) void addToNetworkQueue(LLTextureFetchWorker* worker); + + // Threads: T* void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); + + // Threads: T* void addToHTTPQueue(const LLUUID& id); - void removeRequest(LLTextureFetchWorker* worker, bool cancel); + // XXX possible delete + // Threads: T* + void removeFromHTTPQueue(const LLUUID& id, S32 received_size); + + // Identical to @deleteRequest but with different arguments + // (caller already has the worker pointer). + // + // Threads: T* + void removeRequest(LLTextureFetchWorker* worker, bool cancel); + // Overrides from the LLThread tree + // Locks: Ct bool runCondition(); private: + // Threads: Tmain void sendRequestListToSimulators(); + + // Threads: Ttf /*virtual*/ void startThread(void); + + // Threads: Ttf /*virtual*/ void endThread(void); + + // Threads: Ttf /*virtual*/ void threadedUpdate(void); + + // Threads: Ttf void commonUpdate(); // Metrics command helpers @@ -132,6 +268,8 @@ private: * Takes ownership of the TFRequest object. * * Method locks the command queue. + * + * Threads: T* */ void cmdEnqueue(TFRequest *); @@ -142,6 +280,8 @@ private: * Caller acquires ownership of the object and must dispose of it. * * Method locks the command queue. + * + * Threads: T* */ TFRequest * cmdDequeue(); @@ -151,6 +291,8 @@ private: * additional commands. * * Method locks the command queue. + * + * Threads: Ttf */ void cmdDoWork(); @@ -170,38 +312,64 @@ private: LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlTextureRequest* mCurlGetRequest; - + // Map of all requests by UUID typedef std::map<LLUUID,LLTextureFetchWorker*> map_t; - map_t mRequestMap; + map_t mRequestMap; // Mfq // Set of requests that require network data typedef std::set<LLUUID> queue_t; - queue_t mNetworkQueue; - queue_t mHTTPTextureQueue; + queue_t mNetworkQueue; // Mfnq + queue_t mHTTPTextureQueue; // Mfnq typedef std::map<LLHost,std::set<LLUUID> > cancel_queue_t; - cancel_queue_t mCancelQueue; - F32 mTextureBandwidth; - F32 mMaxBandwidth; + cancel_queue_t mCancelQueue; // Mfnq + F32 mTextureBandwidth; // <none> + F32 mMaxBandwidth; // Mfnq LLTextureInfo mTextureInfo; + // XXX possible delete + U32 mHTTPTextureBits; // Mfnq + + // XXX possible delete + //debug use + U32 mTotalHTTPRequests; + // Out-of-band cross-thread command queue. This command queue // is logically tied to LLQueuedThread's list of // QueuedRequest instances and so must be covered by the // same locks. typedef std::vector<TFRequest *> command_queue_t; - command_queue_t mCommands; + command_queue_t mCommands; // Mfq // If true, modifies some behaviors that help with QA tasks. const bool mQAMode; - // Count of POST requests outstanding. We maintain the count - // indirectly in the CURL request responder's ctor and dtor and - // use it when determining whether or not to sleep the thread. Can't - // use the LLCurl module's request counter as it isn't thread compatible. - // *NOTE: Don't mix Atomic and static, apr_initialize must be called first. - LLAtomic32<S32> mCurlPOSTRequestCount; + // Interfaces and objects into the core http library used + // to make our HTTP requests. These replace the various + // LLCurl interfaces used in the past. + LLCore::HttpRequest * mHttpRequest; // Ttf + LLCore::HttpOptions * mHttpOptions; // Ttf + LLCore::HttpHeaders * mHttpHeaders; // Ttf + LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf + LLCore::HttpRequest::policy_t mHttpPolicyClass; // T* + + // We use a resource semaphore to keep HTTP requests in + // WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the + // transport. This keeps them near where they can be cheaply + // reprioritized rather than dumping them all across a thread + // where it's more expensive to get at them. Requests in either + // SEND_HTTP_REQ or WAIT_HTTP_REQ charge against the semaphore + // and tracking state transitions is critical to liveness. + LLAtomicS32 mHttpSemaphore; // Ttf + Tmain + + typedef std::set<LLUUID> wait_http_res_queue_t; + wait_http_res_queue_t mHttpWaitResource; // Mfnq + + // Cumulative stats on the states/requests issued by + // textures running through here. + U32 mTotalCacheReadCount; // Mfq + U32 mTotalCacheWriteCount; // Mfq + U32 mTotalResourceWaitCount; // Mfq public: // A probabilistically-correct indicator that the current @@ -237,7 +405,7 @@ public: //debug use class LLViewerFetchedTexture; -class LLTextureFetchDebugger +class LLTextureFetchDebugger : public LLCore::HttpHandler { friend class LLTextureFetch; public: @@ -282,11 +450,13 @@ private: e_curl_state mCurlState; S32 mCurlReceivedSize; S32 mHTTPFailCount; + LLCore::HttpHandle mHttpHandle; FetchEntry() : mDecodedLevel(-1), mFetchedSize(0), - mDecodedSize(0) + mDecodedSize(0), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID) {} FetchEntry(LLUUID& id, S32 r_size, /*S32 f_discard, S32 c,*/ S32 level, S32 f_size, S32 d_size) : mID(id), @@ -295,10 +465,15 @@ private: mFetchedSize(f_size), mDecodedSize(d_size), mNeedsAux(false), - mHTTPFailCount(0) + mHTTPFailCount(0), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID) {} }; - std::vector<FetchEntry> mFetchingHistory; + typedef std::vector<FetchEntry> fetch_list_t; + fetch_list_t mFetchingHistory; + + typedef std::map<LLCore::HttpHandle, S32> handle_fetch_map_t; + handle_fetch_map_t mHandleToFetchIndex; e_debug_state mState; @@ -319,7 +494,8 @@ private: LLTextureFetch* mFetcher; LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlTextureRequest* mCurlGetRequest; + LLCore::HttpHeaders* mHttpHeaders; + LLCore::HttpRequest::policy_t mHttpPolicyClass; S32 mNumFetchedTextures; S32 mNumCacheHits; @@ -359,21 +535,18 @@ public: void clearHistory(); void addHistoryEntry(LLTextureFetchWorker* worker); - void setCurlGetRequest(LLCurlTextureRequest* request) { mCurlGetRequest = request;} - LLCurlTextureRequest* getCurlGetRequest() { return mCurlGetRequest;} + // Inherited from LLCore::HttpHandler + // Threads: Ttf + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); void startWork(e_debug_state state); void setStopDebug() {mStopDebug = TRUE;} void tryToStopDebug(); //stop everything - void callbackCacheRead(S32 id, bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); void callbackCacheWrite(S32 id, bool success); void callbackDecoded(S32 id, bool success, LLImageRaw* raw, LLImageRaw* aux); - void callbackHTTP(S32 id, const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success); - + void callbackHTTP(FetchEntry & fetch, LLCore::HttpResponse * response); e_debug_state getState() {return mState;} S32 getNumFetchedTextures() {return mNumFetchedTextures;} diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index c60b4155a0..16c42dbd43 100755 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, 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 @@ -74,7 +74,7 @@ static std::string title_string4(" W x H (Dis) Mem"); static S32 title_x1 = 0; static S32 title_x2 = 460; static S32 title_x3 = title_x2 + 40; -static S32 title_x4 = title_x3 + 50; +static S32 title_x4 = title_x3 + 46; static S32 texture_bar_height = 8; //////////////////////////////////////////////////////////////////////////// @@ -232,6 +232,8 @@ void LLTextureBar::draw() { "DSK", LLColor4::blue }, // CACHE_POST { "NET", LLColor4::green }, // LOAD_FROM_NETWORK { "SIM", LLColor4::green }, // LOAD_FROM_SIMULATOR + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE2 { "REQ", LLColor4::yellow },// SEND_HTTP_REQ { "HTP", LLColor4::green }, // WAIT_HTTP_REQ { "DEC", LLColor4::yellow },// DECODE_IMAGE @@ -239,7 +241,7 @@ void LLTextureBar::draw() { "WRT", LLColor4::purple },// WRITE_TO_CACHE { "WRT", LLColor4::orange },// WAIT_ON_WRITE { "END", LLColor4::red }, // DONE -#define LAST_STATE 12 +#define LAST_STATE 14 { "CRE", LLColor4::magenta }, // LAST_STATE+1 { "FUL", LLColor4::green }, // LAST_STATE+2 { "BAD", LLColor4::red }, // LAST_STATE+3 @@ -345,7 +347,7 @@ void LLTextureBar::draw() // draw the image size at the end { - std::string num_str = llformat("%3dx%3d (%d) %7d", mImagep->getWidth(), mImagep->getHeight(), + std::string num_str = llformat("%3dx%3d (%2d) %7d", mImagep->getWidth(), mImagep->getHeight(), mImagep->getDiscardLevel(), mImagep->hasGLTexture() ? mImagep->getTextureMemory() : 0); LLFontGL::getFontMonospace()->renderUTF8(num_str, 0, title_x4, getRect().getHeight(), color, LLFontGL::LEFT, LLFontGL::TOP); @@ -514,14 +516,18 @@ void LLGLTexMemBar::draw() S32 v_offset = 0;//(S32)((texture_bar_height + 2.2f) * mTextureView->mNumTextureBars + 2.0f); F32 total_texture_downloaded = (F32)gTotalTextureBytes / (1024 * 1024); F32 total_object_downloaded = (F32)gTotalObjectBytes / (1024 * 1024); - U32 total_http_requests = LLAppViewer::getTextureFetch()->getCurlRequest().getTotalIssuedRequests() ; + U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests(); //---------------------------------------------------------------------------- LLGLSUIDefault gls_ui; LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); LLColor4 color; - - std::string text = ""; + // Gray background using completely magic numbers + gGL.color4f(0.f, 0.f, 0.f, 0.25f); + // const LLRect & rect(getRect()); + // gl_rect_2d(-4, v_offset, rect.mRight - rect.mLeft + 2, v_offset + line_height*4); + + std::string text = ""; LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*6, text_color, LLFontGL::LEFT, LLFontGL::TOP); @@ -531,14 +537,26 @@ void LLGLTexMemBar::draw() bound_mem, max_bound_mem, LLRenderTarget::sBytesAllocated/(1024*1024), - LLImageRaw::sGlobalRawMemory >> 20, discard_bias, - cache_usage, cache_max_usage); + LLImageRaw::sGlobalRawMemory >> 20, + discard_bias, + cache_usage, + cache_max_usage); + //, cache_entries, cache_max_entries + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4, text_color, LLFontGL::LEFT, LLFontGL::TOP); - text = llformat("Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d", - total_texture_downloaded, total_object_downloaded, total_http_requests); - //, cache_entries, cache_max_entries + U32 cache_read(0U), cache_write(0U), res_wait(0U); + LLAppViewer::getTextureFetch()->getStateStats(&cache_read, &cache_write, &res_wait); + + text = llformat("Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d Cread: %u Cwrite: %u Rwait: %u", + total_texture_downloaded, + total_object_downloaded, + total_http_requests, + cache_read, + cache_write, + res_wait); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, text_color, LLFontGL::LEFT, LLFontGL::TOP); @@ -552,7 +570,7 @@ void LLGLTexMemBar::draw() LLAppViewer::getTextureCache()->getNumReads(), LLAppViewer::getTextureCache()->getNumWrites(), LLLFSThread::sLocal->getPending(), LLImageRaw::sRawImageCount, - LLAppViewer::getTextureFetch()->getCurlRequest().getNumRequests(), + LLAppViewer::getTextureFetch()->getNumHTTPRequests(), LLAppViewer::getImageDecodeThread()->getPending(), gTextureList.mCreateTextureList.size()); diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 3c3aba23f8..ebf5cc87d1 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1060,9 +1060,21 @@ class Linux_i686Manifest(LinuxManifest): super(Linux_i686Manifest, self).construct() if self.prefix("../packages/lib/release", dst="lib"): - self.path("libapr-1.so*") - self.path("libaprutil-1.so*") - self.path("libbreakpad_client.so*") + self.path("libapr-1.so") + self.path("libapr-1.so.0") + self.path("libapr-1.so.0.4.5") + self.path("libaprutil-1.so") + self.path("libaprutil-1.so.0") + self.path("libaprutil-1.so.0.4.1") + self.path("libboost_program_options-mt.so.1.48.0") + self.path("libboost_regex-mt.so.1.48.0") + self.path("libboost_thread-mt.so.1.48.0") + self.path("libboost_filesystem-mt.so.1.48.0") + self.path("libboost_signals-mt.so.1.48.0") + self.path("libboost_system-mt.so.1.48.0") + self.path("libbreakpad_client.so.0.0.0") + self.path("libbreakpad_client.so.0") + self.path("libbreakpad_client.so") self.path("libcollada14dom.so") self.path("libdb*.so") self.path("libcrypto.so.*") |