diff options
Diffstat (limited to 'indra/llcommon')
| -rw-r--r-- | indra/llcommon/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/llcommon/commoncontrol.cpp | 106 | ||||
| -rw-r--r-- | indra/llcommon/commoncontrol.h | 75 | ||||
| -rw-r--r-- | indra/llcommon/llapr.cpp | 2 | ||||
| -rw-r--r-- | indra/llcommon/llframetimer.cpp | 5 | ||||
| -rw-r--r-- | indra/llcommon/llmemory.cpp | 45 | ||||
| -rw-r--r-- | indra/llcommon/llprofiler.h | 4 | ||||
| -rw-r--r-- | indra/llcommon/llqueuedthread.cpp | 299 | ||||
| -rw-r--r-- | indra/llcommon/llqueuedthread.h | 51 | ||||
| -rw-r--r-- | indra/llcommon/llsys.cpp | 24 | ||||
| -rw-r--r-- | indra/llcommon/llsys.h | 5 | ||||
| -rw-r--r-- | indra/llcommon/lltimer.cpp | 11 | ||||
| -rw-r--r-- | indra/llcommon/llworkerthread.cpp | 27 | ||||
| -rw-r--r-- | indra/llcommon/llworkerthread.h | 11 | ||||
| -rw-r--r-- | indra/llcommon/threadpool.cpp | 60 | ||||
| -rw-r--r-- | indra/llcommon/threadpool.h | 34 | ||||
| -rw-r--r-- | indra/llcommon/workqueue.h | 8 | 
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 --------------------------*/ | 
