diff options
| -rw-r--r-- | indra/llcommon/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | indra/llcommon/llsingleton.h | 14 | ||||
| -rw-r--r-- | indra/llcommon/tests/threadsafeschedule_test.cpp | 4 | ||||
| -rw-r--r-- | indra/llcommon/tests/workqueue_test.cpp | 72 | ||||
| -rw-r--r-- | indra/llcommon/timing.cpp | 25 | ||||
| -rw-r--r-- | indra/llcommon/workqueue.cpp | 30 | ||||
| -rw-r--r-- | indra/llcommon/workqueue.h | 197 | ||||
| -rw-r--r-- | indra/llrender/llimagegl.cpp | 89 | ||||
| -rw-r--r-- | indra/llrender/llimagegl.h | 27 | ||||
| -rw-r--r-- | indra/llwindow/llwindowwin32.cpp | 321 | ||||
| -rw-r--r-- | indra/llwindow/llwindowwin32.h | 36 | ||||
| -rw-r--r-- | indra/newview/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 25 | ||||
| -rw-r--r-- | indra/newview/llappviewer.cpp | 51 | ||||
| -rw-r--r-- | indra/newview/llmainlooprepeater.cpp | 88 | ||||
| -rw-r--r-- | indra/newview/llmainlooprepeater.h | 64 | ||||
| -rw-r--r-- | indra/newview/llstartup.cpp | 17 | ||||
| -rw-r--r-- | indra/newview/llviewertexture.cpp | 34 | ||||
| -rw-r--r-- | indra/newview/llviewertexture.h | 4 | 
19 files changed, 371 insertions, 733 deletions
| diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 9defa6b6c1..782f656406 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -119,8 +119,8 @@ set(llcommon_SOURCE_FILES      lluriparser.cpp      lluuid.cpp      llworkerthread.cpp -    timing.cpp      u64.cpp +    threadpool.cpp      workqueue.cpp      StackWalker.cpp      ) @@ -256,6 +256,7 @@ set(llcommon_HEADER_FILES      lockstatic.h      stdtypes.h      stringize.h +    threadpool.h      threadsafeschedule.h      timer.h      tuple.h diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index f85f961287..6042c0906c 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -847,14 +847,13 @@ template<class T>  class LLSimpleton  {  public: -    static T* sInstance; -     -    static void createInstance()  -    {  +    template <typename... ARGS> +    static void createInstance(ARGS&&... args) +    {          llassert(sInstance == nullptr); -        sInstance = new T();  +        sInstance = new T(std::forward<ARGS>(args)...);      } -     +      static inline T* getInstance() { return sInstance; }      static inline T& instance() { return *getInstance(); }      static inline bool instanceExists() { return sInstance != nullptr; } @@ -864,6 +863,9 @@ public:          delete sInstance;          sInstance = nullptr;      } + +private: +    static T* sInstance;  };  template <class T> diff --git a/indra/llcommon/tests/threadsafeschedule_test.cpp b/indra/llcommon/tests/threadsafeschedule_test.cpp index af67b9f492..c421cc7b1c 100644 --- a/indra/llcommon/tests/threadsafeschedule_test.cpp +++ b/indra/llcommon/tests/threadsafeschedule_test.cpp @@ -46,11 +46,11 @@ namespace tut          // the real time required for each push() call. Explicitly increment          // the timestamp for each one -- but since we're passing explicit          // timestamps, make the queue reorder them. -        queue.push(Queue::TimeTuple(Queue::Clock::now() + 20ms, "ghi")); +        queue.push(Queue::TimeTuple(Queue::Clock::now() + 200ms, "ghi"));          // Given the various push() overloads, you have to match the type          // exactly: conversions are ambiguous.          queue.push("abc"s); -        queue.push(Queue::Clock::now() + 10ms, "def"); +        queue.push(Queue::Clock::now() + 100ms, "def");          queue.close();          auto entry = queue.pop();          ensure_equals("failed to pop first", std::get<0>(entry), "abc"s); diff --git a/indra/llcommon/tests/workqueue_test.cpp b/indra/llcommon/tests/workqueue_test.cpp index d5405400fd..bea3ad911b 100644 --- a/indra/llcommon/tests/workqueue_test.cpp +++ b/indra/llcommon/tests/workqueue_test.cpp @@ -20,7 +20,10 @@  // external library headers  // other Linden headers  #include "../test/lltut.h" +#include "../test/catch_and_store_what_in.h"  #include "llcond.h" +#include "llcoros.h" +#include "lleventcoro.h"  #include "llstring.h"  #include "stringize.h" @@ -138,7 +141,8 @@ namespace tut              [](){ return 17; },              // Note that a postTo() *callback* can safely bind a reference to              // a variable on the invoking thread, because the callback is run -            // on the invoking thread. +            // on the invoking thread. (Of course the bound variable must +            // survive until the callback is called.)              [&result](int i){ result = i; });          // this should post the callback to main          qptr->runOne(); @@ -156,4 +160,70 @@ namespace tut          main.runPending();          ensure_equals("failed to run string callback", alpha, "abc");      } + +    template<> template<> +    void object::test<5>() +    { +        set_test_name("postTo with void return"); +        WorkQueue main("main"); +        auto qptr = WorkQueue::getInstance("queue"); +        std::string observe; +        main.postTo( +            qptr, +            // The ONLY reason we can get away with binding a reference to +            // 'observe' in our work callable is because we're directly +            // calling qptr->runOne() on this same thread. It would be a +            // mistake to do that if some other thread were servicing 'queue'. +            [&observe](){ observe = "queue"; }, +            [&observe](){ observe.append(";main"); }); +        qptr->runOne(); +        main.runOne(); +        ensure_equals("failed to run both lambdas", observe, "queue;main"); +    } + +    template<> template<> +    void object::test<6>() +    { +        set_test_name("waitForResult"); +        std::string stored; +        // Try to call waitForResult() on this thread's main coroutine. It +        // should throw because the main coroutine must service the queue. +        auto what{ catch_what<WorkQueue::Error>( +                [this, &stored](){ stored = queue.waitForResult( +                        [](){ return "should throw"; }); }) }; +        ensure("lambda should not have run", stored.empty()); +        ensure_not("waitForResult() should have thrown", what.empty()); +        ensure(STRINGIZE("should mention waitForResult: " << what), +               what.find("waitForResult") != std::string::npos); + +        // Call waitForResult() on a coroutine, with a string result. +        LLCoros::instance().launch( +            "waitForResult string", +            [this, &stored]() +            { stored = queue.waitForResult( +                    [](){ return "string result"; }); }); +        llcoro::suspend(); +        // Nothing will have happened yet because, even if the coroutine did +        // run immediately, all it did was to queue the inner lambda on +        // 'queue'. Service it. +        queue.runOne(); +        llcoro::suspend(); +        ensure_equals("bad waitForResult return", stored, "string result"); + +        // Call waitForResult() on a coroutine, with a void callable. +        stored.clear(); +        bool done = false; +        LLCoros::instance().launch( +            "waitForResult void", +            [this, &stored, &done]() +            { +                queue.waitForResult([&stored](){ stored = "ran"; }); +                done = true; +            }); +        llcoro::suspend(); +        queue.runOne(); +        llcoro::suspend(); +        ensure_equals("didn't run coroutine", stored, "ran"); +        ensure("void waitForResult() didn't return", done); +    }  } // namespace tut diff --git a/indra/llcommon/timing.cpp b/indra/llcommon/timing.cpp deleted file mode 100644 index c2dc695ef3..0000000000 --- a/indra/llcommon/timing.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/**  - * @file timing.cpp - * @brief This file will be deprecated in the future. - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index e7d40354aa..c74dada2e4 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -26,8 +26,9 @@  using Mutex = LLCoros::Mutex;  using Lock  = LLCoros::LockType; -LL::WorkQueue::WorkQueue(const std::string& name): -    super(makeName(name)) +LL::WorkQueue::WorkQueue(const std::string& name, size_t capacity): +    super(makeName(name)), +    mQueue(capacity)  {      // TODO: register for "LLApp" events so we can implicitly close() on      // viewer shutdown. @@ -38,6 +39,21 @@ void LL::WorkQueue::close()      mQueue.close();  } +size_t LL::WorkQueue::size() +{ +    return mQueue.size(); +} + +bool LL::WorkQueue::isClosed() +{ +    return mQueue.isClosed(); +} + +bool LL::WorkQueue::done() +{ +    return mQueue.done(); +} +  void LL::WorkQueue::runUntilClose()  {      try @@ -130,3 +146,13 @@ void LL::WorkQueue::error(const std::string& msg)  {      LL_ERRS("WorkQueue") << msg << LL_ENDL;  } + +void LL::WorkQueue::checkCoroutine(const std::string& method) +{ +    // By convention, the default coroutine on each thread has an empty name +    // string. See also LLCoros::logname(). +    if (LLCoros::getName().empty()) +    { +        LLTHROW(Error("Do not call " + method + " from a thread's default coroutine")); +    } +} diff --git a/indra/llcommon/workqueue.h b/indra/llcommon/workqueue.h index 8e4b38c2f3..96574a18b9 100644 --- a/indra/llcommon/workqueue.h +++ b/indra/llcommon/workqueue.h @@ -12,14 +12,14 @@  #if ! defined(LL_WORKQUEUE_H)  #define LL_WORKQUEUE_H +#include "llcoros.h" +#include "llexception.h"  #include "llinstancetracker.h"  #include "threadsafeschedule.h"  #include <chrono> +#include <exception>                // std::current_exception  #include <functional>               // std::function -#include <queue>  #include <string> -#include <utility>                  // std::pair -#include <vector>  namespace LL  { @@ -45,11 +45,16 @@ namespace LL          using TimedWork = Queue::TimeTuple;          using Closed    = Queue::Closed; +        struct Error: public LLException +        { +            Error(const std::string& what): LLException(what) {} +        }; +          /**           * You may omit the WorkQueue name, in which case a unique name is           * synthesized; for practical purposes that makes it anonymous.           */ -        WorkQueue(const std::string& name = std::string()); +        WorkQueue(const std::string& name = std::string(), size_t capacity=1024);          /**           * Since the point of WorkQueue is to pass work to some other worker @@ -59,15 +64,36 @@ namespace LL           */          void close(); +        /** +         * WorkQueue supports multiple producers and multiple consumers. In +         * the general case it's misleading to test size(), since any other +         * thread might change it the nanosecond the lock is released. On that +         * basis, some might argue against publishing a size() method at all. +         * +         * But there are two specific cases in which a test based on size() +         * might be reasonable: +         * +         * * If you're the only producer, noticing that size() == 0 is +         *   meaningful. +         * * If you're the only consumer, noticing that size() > 0 is +         *   meaningful. +         */ +        size_t size(); +        /// producer end: are we prevented from pushing any additional items? +        bool isClosed(); +        /// consumer end: are we done, is the queue entirely drained? +        bool done(); +          /*---------------------- fire and forget API -----------------------*/          /// fire-and-forget, but at a particular (future?) time          template <typename CALLABLE>          void post(const TimePoint& time, CALLABLE&& callable)          { -            // Defer reifying an arbitrary CALLABLE until we hit this method. -            // All other methods should accept CALLABLEs of arbitrary type to -            // avoid multiple levels of std::function indirection. +            // Defer reifying an arbitrary CALLABLE until we hit this or +            // postIfOpen(). All other methods should accept CALLABLEs of +            // arbitrary type to avoid multiple levels of std::function +            // indirection.              mQueue.push(TimedWork(time, std::move(callable)));          } @@ -83,6 +109,47 @@ namespace LL          }          /** +         * post work for a particular time, unless the queue is closed before +         * we can post +         */ +        template <typename CALLABLE> +        bool postIfOpen(const TimePoint& time, CALLABLE&& callable) +        { +            // Defer reifying an arbitrary CALLABLE until we hit this or +            // post(). All other methods should accept CALLABLEs of arbitrary +            // type to avoid multiple levels of std::function indirection. +            return mQueue.pushIfOpen(TimedWork(time, std::move(callable))); +        } + +        /** +         * post work, unless the queue is closed before we can post +         */ +        template <typename CALLABLE> +        bool postIfOpen(CALLABLE&& callable) +        { +            return postIfOpen(TimePoint::clock::now(), std::move(callable)); +        } + +        /** +         * Post work to be run at a specified time to another WorkQueue, which +         * may or may not still exist and be open. Return true if we were able +         * to post. +         */ +        template <typename CALLABLE> +        static bool postMaybe(weak_t target, const TimePoint& time, CALLABLE&& callable); + +        /** +         * Post work to another WorkQueue, which may or may not still exist +         * and be open. Return true if we were able to post. +         */ +        template <typename CALLABLE> +        static bool postMaybe(weak_t target, CALLABLE&& callable) +        { +            return postMaybe(target, TimePoint::clock::now(), +                             std::forward<CALLABLE>(callable)); +        } + +        /**           * Launch a callable returning bool that will trigger repeatedly at           * specified interval, until the callable returns false.           * @@ -115,63 +182,8 @@ namespace LL          // Studio compile errors that seem utterly unrelated to this source          // code.          template <typename CALLABLE, typename FOLLOWUP> -        bool postTo(WorkQueue::weak_t target, -                    const TimePoint& time, CALLABLE&& callable, FOLLOWUP&& callback) -        { -            // We're being asked to post to the WorkQueue at target. -            // target is a weak_ptr: have to lock it to check it. -            auto tptr = target.lock(); -            if (! tptr) -                // can't post() if the target WorkQueue has been destroyed -                return false; - -            // Here we believe target WorkQueue still exists. Post to it a -            // lambda that packages our callable, our callback and a weak_ptr -            // to this originating WorkQueue. -            tptr->post( -                time, -                [reply = super::getWeak(), -                 callable = std::move(callable), -                 callback = std::move(callback)] -                () -                { -                    // Call the callable in any case -- but to minimize -                    // copying the result, immediately bind it into a reply -                    // lambda. The reply lambda also binds the original -                    // callback, so that when we, the originating WorkQueue, -                    // finally receive and process the reply lambda, we'll -                    // call the bound callback with the bound result -- on the -                    // same thread that originally called postTo(). -                    auto rlambda = -                        [result = callable(), -                         callback = std::move(callback)] -                        () -                        { callback(std::move(result)); }; -                    // Check if this originating WorkQueue still exists. -                    // Remember, the outer lambda is now running on a thread -                    // servicing the target WorkQueue, and real time has -                    // elapsed since postTo()'s tptr->post() call. -                    // reply is a weak_ptr: have to lock it to check it. -                    auto rptr = reply.lock(); -                    if (rptr) -                    { -                        // Only post reply lambda if the originating WorkQueue -                        // still exists. If not -- who would we tell? Log it? -                        try -                        { -                            rptr->post(std::move(rlambda)); -                        } -                        catch (const Closed&) -                        { -                            // Originating WorkQueue might still exist, but -                            // might be Closed. Same thing: just discard the -                            // callback. -                        } -                    } -                }); -            // looks like we were able to post() -            return true; -        } +        bool postTo(weak_t target, +                    const TimePoint& time, CALLABLE&& callable, FOLLOWUP&& callback);          /**           * Post work to another WorkQueue, requesting a specific callback to @@ -181,10 +193,36 @@ namespace LL           * inaccessible.           */          template <typename CALLABLE, typename FOLLOWUP> -        bool postTo(WorkQueue::weak_t target, -                    CALLABLE&& callable, FOLLOWUP&& callback) +        bool postTo(weak_t target, CALLABLE&& callable, FOLLOWUP&& callback) +        { +            return postTo(target, TimePoint::clock::now(), +                          std::move(callable), std::move(callback)); +        } + +        /** +         * Post work to another WorkQueue to be run at a specified time, +         * blocking the calling coroutine until then, returning the result to +         * caller on completion. +         * +         * In general, we assume that each thread's default coroutine is busy +         * servicing its WorkQueue or whatever. To try to prevent mistakes, we +         * forbid calling waitForResult() from a thread's default coroutine. +         */ +        template <typename CALLABLE> +        auto waitForResult(const TimePoint& time, CALLABLE&& callable); + +        /** +         * Post work to another WorkQueue, blocking the calling coroutine +         * until then, returning the result to caller on completion. +         * +         * In general, we assume that each thread's default coroutine is busy +         * servicing its WorkQueue or whatever. To try to prevent mistakes, we +         * forbid calling waitForResult() from a thread's default coroutine. +         */ +        template <typename CALLABLE> +        auto waitForResult(CALLABLE&& callable)          { -            return postTo(target, TimePoint::clock::now(), std::move(callable), std::move(callback)); +            return waitForResult(TimePoint::clock::now(), std::move(callable));          }          /*--------------------------- worker API ---------------------------*/ @@ -233,6 +271,23 @@ namespace LL          bool runUntil(const TimePoint& until);      private: +        template <typename CALLABLE, typename FOLLOWUP> +        static auto makeReplyLambda(CALLABLE&& callable, FOLLOWUP&& callback); +        /// general case: arbitrary C++ return type +        template <typename CALLABLE, typename FOLLOWUP, typename RETURNTYPE> +        struct MakeReplyLambda; +        /// specialize for CALLABLE returning void +        template <typename CALLABLE, typename FOLLOWUP> +        struct MakeReplyLambda<CALLABLE, FOLLOWUP, void>; + +        /// general case: arbitrary C++ return type +        template <typename CALLABLE, typename RETURNTYPE> +        struct WaitForResult; +        /// specialize for CALLABLE returning void +        template <typename CALLABLE> +        struct WaitForResult<CALLABLE, void>; + +        static void checkCoroutine(const std::string& method);          static void error(const std::string& msg);          static std::string makeName(const std::string& name);          void callWork(const Queue::DataTuple& work); @@ -254,8 +309,8 @@ namespace LL      {      public:          // bind the desired data -        BackJack(WorkQueue::weak_t target, -                 const WorkQueue::TimePoint& start, +        BackJack(weak_t target, +                 const TimePoint& start,                   const std::chrono::duration<Rep, Period>& interval,                   CALLABLE&& callable):              mTarget(target), @@ -302,8 +357,8 @@ namespace LL          }      private: -        WorkQueue::weak_t mTarget; -        WorkQueue::TimePoint mStart; +        weak_t mTarget; +        TimePoint mStart;          std::chrono::duration<Rep, Period> mInterval;          CALLABLE mCallable;      }; diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index ed0e3fb345..894eb8c773 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -172,31 +172,19 @@ BOOL is_little_endian()  	return (*c == 0x78) ;  } -LLImageGLThread* LLImageGLThread::sInstance = nullptr; -  //static   void LLImageGL::initClass(LLWindow* window, S32 num_catagories, BOOL skip_analyze_alpha /* = false */)  {      LL_PROFILE_ZONE_SCOPED;  	sSkipAnalyzeAlpha = skip_analyze_alpha; -    LLImageGLThread::sInstance = new LLImageGLThread(window); -    LLImageGLThread::sInstance->start(); -} - -//static -void LLImageGL::updateClass() -{ -    LL_PROFILE_ZONE_SCOPED; -    LLImageGLThread::sInstance->executeCallbacks(); +    LLImageGLThread::createInstance(window);  }  //static   void LLImageGL::cleanupClass()   {      LL_PROFILE_ZONE_SCOPED; -    LLImageGLThread::sInstance->mFunctionQueue.close(); -    delete LLImageGLThread::sInstance; -    LLImageGLThread::sInstance = nullptr; +    LLImageGLThread::deleteSingleton();  }  //static @@ -504,6 +492,9 @@ void LLImageGL::init(BOOL usemipmaps)  #endif  	mCategory = -1; + +	// Sometimes we have to post work for the main thread. +	mMainQueue = LL::WorkQueue::getInstance("mainloop");  }  void LLImageGL::cleanup() @@ -1536,8 +1527,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_      }      //if we're on the image loading thread, be sure to delete old_texname and update mTexName on the main thread -    if (LLImageGLThread::sInstance != nullptr &&  -        LLThread::currentID() == LLImageGLThread::sInstance->getID()) +    if (! on_main_thread())      {          {              LL_PROFILE_ZONE_NAMED("cglt - sync"); @@ -1554,7 +1544,9 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_          }          ref(); -        LLImageGLThread::sInstance->postCallback([=]() +        LL::WorkQueue::postMaybe( +            mMainQueue, +            [=]()              {                  LL_PROFILE_ZONE_NAMED("cglt - delete callback");                  if (old_texname != 0) @@ -2259,7 +2251,11 @@ void LLImageGL::resetCurTexSizebar()  */    LLImageGLThread::LLImageGLThread(LLWindow* window) -    : LLThread("LLImageGL"), mWindow(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) +    , mWindow(window)  {      LL_PROFILE_ZONE_SCOPED;      mFinished = false; @@ -2268,61 +2264,6 @@ LLImageGLThread::LLImageGLThread(LLWindow* window)      ThreadPool::start();  } -// post a function to be executed on the LLImageGL background thread - -bool LLImageGLThread::post(const std::function<void()>& func) -{ -    try -    { -        mFunctionQueue.post(func); -    } -    catch (LLThreadSafeQueueInterrupt e) -    { -        return false; -    } - -    return true; -} - -//post a callback to be executed on the main thread - -bool LLImageGLThread::postCallback(const std::function<void()>& callback) -{ -    try -    { -        if (!mCallbackQueue.tryPost(callback)) -        { -            mPendingCallbackQ.push(callback); -        } -    } -    catch (LLThreadSafeQueueInterrupt e) -    { -        //thread is closing, drop request -        return false; -    } - -    return true; -} - -void LLImageGLThread::executeCallbacks() -{ -    LL_PROFILE_ZONE_SCOPED; -    //executed from main thread -    mCallbackQueue.runPending(); - -    while (!mPendingCallbackQ.empty()) -    { -        if (mCallbackQueue.tryPost(mPendingCallbackQ.front())) -        { -            mPendingCallbackQ.pop(); -        } -        else -        { -            break; -        } -    } -} -  void LLImageGLThread::run()  {      LL_PROFILE_ZONE_SCOPED; @@ -2330,7 +2271,7 @@ void LLImageGLThread::run()      // WorkQueue, likewise cleanup afterwards.      mWindow->makeContextCurrent(mContext);      gGL.init(); -    mFunctionQueue.runUntilClose(); +    ThreadPool::run();      gGL.shutdown();      mWindow->destroySharedContext(mContext);  } diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index bb46dbc639..ae773bb362 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -37,6 +37,7 @@  #include "llunits.h"  #include "llthreadsafequeue.h"  #include "llrender.h" +#include "threadpool.h"  #include "workqueue.h"  class LLTextureAtlas ; @@ -198,6 +199,7 @@ private:  	void freePickMask();  	LLPointer<LLImageRaw> mSaveData; // used for destroyGL/restoreGL +	LL::WorkQueue::weak_t mMainQueue;  	U8* mPickMask;  //downsampled bitmap approximation of alpha channel.  NULL if no alpha channel  	U16 mPickMaskWidth;  	U16 mPickMaskHeight; @@ -271,7 +273,6 @@ public:  public:  	static void initClass(LLWindow* window, S32 num_catagories, BOOL skip_analyze_alpha = false);  -    static void updateClass();  	static void cleanupClass() ;  private: @@ -307,34 +308,24 @@ public:  }; -class LLImageGLThread : public LLThread +class LLImageGLThread : public LLSimpleton<LLImageGLThread>, LL::ThreadPool  {  public:      LLImageGLThread(LLWindow* window);      // post a function to be executed on the LLImageGL background thread -    bool post(const std::function<void()>& func); - -    //post a callback to be executed on the main thread -    bool postCallback(const std::function<void()>& callback); - -    void executeCallbacks(); +    template <typename CALLABLE> +    bool post(CALLABLE&& func) +    { +        return getQueue().postIfOpen(std::forward<CALLABLE>(func)); +    }      void run() override; -    // Work Queue for background thread -    LL::WorkQueue mFunctionQueue; - -    // Work Queue for main thread (run from updateClass) -    LL::WorkQueue mCallbackQueue; - +private:      LLWindow* mWindow;      void* mContext = nullptr;      LLAtomicBool mFinished; - -    std::queue<std::function<void()>> mPendingCallbackQ; - -    static LLImageGLThread* sInstance;  }; diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 149a92ffff..062dd02903 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -183,23 +183,19 @@ DWORD	LLWindowWin32::sWinIMESentenceMode = IME_SMODE_AUTOMATIC;  LLCoordWindow LLWindowWin32::sWinIMEWindowPosition(-1,-1);  // The following class LLWinImm delegates Windows IMM APIs. -// We need this because some language versions of Windows, -// e.g., US version of Windows XP, doesn't install IMM32.DLL -// as a default, and we can't link against imm32.lib statically. -// I believe DLL loading of this type is best suited to do -// in a static initialization of a class.  What I'm not sure is -// whether it follows the Linden Conding Standard...  -// See http://wiki.secondlife.com/wiki/Coding_standards#Static_Members +// It was originally introduced to support US Windows XP, on which we needed +// to dynamically load IMM32.DLL and use GetProcAddress to resolve its entry +// points. Now that that's moot, we retain this wrapper only for hooks for +// metrics.  class LLWinImm  {  public: -	static bool		isAvailable() { return sTheInstance.mHImmDll != NULL; } +	static bool		isAvailable() { return true; }  public:  	// Wrappers for IMM API.  	static BOOL		isIME(HKL hkl);															 -	static HWND		getDefaultIMEWnd(HWND hwnd);  	static HIMC		getContext(HWND hwnd);													  	static BOOL		releaseContext(HWND hwnd, HIMC himc);  	static BOOL		getOpenStatus(HIMC himc);												 @@ -213,236 +209,96 @@ public:  	static BOOL		setCompositionFont(HIMC himc, LPLOGFONTW logfont);  	static BOOL		setCandidateWindow(HIMC himc, LPCANDIDATEFORM candidate_form);  	static BOOL		notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value); - -private: -	LLWinImm(); -	~LLWinImm(); - -private: -	// Pointers to IMM API. -	BOOL	 	(WINAPI *mImmIsIME)(HKL); -	HWND		(WINAPI *mImmGetDefaultIMEWnd)(HWND); -	HIMC		(WINAPI *mImmGetContext)(HWND); -	BOOL		(WINAPI *mImmReleaseContext)(HWND, HIMC); -	BOOL		(WINAPI *mImmGetOpenStatus)(HIMC); -	BOOL		(WINAPI *mImmSetOpenStatus)(HIMC, BOOL); -	BOOL		(WINAPI *mImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD); -	BOOL		(WINAPI *mImmSetConversionStatus)(HIMC, DWORD, DWORD); -	BOOL		(WINAPI *mImmGetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM); -	BOOL		(WINAPI *mImmSetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM); -	LONG		(WINAPI *mImmGetCompositionString)(HIMC, DWORD, LPVOID, DWORD); -	BOOL		(WINAPI *mImmSetCompositionString)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD); -	BOOL		(WINAPI *mImmSetCompositionFont)(HIMC, LPLOGFONTW); -	BOOL		(WINAPI *mImmSetCandidateWindow)(HIMC, LPCANDIDATEFORM); -	BOOL		(WINAPI *mImmNotifyIME)(HIMC, DWORD, DWORD, DWORD); - -private: -	HMODULE		mHImmDll; -	static LLWinImm sTheInstance;  }; -LLWinImm LLWinImm::sTheInstance; - -LLWinImm::LLWinImm() : mHImmDll(NULL) -{ -	// Check system metrics  -	if ( !GetSystemMetrics( SM_IMMENABLED ) ) -		return; - -	mHImmDll = LoadLibraryA("Imm32"); -	if (mHImmDll != NULL) -	{ -		mImmIsIME               = (BOOL (WINAPI *)(HKL))                    GetProcAddress(mHImmDll, "ImmIsIME"); -		mImmGetDefaultIMEWnd	= (HWND (WINAPI *)(HWND))					GetProcAddress(mHImmDll, "ImmGetDefaultIMEWnd"); -		mImmGetContext          = (HIMC (WINAPI *)(HWND))                   GetProcAddress(mHImmDll, "ImmGetContext"); -		mImmReleaseContext      = (BOOL (WINAPI *)(HWND, HIMC))             GetProcAddress(mHImmDll, "ImmReleaseContext"); -		mImmGetOpenStatus       = (BOOL (WINAPI *)(HIMC))                   GetProcAddress(mHImmDll, "ImmGetOpenStatus"); -		mImmSetOpenStatus       = (BOOL (WINAPI *)(HIMC, BOOL))             GetProcAddress(mHImmDll, "ImmSetOpenStatus"); -		mImmGetConversionStatus = (BOOL (WINAPI *)(HIMC, LPDWORD, LPDWORD)) GetProcAddress(mHImmDll, "ImmGetConversionStatus"); -		mImmSetConversionStatus = (BOOL (WINAPI *)(HIMC, DWORD, DWORD))     GetProcAddress(mHImmDll, "ImmSetConversionStatus"); -		mImmGetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM))   GetProcAddress(mHImmDll, "ImmGetCompositionWindow"); -		mImmSetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM))   GetProcAddress(mHImmDll, "ImmSetCompositionWindow"); -		mImmGetCompositionString= (LONG (WINAPI *)(HIMC, DWORD, LPVOID, DWORD))					GetProcAddress(mHImmDll, "ImmGetCompositionStringW"); -		mImmSetCompositionString= (BOOL (WINAPI *)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD))	GetProcAddress(mHImmDll, "ImmSetCompositionStringW"); -		mImmSetCompositionFont  = (BOOL (WINAPI *)(HIMC, LPLOGFONTW))		GetProcAddress(mHImmDll, "ImmSetCompositionFontW"); -		mImmSetCandidateWindow  = (BOOL (WINAPI *)(HIMC, LPCANDIDATEFORM))  GetProcAddress(mHImmDll, "ImmSetCandidateWindow"); -		mImmNotifyIME			= (BOOL (WINAPI *)(HIMC, DWORD, DWORD, DWORD))	GetProcAddress(mHImmDll, "ImmNotifyIME"); - -		if (mImmIsIME == NULL || -			mImmGetDefaultIMEWnd == NULL || -			mImmGetContext == NULL || -			mImmReleaseContext == NULL || -			mImmGetOpenStatus == NULL || -			mImmSetOpenStatus == NULL || -			mImmGetConversionStatus == NULL || -			mImmSetConversionStatus == NULL || -			mImmGetCompostitionWindow == NULL || -			mImmSetCompostitionWindow == NULL || -			mImmGetCompositionString == NULL || -			mImmSetCompositionString == NULL || -			mImmSetCompositionFont == NULL || -			mImmSetCandidateWindow == NULL || -			mImmNotifyIME == NULL) -		{ -			// If any of the above API entires are not found, we can't use IMM API.   -			// So, turn off the IMM support.  We should log some warning message in  -			// the case, since it is very unusual; these APIs are available from  -			// the beginning, and all versions of IMM32.DLL should have them all.   -			// Unfortunately, this code may be executed before initialization of  -			// the logging channel (LL_WARNS()), and we can't do it here...  Yes, this  -			// is one of disadvantages to use static constraction to DLL loading.  -			FreeLibrary(mHImmDll); -			mHImmDll = NULL; - -			// If we unload the library, make sure all the function pointers are cleared -			mImmIsIME = NULL; -			mImmGetDefaultIMEWnd = NULL; -			mImmGetContext = NULL; -			mImmReleaseContext = NULL; -			mImmGetOpenStatus = NULL; -			mImmSetOpenStatus = NULL; -			mImmGetConversionStatus = NULL; -			mImmSetConversionStatus = NULL; -			mImmGetCompostitionWindow = NULL; -			mImmSetCompostitionWindow = NULL; -			mImmGetCompositionString = NULL; -			mImmSetCompositionString = NULL; -			mImmSetCompositionFont = NULL; -			mImmSetCandidateWindow = NULL; -			mImmNotifyIME = NULL; -		} -	} -} - -  // static   BOOL	LLWinImm::isIME(HKL hkl)  {  -	if ( sTheInstance.mImmIsIME ) -		return sTheInstance.mImmIsIME(hkl);  -	return FALSE; +	return ImmIsIME(hkl);  }  // static   HIMC		LLWinImm::getContext(HWND hwnd)  { -	if ( sTheInstance.mImmGetContext ) -		return sTheInstance.mImmGetContext(hwnd);  -	return 0; +	return ImmGetContext(hwnd);  }  //static   BOOL		LLWinImm::releaseContext(HWND hwnd, HIMC himc)  {  -	if ( sTheInstance.mImmIsIME ) -		return sTheInstance.mImmReleaseContext(hwnd, himc);  -	return FALSE; +	return ImmReleaseContext(hwnd, himc);  }  // static   BOOL		LLWinImm::getOpenStatus(HIMC himc)  {  -	if ( sTheInstance.mImmGetOpenStatus ) -		return sTheInstance.mImmGetOpenStatus(himc);  -	return FALSE; +	return ImmGetOpenStatus(himc);  }  // static   BOOL		LLWinImm::setOpenStatus(HIMC himc, BOOL status)  {  -	if ( sTheInstance.mImmSetOpenStatus ) -		return sTheInstance.mImmSetOpenStatus(himc, status);  -	return FALSE; +	return ImmSetOpenStatus(himc, status);  }  // static   BOOL		LLWinImm::getConversionStatus(HIMC himc, LPDWORD conversion, LPDWORD sentence)	  {  -	if ( sTheInstance.mImmGetConversionStatus ) -		return sTheInstance.mImmGetConversionStatus(himc, conversion, sentence);  -	return FALSE; +	return ImmGetConversionStatus(himc, conversion, sentence);  }  // static   BOOL		LLWinImm::setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence)		  {  -	if ( sTheInstance.mImmSetConversionStatus ) -		return sTheInstance.mImmSetConversionStatus(himc, conversion, sentence);  -	return FALSE; +	return ImmSetConversionStatus(himc, conversion, sentence);  }  // static   BOOL		LLWinImm::getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)					  {  -	if ( sTheInstance.mImmGetCompostitionWindow ) -		return sTheInstance.mImmGetCompostitionWindow(himc, form);	 -	return FALSE; +	return ImmGetCompositionWindow(himc, form);  }  // static   BOOL		LLWinImm::setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)					  {  -	if ( sTheInstance.mImmSetCompostitionWindow ) -		return sTheInstance.mImmSetCompostitionWindow(himc, form);	 -	return FALSE; +	return ImmSetCompositionWindow(himc, form);  }  // static   LONG		LLWinImm::getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length)					  {  -	if ( sTheInstance.mImmGetCompositionString ) -		return sTheInstance.mImmGetCompositionString(himc, index, data, length);	 -	return FALSE; +	return ImmGetCompositionString(himc, index, data, length);  }  // static   BOOL		LLWinImm::setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength)					  {  -	if ( sTheInstance.mImmSetCompositionString ) -		return sTheInstance.mImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength);	 -	return FALSE; +	return ImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength);  }  // static   BOOL		LLWinImm::setCompositionFont(HIMC himc, LPLOGFONTW pFont)					  {  -	if ( sTheInstance.mImmSetCompositionFont ) -		return sTheInstance.mImmSetCompositionFont(himc, pFont);	 -	return FALSE; +	return ImmSetCompositionFont(himc, pFont);  }  // static   BOOL		LLWinImm::setCandidateWindow(HIMC himc, LPCANDIDATEFORM form)					  {  -	if ( sTheInstance.mImmSetCandidateWindow ) -		return sTheInstance.mImmSetCandidateWindow(himc, form);	 -	return FALSE; +	return ImmSetCandidateWindow(himc, form);  }  // static   BOOL		LLWinImm::notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value)					  {  -	if ( sTheInstance.mImmNotifyIME ) -		return sTheInstance.mImmNotifyIME(himc, action, index, value);	 -	return FALSE; +	return ImmNotifyIME(himc, action, index, value);  } - -// ---------------------------------------------------------------------------------------- -LLWinImm::~LLWinImm() -{ -	if (mHImmDll != NULL) -	{ -		FreeLibrary(mHImmDll); -		mHImmDll = NULL; -	} -} - -  class LLMonitorInfo  {  public: @@ -552,8 +408,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,  	: LLWindow(callbacks, fullscreen, flags)  {      sMainThreadId = LLThread::currentID(); -    mWindowThread = new LLWindowWin32Thread(this); -    mWindowThread->start(); +    mWindowThread = new LLWindowWin32Thread();  	//MAINT-516 -- force a load of opengl32.dll just in case windows went sideways   	LoadLibrary(L"opengl32.dll"); @@ -624,7 +479,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,  	// Make an instance of our window then define the window class  	mhInstance = GetModuleHandle(NULL); -	mWndProc = NULL;      // Init Direct Input - needed for joystick / Spacemouse @@ -1048,17 +902,13 @@ void LLWindowWin32::close()                  // Something killed the window while we were busy destroying gl or handle somehow got broken                  LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL;              } -            mWindowHandle = NULL; -            mWindowThread->mFinished = true;          }); - -    while (!mWindowThread->isStopped()) -    { -        //nudge window thread -        PostMessage(mWindowHandle, WM_USER + 0x0017, 0xB0B0, 0x1337); -        std::this_thread::sleep_for(std::chrono::milliseconds(1)); -    } +    // Even though the above lambda might not yet have run, we've already +    // bound mWindowHandle into it by value, which should suffice for the +    // operations we're asking. That's the last time WE should touch it. +    mWindowHandle = NULL; +    mWindowThread->close();  }  BOOL LLWindowWin32::isValid() @@ -1351,51 +1201,7 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen& size, BO          << " Fullscreen: " << mFullscreen          << LL_ENDL; -    auto oldHandle = mWindowHandle; - -    //zero out mWindowHandle and mhDC before destroying window so window thread falls back to peekmessage -    mWindowHandle = 0; -    mhDC = 0; - -    if (oldHandle && !destroy_window_handler(oldHandle)) -    { -        LL_WARNS("Window") << "Failed to properly close window before recreating it!" << LL_ENDL; -    } - -    mWindowHandle = NULL; -    mhDC = 0; - -    mWindowThread->post( -        [this, window_rect, dw_ex_style, dw_style]() -        { -            mWindowHandle = CreateWindowEx(dw_ex_style, -                mWindowClassName, -                mWindowTitle, -                WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style, -                window_rect.left,								// x pos -                window_rect.top,								// y pos -                window_rect.right - window_rect.left,			// width -                window_rect.bottom - window_rect.top,			// height -                NULL, -                NULL, -                mhInstance, -                NULL); - -            if (mWindowHandle) -            { -                mhDC = GetDC(mWindowHandle); -            } -        } -    ); - -    // HACK wait for above handle to become populated -    // TODO: use a future -    int count = 1024; -    while (!mhDC && count > 0) -    { -        Sleep(10); -        --count; -    } +	recreateWindow(window_rect, dw_ex_style, dw_style);  	if (mWindowHandle)  	{ @@ -1723,48 +1529,7 @@ const	S32   max_format  = (S32)num_formats - 1;  			mhDC = 0;											// Zero The Device Context  		} -        auto oldHandle = mWindowHandle; -        mWindowHandle = NULL; -        mhDC = 0; - -        // Destroy The Window -        if (oldHandle && !destroy_window_handler(oldHandle)) -        { -            LL_WARNS("Window") << "Failed to properly close window!" << LL_ENDL; -        }		 - -        mWindowThread->post( -            [this, window_rect, dw_ex_style, dw_style]() -            { -                mWindowHandle = CreateWindowEx(dw_ex_style, -                    mWindowClassName, -                    mWindowTitle, -                    WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style, -                    window_rect.left,								// x pos -                    window_rect.top,								// y pos -                    window_rect.right - window_rect.left,			// width -                    window_rect.bottom - window_rect.top,			// height -                    NULL, -                    NULL, -                    mhInstance, -                    NULL); - -                if (mWindowHandle) -                { -                    mhDC = GetDC(mWindowHandle); -                } -            } -        ); - -        // HACK wait for above handle to become populated -        // TODO: use a future -        int count = 1024; -        while (!mhDC && count > 0) -        { -            PostMessage(oldHandle, WM_USER + 8, 0x1717, 0x3b3b); -            Sleep(10); -            --count; -        } +		recreateWindow(window_rect, dw_ex_style, dw_style);  		if (mWindowHandle)  		{ @@ -2347,7 +2112,7 @@ void LLWindowWin32::gatherInput()      } -    if (mWindowThread->mFunctionQueue.size() > 0) +    if (mWindowThread->getQueue().size())      {          LL_PROFILE_ZONE_NAMED("gi - PostMessage");          kickWindowThread(); @@ -2455,17 +2220,6 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_      if (NULL != window_imp)      { -        // Has user provided their own window callback? -        if (NULL != window_imp->mWndProc) -        { -            LL_PROFILE_ZONE_NAMED("mwp - WndProc"); -            if (!window_imp->mWndProc(h_wnd, u_msg, w_param, l_param)) -            { -                // user has handled window message -                return 0; -            } -        } -          // Juggle to make sure we can get negative positions for when          // mouse is outside window.          LLCoordWindow window_coord((S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param)); @@ -4746,10 +4500,8 @@ std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList()  #endif // LL_WINDOWS -inline LLWindowWin32Thread::LLWindowWin32Thread(LLWindowWin32* window) -    : LLThread("Window Thread"),  -    mWindow(window), -    mFunctionQueue(MAX_QUEUE_SIZE) +inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread() +    : ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE)  {      ThreadPool::start();  } @@ -4813,7 +4565,7 @@ void LLWindowWin32::LLWindowWin32Thread::run()          {              MSG msg;              BOOL status; -            if (mWindow->mhDC == 0) +            if (mhDC == 0)              {                  LL_PROFILE_ZONE_NAMED("w32t - PeekMessage");                  logger.onChange("PeekMessage(", std::hex, mWindowHandle, ")"); @@ -4840,11 +4592,7 @@ void LLWindowWin32::LLWindowWin32Thread::run()              LL_PROFILE_ZONE_NAMED("w32t - Function Queue");              logger.onChange("runPending()");              //process any pending functions -            std::function<void()> curFunc; -            while (mFunctionQueue.tryPopBack(curFunc)) -            { -                curFunc(); -            } +            getQueue().runPending();          }  #if 0 @@ -4857,11 +4605,6 @@ void LLWindowWin32::LLWindowWin32Thread::run()      }  } -void LLWindowWin32Thread::post(const std::function<void()>& func) -{ -    mFunctionQueue.pushFront(func); -} -  void LLWindowWin32::post(const std::function<void()>& func)  {      mFunctionQueue.pushFront(func); diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 8d0193abc8..b02815e990 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -36,44 +36,12 @@  #include "llthread.h"  #include "llthreadsafequeue.h"  #include "llmutex.h" +#include "workqueue.h"  // Hack for async host by name  #define LL_WM_HOST_RESOLVED      (WM_APP + 1)  typedef void (*LLW32MsgCallback)(const MSG &msg); -class LLWindowWin32; - -// Thread that owns the Window Handle -class LLWindowWin32Thread : public LLThread -{ -public: -    class Message -    { -    public: -        LRESULT mMsg; -    }; - -    static const int MAX_QUEUE_SIZE = 2048; - -    LLThreadSafeQueue<MSG> mMessageQueue; -    LLThreadSafeQueue<std::function<void()>> mFunctionQueue; - -    bool mFinished = false; - -    LLWindowWin32Thread(LLWindowWin32* window); - -    void run() override; - -    void post(const std::function<void()>& func); - -private: - -    // call PeekMessage and pull enqueue messages for later processing -    void gatherInput(); -    LLWindowWin32* mWindow = nullptr; - -}; -  class LLWindowWin32 : public LLWindow  {  public: @@ -218,7 +186,6 @@ protected:  	HGLRC		mhRC = 0;			// OpenGL rendering context  	HDC			mhDC = 0;			// Windows Device context handle  	HINSTANCE	mhInstance;		// handle to application instance -	WNDPROC		mWndProc;		// user-installable window proc  	RECT		mOldMouseClip;  // Screen rect to which the mouse cursor was globally constrained before we changed it in clipMouse()  	WPARAM		mLastSizeWParam;  	F32			mOverrideAspectRatio; @@ -280,7 +247,6 @@ protected:  	void kickWindowThread(HWND windowHandle=0);  	friend class LLWindowManager; -    friend class LLWindowWin32Thread;  };  class LLSplashScreenWin32 : public LLSplashScreen diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9b636e5e5d..5f085bb9ad 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -393,7 +393,6 @@ set(viewer_SOURCE_FILES      llloginhandler.cpp      lllogininstance.cpp      llmachineid.cpp -    llmainlooprepeater.cpp      llmanip.cpp      llmaniprotate.cpp      llmanipscale.cpp @@ -1032,7 +1031,6 @@ set(viewer_HEADER_FILES      llloginhandler.h      lllogininstance.h      llmachineid.h -    llmainlooprepeater.h      llmanip.h      llmaniprotate.h      llmanipscale.h @@ -1604,6 +1602,7 @@ if (WINDOWS)          ${WINDOWS_LIBRARIES}          comdlg32          dxguid +        imm32          kernel32          odbc32          odbccp32 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2d821b7451..058da4b66d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3871,6 +3871,17 @@          <key>Value</key>          <integer>1</integer>      </map> +    <key>MainWorkTime</key> +    <map> +        <key>Comment</key> +        <string>Max time per frame devoted to mainloop work queue (in milliseconds)</string> +        <key>Persist</key> +        <integer>1</integer> +        <key>Type</key> +        <string>F32</string> +        <key>Value</key> +        <real>0.1</real> +    </map>      <key>QueueInventoryFetchTimeout</key>      <map>          <key>Comment</key> @@ -12667,6 +12678,20 @@        <key>Value</key>        <string />      </map> +    <key>ThreadPoolSizes</key> +    <map> +      <key>Comment</key> +      <string>Map of size overrides for specific thread pools.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>LLSD</string> +      <key>Value</key> +      <map> +        <key>General</key> +        <integer>4</integer> +      </map> +    </map>      <key>ThrottleBandwidthKBPS</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 5b3cbff9b8..e13c0a2472 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -233,11 +233,12 @@  #include "llavatariconctrl.h"  #include "llgroupiconctrl.h"  #include "llviewerassetstats.h" +#include "workqueue.h" +using namespace LL;  // Include for security api initialization  #include "llsecapi.h"  #include "llmachineid.h" -#include "llmainlooprepeater.h"  #include "llcleanup.h"  #include "llcoproceduremanager.h" @@ -366,6 +367,10 @@ BOOL gLogoutInProgress = FALSE;  BOOL gSimulateMemLeak = FALSE; +// We don't want anyone, especially threads working on the graphics pipeline, +// to have to block due to this WorkQueue being full. +WorkQueue gMainloopWork("mainloop", 1024*1024); +  ////////////////////////////////////////////////////////////  // Internal globals... that should be removed.  static std::string gArgs; @@ -381,42 +386,6 @@ static std::string gLaunchFileOnQuit;  // Used on Win32 for other apps to identify our window (eg, win_setup)  const char* const VIEWER_WINDOW_CLASSNAME = "Second Life"; -//-- LLDeferredTaskList ------------------------------------------------------ - -/** - * A list of deferred tasks. - * - * We sometimes need to defer execution of some code until the viewer gets idle, - * e.g. removing an inventory item from within notifyObservers() may not work out. - * - * Tasks added to this list will be executed in the next LLAppViewer::idle() iteration. - * All tasks are executed only once. - */ -class LLDeferredTaskList: public LLSingleton<LLDeferredTaskList> -{ -	LLSINGLETON_EMPTY_CTOR(LLDeferredTaskList); -	LOG_CLASS(LLDeferredTaskList); - -	friend class LLAppViewer; -	typedef boost::signals2::signal<void()> signal_t; - -	void addTask(const signal_t::slot_type& cb) -	{ -		mSignal.connect(cb); -	} - -	void run() -	{ -		if (!mSignal.empty()) -		{ -			mSignal(); -			mSignal.disconnect_all_slots(); -		} -	} - -	signal_t mSignal; -}; -  //----------------------------------------------------------------------------  // List of entries from strings.xml to always replace @@ -973,9 +942,6 @@ bool LLAppViewer::init()  	}  	LL_INFOS("InitInfo") << "Cache initialization is done." << LL_ENDL ; -	// Initialize the repeater service. -	LLMainLoopRepeater::instance().start(); -      // Initialize event recorder      LLViewerEventRecorder::createInstance(); @@ -2217,8 +2183,6 @@ bool LLAppViewer::cleanup()  	SUBSYSTEM_CLEANUP(LLProxy);      LLCore::LLHttp::cleanup(); -	LLMainLoopRepeater::instance().stop(); -  	ll_close_fail_log();  	LLError::LLCallStacks::cleanup(); @@ -4550,7 +4514,7 @@ bool LLAppViewer::initCache()  void LLAppViewer::addOnIdleCallback(const boost::function<void()>& cb)  { -	LLDeferredTaskList::instance().addTask(cb); +	gMainloopWork.post(cb);  }  void LLAppViewer::loadKeyBindings() @@ -4948,7 +4912,6 @@ void LLAppViewer::idle()  	LLNotificationsUI::LLToast::updateClass();  	LLSmoothInterpolation::updateInterpolants();  	LLMortician::updateClass(); -    LLImageGL::updateClass();  	LLFilePickerThread::clearDead();  //calls LLFilePickerThread::notify()  	LLDirPickerThread::clearDead();  	F32 dt_raw = idle_timer.getElapsedTimeAndResetF32(); diff --git a/indra/newview/llmainlooprepeater.cpp b/indra/newview/llmainlooprepeater.cpp deleted file mode 100644 index 6736e9a950..0000000000 --- a/indra/newview/llmainlooprepeater.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/**  - * @file llmachineid.cpp - * @brief retrieves unique machine ids - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" -#include "llapr.h" -#include "llevents.h" -#include "llmainlooprepeater.h" - - - -// LLMainLoopRepeater -//----------------------------------------------------------------------------- - - -LLMainLoopRepeater::LLMainLoopRepeater(void): -	mQueue(0) -{ -	; // No op. -} - - -void LLMainLoopRepeater::start(void) -{ -	if(mQueue != 0) return; - -	mQueue = new LLThreadSafeQueue<LLSD>(1024); -	mMainLoopConnection = LLEventPumps::instance(). -		obtain("mainloop").listen(LLEventPump::inventName(), boost::bind(&LLMainLoopRepeater::onMainLoop, this, _1)); -	mRepeaterConnection = LLEventPumps::instance(). -		obtain("mainlooprepeater").listen(LLEventPump::inventName(), boost::bind(&LLMainLoopRepeater::onMessage, this, _1)); -} - - -void LLMainLoopRepeater::stop(void) -{ -	mMainLoopConnection.release(); -	mRepeaterConnection.release(); - -	delete mQueue; -	mQueue = 0; -} - - -bool LLMainLoopRepeater::onMainLoop(LLSD const &) -{ -	LLSD message; -	while(mQueue->tryPopBack(message)) { -		std::string pump = message["pump"].asString(); -		if(pump.length() == 0 ) continue; // No pump. -		LLEventPumps::instance().obtain(pump).post(message["payload"]); -	} -	return false; -} - - -bool LLMainLoopRepeater::onMessage(LLSD const & event) -{ -	try { -		mQueue->pushFront(event); -	} catch(LLThreadSafeQueueError & e) { -		LL_WARNS() << "could not repeat message (" << e.what() << ")" <<  -			event.asString() << LL_ENDL; -	} -	return false; -} diff --git a/indra/newview/llmainlooprepeater.h b/indra/newview/llmainlooprepeater.h deleted file mode 100644 index 2ec3a74e4a..0000000000 --- a/indra/newview/llmainlooprepeater.h +++ /dev/null @@ -1,64 +0,0 @@ -/**  - * @file llmainlooprepeater.h - * @brief a service for repeating messages on the main loop. - * - * $LicenseInfo:firstyear=2010&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLMAINLOOPREPEATER_H -#define LL_LLMAINLOOPREPEATER_H - - -#include "llsd.h" -#include "llthreadsafequeue.h" - - -// -// A service which creates the pump 'mainlooprepeater' to which any thread can -// post a message that will be re-posted on the main loop. -// -// The posted message should contain two map elements: pump and payload.  The -// pump value is a string naming the pump to which the message should be -// re-posted.  The payload value is what will be posted to the designated pump. -// -class LLMainLoopRepeater: -	public LLSingleton<LLMainLoopRepeater> -{ -	LLSINGLETON(LLMainLoopRepeater); -public: -	// Start the repeater service. -	void start(void); -	 -	// Stop the repeater service. -	void stop(void); -	 -private: -	LLTempBoundListener mMainLoopConnection; -	LLTempBoundListener mRepeaterConnection; -	LLThreadSafeQueue<LLSD> * mQueue; -	 -	bool onMainLoop(LLSD const &); -	bool onMessage(LLSD const & event); -}; - - -#endif diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 8d21b04511..df066fb7ed 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -206,6 +206,9 @@  #include "llstacktrace.h" +#include "threadpool.h" + +  #if LL_WINDOWS  #include "lldxhardware.h"  #endif @@ -303,6 +306,20 @@ void callback_cache_name(const LLUUID& id, const std::string& full_name, bool is  // local classes  // +void launchThreadPool() +{ +    LLSD poolSizes{ gSavedSettings.getLLSD("ThreadPoolSizes") }; +    LLSD sizeSpec{ poolSizes["General"] }; +    LLSD::Integer size{ sizeSpec.isInteger()? sizeSpec.asInteger() : 3 }; +    LL_DEBUGS("ThreadPool") << "Instantiating General pool with " +                            << size << " threads" << LL_ENDL; +    // Use a function-static ThreadPool: static duration, but instantiated +    // only on demand. +    // We don't want anyone, especially the main thread, to have to block +    // due to this ThreadPool being full. +    static LL::ThreadPool pool("General", size, 1024*1024); +} +  void update_texture_fetch()  {  	LLAppViewer::getTextureCache()->update(1); // unpauses the texture cache thread diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index e6ac701644..f932acd48c 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -679,6 +679,9 @@ void LLViewerTexture::init(bool firstinit)  	mVolumeList[LLRender::LIGHT_TEX].clear();  	mVolumeList[LLRender::SCULPT_TEX].clear(); + +	mMainQueue	= LL::WorkQueue::getInstance("mainloop"); +	mImageQueue = LL::WorkQueue::getInstance("LLImageGL");  }  //virtual  @@ -1622,17 +1625,26 @@ void LLViewerFetchedTexture::scheduleCreateTexture()      {          mNeedsCreateTexture = TRUE;  #if LL_WINDOWS //flip to 0 to revert to single-threaded OpenGL texture uploads -        if (!LLImageGLThread::sInstance->post([this]() -            { -                //actually create the texture on a background thread -                createTexture(); -                LLImageGLThread::sInstance->postCallback([this]() -                    { -                        //finalize on main thread -                        postCreateTexture(); -                        unref(); -                    }); -            })) +        auto mainq = mMainQueue.lock(); +        if (mainq) +        { +            mainq->postTo( +                mImageQueue, +                // work to be done on LLImageGL worker thread +                [this]() +                { +                    //actually create the texture on a background thread +                    createTexture(); +                }, +                // callback to be run on main thread +                [this]() +                { +                    //finalize on main thread +                    postCreateTexture(); +                    unref(); +                }); +        } +        else  #endif          {              gTextureList.mCreateTextureList.insert(this); diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index f9f1bfef44..4cd4c7cd39 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -35,6 +35,7 @@  #include "llrender.h"  #include "llmetricperformancetester.h"  #include "httpcommon.h" +#include "workqueue.h"  #include <map>  #include <list> @@ -213,6 +214,9 @@ protected:  	//do not use LLPointer here.  	LLViewerMediaTexture* mParcelMedia ; +	LL::WorkQueue::weak_t mMainQueue; +	LL::WorkQueue::weak_t mImageQueue; +  	static F32 sTexelPixelRatio;  public:  	static const U32 sCurrentFileVersion;	 | 
