summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorBrad Payne (Vir Linden) <vir@lindenlab.com>2022-09-01 15:12:14 +0100
committerBrad Payne (Vir Linden) <vir@lindenlab.com>2022-09-01 15:12:14 +0100
commit3c3442cbbc38beff1ab82ffcc7f81390c7979361 (patch)
treeb2e60cc8818e372706a2c3463ca2a2f255066b79 /indra/llcommon
parent2c692f635da67990f842f20adf3b42d870d42fdf (diff)
parentce685a12b9acc888a39c336c91ae6272db74ce91 (diff)
Merge branch 'DRTVWR-559' of https://bitbucket.org/lindenlab/viewer into DRTVWR-559
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/CMakeLists.txt2
-rw-r--r--indra/llcommon/commoncontrol.cpp106
-rw-r--r--indra/llcommon/commoncontrol.h75
-rw-r--r--indra/llcommon/llapr.cpp2
-rw-r--r--indra/llcommon/llframetimer.cpp5
-rw-r--r--indra/llcommon/llmemory.cpp45
-rw-r--r--indra/llcommon/llprofiler.h4
-rw-r--r--indra/llcommon/llqueuedthread.cpp299
-rw-r--r--indra/llcommon/llqueuedthread.h51
-rw-r--r--indra/llcommon/llsys.cpp24
-rw-r--r--indra/llcommon/llsys.h5
-rw-r--r--indra/llcommon/lltimer.cpp11
-rw-r--r--indra/llcommon/llworkerthread.cpp27
-rw-r--r--indra/llcommon/llworkerthread.h11
-rw-r--r--indra/llcommon/threadpool.cpp60
-rw-r--r--indra/llcommon/threadpool.h34
-rw-r--r--indra/llcommon/workqueue.h8
17 files changed, 538 insertions, 231 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 59aa731af2..62307c6e26 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -29,6 +29,7 @@ include_directories(
# ${LLCOMMON_LIBRARIES})
set(llcommon_SOURCE_FILES
+ commoncontrol.cpp
indra_constants.cpp
llallocator.cpp
llallocator_heap_profile.cpp
@@ -130,6 +131,7 @@ set(llcommon_HEADER_FILES
chrono.h
classic_callback.h
+ commoncontrol.h
ctype_workaround.h
fix_macros.h
indra_constants.h
diff --git a/indra/llcommon/commoncontrol.cpp b/indra/llcommon/commoncontrol.cpp
new file mode 100644
index 0000000000..81e66baf8c
--- /dev/null
+++ b/indra/llcommon/commoncontrol.cpp
@@ -0,0 +1,106 @@
+/**
+ * @file commoncontrol.cpp
+ * @author Nat Goodspeed
+ * @date 2022-06-08
+ * @brief Implementation for commoncontrol.
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Copyright (c) 2022, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "commoncontrol.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "llevents.h"
+#include "llsdutil.h"
+
+LLSD LL::CommonControl::access(const LLSD& params)
+{
+ // We can't actually introduce a link-time dependency on llxml, or on any
+ // global LLControlGroup (*koff* gSavedSettings *koff*) but we can issue a
+ // runtime query. If we're running as part of a viewer with
+ // LLViewerControlListener, we can use that to interact with any
+ // instantiated LLControGroup.
+ LLSD response;
+ {
+ LLEventStream reply("reply");
+ LLTempBoundListener connection = reply.listen("listener",
+ [&response] (const LLSD& event)
+ {
+ response = event;
+ return false;
+ });
+ LLSD rparams{ params };
+ rparams["reply"] = reply.getName();
+ LLEventPumps::instance().obtain("LLViewerControl").post(rparams);
+ }
+ // LLViewerControlListener responds immediately. If it's listening at all,
+ // it will already have set response.
+ if (! response.isDefined())
+ {
+ LLTHROW(NoListener("No LLViewerControl listener instantiated"));
+ }
+ LLSD error{ response["error"] };
+ if (error.isDefined())
+ {
+ LLTHROW(ParamError(error));
+ }
+ response.erase("error");
+ response.erase("reqid");
+ return response;
+}
+
+/// set control group.key to defined default value
+LLSD LL::CommonControl::set_default(const std::string& group, const std::string& key)
+{
+ return access(llsd::map("op", "set",
+ "group", group, "key", key))["value"];
+}
+
+/// set control group.key to specified value
+LLSD LL::CommonControl::set(const std::string& group, const std::string& key, const LLSD& value)
+{
+ return access(llsd::map("op", "set",
+ "group", group, "key", key, "value", value))["value"];
+}
+
+/// toggle boolean control group.key
+LLSD LL::CommonControl::toggle(const std::string& group, const std::string& key)
+{
+ return access(llsd::map("op", "toggle",
+ "group", group, "key", key))["value"];
+}
+
+/// get the definition for control group.key, (! isDefined()) if bad
+/// ["name"], ["type"], ["value"], ["comment"]
+LLSD LL::CommonControl::get_def(const std::string& group, const std::string& key)
+{
+ return access(llsd::map("op", "get",
+ "group", group, "key", key));
+}
+
+/// get the value of control group.key
+LLSD LL::CommonControl::get(const std::string& group, const std::string& key)
+{
+ return access(llsd::map("op", "get",
+ "group", group, "key", key))["value"];
+}
+
+/// get defined groups
+std::vector<std::string> LL::CommonControl::get_groups()
+{
+ auto groups{ access(llsd::map("op", "groups"))["groups"] };
+ return { groups.beginArray(), groups.endArray() };
+}
+
+/// get definitions for all variables in group
+LLSD LL::CommonControl::get_vars(const std::string& group)
+{
+ return access(llsd::map("op", "vars", "group", group))["vars"];
+}
diff --git a/indra/llcommon/commoncontrol.h b/indra/llcommon/commoncontrol.h
new file mode 100644
index 0000000000..07d4a45ac5
--- /dev/null
+++ b/indra/llcommon/commoncontrol.h
@@ -0,0 +1,75 @@
+/**
+ * @file commoncontrol.h
+ * @author Nat Goodspeed
+ * @date 2022-06-08
+ * @brief Access LLViewerControl LLEventAPI, if process has one.
+ *
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
+ * Copyright (c) 2022, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_COMMONCONTROL_H)
+#define LL_COMMONCONTROL_H
+
+#include <vector>
+#include "llexception.h"
+#include "llsd.h"
+
+namespace LL
+{
+ class CommonControl
+ {
+ public:
+ struct Error: public LLException
+ {
+ Error(const std::string& what): LLException(what) {}
+ };
+
+ /// Exception thrown if there's no LLViewerControl LLEventAPI
+ struct NoListener: public Error
+ {
+ NoListener(const std::string& what): Error(what) {}
+ };
+
+ struct ParamError: public Error
+ {
+ ParamError(const std::string& what): Error(what) {}
+ };
+
+ /// set control group.key to defined default value
+ static
+ LLSD set_default(const std::string& group, const std::string& key);
+
+ /// set control group.key to specified value
+ static
+ LLSD set(const std::string& group, const std::string& key, const LLSD& value);
+
+ /// toggle boolean control group.key
+ static
+ LLSD toggle(const std::string& group, const std::string& key);
+
+ /// get the definition for control group.key, (! isDefined()) if bad
+ /// ["name"], ["type"], ["value"], ["comment"]
+ static
+ LLSD get_def(const std::string& group, const std::string& key);
+
+ /// get the value of control group.key
+ static
+ LLSD get(const std::string& group, const std::string& key);
+
+ /// get defined groups
+ static
+ std::vector<std::string> get_groups();
+
+ /// get definitions for all variables in group
+ static
+ LLSD get_vars(const std::string& group);
+
+ private:
+ static
+ LLSD access(const LLSD& params);
+ };
+} // namespace LL
+
+#endif /* ! defined(LL_COMMONCONTROL_H) */
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index db94765871..b537102510 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -526,6 +526,7 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
//static
S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
{
+ LL_PROFILE_ZONE_SCOPED;
//*****************************************
LLAPRFilePoolScope scope(pool);
apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), APR_READ|APR_BINARY);
@@ -570,6 +571,7 @@ S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nb
//static
S32 LLAPRFile::writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
{
+ LL_PROFILE_ZONE_SCOPED;
apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
if (offset < 0)
{
diff --git a/indra/llcommon/llframetimer.cpp b/indra/llcommon/llframetimer.cpp
index 58af32f1af..1e9920746b 100644
--- a/indra/llcommon/llframetimer.cpp
+++ b/indra/llcommon/llframetimer.cpp
@@ -29,11 +29,6 @@
#include "llframetimer.h"
-// We don't bother building a stand alone lib; we just need to include the one source file for Tracy support
-//#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY || LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
-// #include "TracyClient.cpp"
-//#endif // LL_PROFILER_CONFIGURATION
-
// Static members
//LLTimer LLFrameTimer::sInternalTimer;
U64 LLFrameTimer::sStartTotalTime = totalTime();
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index 849867586a..749b66b472 100644
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -35,6 +35,7 @@
# include <sys/types.h>
# include <mach/task.h>
# include <mach/mach_init.h>
+#include <mach/mach_host.h>
#elif LL_LINUX
# include <unistd.h>
#endif
@@ -109,6 +110,50 @@ void LLMemory::updateMemoryInfo()
{
sAvailPhysicalMemInKB = U32Kilobytes(0);
}
+
+#elif defined(LL_DARWIN)
+ task_vm_info info;
+ mach_msg_type_number_t infoCount = TASK_VM_INFO_COUNT;
+ // MACH_TASK_BASIC_INFO reports the same resident_size, but does not tell us the reusable bytes or phys_footprint.
+ if (task_info(mach_task_self(), TASK_VM_INFO, reinterpret_cast<task_info_t>(&info), &infoCount) == KERN_SUCCESS)
+ {
+ // Our Windows definition of PagefileUsage is documented by Microsoft as "the total amount of
+ // memory that the memory manager has committed for a running process", which is rss.
+ sAllocatedPageSizeInKB = U32Bytes(info.resident_size);
+
+ // Activity Monitor => Inspect Process => Real Memory Size appears to report resident_size
+ // Activity monitor => main window memory column appears to report phys_footprint, which spot checks as at least 30% less.
+ // I think that is because of compression, which isn't going to give us a consistent measurement. We want uncompressed totals.
+ //
+ // In between is resident_size - reusable. This is what Chrome source code uses, with source comments saying it is 'the "Real Memory" value
+ // reported for the app by the Memory Monitor in Instruments.' It is still about 8% bigger than phys_footprint.
+ //
+ // (On Windows, we use WorkingSetSize.)
+ sAllocatedMemInKB = U32Bytes(info.resident_size - info.reusable);
+ }
+ else
+ {
+ LL_WARNS() << "task_info failed" << LL_ENDL;
+ }
+
+ // Total installed and available physical memory are properties of the host, not just our process.
+ vm_statistics64_data_t vmstat;
+ mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
+ mach_port_t host = mach_host_self();
+ vm_size_t page_size;
+ host_page_size(host, &page_size);
+ kern_return_t result = host_statistics64(host, HOST_VM_INFO64, reinterpret_cast<host_info_t>(&vmstat), &count);
+ if (result == KERN_SUCCESS) {
+ // This is what Chrome reports as 'the "Physical Memory Free" value reported by the Memory Monitor in Instruments.'
+ // Note though that inactive pages are not included here and not yet free, but could become so under memory pressure.
+ sAvailPhysicalMemInKB = U32Bytes(vmstat.free_count * page_size);
+ sMaxPhysicalMemInKB = LLMemoryInfo::getHardwareMemSize();
+ }
+ else
+ {
+ LL_WARNS() << "task_info failed" << LL_ENDL;
+ }
+
#else
//not valid for other systems for now.
sAllocatedMemInKB = U64Bytes(LLMemory::getCurrentRSS());
diff --git a/indra/llcommon/llprofiler.h b/indra/llcommon/llprofiler.h
index bc46128d21..c0f5868db3 100644
--- a/indra/llcommon/llprofiler.h
+++ b/indra/llcommon/llprofiler.h
@@ -134,6 +134,10 @@ extern thread_local bool gProfilerEnabled;
#define LL_PROFILE_ZONE_ERR(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0XFF0000 ) // RGB yellow
#define LL_PROFILE_ZONE_INFO(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0X00FFFF ) // RGB cyan
#define LL_PROFILE_ZONE_WARN(name) LL_PROFILE_ZONE_NAMED_COLOR( name, 0x0FFFF00 ) // RGB red
+ //#define LL_PROFILE_ALLOC(ptr, size) TracyAlloc(ptr, size) // memory allocation tracking currently not working
+ //#define LL_PROFILE_FREE(ptr) TracyFree(ptr)
+ #define LL_PROFILE_ALLOC(ptr, size) (void)(ptr); (void)(size);
+ #define LL_PROFILE_FREE(ptr) (void)(ptr);
#endif
#else
#define LL_PROFILER_FRAME_END
diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp
index 8cef4293cd..155e32ebae 100644
--- a/indra/llcommon/llqueuedthread.cpp
+++ b/indra/llcommon/llqueuedthread.cpp
@@ -26,20 +26,26 @@
#include "linden_common.h"
#include "llqueuedthread.h"
+#include <chrono>
+
#include "llstl.h"
#include "lltimer.h" // ms_sleep()
-#include "lltracethreadrecorder.h"
+#include "llmutex.h"
//============================================================================
// MAIN THREAD
LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool should_pause) :
- LLThread(name),
- mThreaded(threaded),
- mIdleThread(TRUE),
- mNextHandle(0),
- mStarted(FALSE)
+ LLThread(name),
+ mIdleThread(TRUE),
+ mNextHandle(0),
+ mStarted(FALSE),
+ mThreaded(threaded),
+ mRequestQueue(name, 1024 * 1024)
{
+ llassert(threaded); // not threaded implementation is deprecated
+ mMainQueue = LL::WorkQueue::getInstance("mainloop");
+
if (mThreaded)
{
if(should_pause)
@@ -69,6 +75,11 @@ void LLQueuedThread::shutdown()
unpause(); // MAIN THREAD
if (mThreaded)
{
+ if (mRequestQueue.size() == 0)
+ {
+ mRequestQueue.close();
+ }
+
S32 timeout = 100;
for ( ; timeout>0; timeout--)
{
@@ -104,6 +115,8 @@ void LLQueuedThread::shutdown()
{
LL_WARNS() << "~LLQueuedThread() called with active requests: " << active_count << LL_ENDL;
}
+
+ mRequestQueue.close();
}
//----------------------------------------------------------------------------
@@ -112,6 +125,7 @@ void LLQueuedThread::shutdown()
// virtual
S32 LLQueuedThread::update(F32 max_time_ms)
{
+ LL_PROFILE_ZONE_SCOPED;
if (!mStarted)
{
if (!mThreaded)
@@ -125,29 +139,34 @@ S32 LLQueuedThread::update(F32 max_time_ms)
S32 LLQueuedThread::updateQueue(F32 max_time_ms)
{
- F64 max_time = (F64)max_time_ms * .001;
- LLTimer timer;
- S32 pending = 1;
-
+ LL_PROFILE_ZONE_SCOPED;
// Frame Update
if (mThreaded)
{
- pending = getPending();
- if(pending > 0)
+ // schedule a call to threadedUpdate for every call to updateQueue
+ if (!isQuitting())
+ {
+ mRequestQueue.post([=]()
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qt - update");
+ mIdleThread = FALSE;
+ threadedUpdate();
+ mIdleThread = TRUE;
+ }
+ );
+ }
+
+ if(getPending() > 0)
{
- unpause();
- }
+ unpause();
+ }
}
else
{
- while (pending > 0)
- {
- pending = processNextRequest();
- if (max_time && timer.getElapsedTimeF64() > max_time)
- break;
- }
+ mRequestQueue.runFor(std::chrono::microseconds((int) (max_time_ms*1000.f)));
+ threadedUpdate();
}
- return pending;
+ return getPending();
}
void LLQueuedThread::incQueue()
@@ -166,11 +185,7 @@ void LLQueuedThread::incQueue()
// May be called from any thread
S32 LLQueuedThread::getPending()
{
- S32 res;
- lockData();
- res = mRequestQueue.size();
- unlockData();
- return res;
+ return mRequestQueue.size();
}
// MAIN thread
@@ -195,35 +210,28 @@ void LLQueuedThread::waitOnPending()
// MAIN thread
void LLQueuedThread::printQueueStats()
{
- lockData();
- if (!mRequestQueue.empty())
+ U32 size = mRequestQueue.size();
+ if (size > 0)
{
- QueuedRequest *req = *mRequestQueue.begin();
- LL_INFOS() << llformat("Pending Requests:%d Current status:%d", mRequestQueue.size(), req->getStatus()) << LL_ENDL;
+ LL_INFOS() << llformat("Pending Requests:%d ", mRequestQueue.size()) << LL_ENDL;
}
else
{
LL_INFOS() << "Queued Thread Idle" << LL_ENDL;
}
- unlockData();
}
// MAIN thread
LLQueuedThread::handle_t LLQueuedThread::generateHandle()
{
- lockData();
- while ((mNextHandle == nullHandle()) || (mRequestHash.find(mNextHandle)))
- {
- mNextHandle++;
- }
- const LLQueuedThread::handle_t res = mNextHandle++;
- unlockData();
+ U32 res = ++mNextHandle;
return res;
}
// MAIN thread
bool LLQueuedThread::addRequest(QueuedRequest* req)
{
+ LL_PROFILE_ZONE_SCOPED;
if (mStatus == QUITTING)
{
return false;
@@ -231,14 +239,14 @@ bool LLQueuedThread::addRequest(QueuedRequest* req)
lockData();
req->setStatus(STATUS_QUEUED);
- mRequestQueue.insert(req);
- mRequestHash.insert(req);
+ mRequestHash.insert(req);
#if _DEBUG
// LL_INFOS() << llformat("LLQueuedThread::Added req [%08d]",handle) << LL_ENDL;
#endif
unlockData();
- incQueue();
+ llassert(!mDataLock->isSelfLocked());
+ mRequestQueue.post([this, req]() { processRequest(req); });
return true;
}
@@ -246,6 +254,7 @@ bool LLQueuedThread::addRequest(QueuedRequest* req)
// MAIN thread
bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete)
{
+ LL_PROFILE_ZONE_SCOPED;
llassert (handle != nullHandle());
bool res = false;
bool waspaused = isPaused();
@@ -312,6 +321,7 @@ LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle)
void LLQueuedThread::abortRequest(handle_t handle, bool autocomplete)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
lockData();
QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
if (req)
@@ -333,30 +343,9 @@ void LLQueuedThread::setFlags(handle_t handle, U32 flags)
unlockData();
}
-void LLQueuedThread::setPriority(handle_t handle, U32 priority)
-{
- lockData();
- QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
- if (req)
- {
- if(req->getStatus() == STATUS_INPROGRESS)
- {
- // not in list
- req->setPriority(priority);
- }
- else if(req->getStatus() == STATUS_QUEUED)
- {
- // remove from list then re-insert
- llverify(mRequestQueue.erase(req) == 1);
- req->setPriority(priority);
- mRequestQueue.insert(req);
- }
- }
- unlockData();
-}
-
bool LLQueuedThread::completeRequest(handle_t handle)
{
+ LL_PROFILE_ZONE_SCOPED;
bool res = false;
lockData();
QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
@@ -399,89 +388,115 @@ bool LLQueuedThread::check()
//============================================================================
// Runs on its OWN thread
-S32 LLQueuedThread::processNextRequest()
+void LLQueuedThread::processRequest(LLQueuedThread::QueuedRequest* req)
{
- QueuedRequest *req;
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
+
+ mIdleThread = FALSE;
+ //threadedUpdate();
+
// Get next request from pool
lockData();
- while(1)
+ if ((req->getFlags() & FLAG_ABORT) || (mStatus == QUITTING))
{
- req = NULL;
- if (mRequestQueue.empty())
+ LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - abort");
+ req->setStatus(STATUS_ABORTED);
+ req->finishRequest(false);
+ if (req->getFlags() & FLAG_AUTO_COMPLETE)
{
- break;
- }
- req = *mRequestQueue.begin();
- mRequestQueue.erase(mRequestQueue.begin());
- if ((req->getFlags() & FLAG_ABORT) || (mStatus == QUITTING))
- {
- req->setStatus(STATUS_ABORTED);
- req->finishRequest(false);
- if (req->getFlags() & FLAG_AUTO_COMPLETE)
- {
- mRequestHash.erase(req);
- req->deleteRequest();
-// check();
- }
- continue;
- }
- llassert_always(req->getStatus() == STATUS_QUEUED);
- break;
- }
- U32 start_priority = 0 ;
- if (req)
- {
- req->setStatus(STATUS_INPROGRESS);
- start_priority = req->getPriority();
- }
- unlockData();
-
- // This is the only place we will call req->setStatus() after
- // it has initially been seet to STATUS_QUEUED, so it is
- // safe to access req.
- if (req)
- {
- // process request
- bool complete = req->processRequest();
-
- if (complete)
- {
- lockData();
- req->setStatus(STATUS_COMPLETE);
- req->finishRequest(true);
- if (req->getFlags() & FLAG_AUTO_COMPLETE)
- {
- mRequestHash.erase(req);
- req->deleteRequest();
+ mRequestHash.erase(req);
+ req->deleteRequest();
// check();
- }
- unlockData();
}
- else
- {
- lockData();
- req->setStatus(STATUS_QUEUED);
- mRequestQueue.insert(req);
- unlockData();
- if (mThreaded && start_priority < PRIORITY_NORMAL)
- {
- ms_sleep(1); // sleep the thread a little
- }
- }
-
- LLTrace::get_thread_recorder()->pushToParent();
+ unlockData();
}
+ else
+ {
+ llassert_always(req->getStatus() == STATUS_QUEUED);
+
+ if (req)
+ {
+ req->setStatus(STATUS_INPROGRESS);
+ }
+ unlockData();
+
+ // This is the only place we will call req->setStatus() after
+ // it has initially been seet to STATUS_QUEUED, so it is
+ // safe to access req.
+ if (req)
+ {
+ // process request
+ bool complete = req->processRequest();
+
+ if (complete)
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - complete");
+ lockData();
+ req->setStatus(STATUS_COMPLETE);
+ req->finishRequest(true);
+ if (req->getFlags() & FLAG_AUTO_COMPLETE)
+ {
+ mRequestHash.erase(req);
+ req->deleteRequest();
+ // check();
+ }
+ unlockData();
+ }
+ else
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - retry");
+ //put back on queue and try again in 0.1ms
+ lockData();
+ req->setStatus(STATUS_QUEUED);
+
+ unlockData();
+
+ llassert(!mDataLock->isSelfLocked());
+
+#if 0
+ // try again on next frame
+ // NOTE: tried using "post" with a time in the future, but this
+ // would invariably cause this thread to wait for a long time (10+ ms)
+ // while work is pending
+ bool ret = LL::WorkQueue::postMaybe(
+ mMainQueue,
+ [=]()
+ {
+ LL_PROFILE_ZONE_NAMED("processRequest - retry");
+ mRequestQueue.post([=]()
+ {
+ LL_PROFILE_ZONE_NAMED("processRequest - retry"); // <-- not redundant, track retry on both queues
+ processRequest(req);
+ });
+ });
+ llassert(ret);
+#else
+ using namespace std::chrono_literals;
+ auto retry_time = LL::WorkQueue::TimePoint::clock::now() + 16ms;
+ mRequestQueue.post([=]
+ {
+ LL_PROFILE_ZONE_NAMED("processRequest - retry");
+ while (LL::WorkQueue::TimePoint::clock::now() < retry_time)
+ {
+ std::this_thread::yield(); //note: don't use LLThread::yield here to avoid
+ }
+ processRequest(req);
+ });
+#endif
+
+ }
+ }
+ }
- S32 pending = getPending();
- return pending;
+ mIdleThread = TRUE;
}
// virtual
bool LLQueuedThread::runCondition()
{
// mRunCondition must be locked here
- if (mRequestQueue.empty() && mIdleThread)
+ if (mRequestQueue.size() == 0 && mIdleThread)
return false;
else
return true;
@@ -495,18 +510,13 @@ void LLQueuedThread::run()
startThread();
mStarted = TRUE;
- while (1)
+
+ /*while (1)
{
+ LL_PROFILE_ZONE_SCOPED;
// this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state.
checkPause();
- if (isQuitting())
- {
- LLTrace::get_thread_recorder()->pushToParent();
- endThread();
- break;
- }
-
mIdleThread = FALSE;
threadedUpdate();
@@ -515,12 +525,18 @@ void LLQueuedThread::run()
if (pending_work == 0)
{
+ //LL_PROFILE_ZONE_NAMED("LLQueuedThread - sleep");
mIdleThread = TRUE;
- ms_sleep(1);
+ //ms_sleep(1);
}
//LLThread::yield(); // thread should yield after each request
- }
+ }*/
+ mRequestQueue.runUntilClose();
+
+ endThread();
LL_INFOS() << "LLQueuedThread " << mName << " EXITING." << LL_ENDL;
+
+
}
// virtual
@@ -540,10 +556,9 @@ void LLQueuedThread::threadedUpdate()
//============================================================================
-LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U32 priority, U32 flags) :
+LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U32 flags) :
LLSimpleHashEntry<LLQueuedThread::handle_t>(handle),
mStatus(STATUS_UNKNOWN),
- mPriority(priority),
mFlags(flags)
{
}
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h
index 5d3f873646..6b82ccc434 100644
--- a/indra/llcommon/llqueuedthread.h
+++ b/indra/llcommon/llqueuedthread.h
@@ -36,6 +36,7 @@
#include "llthread.h"
#include "llsimplehash.h"
+#include "workqueue.h"
//============================================================================
// Note: ~LLQueuedThread is O(N) N=# of queued threads, assumed to be small
@@ -45,15 +46,6 @@ class LL_COMMON_API LLQueuedThread : public LLThread
{
//------------------------------------------------------------------------
public:
- enum priority_t {
- PRIORITY_IMMEDIATE = 0x7FFFFFFF,
- PRIORITY_URGENT = 0x40000000,
- PRIORITY_HIGH = 0x30000000,
- PRIORITY_NORMAL = 0x20000000,
- PRIORITY_LOW = 0x10000000,
- PRIORITY_LOWBITS = 0x0FFFFFFF,
- PRIORITY_HIGHBITS = 0x70000000
- };
enum status_t {
STATUS_EXPIRED = -1,
STATUS_UNKNOWN = 0,
@@ -82,28 +74,17 @@ public:
virtual ~QueuedRequest(); // use deleteRequest()
public:
- QueuedRequest(handle_t handle, U32 priority, U32 flags = 0);
+ QueuedRequest(handle_t handle, U32 flags = 0);
status_t getStatus()
{
return mStatus;
}
- U32 getPriority() const
- {
- return mPriority;
- }
U32 getFlags() const
{
return mFlags;
}
- bool higherPriority(const QueuedRequest& second) const
- {
- if ( mPriority == second.mPriority)
- return mHashKey < second.mHashKey;
- else
- return mPriority > second.mPriority;
- }
-
+
protected:
status_t setStatus(status_t newstatus)
{
@@ -121,28 +102,11 @@ public:
virtual void finishRequest(bool completed); // Always called from thread after request has completed or aborted
virtual void deleteRequest(); // Only method to delete a request
- void setPriority(U32 pri)
- {
- // Only do this on a request that is not in a queued list!
- mPriority = pri;
- };
-
protected:
LLAtomicBase<status_t> mStatus;
- U32 mPriority;
U32 mFlags;
};
-protected:
- struct queued_request_less
- {
- bool operator()(const QueuedRequest* lhs, const QueuedRequest* rhs) const
- {
- return lhs->higherPriority(*rhs); // higher priority in front of queue (set)
- }
- };
-
-
//------------------------------------------------------------------------
public:
@@ -167,7 +131,7 @@ private:
protected:
handle_t generateHandle();
bool addRequest(QueuedRequest* req);
- S32 processNextRequest(void);
+ void processRequest(QueuedRequest* req);
void incQueue();
public:
@@ -186,7 +150,6 @@ public:
status_t getRequestStatus(handle_t handle);
void abortRequest(handle_t handle, bool autocomplete);
void setFlags(handle_t handle, U32 flags);
- void setPriority(handle_t handle, U32 priority);
bool completeRequest(handle_t handle);
// This is public for support classes like LLWorkerThread,
// but generally the methods above should be used.
@@ -200,8 +163,10 @@ protected:
BOOL mStarted; // required when mThreaded is false to call startThread() from update()
LLAtomicBool mIdleThread; // request queue is empty (or we are quitting) and the thread is idle
- typedef std::set<QueuedRequest*, queued_request_less> request_queue_t;
- request_queue_t mRequestQueue;
+ //typedef std::set<QueuedRequest*, queued_request_less> request_queue_t;
+ //request_queue_t mRequestQueue;
+ LL::WorkQueue mRequestQueue;
+ LL::WorkQueue::weak_t mMainQueue;
enum { REQUEST_HASH_SIZE = 512 }; // must be power of 2
typedef LLSimpleHash<handle_t, REQUEST_HASH_SIZE> request_hash_t;
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index 9b6bb3826c..45417bfa37 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -710,20 +710,28 @@ static U32Kilobytes LLMemoryAdjustKBResult(U32Kilobytes inKB)
}
#endif
+#if LL_DARWIN
+// static
+U32Kilobytes LLMemoryInfo::getHardwareMemSize()
+{
+ // This might work on Linux as well. Someone check...
+ uint64_t phys = 0;
+ int mib[2] = { CTL_HW, HW_MEMSIZE };
+
+ size_t len = sizeof(phys);
+ sysctl(mib, 2, &phys, &len, NULL, 0);
+
+ return U64Bytes(phys);
+}
+#endif
+
U32Kilobytes LLMemoryInfo::getPhysicalMemoryKB() const
{
#if LL_WINDOWS
return LLMemoryAdjustKBResult(U32Kilobytes(mStatsMap["Total Physical KB"].asInteger()));
#elif LL_DARWIN
- // This might work on Linux as well. Someone check...
- uint64_t phys = 0;
- int mib[2] = { CTL_HW, HW_MEMSIZE };
-
- size_t len = sizeof(phys);
- sysctl(mib, 2, &phys, &len, NULL, 0);
-
- return U64Bytes(phys);
+ return getHardwareMemSize();
#elif LL_LINUX
U64 phys = 0;
diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h
index cb92cb0ac6..538e61c521 100644
--- a/indra/llcommon/llsys.h
+++ b/indra/llcommon/llsys.h
@@ -117,7 +117,10 @@ public:
LLMemoryInfo(); ///< Default constructor
void stream(std::ostream& s) const; ///< output text info to s
- U32Kilobytes getPhysicalMemoryKB() const;
+ U32Kilobytes getPhysicalMemoryKB() const;
+#if LL_DARWIN
+ static U32Kilobytes getHardwareMemSize(); // Because some Mac linkers won't let us reference extern gSysMemory from a different lib.
+#endif
//get the available memory infomation in KiloBytes.
static void getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb);
diff --git a/indra/llcommon/lltimer.cpp b/indra/llcommon/lltimer.cpp
index aaa6df325c..39dfee3755 100644
--- a/indra/llcommon/lltimer.cpp
+++ b/indra/llcommon/lltimer.cpp
@@ -30,6 +30,9 @@
#include "u64.h"
+#include <chrono>
+#include <thread>
+
#if LL_WINDOWS
# include "llwin32headerslean.h"
#elif LL_LINUX || LL_DARWIN
@@ -64,7 +67,13 @@ LLTimer* LLTimer::sTimer = NULL;
#if LL_WINDOWS
void ms_sleep(U32 ms)
{
- Sleep(ms);
+ LL_PROFILE_ZONE_SCOPED;
+ using TimePoint = std::chrono::steady_clock::time_point;
+ auto resume_time = TimePoint::clock::now() + std::chrono::milliseconds(ms);
+ while (TimePoint::clock::now() < resume_time)
+ {
+ std::this_thread::yield(); //note: don't use LLThread::yield here to avoid yielding for too long
+ }
}
U32 micro_sleep(U64 us, U32 max_yields)
diff --git a/indra/llcommon/llworkerthread.cpp b/indra/llcommon/llworkerthread.cpp
index 4b91b2caca..02ce4823b8 100644
--- a/indra/llcommon/llworkerthread.cpp
+++ b/indra/llcommon/llworkerthread.cpp
@@ -133,11 +133,11 @@ S32 LLWorkerThread::update(F32 max_time_ms)
//----------------------------------------------------------------------------
-LLWorkerThread::handle_t LLWorkerThread::addWorkRequest(LLWorkerClass* workerclass, S32 param, U32 priority)
+LLWorkerThread::handle_t LLWorkerThread::addWorkRequest(LLWorkerClass* workerclass, S32 param)
{
handle_t handle = generateHandle();
- WorkRequest* req = new WorkRequest(handle, priority, workerclass, param);
+ WorkRequest* req = new WorkRequest(handle, workerclass, param);
bool res = addRequest(req);
if (!res)
@@ -160,8 +160,8 @@ void LLWorkerThread::deleteWorker(LLWorkerClass* workerclass)
//============================================================================
// Runs on its OWN thread
-LLWorkerThread::WorkRequest::WorkRequest(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param) :
- LLQueuedThread::QueuedRequest(handle, priority),
+LLWorkerThread::WorkRequest::WorkRequest(handle_t handle, LLWorkerClass* workerclass, S32 param) :
+ LLQueuedThread::QueuedRequest(handle),
mWorkerClass(workerclass),
mParam(param)
{
@@ -180,6 +180,7 @@ void LLWorkerThread::WorkRequest::deleteRequest()
// virtual
bool LLWorkerThread::WorkRequest::processRequest()
{
+ LL_PROFILE_ZONE_SCOPED;
LLWorkerClass* workerclass = getWorkerClass();
workerclass->setWorking(true);
bool complete = workerclass->doWork(getParam());
@@ -190,6 +191,7 @@ bool LLWorkerThread::WorkRequest::processRequest()
// virtual
void LLWorkerThread::WorkRequest::finishRequest(bool completed)
{
+ LL_PROFILE_ZONE_SCOPED;
LLWorkerClass* workerclass = getWorkerClass();
workerclass->finishWork(getParam(), completed);
U32 flags = LLWorkerClass::WCF_WORK_FINISHED | (completed ? 0 : LLWorkerClass::WCF_WORK_ABORTED);
@@ -203,7 +205,6 @@ LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& na
: mWorkerThread(workerthread),
mWorkerClassName(name),
mRequestHandle(LLWorkerThread::nullHandle()),
- mRequestPriority(LLWorkerThread::PRIORITY_NORMAL),
mMutex(),
mWorkFlags(0)
{
@@ -292,7 +293,7 @@ bool LLWorkerClass::yield()
//----------------------------------------------------------------------------
// calls startWork, adds doWork() to queue
-void LLWorkerClass::addWork(S32 param, U32 priority)
+void LLWorkerClass::addWork(S32 param)
{
mMutex.lock();
llassert_always(!(mWorkFlags & (WCF_WORKING|WCF_HAVE_WORK)));
@@ -306,7 +307,7 @@ void LLWorkerClass::addWork(S32 param, U32 priority)
startWork(param);
clearFlags(WCF_WORK_FINISHED|WCF_WORK_ABORTED);
setFlags(WCF_HAVE_WORK);
- mRequestHandle = mWorkerThread->addWorkRequest(this, param, priority);
+ mRequestHandle = mWorkerThread->addWorkRequest(this, param);
mMutex.unlock();
}
@@ -321,7 +322,6 @@ void LLWorkerClass::abortWork(bool autocomplete)
if (mRequestHandle != LLWorkerThread::nullHandle())
{
mWorkerThread->abortRequest(mRequestHandle, autocomplete);
- mWorkerThread->setPriority(mRequestHandle, LLQueuedThread::PRIORITY_IMMEDIATE);
setFlags(WCF_ABORT_REQUESTED);
}
mMutex.unlock();
@@ -395,16 +395,5 @@ void LLWorkerClass::scheduleDelete()
}
}
-void LLWorkerClass::setPriority(U32 priority)
-{
- mMutex.lock();
- if (mRequestHandle != LLWorkerThread::nullHandle() && mRequestPriority != priority)
- {
- mRequestPriority = priority;
- mWorkerThread->setPriority(mRequestHandle, priority);
- }
- mMutex.unlock();
-}
-
//============================================================================
diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h
index 0387e75c65..06bbb7369e 100644
--- a/indra/llcommon/llworkerthread.h
+++ b/indra/llcommon/llworkerthread.h
@@ -56,7 +56,7 @@ public:
virtual ~WorkRequest(); // use deleteRequest()
public:
- WorkRequest(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param);
+ WorkRequest(handle_t handle, LLWorkerClass* workerclass, S32 param);
S32 getParam()
{
@@ -90,7 +90,7 @@ public:
/*virtual*/ S32 update(F32 max_time_ms);
- handle_t addWorkRequest(LLWorkerClass* workerclass, S32 param, U32 priority = PRIORITY_NORMAL);
+ handle_t addWorkRequest(LLWorkerClass* workerclass, S32 param);
S32 getNumDeletes() { return (S32)mDeleteList.size(); } // debug
@@ -151,10 +151,6 @@ public:
bool isWorking() { return getFlags(WCF_WORKING); }
bool wasAborted() { return getFlags(WCF_ABORT_REQUESTED); }
- // setPriority(): changes the priority of a request
- void setPriority(U32 priority);
- U32 getPriority() { return mRequestPriority; }
-
const std::string& getName() const { return mWorkerClassName; }
protected:
@@ -169,7 +165,7 @@ protected:
void setWorkerThread(LLWorkerThread* workerthread);
// addWork(): calls startWork, adds doWork() to queue
- void addWork(S32 param, U32 priority = LLWorkerThread::PRIORITY_NORMAL);
+ void addWork(S32 param);
// abortWork(): requests that work be aborted
void abortWork(bool autocomplete);
@@ -193,7 +189,6 @@ protected:
LLWorkerThread* mWorkerThread;
std::string mWorkerClassName;
handle_t mRequestHandle;
- U32 mRequestPriority; // last priority set
private:
LLMutex mMutex;
diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp
index ba914035e2..f49dd40a8b 100644
--- a/indra/llcommon/threadpool.cpp
+++ b/indra/llcommon/threadpool.cpp
@@ -17,14 +17,17 @@
// std headers
// external library headers
// other Linden headers
+#include "commoncontrol.h"
#include "llerror.h"
#include "llevents.h"
+#include "llsd.h"
#include "stringize.h"
LL::ThreadPool::ThreadPool(const std::string& name, size_t threads, size_t capacity):
+ super(name),
mQueue(name, capacity),
mName("ThreadPool:" + name),
- mThreadCount(threads)
+ mThreadCount(getConfiguredWidth(name, threads))
{}
void LL::ThreadPool::start()
@@ -86,3 +89,58 @@ void LL::ThreadPool::run()
{
mQueue.runUntilClose();
}
+
+//static
+size_t LL::ThreadPool::getConfiguredWidth(const std::string& name, size_t dft)
+{
+ LLSD poolSizes;
+ try
+ {
+ poolSizes = LL::CommonControl::get("Global", "ThreadPoolSizes");
+ // "ThreadPoolSizes" is actually a map containing the sizes of
+ // interest -- or should be, if this process has an
+ // LLViewerControlListener instance and its settings include
+ // "ThreadPoolSizes". If we failed to retrieve it, perhaps we're in a
+ // program that doesn't define that, or perhaps there's no such
+ // setting, or perhaps we're asking too early, before the LLEventAPI
+ // itself has been instantiated. In any of those cases, it seems worth
+ // warning.
+ if (! poolSizes.isDefined())
+ {
+ // Note: we don't warn about absence of an override key for a
+ // particular ThreadPool name, that's fine. This warning is about
+ // complete absence of a ThreadPoolSizes setting, which we expect
+ // in a normal viewer session.
+ LL_WARNS("ThreadPool") << "No 'ThreadPoolSizes' setting for ThreadPool '"
+ << name << "'" << LL_ENDL;
+ }
+ }
+ catch (const LL::CommonControl::Error& exc)
+ {
+ // We don't want ThreadPool to *require* LLViewerControlListener.
+ // Just log it and carry on.
+ LL_WARNS("ThreadPool") << "Can't check 'ThreadPoolSizes': " << exc.what() << LL_ENDL;
+ }
+
+ LL_DEBUGS("ThreadPool") << "ThreadPoolSizes = " << poolSizes << LL_ENDL;
+ // LLSD treats an undefined value as an empty map when asked to retrieve a
+ // key, so we don't need this to be conditional.
+ LLSD sizeSpec{ poolSizes[name] };
+ // We retrieve sizeSpec as LLSD, rather than immediately as LLSD::Integer,
+ // so we can distinguish the case when it's undefined.
+ return sizeSpec.isInteger() ? sizeSpec.asInteger() : dft;
+}
+
+//static
+size_t LL::ThreadPool::getWidth(const std::string& name, size_t dft)
+{
+ auto instance{ getInstance(name) };
+ if (instance)
+ {
+ return instance->getWidth();
+ }
+ else
+ {
+ return getConfiguredWidth(name, dft);
+ }
+}
diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h
index b79c9b9090..b49d511257 100644
--- a/indra/llcommon/threadpool.h
+++ b/indra/llcommon/threadpool.h
@@ -22,14 +22,25 @@
namespace LL
{
- class ThreadPool
+ class ThreadPool: public LLInstanceTracker<ThreadPool, std::string>
{
+ private:
+ using super = LLInstanceTracker<ThreadPool, std::string>;
public:
/**
* Pass ThreadPool a string name. This can be used to look up the
* relevant WorkQueue.
+ *
+ * The number of threads you pass sets the compile-time default. But
+ * if the user has overridden the LLSD map in the "ThreadPoolSizes"
+ * setting with a key matching this ThreadPool name, that setting
+ * overrides this parameter.
+ *
+ * Pass an explicit capacity to limit the size of the queue.
+ * Constraining the queue can cause a submitter to block. Do not
+ * constrain any ThreadPool accepting work from the main thread.
*/
- ThreadPool(const std::string& name, size_t threads=1, size_t capacity=1024);
+ ThreadPool(const std::string& name, size_t threads=1, size_t capacity=1024*1024);
virtual ~ThreadPool();
/**
@@ -57,6 +68,25 @@ namespace LL
*/
virtual void run();
+ /**
+ * getConfiguredWidth() returns the setting, if any, for the specified
+ * ThreadPool name. Returns dft if the "ThreadPoolSizes" map does not
+ * contain the specified name.
+ */
+ static
+ size_t getConfiguredWidth(const std::string& name, size_t dft=0);
+
+ /**
+ * This getWidth() returns the width of the instantiated ThreadPool
+ * with the specified name, if any. If no instance exists, returns its
+ * getConfiguredWidth() if any. If there's no instance and no relevant
+ * override, return dft. Presumably dft should match the threads
+ * parameter passed to the ThreadPool constructor call that will
+ * eventually instantiate the ThreadPool with that name.
+ */
+ static
+ size_t getWidth(const std::string& name, size_t dft);
+
private:
void run(const std::string& name);
diff --git a/indra/llcommon/workqueue.h b/indra/llcommon/workqueue.h
index 96574a18b9..784327f929 100644
--- a/indra/llcommon/workqueue.h
+++ b/indra/llcommon/workqueue.h
@@ -162,9 +162,15 @@ namespace LL
CALLABLE&& callable);
template <typename CALLABLE>
+ bool tryPost(const TimePoint& time, CALLABLE&& callable)
+ {
+ return mQueue.tryPush(TimedWork(time, std::move(callable)));
+ }
+
+ template <typename CALLABLE>
bool tryPost(CALLABLE&& callable)
{
- return mQueue.tryPush(TimedWork(TimePoint::clock::now(), std::move(callable)));
+ return mQueue.tryPush(TimePoint::clock::now(), std::move(callable));
}
/*------------------------- handshake API --------------------------*/