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