summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llcommon/CMakeLists.txt3
-rw-r--r--indra/llcommon/llsingleton.h24
-rw-r--r--indra/llcommon/llthreadsafequeue.h30
-rw-r--r--indra/llcommon/tests/threadsafeschedule_test.cpp4
-rw-r--r--indra/llcommon/tests/workqueue_test.cpp72
-rw-r--r--indra/llcommon/threadpool.cpp80
-rw-r--r--indra/llcommon/threadpool.h62
-rw-r--r--indra/llcommon/timing.cpp25
-rw-r--r--indra/llcommon/workqueue.cpp30
-rw-r--r--indra/llcommon/workqueue.h378
-rw-r--r--indra/llrender/llimagegl.cpp91
-rw-r--r--indra/llrender/llimagegl.h27
-rw-r--r--indra/llui/CMakeLists.txt6
-rw-r--r--indra/llui/llviewereventrecorder.cpp2
-rw-r--r--indra/llwindow/llwindowwin32.cpp426
-rw-r--r--indra/llwindow/llwindowwin32.h48
-rw-r--r--indra/newview/CMakeLists.txt3
-rw-r--r--indra/newview/app_settings/settings.xml25
-rw-r--r--indra/newview/llappviewer.cpp66
-rw-r--r--indra/newview/llenvironment.cpp1
-rw-r--r--indra/newview/llmainlooprepeater.cpp88
-rw-r--r--indra/newview/llmainlooprepeater.h64
-rw-r--r--indra/newview/llselectmgr.cpp2
-rw-r--r--indra/newview/llstartup.cpp20
-rw-r--r--indra/newview/llviewercamera.cpp2
-rw-r--r--indra/newview/llviewertexture.cpp34
-rw-r--r--indra/newview/llviewertexture.h4
-rw-r--r--indra/newview/llworld.cpp2
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
//