147 lines
4.5 KiB
C++
147 lines
4.5 KiB
C++
/**
|
|
* @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 "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(getConfiguredWidth(name, threads))
|
|
{}
|
|
|
|
void LL::ThreadPool::start()
|
|
{
|
|
for (size_t i = 0; i < mThreadCount; ++i)
|
|
{
|
|
std::string tname{ stringize(mName, ':', (i+1), '/', mThreadCount) };
|
|
mThreads.emplace_back(tname, [this, tname]()
|
|
{
|
|
LL_PROFILER_SET_THREAD_NAME(tname.c_str());
|
|
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();
|
|
}
|
|
|
|
//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);
|
|
}
|
|
}
|