diff options
| author | Dave Houlton <euclid@lindenlab.com> | 2021-11-15 09:25:35 -0700 | 
|---|---|---|
| committer | Dave Houlton <euclid@lindenlab.com> | 2021-11-15 09:25:35 -0700 | 
| commit | 029b41c0419e975bbb28454538b46dc69ce5d2ba (patch) | |
| tree | 4f9a28bb36ee07fe9a7b45a434384afd1f24bb85 | |
| parent | aeed774ff9cc55c0c1dd2784e23b2366ff367fbe (diff) | |
Revert "SL-16220: Merge branch 'origin/DRTVWR-546' into glthread"
This reverts commit 5188a26a8521251dda07ac0140bb129f28417e49, reversing
changes made to 819088563e13f1d75e048311fbaf0df4a79b7e19.
28 files changed, 770 insertions, 849 deletions
| diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 78d6ea3090..ad6d3a5049 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,7 +256,6 @@ 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 6042c0906c..10a8ecfedb 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -847,28 +847,22 @@ template<class T>  class LLSimpleton  {  public: -    template <typename... ARGS> -    static void createInstance(ARGS&&... args) -    { +    static T* sInstance; +     +    static void createInstance()  +    {           llassert(sInstance == nullptr); -        sInstance = new T(std::forward<ARGS>(args)...); +        sInstance = new T();       } - +          static inline T* getInstance() { return sInstance; }      static inline T& instance() { return *getInstance(); }      static inline bool instanceExists() { return sInstance != nullptr; } -    static void deleteSingleton() -    { -        delete sInstance; -        sInstance = nullptr; +    static void deleteSingleton() {  +        delete sInstance;  +        sInstance = nullptr;       } - -private: -    static T* sInstance;  }; -template <class T> -T* LLSimpleton<T>::sInstance{ nullptr }; -  #endif diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h index 5c934791fe..06e8d8f609 100644 --- a/indra/llcommon/llthreadsafequeue.h +++ b/indra/llcommon/llthreadsafequeue.h @@ -85,8 +85,8 @@ public:  	LLThreadSafeQueue(U32 capacity = 1024);  	virtual ~LLThreadSafeQueue() {} -	// Add an element to the queue (will block if the queue has reached -	// capacity). +	// Add an element to the queue (will block if the queue has +	// reached capacity).  	//  	// This call will raise an interrupt error if the queue is closed while  	// the caller is blocked. @@ -95,11 +95,6 @@ public:  	// legacy name  	void pushFront(ElementT const & element) { return push(element); } -	// Add an element to the queue (will block if the queue has reached -	// capacity). Return false if the queue is closed before push is possible. -	template <typename T> -	bool pushIfOpen(T&& element); -  	// Try to add an element to the queue without blocking. Returns  	// true only if the element was actually added.  	template <typename T> @@ -316,8 +311,8 @@ bool LLThreadSafeQueue<ElementT, QueueT>::push_(lock_t& lock, T&& element)  template <typename ElementT, typename QueueT> -template <typename T> -bool LLThreadSafeQueue<ElementT, QueueT>::pushIfOpen(T&& element) +template<typename T> +void LLThreadSafeQueue<ElementT, QueueT>::push(T&& element)  {      lock_t lock1(mLock);      while (true) @@ -326,10 +321,12 @@ bool LLThreadSafeQueue<ElementT, QueueT>::pushIfOpen(T&& element)          // drained or not: the moment either end calls close(), further push()          // operations will fail.          if (mClosed) -            return false; +        { +            LLTHROW(LLThreadSafeQueueInterrupt()); +        }          if (push_(lock1, std::forward<T>(element))) -            return true; +            return;          // Storage Full. Wait for signal.          mCapacityCond.wait(lock1); @@ -337,17 +334,6 @@ bool LLThreadSafeQueue<ElementT, QueueT>::pushIfOpen(T&& element)  } -template <typename ElementT, typename QueueT> -template<typename T> -void LLThreadSafeQueue<ElementT, QueueT>::push(T&& element) -{ -    if (! pushIfOpen(std::forward<T>(element))) -    { -        LLTHROW(LLThreadSafeQueueInterrupt()); -    } -} - -  template<typename ElementT, typename QueueT>  template<typename T>  bool LLThreadSafeQueue<ElementT, QueueT>::tryPush(T&& element) diff --git a/indra/llcommon/tests/threadsafeschedule_test.cpp b/indra/llcommon/tests/threadsafeschedule_test.cpp index c421cc7b1c..af67b9f492 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() + 200ms, "ghi")); +        queue.push(Queue::TimeTuple(Queue::Clock::now() + 20ms, "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() + 100ms, "def"); +        queue.push(Queue::Clock::now() + 10ms, "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 bea3ad911b..d5405400fd 100644 --- a/indra/llcommon/tests/workqueue_test.cpp +++ b/indra/llcommon/tests/workqueue_test.cpp @@ -20,10 +20,7 @@  // 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" @@ -141,8 +138,7 @@ 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. (Of course the bound variable must -            // survive until the callback is called.) +            // on the invoking thread.              [&result](int i){ result = i; });          // this should post the callback to main          qptr->runOne(); @@ -160,70 +156,4 @@ 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/threadpool.cpp b/indra/llcommon/threadpool.cpp deleted file mode 100644 index cf25cc838e..0000000000 --- a/indra/llcommon/threadpool.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @file   threadpool.cpp - * @author Nat Goodspeed - * @date   2021-10-21 - * @brief  Implementation for threadpool. - *  - * $LicenseInfo:firstyear=2021&license=viewerlgpl$ - * Copyright (c) 2021, Linden Research, Inc. - * $/LicenseInfo$ - */ - -// Precompiled header -#include "linden_common.h" -// associated header -#include "threadpool.h" -// STL headers -// std headers -// external library headers -// other Linden headers -#include "llerror.h" -#include "llevents.h" -#include "stringize.h" - -LL::ThreadPool::ThreadPool(const std::string& name, size_t threads, size_t capacity): -    mQueue(name, capacity), -    mName("ThreadPool:" + name) -{ -    for (size_t i = 0; i < threads; ++i) -    { -        std::string tname{ STRINGIZE(mName << ':' << (i+1) << '/' << threads) }; -        mThreads.emplace_back(tname, [this, tname](){ run(tname); }); -    } -    // Listen on "LLApp", and when the app is shutting down, close the queue -    // and join the workers. -    LLEventPumps::instance().obtain("LLApp").listen( -        mName, -        [this](const LLSD& stat) -        { -            std::string status(stat["status"]); -            if (status != "running") -            { -                // viewer is starting shutdown -- proclaim the end is nigh! -                LL_DEBUGS("ThreadPool") << mName << " saw " << status << LL_ENDL; -                close(); -            } -            return false; -        }); -} - -LL::ThreadPool::~ThreadPool() -{ -    close(); -} - -void LL::ThreadPool::close() -{ -    if (! mQueue.isClosed()) -    { -        LL_DEBUGS("ThreadPool") << mName << " closing queue and joining threads" << LL_ENDL; -        mQueue.close(); -        for (auto& pair: mThreads) -        { -            LL_DEBUGS("ThreadPool") << mName << " waiting on thread " << pair.first << LL_ENDL; -            pair.second.join(); -        } -        LL_DEBUGS("ThreadPool") << mName << " shutdown complete" << LL_ENDL; -    } -} - -void LL::ThreadPool::run(const std::string& name) -{ -    LL_DEBUGS("ThreadPool") << name << " starting" << LL_ENDL; -    run(); -    LL_DEBUGS("ThreadPool") << name << " stopping" << LL_ENDL; -} - -void LL::ThreadPool::run() -{ -    mQueue.runUntilClose(); -} diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h deleted file mode 100644 index 1ca24aec58..0000000000 --- a/indra/llcommon/threadpool.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @file   threadpool.h - * @author Nat Goodspeed - * @date   2021-10-21 - * @brief  ThreadPool configures a WorkQueue along with a pool of threads to - *         service it. - *  - * $LicenseInfo:firstyear=2021&license=viewerlgpl$ - * Copyright (c) 2021, Linden Research, Inc. - * $/LicenseInfo$ - */ - -#if ! defined(LL_THREADPOOL_H) -#define LL_THREADPOOL_H - -#include "workqueue.h" -#include <string> -#include <thread> -#include <utility>                  // std::pair -#include <vector> - -namespace LL -{ - -    class ThreadPool -    { -    public: -        /** -         * Pass ThreadPool a string name. This can be used to look up the -         * relevant WorkQueue. -         */ -        ThreadPool(const std::string& name, size_t threads=1, size_t capacity=1024); -        virtual ~ThreadPool(); - -        /** -         * ThreadPool listens for application shutdown messages on the "LLApp" -         * LLEventPump. Call close() to shut down this ThreadPool early. -         */ -        void close(); - -        std::string getName() const { return mName; } -        size_t getWidth() const { return mThreads.size(); } -        /// obtain a non-const reference to the WorkQueue to post work to it -        WorkQueue& getQueue() { return mQueue; } - -        /** -         * Override run() if you need special processing. The default run() -         * implementation simply calls WorkQueue::runUntilClose(). -         */ -        virtual void run(); - -    private: -        void run(const std::string& name); - -        WorkQueue mQueue; -        std::string mName; -        std::vector<std::pair<std::string, std::thread>> mThreads; -    }; - -} // namespace LL - -#endif /* ! defined(LL_THREADPOOL_H) */ diff --git a/indra/llcommon/timing.cpp b/indra/llcommon/timing.cpp new file mode 100644 index 0000000000..c2dc695ef3 --- /dev/null +++ b/indra/llcommon/timing.cpp @@ -0,0 +1,25 @@ +/**  + * @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 633594ceea..b32357e832 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -26,9 +26,8 @@  using Mutex = LLCoros::Mutex;  using Lock  = LLCoros::LockType; -LL::WorkQueue::WorkQueue(const std::string& name, size_t capacity): -    super(makeName(name)), -    mQueue(capacity) +LL::WorkQueue::WorkQueue(const std::string& name): +    super(makeName(name))  {      // TODO: register for "LLApp" events so we can implicitly close() on      // viewer shutdown. @@ -39,21 +38,6 @@ 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 @@ -144,13 +128,3 @@ 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 c25d787425..5ec790da79 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,16 +45,11 @@ 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(), size_t capacity=1024); +        WorkQueue(const std::string& name = std::string());          /**           * Since the point of WorkQueue is to pass work to some other worker @@ -64,36 +59,15 @@ 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 or -            // postIfOpen(). 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 method. +            // All other methods should accept CALLABLEs of arbitrary type to +            // avoid multiple levels of std::function indirection.              mQueue.push(TimedWork(time, std::move(callable)));          } @@ -109,47 +83,6 @@ 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.           * @@ -182,8 +115,63 @@ namespace LL          // Studio compile errors that seem utterly unrelated to this source          // code.          template <typename CALLABLE, typename FOLLOWUP> -        bool postTo(weak_t target, -                    const TimePoint& time, CALLABLE&& callable, FOLLOWUP&& callback); +        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; +        }          /**           * Post work to another WorkQueue, requesting a specific callback to @@ -193,36 +181,10 @@ namespace LL           * inaccessible.           */          template <typename CALLABLE, typename FOLLOWUP> -        bool postTo(weak_t target, CALLABLE&& callable, FOLLOWUP&& callback) +        bool postTo(WorkQueue::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 waitForResult(TimePoint::clock::now(), std::move(callable)); +            return postTo(target, TimePoint::clock::now(), std::move(callable), std::move(callback));          }          /*--------------------------- worker API ---------------------------*/ @@ -270,23 +232,6 @@ 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); @@ -308,8 +253,8 @@ namespace LL      {      public:          // bind the desired data -        BackJack(weak_t target, -                 const TimePoint& start, +        BackJack(WorkQueue::weak_t target, +                 const WorkQueue::TimePoint& start,                   const std::chrono::duration<Rep, Period>& interval,                   CALLABLE&& callable):              mTarget(target), @@ -356,8 +301,8 @@ namespace LL          }      private: -        weak_t mTarget; -        TimePoint mStart; +        WorkQueue::weak_t mTarget; +        WorkQueue::TimePoint mStart;          std::chrono::duration<Rep, Period> mInterval;          CALLABLE mCallable;      }; @@ -385,187 +330,6 @@ namespace LL                   getWeak(), TimePoint::clock::now(), interval, std::move(callable)));      } -    /// general case: arbitrary C++ return type -    template <typename CALLABLE, typename FOLLOWUP, typename RETURNTYPE> -    struct WorkQueue::MakeReplyLambda -    { -        auto operator()(CALLABLE&& callable, FOLLOWUP&& callback) -        { -            // Call the callable in any case -- but to minimize -            // copying the result, immediately bind it into the 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(). -            return -                [result = std::forward<CALLABLE>(callable)(), -                 callback = std::move(callback)] -                () -                { callback(std::move(result)); }; -        } -    }; - -    /// specialize for CALLABLE returning void -    template <typename CALLABLE, typename FOLLOWUP> -    struct WorkQueue::MakeReplyLambda<CALLABLE, FOLLOWUP, void> -    { -        auto operator()(CALLABLE&& callable, FOLLOWUP&& callback) -        { -            // Call the callable, which produces no result. -            std::forward<CALLABLE>(callable)(); -            // Our completion callback is simply the caller's callback. -            return std::move(callback); -        } -    }; - -    template <typename CALLABLE, typename FOLLOWUP> -    auto WorkQueue::makeReplyLambda(CALLABLE&& callable, FOLLOWUP&& callback) -    { -        return MakeReplyLambda<CALLABLE, FOLLOWUP, -                               decltype(std::forward<CALLABLE>(callable)())>() -            (std::move(callable), std::move(callback)); -    } - -    template <typename CALLABLE, typename FOLLOWUP> -    bool WorkQueue::postTo(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)] -            () -            { -                // Use postMaybe() below in case this originating WorkQueue -                // has been closed or destroyed. 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. -                try -                { -                    // Make a reply lambda to repost to THIS WorkQueue. -                    // Delegate to makeReplyLambda() so we can partially -                    // specialize on void return. -                    postMaybe(reply, makeReplyLambda(std::move(callable), std::move(callback))); -                } -                catch (...) -                { -                    // Either variant of makeReplyLambda() is responsible for -                    // calling the caller's callable. If that throws, return -                    // the exception to the originating thread. -                    postMaybe( -                        reply, -                        // Bind the current exception to transport back to the -                        // originating WorkQueue. Once there, rethrow it. -                        [exc = std::current_exception()](){ std::rethrow_exception(exc); }); -                } -            }); - -        // looks like we were able to post() -        return true; -    } - -    template <typename CALLABLE> -    bool WorkQueue::postMaybe(weak_t target, const TimePoint& time, CALLABLE&& callable) -    { -        // target is a weak_ptr: have to lock it to check it -        auto tptr = target.lock(); -        if (tptr) -        { -            try -            { -                tptr->post(time, std::forward<CALLABLE>(callable)); -                // we were able to post() -                return true; -            } -            catch (const Closed&) -            { -                // target WorkQueue still exists, but is Closed -            } -        } -        // either target no longer exists, or its WorkQueue is Closed -        return false; -    } - -    /// general case: arbitrary C++ return type -    template <typename CALLABLE, typename RETURNTYPE> -    struct WorkQueue::WaitForResult -    { -        auto operator()(WorkQueue* self, const TimePoint& time, CALLABLE&& callable) -        { -            LLCoros::Promise<RETURNTYPE> promise; -            self->post( -                time, -                // We dare to bind a reference to Promise because it's -                // specifically designed for cross-thread communication. -                [&promise, callable = std::move(callable)]() -                { -                    try -                    { -                        // call the caller's callable and trigger promise with result -                        promise.set_value(callable()); -                    } -                    catch (...) -                    { -                        promise.set_exception(std::current_exception()); -                    } -                }); -            auto future{ LLCoros::getFuture(promise) }; -            // now, on the calling thread, wait for that result -            LLCoros::TempStatus st("waiting for WorkQueue::waitForResult()"); -            return future.get(); -        } -    }; - -    /// specialize for CALLABLE returning void -    template <typename CALLABLE> -    struct WorkQueue::WaitForResult<CALLABLE, void> -    { -        void operator()(WorkQueue* self, const TimePoint& time, CALLABLE&& callable) -        { -            LLCoros::Promise<void> promise; -            self->post( -                time, -                // &promise is designed for cross-thread access -                [&promise, callable = std::move(callable)]() -                { -                    try -                    { -                        callable(); -                        promise.set_value(); -                    } -                    catch (...) -                    { -                        promise.set_exception(std::current_exception()); -                    } -                }); -            auto future{ LLCoros::getFuture(promise) }; -            // block until set_value() -            LLCoros::TempStatus st("waiting for void WorkQueue::waitForResult()"); -            future.get(); -        } -    }; - -    template <typename CALLABLE> -    auto WorkQueue::waitForResult(const TimePoint& time, CALLABLE&& callable) -    { -        checkCoroutine("waitForResult()"); -        // derive callable's return type so we can specialize for void -        return WaitForResult<CALLABLE, decltype(std::forward<CALLABLE>(callable)())>() -            (this, time, std::forward<CALLABLE>(callable)); -    } -  } // namespace LL  #endif /* ! defined(LL_WORKQUEUE_H) */ diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 1b6920fe3b..cbc5392882 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -172,19 +172,31 @@ 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::createInstance(window); +    LLImageGLThread::sInstance = new LLImageGLThread(window); +    LLImageGLThread::sInstance->start(); +} + +//static +void LLImageGL::updateClass() +{ +    LL_PROFILE_ZONE_SCOPED; +    LLImageGLThread::sInstance->executeCallbacks();  }  //static   void LLImageGL::cleanupClass()   {      LL_PROFILE_ZONE_SCOPED; -    LLImageGLThread::deleteSingleton(); +    LLImageGLThread::sInstance->mFunctionQueue.close(); +    delete LLImageGLThread::sInstance; +    LLImageGLThread::sInstance = nullptr;  }  //static @@ -492,9 +504,6 @@ 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() @@ -1527,7 +1536,8 @@ 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 (! on_main_thread()) +    if (LLImageGLThread::sInstance != nullptr &&  +        LLThread::currentID() == LLImageGLThread::sInstance->getID())      {          {              LL_PROFILE_ZONE_NAMED("cglt - sync"); @@ -1544,9 +1554,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_          }          ref(); -        LL::WorkQueue::postMaybe( -            mMainQueue, -            [=]() +        LLImageGLThread::sInstance->postCallback([=]()              {                  LL_PROFILE_ZONE_NAMED("cglt - delete callback");                  if (old_texname != 0) @@ -2251,24 +2259,73 @@ void LLImageGL::resetCurTexSizebar()  */    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) -    , mWindow(window) +    : LLThread("LLImageGL"), mWindow(window)  {      mFinished = false;      mContext = mWindow->createSharedContext();  } +// 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()  { -    // We must perform setup on this thread before actually servicing our -    // WorkQueue, likewise cleanup afterwards.      mWindow->makeContextCurrent(mContext);      gGL.init(); -    ThreadPool::run(); +    mFunctionQueue.runUntilClose();      gGL.shutdown();      mWindow->destroySharedContext(mContext);  } diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 27496def1d..8264e4a5f2 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -37,7 +37,6 @@  #include "llunits.h"  #include "llthreadsafequeue.h"  #include "llrender.h" -#include "threadpool.h"  #include "workqueue.h"  class LLTextureAtlas ; @@ -199,7 +198,6 @@ 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; @@ -273,6 +271,7 @@ public:  public:  	static void initClass(LLWindow* window, S32 num_catagories, BOOL skip_analyze_alpha = false);  +    static void updateClass();  	static void cleanupClass() ;  private: @@ -308,24 +307,34 @@ public:  }; -class LLImageGLThread : public LLSimpleton<LLImageGLThread>, LL::ThreadPool +class LLImageGLThread : public LLThread  {  public:      LLImageGLThread(LLWindow* window);      // post a function to be executed on the LLImageGL background thread -    template <typename CALLABLE> -    bool post(CALLABLE&& func) -    { -        return getQueue().postIfOpen(std::forward<CALLABLE>(func)); -    } +    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();      void run() override; -private: +    // Work Queue for background thread +    LL::WorkQueue mFunctionQueue; + +    // Work Queue for main thread (run from updateClass) +    LL::WorkQueue mCallbackQueue; +      LLWindow* mWindow;      void* mContext;      LLAtomicBool mFinished; + +    std::queue<std::function<void()>> mPendingCallbackQ; + +    static LLImageGLThread* sInstance;  }; diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 55c1655d7b..f781ff4110 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -308,10 +308,6 @@ if(LL_TESTS)        ${BOOST_FIBER_LIBRARY} ${BOOST_CONTEXT_LIBRARY} ${BOOST_SYSTEM_LIBRARY}        ${WINDOWS_LIBRARIES})    if(NOT LINUX) -    if(WINDOWS) -      LL_ADD_INTEGRATION_TEST(llurlentry llurlentry.cpp "imm32;${test_libs}") -    else(WINDOWS) -      LL_ADD_INTEGRATION_TEST(llurlentry llurlentry.cpp "${test_libs}") -    endif(WINDOWS) +    LL_ADD_INTEGRATION_TEST(llurlentry llurlentry.cpp "${test_libs}")    endif(NOT LINUX)  endif(LL_TESTS) diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp index cb000aef74..5a44ec947a 100644 --- a/indra/llui/llviewereventrecorder.cpp +++ b/indra/llui/llviewereventrecorder.cpp @@ -28,6 +28,8 @@  #include "llui.h"  #include "llleap.h" +LLViewerEventRecorder* LLSimpleton<LLViewerEventRecorder>::sInstance = nullptr; +  LLViewerEventRecorder::LLViewerEventRecorder() {    clear(UNDEFINED); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 3f3dd43daf..e52624d66a 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -55,8 +55,6 @@  #include <shellapi.h>  #include <fstream>  #include <Imm.h> -#include <future> -#include <utility>                  // std::pair  // Require DirectInput version 8  #define DIRECTINPUT_VERSION 0x0800 @@ -176,19 +174,23 @@ DWORD	LLWindowWin32::sWinIMESentenceMode = IME_SMODE_AUTOMATIC;  LLCoordWindow LLWindowWin32::sWinIMEWindowPosition(-1,-1);  // The following class LLWinImm delegates Windows IMM APIs. -// 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. +// 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  class LLWinImm  {  public: -	static bool		isAvailable() { return true; } +	static bool		isAvailable() { return sTheInstance.mHImmDll != NULL; }  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);												 @@ -202,96 +204,236 @@ 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)  {  -	return ImmIsIME(hkl); +	if ( sTheInstance.mImmIsIME ) +		return sTheInstance.mImmIsIME(hkl);  +	return FALSE;  }  // static   HIMC		LLWinImm::getContext(HWND hwnd)  { -	return ImmGetContext(hwnd); +	if ( sTheInstance.mImmGetContext ) +		return sTheInstance.mImmGetContext(hwnd);  +	return 0;  }  //static   BOOL		LLWinImm::releaseContext(HWND hwnd, HIMC himc)  {  -	return ImmReleaseContext(hwnd, himc); +	if ( sTheInstance.mImmIsIME ) +		return sTheInstance.mImmReleaseContext(hwnd, himc);  +	return FALSE;  }  // static   BOOL		LLWinImm::getOpenStatus(HIMC himc)  {  -	return ImmGetOpenStatus(himc); +	if ( sTheInstance.mImmGetOpenStatus ) +		return sTheInstance.mImmGetOpenStatus(himc);  +	return FALSE;  }  // static   BOOL		LLWinImm::setOpenStatus(HIMC himc, BOOL status)  {  -	return ImmSetOpenStatus(himc, status); +	if ( sTheInstance.mImmSetOpenStatus ) +		return sTheInstance.mImmSetOpenStatus(himc, status);  +	return FALSE;  }  // static   BOOL		LLWinImm::getConversionStatus(HIMC himc, LPDWORD conversion, LPDWORD sentence)	  {  -	return ImmGetConversionStatus(himc, conversion, sentence); +	if ( sTheInstance.mImmGetConversionStatus ) +		return sTheInstance.mImmGetConversionStatus(himc, conversion, sentence);  +	return FALSE;  }  // static   BOOL		LLWinImm::setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence)		  {  -	return ImmSetConversionStatus(himc, conversion, sentence); +	if ( sTheInstance.mImmSetConversionStatus ) +		return sTheInstance.mImmSetConversionStatus(himc, conversion, sentence);  +	return FALSE;  }  // static   BOOL		LLWinImm::getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)					  {  -	return ImmGetCompositionWindow(himc, form); +	if ( sTheInstance.mImmGetCompostitionWindow ) +		return sTheInstance.mImmGetCompostitionWindow(himc, form);	 +	return FALSE;  }  // static   BOOL		LLWinImm::setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)					  {  -	return ImmSetCompositionWindow(himc, form); +	if ( sTheInstance.mImmSetCompostitionWindow ) +		return sTheInstance.mImmSetCompostitionWindow(himc, form);	 +	return FALSE;  }  // static   LONG		LLWinImm::getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length)					  {  -	return ImmGetCompositionString(himc, index, data, length); +	if ( sTheInstance.mImmGetCompositionString ) +		return sTheInstance.mImmGetCompositionString(himc, index, data, length);	 +	return FALSE;  }  // static   BOOL		LLWinImm::setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength)					  {  -	return ImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength); +	if ( sTheInstance.mImmSetCompositionString ) +		return sTheInstance.mImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength);	 +	return FALSE;  }  // static   BOOL		LLWinImm::setCompositionFont(HIMC himc, LPLOGFONTW pFont)					  {  -	return ImmSetCompositionFont(himc, pFont); +	if ( sTheInstance.mImmSetCompositionFont ) +		return sTheInstance.mImmSetCompositionFont(himc, pFont);	 +	return FALSE;  }  // static   BOOL		LLWinImm::setCandidateWindow(HIMC himc, LPCANDIDATEFORM form)					  {  -	return ImmSetCandidateWindow(himc, form); +	if ( sTheInstance.mImmSetCandidateWindow ) +		return sTheInstance.mImmSetCandidateWindow(himc, form);	 +	return FALSE;  }  // static   BOOL		LLWinImm::notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value)					  {  -	return ImmNotifyIME(himc, action, index, value); +	if ( sTheInstance.mImmNotifyIME ) +		return sTheInstance.mImmNotifyIME(himc, action, index, value);	 +	return FALSE;  } + +// ---------------------------------------------------------------------------------------- +LLWinImm::~LLWinImm() +{ +	if (mHImmDll != NULL) +	{ +		FreeLibrary(mHImmDll); +		mHImmDll = NULL; +	} +} + +  class LLMonitorInfo  {  public: @@ -326,32 +468,6 @@ private:  static LLMonitorInfo sMonitorInfo; -// Thread that owns the Window Handle -// This whole struct is private to LLWindowWin32, which needs to mess with its -// members, which is why it's a struct rather than a class. In effect, we make -// the containing class a friend. -struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool -{ -    static const int MAX_QUEUE_SIZE = 2048; - -    LLThreadSafeQueue<MSG> mMessageQueue; - -    LLWindowWin32Thread(); - -    void run() override; - -    template <typename CALLABLE> -    void post(CALLABLE&& func) -    { -        getQueue().post(std::forward<CALLABLE>(func)); -    } - -    // call PeekMessage and pull enqueue messages for later processing -    void gatherInput(); -    HWND mWindowHandle = NULL; -    HDC mhDC = 0; -}; -  LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,  							 const std::string& title, const std::string& name, S32 x, S32 y, S32 width, @@ -363,7 +479,8 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,  	: LLWindow(callbacks, fullscreen, flags)  {      sMainThreadId = LLThread::currentID(); -    mWindowThread = new LLWindowWin32Thread(); +    mWindowThread = new LLWindowWin32Thread(this); +    mWindowThread->start();  	//MAINT-516 -- force a load of opengl32.dll just in case windows went sideways   	LoadLibrary(L"opengl32.dll"); @@ -434,6 +551,7 @@ 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 @@ -857,13 +975,17 @@ 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;          }); -    // 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(); + +    while (!mWindowThread->isStopped()) +    { +        //nudge window thread +        PostMessage(mWindowHandle, WM_USER + 0x0017, 0xB0B0, 0x1337); +        std::this_thread::sleep_for(std::chrono::milliseconds(1)); +    }  }  BOOL LLWindowWin32::isValid() @@ -1156,7 +1278,51 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen& size, BO          << " Fullscreen: " << mFullscreen          << LL_ENDL; -	recreateWindow(window_rect, dw_ex_style, dw_style); +    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; +    }  	if (mWindowHandle)  	{ @@ -1484,7 +1650,48 @@ const	S32   max_format  = (S32)num_formats - 1;  			mhDC = 0;											// Zero The Device Context  		} -		recreateWindow(window_rect, dw_ex_style, dw_style); +        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; +        }  		if (mWindowHandle)  		{ @@ -1621,64 +1828,6 @@ const	S32   max_format  = (S32)num_formats - 1;  	return TRUE;  } -void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw_style) -{ -    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; -    } - -    std::promise<std::pair<HWND, HDC>> promise; -    mWindowThread->post( -        [this, window_rect, dw_ex_style, dw_style, &promise]() -        { -            auto handle = 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 (! handle) -            { -                // Failed to create window: clear the variables. This -                // assignment is valid because we're running on mWindowThread. -                mWindowThread->mWindowHandle = NULL; -                mWindowThread->mhDC = 0; -            } -            else -            { -                // Update mWindowThread's own mWindowHandle and mhDC. -                mWindowThread->mWindowHandle = handle; -                mWindowThread->mhDC = GetDC(handle); -            } -                 -            // It's important to wake up the future either way. -            promise.set_value(std::make_pair(mWindowThread->mWindowHandle, mWindowThread->mhDC)); -        } -    ); - -    auto future = promise.get_future(); -    // This blocks until mWindowThread processes CreateWindowEx() and calls -    // promise.set_value(). -    auto pair = future.get(); -    mWindowHandle = pair.first; -    mhDC = pair.second; -} -  void* LLWindowWin32::createSharedContext()  {      S32 attribs[] = @@ -2032,14 +2181,12 @@ void LLWindowWin32::gatherInput()      } -    if (mWindowThread->getQueue().size()) +    if (mWindowThread->mFunctionQueue.size() > 0)      {          LL_PROFILE_ZONE_NAMED("gi - PostMessage");          if (mWindowHandle) -        { -            // post a nonsense user message to wake up the Window Thread in -            // case any functions are pending and no windows events came -            // through this frame +        { // post a nonsense user message to wake up the Window Thread in case any functions are pending +            // and no windows events came through this frame              PostMessage(mWindowHandle, WM_USER + 0x0017, 0xB0B0, 0x1337);          }      } @@ -2129,6 +2276,17 @@ 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)); @@ -4409,32 +4567,35 @@ std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList()  #endif // LL_WINDOWS -inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread() -    : ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE) +inline LLWindowWin32Thread::LLWindowWin32Thread(LLWindowWin32* window) +    : LLThread("Window Thread"),  +    mWindow(window), +    mFunctionQueue(MAX_QUEUE_SIZE)  { +  } -void LLWindowWin32::LLWindowWin32Thread::run() +inline void LLWindowWin32Thread::run()  { -    sWindowThreadId = std::this_thread::get_id(); -    while (! getQueue().done()) +    sWindowThreadId = getID(); +    while (!mFinished)      {          LL_PROFILE_ZONE_SCOPED; -        if (mWindowHandle != 0) +        if (mWindow && mWindow->mWindowHandle != 0)          {              MSG msg;              BOOL status; -            if (mhDC == 0) +            if (mWindow->mhDC == 0)              {                  LL_PROFILE_ZONE_NAMED("w32t - PeekMessage"); -                status = PeekMessage(&msg, mWindowHandle, 0, 0, PM_REMOVE); +                status = PeekMessage(&msg, mWindow->mWindowHandle, 0, 0, PM_REMOVE);              }              else              {                  LL_PROFILE_ZONE_NAMED("w32t - GetMessage"); -                status = GetMessage(&msg, mWindowHandle, 0, 0); +                status = GetMessage(&msg, mWindow->mWindowHandle, 0, 0);              }              if (status > 0)              { @@ -4448,7 +4609,11 @@ void LLWindowWin32::LLWindowWin32Thread::run()          {              LL_PROFILE_ZONE_NAMED("w32t - Function Queue");              //process any pending functions -            getQueue().runPending(); +            std::function<void()> curFunc; +            while (mFunctionQueue.tryPopBack(curFunc)) +            { +                curFunc(); +            }          }  #if 0 @@ -4460,6 +4625,11 @@ 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 5966061177..d082080807 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -36,12 +36,44 @@  #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: @@ -186,6 +218,7 @@ 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; @@ -237,15 +270,14 @@ protected:  	BOOL			mMouseVanish; -	struct LLWindowWin32Thread; -	LLWindowWin32Thread* mWindowThread = nullptr; -	LLThreadSafeQueue<std::function<void()>> mFunctionQueue; -	LLThreadSafeQueue<std::function<void()>> mMouseQueue; -	void post(const std::function<void()>& func); -	void postMouseButtonEvent(const std::function<void()>& func); -	void recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw_style); +    LLWindowWin32Thread* mWindowThread = nullptr; +    LLThreadSafeQueue<std::function<void()>> mFunctionQueue; +    LLThreadSafeQueue<std::function<void()>> mMouseQueue; +    void post(const std::function<void()>& func); +    void postMouseButtonEvent(const std::function<void()>& func);  	friend class LLWindowManager; +    friend class LLWindowWin32Thread;  };  class LLSplashScreenWin32 : public LLSplashScreen diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 6d090be33a..631089f6ce 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -393,6 +393,7 @@ set(viewer_SOURCE_FILES      llloginhandler.cpp      lllogininstance.cpp      llmachineid.cpp +    llmainlooprepeater.cpp      llmanip.cpp      llmaniprotate.cpp      llmanipscale.cpp @@ -1031,6 +1032,7 @@ set(viewer_HEADER_FILES      llloginhandler.h      lllogininstance.h      llmachineid.h +    llmainlooprepeater.h      llmanip.h      llmaniprotate.h      llmanipscale.h @@ -1602,7 +1604,6 @@ 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 607e531e62..f15b5d0981 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3858,17 +3858,6 @@          <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> @@ -12674,20 +12663,6 @@        <key>Value</key>        <integer>50</integer>      </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 93e5c2e341..89756d0881 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -233,12 +233,11 @@  #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" @@ -367,10 +366,6 @@ 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; @@ -386,6 +381,42 @@ 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 @@ -943,6 +974,9 @@ bool LLAppViewer::init()  	}  	LL_INFOS("InitInfo") << "Cache initialization is done." << LL_ENDL ; +	// Initialize the repeater service. +	LLMainLoopRepeater::instance().start(); +      // Initialize event recorder      LLViewerEventRecorder::createInstance(); @@ -2158,6 +2192,8 @@ bool LLAppViewer::cleanup()  	SUBSYSTEM_CLEANUP(LLProxy);      LLCore::LLHttp::cleanup(); +	LLMainLoopRepeater::instance().stop(); +  	ll_close_fail_log();  	LLError::LLCallStacks::cleanup(); @@ -4452,7 +4488,7 @@ bool LLAppViewer::initCache()  void LLAppViewer::addOnIdleCallback(const boost::function<void()>& cb)  { -	gMainloopWork.post(cb); +	LLDeferredTaskList::instance().addTask(cb);  }  void LLAppViewer::loadKeyBindings() @@ -4850,6 +4886,7 @@ 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(); @@ -5226,19 +5263,8 @@ void LLAppViewer::idle()  		}  	} -	// Service the WorkQueue we use for replies from worker threads. -	// Use function statics for the timeslice setting so we only have to fetch -	// and convert MainWorkTime once. -	static F32 MainWorkTimeRaw = gSavedSettings.getF32("MainWorkTime"); -	static F32Milliseconds MainWorkTimeMs(MainWorkTimeRaw); -	// MainWorkTime is specified in fractional milliseconds, but std::chrono -	// uses integer representations. What if we want less than a microsecond? -	// Use nanoseconds. We're very sure we will never need to specify a -	// MainWorkTime that would be larger than we could express in -	// std::chrono::nanoseconds. -	static std::chrono::nanoseconds MainWorkTimeNanoSec{ -		std::chrono::nanoseconds::rep(MainWorkTimeMs.value() * 1000000)}; -	gMainloopWork.runFor(MainWorkTimeNanoSec); +	// Execute deferred tasks. +	LLDeferredTaskList::instance().run();  	// Handle shutdown process, for example,  	// wait for floaters to close, send quit message, diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp index 1a66f10b8f..dba24b3d02 100644 --- a/indra/newview/llenvironment.cpp +++ b/indra/newview/llenvironment.cpp @@ -824,6 +824,7 @@ std::string env_selection_to_string(LLEnvironment::EnvSelection_t sel)  #undef RTNENUM  } +LLEnvironment* LLSimpleton<LLEnvironment>::sInstance = nullptr;  //-------------------------------------------------------------------------  LLEnvironment::LLEnvironment():      mCloudScrollDelta(), diff --git a/indra/newview/llmainlooprepeater.cpp b/indra/newview/llmainlooprepeater.cpp new file mode 100644 index 0000000000..6736e9a950 --- /dev/null +++ b/indra/newview/llmainlooprepeater.cpp @@ -0,0 +1,88 @@ +/**  + * @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 new file mode 100644 index 0000000000..2ec3a74e4a --- /dev/null +++ b/indra/newview/llmainlooprepeater.h @@ -0,0 +1,64 @@ +/**  + * @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/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index bc00c518e9..53247031b4 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -97,6 +97,8 @@  #include "llglheaders.h"  #include "llinventoryobserver.h" +LLSelectMgr* LLSimpleton<LLSelectMgr>::sInstance = nullptr; +  LLViewerObject* getSelectedParentObject(LLViewerObject *object) ;  //  // Consts diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 9a4149948c..57c5074804 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -205,9 +205,6 @@  #include "llstacktrace.h" -#include "threadpool.h" - -  #if LL_WINDOWS  #include "lldxhardware.h"  #endif @@ -304,20 +301,6 @@ 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 @@ -1506,9 +1489,6 @@ bool idle_startup()  		gAgentCamera.resetCamera();  		display_startup(); -		// start up the ThreadPool we'll use for textures et al. -		launchThreadPool(); -  		// Initialize global class data needed for surfaces (i.e. textures)  		LL_DEBUGS("AppInit") << "Initializing sky..." << LL_ENDL;  		// Initialize all of the viewer object classes for the first time (doing things like texture fetches. diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp index 5d8e80cc41..5ebce115f6 100644 --- a/indra/newview/llviewercamera.cpp +++ b/indra/newview/llviewercamera.cpp @@ -54,6 +54,8 @@  // System includes  #include <iomanip> // for setprecision +LLViewerCamera* LLSimpleton<LLViewerCamera>::sInstance = nullptr; +  LLTrace::CountStatHandle<> LLViewerCamera::sVelocityStat("camera_velocity");  LLTrace::CountStatHandle<> LLViewerCamera::sAngularVelocityStat("camera_angular_velocity"); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 498e4ef8bc..fbc5830a5c 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -679,9 +679,6 @@ 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  @@ -1625,26 +1622,17 @@ void LLViewerFetchedTexture::scheduleCreateTexture()      {          mNeedsCreateTexture = TRUE;  #if LL_WINDOWS //flip to 0 to revert to single-threaded OpenGL texture uploads -        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 +        if (!LLImageGLThread::sInstance->post([this]() +            { +                //actually create the texture on a background thread +                createTexture(); +                LLImageGLThread::sInstance->postCallback([this]() +                    { +                        //finalize on main thread +                        postCreateTexture(); +                        unref(); +                    }); +            }))  #endif          {              gTextureList.mCreateTextureList.insert(this); diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index 4cd4c7cd39..f9f1bfef44 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -35,7 +35,6 @@  #include "llrender.h"  #include "llmetricperformancetester.h"  #include "httpcommon.h" -#include "workqueue.h"  #include <map>  #include <list> @@ -214,9 +213,6 @@ 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;	 diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 5f62908009..0a8457eb2c 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -62,6 +62,8 @@  #include <cstring> +LLWorld* LLSimpleton<LLWorld>::sInstance = nullptr; +  //  // Globals  // | 
