From cdbd06e8ed6e3f4285a61f5c0b607a65dfdf8dfd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 25 May 2022 19:22:09 -0400 Subject: Increment viewer version to 6.6.1 following promotion of DRTVWR-546 --- indra/newview/VIEWER_VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 826f5ce030..09a7391e4e 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.6.0 +6.6.1 -- cgit v1.2.3 From ef87eb7fa80a72b94d67d5ab680f60a837dd1ddd Mon Sep 17 00:00:00 2001 From: Cosmic Linden Date: Tue, 31 May 2022 12:49:53 -0700 Subject: SL-17483: Make ThreadPool inherit LLInstanceTracker (cherry picked from commit 41d6a0e222241606c317281e2f0b211e16813dd5) --- indra/llcommon/threadpool.cpp | 1 + indra/llcommon/threadpool.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp index ba914035e2..d5adf11264 100644 --- a/indra/llcommon/threadpool.cpp +++ b/indra/llcommon/threadpool.cpp @@ -22,6 +22,7 @@ #include "stringize.h" LL::ThreadPool::ThreadPool(const std::string& name, size_t threads, size_t capacity): + super(name), mQueue(name, capacity), mName("ThreadPool:" + name), mThreadCount(threads) diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h index b79c9b9090..f8eec3b457 100644 --- a/indra/llcommon/threadpool.h +++ b/indra/llcommon/threadpool.h @@ -22,8 +22,10 @@ namespace LL { - class ThreadPool + class ThreadPool: public LLInstanceTracker { + private: + using super = LLInstanceTracker; public: /** * Pass ThreadPool a string name. This can be used to look up the -- cgit v1.2.3 From ac99e979f43d49402a24b2f58a154c4ef1583efd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 9 Jun 2022 10:18:29 -0400 Subject: SL-17483: Make it possible to override width of any ThreadPool. Introduce CommonControl, which in a running viewer (or any program containing an LLViewerControlListener instance) gives access to LLViewerControl functionality, e.g. getting, setting or enumerating control variables -- without introducing a link dependency on newview. Make ThreadPool's constructor consult CommonControl to check for an override for the width of the new ThreadPool in the Global (i.e. gSavedSettings) setting ThreadPoolSizes, and honor that if found. Introduce static ThreadPool methods getConfiguredWidth(), to query for such an override on any particular ThreadPool name; and getWidth(), to ask for the width of an instance if that instance already exists, else the width with which it *would* be instantiated. --- indra/llcommon/CMakeLists.txt | 2 + indra/llcommon/commoncontrol.cpp | 104 +++++++++++++++++++++++++++++++++++++++ indra/llcommon/commoncontrol.h | 75 ++++++++++++++++++++++++++++ indra/llcommon/threadpool.cpp | 51 ++++++++++++++++++- indra/llcommon/threadpool.h | 30 ++++++++++- 5 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 indra/llcommon/commoncontrol.cpp create mode 100644 indra/llcommon/commoncontrol.h diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index ca8b5e946f..4dbf1282c4 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -29,6 +29,7 @@ include_directories( # ${LLCOMMON_LIBRARIES}) set(llcommon_SOURCE_FILES + commoncontrol.cpp indra_constants.cpp llallocator.cpp llallocator_heap_profile.cpp @@ -129,6 +130,7 @@ set(llcommon_HEADER_FILES CMakeLists.txt chrono.h + commoncontrol.h ctype_workaround.h fix_macros.h indra_constants.h diff --git a/indra/llcommon/commoncontrol.cpp b/indra/llcommon/commoncontrol.cpp new file mode 100644 index 0000000000..2c2a2abeb0 --- /dev/null +++ b/indra/llcommon/commoncontrol.cpp @@ -0,0 +1,104 @@ +/** + * @file commoncontrol.cpp + * @author Nat Goodspeed + * @date 2022-06-08 + * @brief Implementation for commoncontrol. + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Copyright (c) 2022, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "commoncontrol.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llevents.h" +#include "llsdutil.h" + +LLSD LL::CommonControl::access(const LLSD& params) +{ + // We can't actually introduce a link-time dependency on llxml, or on any + // global LLControlGroup (*koff* gSavedSettings *koff*) but we can issue a + // runtime query. If we're running as part of a viewer with + // LLViewerControlListener, we can use that to interact with any + // instantiated LLControGroup. + LLSD response; + { + LLEventStream reply("reply"); + LLTempBoundListener connection = reply.listen("listener", + [&response] (const LLSD& event) + { + response = event; + return false; + }); + LLSD rparams{ params }; + rparams["reply"] = reply.getName(); + LLEventPumps::instance().obtain("LLViewerControl").post(rparams); + } + // LLViewerControlListener responds immediately. If it's listening at all, + // it will already have set response. + if (! response.isDefined()) + { + LLTHROW(NoListener("No LLViewerControl listener instantiated")); + } + LLSD error{ response["error"] }; + if (error.isDefined()) + { + LLTHROW(ParamError(error)); + } + return response; +} + +/// set control group.key to defined default value +LLSD LL::CommonControl::set_default(const std::string& group, const std::string& key) +{ + return access(llsd::map("op", "set", + "group", group, "key", key))["value"]; +} + +/// set control group.key to specified value +LLSD LL::CommonControl::set(const std::string& group, const std::string& key, const LLSD& value) +{ + return access(llsd::map("op", "set", + "group", group, "key", key, "value", value))["value"]; +} + +/// toggle boolean control group.key +LLSD LL::CommonControl::toggle(const std::string& group, const std::string& key) +{ + return access(llsd::map("op", "toggle", + "group", group, "key", key))["value"]; +} + +/// get the definition for control group.key, (! isDefined()) if bad +/// ["name"], ["type"], ["value"], ["comment"] +LLSD LL::CommonControl::get_def(const std::string& group, const std::string& key) +{ + return access(llsd::map("op", "get", + "group", group, "key", key)); +} + +/// get the value of control group.key +LLSD LL::CommonControl::get(const std::string& group, const std::string& key) +{ + return access(llsd::map("op", "get", + "group", group, "key", key))["value"]; +} + +/// get defined groups +std::vector LL::CommonControl::get_groups() +{ + auto groups{ access(llsd::map("op", "groups"))["groups"] }; + return { groups.beginArray(), groups.endArray() }; +} + +/// get definitions for all variables in group +LLSD LL::CommonControl::get_vars(const std::string& group) +{ + return access(llsd::map("op", "vars", "group", group))["vars"]; +} diff --git a/indra/llcommon/commoncontrol.h b/indra/llcommon/commoncontrol.h new file mode 100644 index 0000000000..07d4a45ac5 --- /dev/null +++ b/indra/llcommon/commoncontrol.h @@ -0,0 +1,75 @@ +/** + * @file commoncontrol.h + * @author Nat Goodspeed + * @date 2022-06-08 + * @brief Access LLViewerControl LLEventAPI, if process has one. + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Copyright (c) 2022, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_COMMONCONTROL_H) +#define LL_COMMONCONTROL_H + +#include +#include "llexception.h" +#include "llsd.h" + +namespace LL +{ + class CommonControl + { + public: + struct Error: public LLException + { + Error(const std::string& what): LLException(what) {} + }; + + /// Exception thrown if there's no LLViewerControl LLEventAPI + struct NoListener: public Error + { + NoListener(const std::string& what): Error(what) {} + }; + + struct ParamError: public Error + { + ParamError(const std::string& what): Error(what) {} + }; + + /// set control group.key to defined default value + static + LLSD set_default(const std::string& group, const std::string& key); + + /// set control group.key to specified value + static + LLSD set(const std::string& group, const std::string& key, const LLSD& value); + + /// toggle boolean control group.key + static + LLSD toggle(const std::string& group, const std::string& key); + + /// get the definition for control group.key, (! isDefined()) if bad + /// ["name"], ["type"], ["value"], ["comment"] + static + LLSD get_def(const std::string& group, const std::string& key); + + /// get the value of control group.key + static + LLSD get(const std::string& group, const std::string& key); + + /// get defined groups + static + std::vector get_groups(); + + /// get definitions for all variables in group + static + LLSD get_vars(const std::string& group); + + private: + static + LLSD access(const LLSD& params); + }; +} // namespace LL + +#endif /* ! defined(LL_COMMONCONTROL_H) */ diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp index d5adf11264..10d67abf37 100644 --- a/indra/llcommon/threadpool.cpp +++ b/indra/llcommon/threadpool.cpp @@ -17,15 +17,17 @@ // std headers // external library headers // other Linden headers +#include "commoncontrol.h" #include "llerror.h" #include "llevents.h" +#include "llsd.h" #include "stringize.h" LL::ThreadPool::ThreadPool(const std::string& name, size_t threads, size_t capacity): super(name), mQueue(name, capacity), mName("ThreadPool:" + name), - mThreadCount(threads) + mThreadCount(getConfiguredWidth(name, threads)) {} void LL::ThreadPool::start() @@ -87,3 +89,50 @@ void LL::ThreadPool::run() { mQueue.runUntilClose(); } + +//static +size_t LL::ThreadPool::getConfiguredWidth(const std::string& name, size_t dft=0) +{ + LLSD poolSizes{ LL::CommonControl::get("Global", "ThreadPoolSizes") }; + // "ThreadPoolSizes" is actually a map containing the sizes of interest -- + // or should be, if this process has an "LLViewerControl" LLEventAPI + // instance and its settings include "ThreadPoolSizes". If we failed to + // retrieve it, perhaps we're in a program that doesn't define that, or + // perhaps there's no such setting, or perhaps we're asking too early, + // before the LLEventAPI itself has been instantiated. In any of those + // cases, it seems worth warning. + if (! poolSizes.isDefined()) + { + // Note: we don't warn about absence of an override key for a + // particular ThreadPool name, that's fine. This warning is about + // complete absence of a ThreadPoolSizes setting, which we expect in a + // normal viewer session. + LL_WARNS("ThreadPool") << "No 'ThreadPoolSizes' setting for ThreadPool '" + << name << "'" << LL_ENDL; + } + else + { + //LL_DEBUGS + LL_INFOS("ThreadPool") << "ThreadPoolSizes = " << poolSizes << LL_ENDL; + } + // LLSD treats an undefined value as an empty map when asked to retrieve a + // key, so we don't need this to be conditional. + LLSD sizeSpec{ poolSizes[name] }; + // We retrieve sizeSpec as LLSD, rather than immediately as LLSD::Integer, + // so we can distinguish the case when it's undefined. + return sizeSpec.isInteger() ? sizeSpec.asInteger() : dft; +} + +//static +size_t LL::ThreadPool::getWidth(const std::string& name, size_t dft) +{ + auto instance{ getInstance(name) }; + if (instance) + { + return instance->getWidth(); + } + else + { + return getConfiguredWidth(name, dft); + } +} diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h index f8eec3b457..b49d511257 100644 --- a/indra/llcommon/threadpool.h +++ b/indra/llcommon/threadpool.h @@ -30,8 +30,17 @@ namespace LL /** * Pass ThreadPool a string name. This can be used to look up the * relevant WorkQueue. + * + * The number of threads you pass sets the compile-time default. But + * if the user has overridden the LLSD map in the "ThreadPoolSizes" + * setting with a key matching this ThreadPool name, that setting + * overrides this parameter. + * + * Pass an explicit capacity to limit the size of the queue. + * Constraining the queue can cause a submitter to block. Do not + * constrain any ThreadPool accepting work from the main thread. */ - ThreadPool(const std::string& name, size_t threads=1, size_t capacity=1024); + ThreadPool(const std::string& name, size_t threads=1, size_t capacity=1024*1024); virtual ~ThreadPool(); /** @@ -59,6 +68,25 @@ namespace LL */ virtual void run(); + /** + * getConfiguredWidth() returns the setting, if any, for the specified + * ThreadPool name. Returns dft if the "ThreadPoolSizes" map does not + * contain the specified name. + */ + static + size_t getConfiguredWidth(const std::string& name, size_t dft=0); + + /** + * This getWidth() returns the width of the instantiated ThreadPool + * with the specified name, if any. If no instance exists, returns its + * getConfiguredWidth() if any. If there's no instance and no relevant + * override, return dft. Presumably dft should match the threads + * parameter passed to the ThreadPool constructor call that will + * eventually instantiate the ThreadPool with that name. + */ + static + size_t getWidth(const std::string& name, size_t dft); + private: void run(const std::string& name); -- cgit v1.2.3 From 276647789a7080c0d44ec8e4375daf0f42b73f17 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 9 Jun 2022 10:36:13 -0400 Subject: SL-17483: Simplify ThreadPool instantiations. Now that LL::ThreadPool's constructor has subsumed the work of discovering a runtime override width, LLAppViewer::initGeneralThread() can simply pass the compile-time default width instead of redundantly checking ThreadPoolSizes. Also the default ThreadPool capacity has been bumped up to what "General" and "LLImageGL" were requesting, so they need not pass that explicitly or explain in comments why they're doing it. But until we start throwing work at the "General" ThreadPool, configure it down to 1 thread in settings.xml. --- indra/llrender/llimagegl.cpp | 6 ++---- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llappviewer.cpp | 9 +-------- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 9bd3a0a6b0..42460d23b8 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -2412,10 +2412,8 @@ void LLImageGL::checkActiveThread() std::atomic LLImageGLThread::sFreeVRAMMegabytes(4096); //if free vram is unknown, default to 4GB LLImageGLThread::LLImageGLThread(LLWindow* window) - // We want exactly one thread, but a very large capacity: we never want - // anyone, especially inner-loop render code, to have to block on post() - // because we're full. - : ThreadPool("LLImageGL", 1, 1024*1024) + // We want exactly one thread. + : ThreadPool("LLImageGL", 1) , mWindow(window) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 0000ae18bd..9c27e2a42e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -12766,7 +12766,7 @@ Value General - 4 + 1 ThrottleBandwidthKBPS diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 3bcd4f9a49..334df4ee20 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2183,14 +2183,7 @@ void LLAppViewer::initGeneralThread() return; } - LLSD poolSizes{ gSavedSettings.getLLSD("ThreadPoolSizes") }; - LLSD sizeSpec{ poolSizes["General"] }; - LLSD::Integer poolSize{ sizeSpec.isInteger() ? sizeSpec.asInteger() : 3 }; - LL_DEBUGS("ThreadPool") << "Instantiating General pool with " - << poolSize << " threads" << LL_ENDL; - // We don't want anyone, especially the main thread, to have to block - // due to this ThreadPool being full. - mGeneralThreadPool = new LL::ThreadPool("General", poolSize, 1024 * 1024); + mGeneralThreadPool = new LL::ThreadPool("General", 3); mGeneralThreadPool->start(); } -- cgit v1.2.3 From 1ff79a430d9d55cbb4b4ba55018c791a8a933916 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 9 Jun 2022 11:19:21 -0400 Subject: SL-17483: Fix ThreadPool::getConfiguredWidth() compile error. Log ThreadPoolSizes at DEBUG level, not INFO. --- indra/llcommon/threadpool.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp index 10d67abf37..e8daf549ef 100644 --- a/indra/llcommon/threadpool.cpp +++ b/indra/llcommon/threadpool.cpp @@ -91,7 +91,7 @@ void LL::ThreadPool::run() } //static -size_t LL::ThreadPool::getConfiguredWidth(const std::string& name, size_t dft=0) +size_t LL::ThreadPool::getConfiguredWidth(const std::string& name, size_t dft) { LLSD poolSizes{ LL::CommonControl::get("Global", "ThreadPoolSizes") }; // "ThreadPoolSizes" is actually a map containing the sizes of interest -- @@ -112,8 +112,7 @@ size_t LL::ThreadPool::getConfiguredWidth(const std::string& name, size_t dft=0) } else { - //LL_DEBUGS - LL_INFOS("ThreadPool") << "ThreadPoolSizes = " << poolSizes << LL_ENDL; + LL_DEBUGS("ThreadPool") << "ThreadPoolSizes = " << poolSizes << LL_ENDL; } // LLSD treats an undefined value as an empty map when asked to retrieve a // key, so we don't need this to be conditional. -- cgit v1.2.3 From 50dca86f64a167fe0db901310d04784b2f0dfa1f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 9 Jun 2022 12:06:23 -0400 Subject: SL-17483: Recast LLImageDecodeThread as a facade for ThreadPool. Remove all references to LLQueuedThread (but emulate a couple bits of its API such as handle_t and getPending()). Migrate ImageRequest into llimageworker.cpp. It has never been part of LLImageDecodeThread's public API. Remove ImageRequest tests. Remove all references to LLImageDecodeThread::pause(). The idea of pausing another thread is bizarre to me, and LLThreadPool has no such operation. Nor does it have an abortRequest(). --- indra/llimage/llimageworker.cpp | 99 ++++++++++++++++++++---------- indra/llimage/llimageworker.h | 61 +++++------------- indra/llimage/tests/llimageworker_test.cpp | 66 -------------------- indra/newview/app_settings/settings.xml | 2 + indra/newview/llappviewer.cpp | 11 ++-- indra/newview/lltexturefetch.cpp | 6 +- indra/newview/llviewerwindow.cpp | 1 - indra/test/test.cpp | 2 +- 8 files changed, 91 insertions(+), 157 deletions(-) diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp index d8503396d7..3c21499673 100644 --- a/indra/llimage/llimageworker.cpp +++ b/indra/llimage/llimageworker.cpp @@ -28,44 +28,83 @@ #include "llimageworker.h" #include "llimagedxt.h" +#include "threadpool.h" + +/*--------------------------------------------------------------------------*/ +class ImageRequest +{ +public: + ImageRequest(LLPointer image, + S32 discard, BOOL needs_aux, + LLPointer responder); + virtual ~ImageRequest(); + + /*virtual*/ bool processRequest(); + /*virtual*/ void finishRequest(bool completed); + +private: + // input + LLPointer mFormattedImage; + S32 mDiscardLevel; + BOOL mNeedsAux; + // output + LLPointer mDecodedImageRaw; + LLPointer mDecodedImageAux; + BOOL mDecodedRaw; + BOOL mDecodedAux; + LLPointer mResponder; +}; + //---------------------------------------------------------------------------- // MAIN THREAD -LLImageDecodeThread::LLImageDecodeThread(bool threaded) - : LLQueuedThread("imagedecode", threaded) +LLImageDecodeThread::LLImageDecodeThread(bool /*threaded*/) { - mCreationMutex = new LLMutex(); + mThreadPool.reset(new LL::ThreadPool("ImageDecode", 8)); + mThreadPool->start(); } //virtual LLImageDecodeThread::~LLImageDecodeThread() -{ - delete mCreationMutex ; -} +{} // MAIN THREAD // virtual S32 LLImageDecodeThread::update(F32 max_time_ms) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - S32 res = LLQueuedThread::update(max_time_ms); - return res; + return getPending(); } -LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLImageFormatted* image, - S32 discard, BOOL needs_aux, Responder* responder) +S32 LLImageDecodeThread::getPending() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - handle_t handle = generateHandle(); + return mThreadPool->getQueue().size(); +} - ImageRequest* req = new ImageRequest(handle, image, - discard, needs_aux, - responder); +LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLPointer image, + S32 discard, BOOL needs_aux, LLPointer responder) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - addRequest(req); + // Instantiate the ImageRequest right in the lambda, why not? + mThreadPool->getQueue().post( + [req = ImageRequest(image, discard, needs_aux, responder)] + () mutable + { + auto done = req.processRequest(); + req.finishRequest(done); + }); + + // It's important to our consumer (LLTextureFetchWorker) that we return a + // nonzero handle. It is NOT important that the nonzero handle be unique: + // nothing is ever done with it except to compare it to zero, or zero it. + return 17; +} - return handle; +void LLImageDecodeThread::shutdown() +{ + mThreadPool->close(); } LLImageDecodeThread::Responder::~Responder() @@ -74,11 +113,10 @@ LLImageDecodeThread::Responder::~Responder() //---------------------------------------------------------------------------- -LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatted* image, - S32 discard, BOOL needs_aux, - LLImageDecodeThread::Responder* responder) - : LLQueuedThread::QueuedRequest(handle, FLAG_AUTO_COMPLETE), - mFormattedImage(image), +ImageRequest::ImageRequest(LLPointer image, + S32 discard, BOOL needs_aux, + LLPointer responder) + : mFormattedImage(image), mDiscardLevel(discard), mNeedsAux(needs_aux), mDecodedRaw(FALSE), @@ -87,7 +125,7 @@ LLImageDecodeThread::ImageRequest::ImageRequest(handle_t handle, LLImageFormatte { } -LLImageDecodeThread::ImageRequest::~ImageRequest() +ImageRequest::~ImageRequest() { mDecodedImageRaw = NULL; mDecodedImageAux = NULL; @@ -98,7 +136,7 @@ LLImageDecodeThread::ImageRequest::~ImageRequest() // Returns true when done, whether or not decode was successful. -bool LLImageDecodeThread::ImageRequest::processRequest() +bool ImageRequest::processRequest() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; const F32 decode_time_slice = 0.f; //disable time slicing @@ -125,7 +163,7 @@ bool LLImageDecodeThread::ImageRequest::processRequest() mFormattedImage->getHeight(), mFormattedImage->getComponents()); } - done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); // 1ms + done = mFormattedImage->decode(mDecodedImageRaw, decode_time_slice); // some decoders are removing data when task is complete and there were errors mDecodedRaw = done && mDecodedImageRaw->getData(); } @@ -138,14 +176,14 @@ bool LLImageDecodeThread::ImageRequest::processRequest() mFormattedImage->getHeight(), 1); } - done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); // 1ms + done = mFormattedImage->decodeChannels(mDecodedImageAux, decode_time_slice, 4, 4); mDecodedAux = done && mDecodedImageAux->getData(); } return done; } -void LLImageDecodeThread::ImageRequest::finishRequest(bool completed) +void ImageRequest::finishRequest(bool completed) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; if (mResponder.notNull()) @@ -155,10 +193,3 @@ void LLImageDecodeThread::ImageRequest::finishRequest(bool completed) } // Will automatically be deleted } - -// Used by unit test only -// Checks that a responder exists for this instance so that something can happen when completion is reached -bool LLImageDecodeThread::ImageRequest::tut_isOK() -{ - return mResponder.notNull(); -} diff --git a/indra/llimage/llimageworker.h b/indra/llimage/llimageworker.h index e0a94d2841..6a0b2b4681 100644 --- a/indra/llimage/llimageworker.h +++ b/indra/llimage/llimageworker.h @@ -29,9 +29,13 @@ #include "llimage.h" #include "llpointer.h" -#include "llworkerthread.h" -class LLImageDecodeThread : public LLQueuedThread +namespace LL +{ + class ThreadPool; +} // namespace LL + +class LLImageDecodeThread { public: class Responder : public LLThreadSafeRefCount @@ -42,57 +46,24 @@ public: virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) = 0; }; - class ImageRequest : public LLQueuedThread::QueuedRequest - { - protected: - virtual ~ImageRequest(); // use deleteRequest() - - public: - ImageRequest(handle_t handle, LLImageFormatted* image, - S32 discard, BOOL needs_aux, - LLImageDecodeThread::Responder* responder); - - /*virtual*/ bool processRequest(); - /*virtual*/ void finishRequest(bool completed); - - // Used by unit tests to check the consitency of the request instance - bool tut_isOK(); - - private: - // input - LLPointer mFormattedImage; - S32 mDiscardLevel; - BOOL mNeedsAux; - // output - LLPointer mDecodedImageRaw; - LLPointer mDecodedImageAux; - BOOL mDecodedRaw; - BOOL mDecodedAux; - LLPointer mResponder; - }; - public: LLImageDecodeThread(bool threaded = true); virtual ~LLImageDecodeThread(); - handle_t decodeImage(LLImageFormatted* image, + // meant to resemble LLQueuedThread::handle_t + typedef U32 handle_t; + handle_t decodeImage(LLPointer image, S32 discard, BOOL needs_aux, - Responder* responder); + LLPointer responder); + S32 getPending(); S32 update(F32 max_time_ms); + void shutdown(); private: - struct creation_info - { - handle_t handle; - LLPointer image; - S32 discard; - BOOL needs_aux; - LLPointer responder; - creation_info(handle_t h, LLImageFormatted* i, U32 p, S32 d, BOOL aux, Responder* r) - : handle(h), image(i), discard(d), needs_aux(aux), responder(r) - {} - }; - LLMutex* mCreationMutex; + // As of SL-17483, LLImageDecodeThread is no longer itself an + // LLQueuedThread - instead this is the API by which we submit work to the + // "ImageDecode" ThreadPool. + std::unique_ptr mThreadPool; }; #endif diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp index d36d35aba4..0a97b739b0 100644 --- a/indra/llimage/tests/llimageworker_test.cpp +++ b/indra/llimage/tests/llimageworker_test.cpp @@ -125,42 +125,11 @@ namespace tut } }; - // Test wrapper declaration : image worker - // Note: this class is not meant to be instantiated outside an LLImageDecodeThread instance - // but it's not a bad idea to get its public API a good shake as part of a thorough unit test set. - // Some gotcha with the destructor though (see below). - struct imagerequest_test - { - // Instance to be tested - LLImageDecodeThread::ImageRequest* mRequest; - bool done; - - // Constructor and destructor of the test wrapper - imagerequest_test() - { - done = false; - - mRequest = new LLImageDecodeThread::ImageRequest(0, 0, - 0, FALSE, - new responder_test(&done)); - } - ~imagerequest_test() - { - // We should delete the object *but*, because its destructor is protected, that cannot be - // done from outside an LLImageDecodeThread instance... So we leak memory here... It's fine... - //delete mRequest; - } - }; - // Tut templating thingamagic: test group, object and test instance typedef test_group imagedecodethread_t; typedef imagedecodethread_t::object imagedecodethread_object_t; tut::imagedecodethread_t tut_imagedecodethread("LLImageDecodeThread"); - typedef test_group imagerequest_t; - typedef imagerequest_t::object imagerequest_object_t; - tut::imagerequest_t tut_imagerequest("LLImageRequest"); - // --------------------------------------------------------------------------------------- // Test functions // Notes: @@ -172,21 +141,6 @@ namespace tut // --------------------------------------------------------------------------------------- // Test the LLImageDecodeThread interface // --------------------------------------------------------------------------------------- - // - // Note on Unit Testing Queued Thread Classes - // - // Since methods on such a class are called on a separate loop and that we can't insert tut - // ensure() calls in there, we exercise the class with 2 sets of tests: - // - 1: Test as a single threaded instance: We declare the class but ask for no thread - // to be spawned (easy with LLThreads since there's a boolean argument on the constructor - // just for that). We can then unit test each public method like we do on a normal class. - // - 2: Test as a threaded instance: We let the thread launch and check that its external - // behavior is as expected (i.e. it runs, can accept a work order and processes - // it). Typically though there's no guarantee that this exercises all the methods of the - // class which is why we also need the previous "non threaded" set of unit tests for - // complete coverage. - // - // --------------------------------------------------------------------------------------- template<> template<> void imagedecodethread_object_t::test<1>() @@ -211,24 +165,4 @@ namespace tut // Verifies that the responder has now been called ensure("LLImageDecodeThread: threaded work unit not processed", done == true); } - - // --------------------------------------------------------------------------------------- - // Test the LLImageDecodeThread::ImageRequest interface - // --------------------------------------------------------------------------------------- - - template<> template<> - void imagerequest_object_t::test<1>() - { - // Test that we start with a correct request at creation - ensure("LLImageDecodeThread::ImageRequest::ImageRequest() constructor test failed", mRequest->tut_isOK()); - bool res = mRequest->processRequest(); - // Verifies that we processed the request successfully - ensure("LLImageDecodeThread::ImageRequest::processRequest() processing request test failed", res == true); - // Check that we can call the finishing call safely - try { - mRequest->finishRequest(false); - } catch (...) { - fail("LLImageDecodeThread::ImageRequest::finishRequest() test failed"); - } - } } diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 3201848f38..bc4945eca5 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -12646,6 +12646,8 @@ General 1 + ImageDecode + 9 ThrottleBandwidthKBPS diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index bc38a96ef2..ae0c9d4dd6 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1548,7 +1548,6 @@ bool LLAppViewer::doFrame() { S32 non_interactive_ms_sleep_time = 100; LLAppViewer::getTextureCache()->pause(); - LLAppViewer::getImageDecodeThread()->pause(); ms_sleep(non_interactive_ms_sleep_time); } @@ -1568,7 +1567,6 @@ bool LLAppViewer::doFrame() ms_sleep(milliseconds_to_sleep); // also pause worker threads during this wait period LLAppViewer::getTextureCache()->pause(); - LLAppViewer::getImageDecodeThread()->pause(); } } @@ -1617,7 +1615,6 @@ bool LLAppViewer::doFrame() { LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df getTextureCache" ) LLAppViewer::getTextureCache()->pause(); - LLAppViewer::getImageDecodeThread()->pause(); LLAppViewer::getTextureFetch()->pause(); } if(!total_io_pending) //pause file threads if nothing to process. @@ -2051,10 +2048,10 @@ bool LLAppViewer::cleanup() sTextureCache->shutdown(); sImageDecodeThread->shutdown(); sPurgeDiskCacheThread->shutdown(); - if (mGeneralThreadPool) - { - mGeneralThreadPool->close(); - } + if (mGeneralThreadPool) + { + mGeneralThreadPool->close(); + } sTextureFetch->shutDownTextureCacheThread() ; sTextureFetch->shutDownImageDecodeThread() ; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 64ccbf5e4a..539e2454ea 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2113,10 +2113,10 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe // Threads: Tmain void LLTextureFetchWorker::endWork(S32 param, bool aborted) { - LL_PROFILE_ZONE_SCOPED; + LL_PROFILE_ZONE_SCOPED; if (mDecodeHandle != 0) { - mFetcher->mImageDecodeThread->abortRequest(mDecodeHandle, false); + // LL::ThreadPool has no operation to cancel a particular work item mDecodeHandle = 0; } mFormattedImage = NULL; @@ -3169,7 +3169,7 @@ void LLTextureFetch::shutDownImageDecodeThread() { if(mImageDecodeThread) { - llassert_always(mImageDecodeThread->isQuitting() || mImageDecodeThread->isStopped()) ; + delete mImageDecodeThread; mImageDecodeThread = NULL ; } } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index e27b5caab7..15f20d1d34 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -5476,7 +5476,6 @@ void LLViewerWindow::stopGL(BOOL save_state) // Pause texture decode threads (will get unpaused during main loop) LLAppViewer::getTextureCache()->pause(); - LLAppViewer::getImageDecodeThread()->pause(); LLAppViewer::getTextureFetch()->pause(); gSky.destroyGL(); diff --git a/indra/test/test.cpp b/indra/test/test.cpp index bb48216b2b..28f25087ac 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -401,7 +401,7 @@ public: { // Per http://confluence.jetbrains.net/display/TCD65/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ServiceMessages std::string result; - BOOST_FOREACH(char c, str) + for (char c : str) { switch (c) { -- cgit v1.2.3 From 1e28b8d992e6f24f85fde5c86bf9824bf7c7d277 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 9 Jun 2022 17:03:28 -0400 Subject: SL-17483: Let one override keyword into your class... and all the rest require it, too. --- indra/newview/llviewertexture.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index f3bc9061a2..088d11538a 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -302,10 +302,10 @@ public: }; public: - /*virtual*/ S8 getType() const ; + /*virtual*/ S8 getType() const override ; FTType getFTType() const; - /*virtual*/ void forceImmediateUpdate() ; - /*virtual*/ void dump() ; + /*virtual*/ void forceImmediateUpdate() override; + /*virtual*/ void dump() override; // Set callbacks to get called when the image gets updated with higher // resolution versions. @@ -356,14 +356,14 @@ public: // Override the computation of discard levels if we know the exact output // size of the image. Used for UI textures to not decode, even if we have // more data. - /*virtual*/ void setKnownDrawSize(S32 width, S32 height); + /*virtual*/ void setKnownDrawSize(S32 width, S32 height) override; // Set the debug text of all Viewer Objects associated with this texture // to the specified text void setDebugText(const std::string& text); void setIsMissingAsset(BOOL is_missing = true); - /*virtual*/ BOOL isMissingAsset() const { return mIsMissingAsset; } + /*virtual*/ BOOL isMissingAsset() const override { return mIsMissingAsset; } // returns dimensions of original image for local files (before power of two scaling) // and returns 0 for all asset system images @@ -406,7 +406,7 @@ public: BOOL isRawImageValid()const { return mIsRawImageValid ; } void forceToSaveRawImage(S32 desired_discard = 0, F32 kept_time = 0.f) ; void forceToRefetchTexture(S32 desired_discard = 0, F32 kept_time = 60.f); - /*virtual*/ void setCachedRawImage(S32 discard_level, LLImageRaw* imageraw) ; + /*virtual*/ void setCachedRawImage(S32 discard_level, LLImageRaw* imageraw) override; void destroySavedRawImage() ; LLImageRaw* getSavedRawImage() ; BOOL hasSavedRawImage() const ; @@ -422,10 +422,10 @@ public: void setInFastCacheList(bool in_list) { mInFastCacheList = in_list; } bool isInFastCacheList() { return mInFastCacheList; } - /*virtual*/bool isActiveFetching(); //is actively in fetching by the fetching pipeline. + /*virtual*/bool isActiveFetching() override; //is actively in fetching by the fetching pipeline. protected: - /*virtual*/ void switchToCachedImage(); + /*virtual*/ void switchToCachedImage() override; S32 getCurrentDiscardLevelForFetching() ; private: -- cgit v1.2.3 From 64209ddeeafd944f82da6f13a6e790f9b542b3ff Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 9 Jun 2022 17:04:26 -0400 Subject: SL-17483: Add integration test for CommonControl and for LLViewerControlListener, to which it talks. Fix glitches detected by the tests. --- indra/llcommon/commoncontrol.cpp | 2 + indra/llcommon/threadpool.cpp | 43 +++-- indra/newview/CMakeLists.txt | 13 ++ .../newview/tests/llviewercontrollistener_test.cpp | 174 +++++++++++++++++++++ 4 files changed, 215 insertions(+), 17 deletions(-) create mode 100644 indra/newview/tests/llviewercontrollistener_test.cpp diff --git a/indra/llcommon/commoncontrol.cpp b/indra/llcommon/commoncontrol.cpp index 2c2a2abeb0..81e66baf8c 100644 --- a/indra/llcommon/commoncontrol.cpp +++ b/indra/llcommon/commoncontrol.cpp @@ -51,6 +51,8 @@ LLSD LL::CommonControl::access(const LLSD& params) { LLTHROW(ParamError(error)); } + response.erase("error"); + response.erase("reqid"); return response; } diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp index e8daf549ef..f49dd40a8b 100644 --- a/indra/llcommon/threadpool.cpp +++ b/indra/llcommon/threadpool.cpp @@ -93,27 +93,36 @@ void LL::ThreadPool::run() //static size_t LL::ThreadPool::getConfiguredWidth(const std::string& name, size_t dft) { - LLSD poolSizes{ LL::CommonControl::get("Global", "ThreadPoolSizes") }; - // "ThreadPoolSizes" is actually a map containing the sizes of interest -- - // or should be, if this process has an "LLViewerControl" LLEventAPI - // instance and its settings include "ThreadPoolSizes". If we failed to - // retrieve it, perhaps we're in a program that doesn't define that, or - // perhaps there's no such setting, or perhaps we're asking too early, - // before the LLEventAPI itself has been instantiated. In any of those - // cases, it seems worth warning. - if (! poolSizes.isDefined()) + LLSD poolSizes; + try { - // Note: we don't warn about absence of an override key for a - // particular ThreadPool name, that's fine. This warning is about - // complete absence of a ThreadPoolSizes setting, which we expect in a - // normal viewer session. - LL_WARNS("ThreadPool") << "No 'ThreadPoolSizes' setting for ThreadPool '" - << name << "'" << LL_ENDL; + poolSizes = LL::CommonControl::get("Global", "ThreadPoolSizes"); + // "ThreadPoolSizes" is actually a map containing the sizes of + // interest -- or should be, if this process has an + // LLViewerControlListener instance and its settings include + // "ThreadPoolSizes". If we failed to retrieve it, perhaps we're in a + // program that doesn't define that, or perhaps there's no such + // setting, or perhaps we're asking too early, before the LLEventAPI + // itself has been instantiated. In any of those cases, it seems worth + // warning. + if (! poolSizes.isDefined()) + { + // Note: we don't warn about absence of an override key for a + // particular ThreadPool name, that's fine. This warning is about + // complete absence of a ThreadPoolSizes setting, which we expect + // in a normal viewer session. + LL_WARNS("ThreadPool") << "No 'ThreadPoolSizes' setting for ThreadPool '" + << name << "'" << LL_ENDL; + } } - else + catch (const LL::CommonControl::Error& exc) { - LL_DEBUGS("ThreadPool") << "ThreadPoolSizes = " << poolSizes << LL_ENDL; + // We don't want ThreadPool to *require* LLViewerControlListener. + // Just log it and carry on. + LL_WARNS("ThreadPool") << "Can't check 'ThreadPoolSizes': " << exc.what() << LL_ENDL; } + + LL_DEBUGS("ThreadPool") << "ThreadPoolSizes = " << poolSizes << LL_ENDL; // LLSD treats an undefined value as an empty map when asked to retrieve a // key, so we don't need this to be conditional. LLSD sizeSpec{ poolSizes[name] }; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c253eca94e..f13ce85495 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2506,6 +2506,19 @@ if (LL_TESTS) "${test_libs}" ) + set(llviewercontrollistener_test_sources + llviewercontrollistener.cpp + ../llxml/llcontrol.cpp + ../llxml/llxmltree.cpp + ../llxml/llxmlparser.cpp + ../llcommon/commoncontrol.cpp + ) + + LL_ADD_INTEGRATION_TEST(llviewercontrollistener + "${llviewercontrollistener_test_sources}" + "${test_libs}" + ) + LL_ADD_INTEGRATION_TEST(llviewernetwork llviewernetwork.cpp "${test_libs}" diff --git a/indra/newview/tests/llviewercontrollistener_test.cpp b/indra/newview/tests/llviewercontrollistener_test.cpp new file mode 100644 index 0000000000..6d100ef984 --- /dev/null +++ b/indra/newview/tests/llviewercontrollistener_test.cpp @@ -0,0 +1,174 @@ +/** + * @file llviewercontrollistener_test.cpp + * @author Nat Goodspeed + * @date 2022-06-09 + * @brief Test for llviewercontrollistener. + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Copyright (c) 2022, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "llviewerprecompiledheaders.h" +// associated header +#include "llviewercontrollistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "../test/lltut.h" +#include "../test/catch_and_store_what_in.h" // catch_what() +#include "commoncontrol.h" +#include "llcontrol.h" // LLControlGroup +#include "llviewercontrollistener.h" + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + void ensure_contains(const std::string& msg, const std::string& substr) + { + ensure_contains("Exception does not contain " + substr, msg, substr); + } + + struct llviewercontrollistener_data + { + LLControlGroup Global{"FakeGlobal"}; + + llviewercontrollistener_data() + { + Global.declareString("strvar", "woof", "string variable"); + // together we will stroll the boolvar, ma cherie + Global.declareBOOL("boolvar", TRUE, "bool variable"); + } + }; + typedef test_group llviewercontrollistener_group; + typedef llviewercontrollistener_group::object object; + llviewercontrollistener_group llviewercontrollistenergrp("llviewercontrollistener"); + + template<> template<> + void object::test<1>() + { + set_test_name("CommonControl no listener"); + // Not implemented: the linker drags in LLViewerControlListener when + // we bring in LLViewerControl. + } + + template<> template<> + void object::test<2>() + { + set_test_name("CommonControl bad group"); + std::string threw{ catch_what( + [](){ LL::CommonControl::get("Nonexistent", "Variable"); }) }; + ensure_contains(threw, "group"); + ensure_contains(threw, "Nonexistent"); + } + + template<> template<> + void object::test<3>() + { + set_test_name("CommonControl bad variable"); + std::string threw{ catch_what( + [](){ LL::CommonControl::get("FakeGlobal", "Nonexistent"); }) }; + ensure_contains(threw, "key"); + ensure_contains(threw, "Nonexistent"); + } + + template<> template<> + void object::test<4>() + { + set_test_name("CommonControl toggle string"); + std::string threw{ catch_what( + [](){ LL::CommonControl::toggle("FakeGlobal", "strvar"); }) }; + ensure_contains(threw, "non-boolean"); + ensure_contains(threw, "strvar"); + } + + template<> template<> + void object::test<5>() + { + set_test_name("CommonControl list bad group"); + std::string threw{ catch_what( + [](){ LL::CommonControl::get_vars("Nonexistent"); }) }; + ensure_contains(threw, "group"); + ensure_contains(threw, "Nonexistent"); + } + + template<> template<> + void object::test<6>() + { + set_test_name("CommonControl get"); + auto strvar{ LL::CommonControl::get("FakeGlobal", "strvar") }; + ensure_equals(strvar, "woof"); + auto boolvar{ LL::CommonControl::get("FakeGlobal", "boolvar") }; + ensure(boolvar); + } + + template<> template<> + void object::test<7>() + { + set_test_name("CommonControl set, set_default, toggle"); + + std::string newstr{ LL::CommonControl::set("FakeGlobal", "strvar", "mouse").asString() }; + ensure_equals(newstr, "mouse"); + ensure_equals(LL::CommonControl::get("FakeGlobal", "strvar").asString(), "mouse"); + ensure_equals(LL::CommonControl::set_default("FakeGlobal", "strvar").asString(), "woof"); + + bool newbool{ LL::CommonControl::set("FakeGlobal", "boolvar", false) }; + ensure(! newbool); + ensure(! LL::CommonControl::get("FakeGlobal", "boolvar").asBoolean()); + ensure(LL::CommonControl::set_default("FakeGlobal", "boolvar").asBoolean()); + ensure(! LL::CommonControl::toggle("FakeGlobal", "boolvar").asBoolean()); + } + + template<> template<> + void object::test<8>() + { + set_test_name("CommonControl get_def"); + LLSD def{ LL::CommonControl::get_def("FakeGlobal", "strvar") }; + ensure_equals( + def, + llsd::map("name", "strvar", + "type", "String", + "value", "woof", + "comment", "string variable")); + } + + template<> template<> + void object::test<9>() + { + set_test_name("CommonControl get_groups"); + std::vector groups{ LL::CommonControl::get_groups() }; + ensure_equals(groups.size(), 1); + ensure_equals(groups[0], "FakeGlobal"); + } + + template<> template<> + void object::test<10>() + { + set_test_name("CommonControl get_vars"); + LLSD vars{ LL::CommonControl::get_vars("FakeGlobal") }; + // convert from array (unpredictable order) to map + LLSD varsmap{ LLSD::emptyMap() }; + for (auto& var : llsd::inArray(vars)) + { + varsmap[var["name"].asString()] = var; + } + // comparing maps is order-insensitive + ensure_equals( + varsmap, + llsd::map( + "strvar", + llsd::map("name", "strvar", + "type", "String", + "value", "woof", + "comment", "string variable"), + "boolvar", + llsd::map("name", "boolvar", + "type", "Boolean", + "value", TRUE, + "comment", "bool variable"))); + } +} // namespace tut -- cgit v1.2.3 From 3b043d90b60eca17dfcc014b21c4c8fc5b432384 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 13 Jun 2022 15:18:14 -0400 Subject: SL-17483: Per review feedback, avoid LLPointer refcount twiddling when passing LLPointers into functions. Only increment the refcount when storing in new ImageRequest. --- indra/llimage/llimageworker.cpp | 17 +++++++++++------ indra/llimage/llimageworker.h | 4 ++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp index 3c21499673..0093958e6d 100644 --- a/indra/llimage/llimageworker.cpp +++ b/indra/llimage/llimageworker.cpp @@ -34,15 +34,17 @@ class ImageRequest { public: - ImageRequest(LLPointer image, + ImageRequest(const LLPointer& image, S32 discard, BOOL needs_aux, - LLPointer responder); + const LLPointer& responder); virtual ~ImageRequest(); /*virtual*/ bool processRequest(); /*virtual*/ void finishRequest(bool completed); private: + // LLPointers stored in ImageRequest MUST be LLPointer instances rather + // than references: we need to increment the refcount when storing these. // input LLPointer mFormattedImage; S32 mDiscardLevel; @@ -82,8 +84,11 @@ S32 LLImageDecodeThread::getPending() return mThreadPool->getQueue().size(); } -LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage(LLPointer image, - S32 discard, BOOL needs_aux, LLPointer responder) +LLImageDecodeThread::handle_t LLImageDecodeThread::decodeImage( + const LLPointer& image, + S32 discard, + BOOL needs_aux, + const LLPointer& responder) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; @@ -113,9 +118,9 @@ LLImageDecodeThread::Responder::~Responder() //---------------------------------------------------------------------------- -ImageRequest::ImageRequest(LLPointer image, +ImageRequest::ImageRequest(const LLPointer& image, S32 discard, BOOL needs_aux, - LLPointer responder) + const LLPointer& responder) : mFormattedImage(image), mDiscardLevel(discard), mNeedsAux(needs_aux), diff --git a/indra/llimage/llimageworker.h b/indra/llimage/llimageworker.h index 6a0b2b4681..18398d9ae2 100644 --- a/indra/llimage/llimageworker.h +++ b/indra/llimage/llimageworker.h @@ -52,9 +52,9 @@ public: // meant to resemble LLQueuedThread::handle_t typedef U32 handle_t; - handle_t decodeImage(LLPointer image, + handle_t decodeImage(const LLPointer& image, S32 discard, BOOL needs_aux, - LLPointer responder); + const LLPointer& responder); S32 getPending(); S32 update(F32 max_time_ms); void shutdown(); -- cgit v1.2.3