Revert "SL-16220: Merge branch 'origin/DRTVWR-546' into glthread"
This reverts commitmaster5188a26a85, reversing changes made to819088563e.
parent
aeed774ff9
commit
029b41c041
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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) */
|
||||
|
|
@ -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$
|
||||
*/
|
||||
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
|
||||
|
|
@ -108,47 +82,6 @@ namespace LL
|
|||
post(TimePoint::clock::now(), std::move(callable));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
#include "llui.h"
|
||||
#include "llleap.h"
|
||||
|
||||
LLViewerEventRecorder* LLSimpleton<LLViewerEventRecorder>::sInstance = nullptr;
|
||||
|
||||
LLViewerEventRecorder::LLViewerEventRecorder() {
|
||||
|
||||
clear(UNDEFINED);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -824,6 +824,7 @@ std::string env_selection_to_string(LLEnvironment::EnvSelection_t sel)
|
|||
#undef RTNENUM
|
||||
}
|
||||
|
||||
LLEnvironment* LLSimpleton<LLEnvironment>::sInstance = nullptr;
|
||||
//-------------------------------------------------------------------------
|
||||
LLEnvironment::LLEnvironment():
|
||||
mCloudScrollDelta(),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -97,6 +97,8 @@
|
|||
#include "llglheaders.h"
|
||||
#include "llinventoryobserver.h"
|
||||
|
||||
LLSelectMgr* LLSimpleton<LLSelectMgr>::sInstance = nullptr;
|
||||
|
||||
LLViewerObject* getSelectedParentObject(LLViewerObject *object) ;
|
||||
//
|
||||
// Consts
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@
|
|||
#include <cstring>
|
||||
|
||||
|
||||
LLWorld* LLSimpleton<LLWorld>::sInstance = nullptr;
|
||||
|
||||
//
|
||||
// Globals
|
||||
//
|
||||
|
|
|
|||
Loading…
Reference in New Issue