From 11afa09ea3f56c0e20eb195ae1520a88602ceaca Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 22 Oct 2021 11:36:31 -0400 Subject: SL-16220: Add LL::ThreadPool class and a "General" instance. ThreadPool bundles a WorkQueue with the specified number of worker threads to service it. Each ThreadPool has a name that can be used to locate its WorkQueue. Each worker thread calls WorkQueue::runUntilClose(). ThreadPool listens on the "LLApp" LLEventPump for shutdown notification. On receiving that, it closes its WorkQueue and then join()s each of its worker threads for orderly shutdown. Add a settings.xml entry "ThreadPoolSizes", the first LLSD-valued settings entry to expect a map: pool name->size. The expectation is that usually code instantiating a particular ThreadPool will have a default size in mind, but it should check "ThreadPoolSizes" for a user override. Make idle_startup()'s STATE_SEED_CAP_GRANTED state instantiate a "General" ThreadPool. This is function-static for lazy initialization. Eliminate LLMainLoopRepeater, which is completely unreferenced. Any potential future use cases are better addressed by posting to the main loop's WorkQueue. Eliminate llappviewer.cpp's private LLDeferredTaskList class, which implemented LLAppViewer::addOnIdleCallback(). Make addOnIdleCallback() post work to the main loop's WorkQueue instead. --- indra/llcommon/workqueue.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'indra/llcommon/workqueue.cpp') diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index ffc9a97dc0..114aeea1f3 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -38,6 +38,16 @@ void LL::WorkQueue::close() mQueue.close(); } +bool LL::WorkQueue::isClosed() +{ + return mQueue.isClosed(); +} + +bool LL::WorkQueue::done() +{ + return mQueue.done(); +} + void LL::WorkQueue::runUntilClose() { try -- cgit v1.2.3 From 023d39963e850356e1af6eec7f857e2534ce8d38 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 25 Oct 2021 17:31:27 -0400 Subject: SL-16220: WorkQueue::runOn() methods submit work, wait for result. The idea is that you can call runOn(target, callable) from a (non-default) coroutine and block that coroutine until the result becomes available. As a safety check, we forbid calling runOn() from a thread's default coroutine, assuming that a given thread's default coroutine is the one servicing the relevant WorkQueue. --- indra/llcommon/workqueue.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'indra/llcommon/workqueue.cpp') diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index 114aeea1f3..f7ffc8233c 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -26,6 +26,11 @@ using Mutex = LLCoros::Mutex; using Lock = LLCoros::LockType; +struct NotOnDftCoro: public LLException +{ + NotOnDftCoro(const std::string& what): LLException(what) {} +}; + LL::WorkQueue::WorkQueue(const std::string& name): super(makeName(name)) { @@ -136,3 +141,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(NotOnDftCoro("Do not call " + method + " from a thread's default coroutine")); + } +} -- cgit v1.2.3 From e6eebea8da545350f6684c191c633dd2fbc6f6f1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 26 Oct 2021 11:49:53 -0400 Subject: SL-16220: Change WorkQueue::runOn() to waitForResult(). In addition to the name making the blocking explicit, we changed the signature: instead of specifying a target WorkQueue on which to run, waitForResult() runs the passed callable on its own WorkQueue. Why is that? Because, unlike postTo(), we do not require a handshake between two different WorkQueues. postTo() allows running arbitrary callback code, setting variables or whatever, on the originating WorkQueue (presumably on the originating thread). waitForResult() synchronizes using Promise/Future, which are explicitly designed for cross-thread communication. We need not call set_value() on the originating thread, so we don't need a postTo() callback lambda. --- indra/llcommon/workqueue.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'indra/llcommon/workqueue.cpp') diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index f7ffc8233c..ac3086aac5 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -26,11 +26,6 @@ using Mutex = LLCoros::Mutex; using Lock = LLCoros::LockType; -struct NotOnDftCoro: public LLException -{ - NotOnDftCoro(const std::string& what): LLException(what) {} -}; - LL::WorkQueue::WorkQueue(const std::string& name): super(makeName(name)) { @@ -148,6 +143,6 @@ void LL::WorkQueue::checkCoroutine(const std::string& method) // string. See also LLCoros::logname(). if (LLCoros::getName().empty()) { - LLTHROW(NotOnDftCoro("Do not call " + method + " from a thread's default coroutine")); + LLTHROW(Error("Do not call " + method + " from a thread's default coroutine")); } } -- cgit v1.2.3