diff options
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 // |