forked from qt-creator/qt-creator
QmlDesigner: Timeout thread
The thread will be stopped after 10min. And then started if needed. Change-Id: I66f64081353fa4f24c7927139d1676ee8de85679 Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io> Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -12,20 +12,7 @@ namespace QmlDesigner {
|
||||
AsynchronousExplicitImageCache::AsynchronousExplicitImageCache(ImageCacheStorageInterface &storage)
|
||||
: m_storage(storage)
|
||||
{
|
||||
m_backgroundThread = std::thread{[this] {
|
||||
while (isRunning()) {
|
||||
if (auto entry = getEntry(); entry) {
|
||||
request(entry->name,
|
||||
entry->extraId,
|
||||
entry->requestType,
|
||||
std::move(entry->captureCallback),
|
||||
std::move(entry->abortCallback),
|
||||
m_storage);
|
||||
}
|
||||
|
||||
waitForEntries();
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
AsynchronousExplicitImageCache::~AsynchronousExplicitImageCache()
|
||||
@@ -110,9 +97,10 @@ void AsynchronousExplicitImageCache::clean()
|
||||
clearEntries();
|
||||
}
|
||||
|
||||
std::optional<AsynchronousExplicitImageCache::RequestEntry> AsynchronousExplicitImageCache::getEntry()
|
||||
std::optional<AsynchronousExplicitImageCache::RequestEntry> AsynchronousExplicitImageCache::getEntry(
|
||||
std::unique_lock<std::mutex> lock)
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
auto l = std::move(lock);
|
||||
|
||||
if (m_requestEntries.empty())
|
||||
return {};
|
||||
@@ -131,6 +119,8 @@ void AsynchronousExplicitImageCache::addEntry(Utils::PathString &&name,
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
|
||||
ensureThreadIsRunning();
|
||||
|
||||
m_requestEntries.emplace_back(std::move(name),
|
||||
std::move(extraId),
|
||||
std::move(captureCallback),
|
||||
@@ -146,11 +136,23 @@ void AsynchronousExplicitImageCache::clearEntries()
|
||||
m_requestEntries.clear();
|
||||
}
|
||||
|
||||
void AsynchronousExplicitImageCache::waitForEntries()
|
||||
std::tuple<std::unique_lock<std::mutex>, bool> AsynchronousExplicitImageCache::waitForEntries()
|
||||
{
|
||||
using namespace std::literals::chrono_literals;
|
||||
std::unique_lock lock{m_mutex};
|
||||
if (m_requestEntries.empty())
|
||||
m_condition.wait(lock, [&] { return m_requestEntries.size() || m_finishing; });
|
||||
if (m_finishing)
|
||||
return {std::move(lock), true};
|
||||
if (m_requestEntries.empty()) {
|
||||
auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] {
|
||||
return m_requestEntries.size() || m_finishing;
|
||||
});
|
||||
|
||||
if (timedOutWithoutEntriesOrFinishing || m_finishing) {
|
||||
m_sleeping = true;
|
||||
return {std::move(lock), true};
|
||||
}
|
||||
}
|
||||
return {std::move(lock), false};
|
||||
}
|
||||
|
||||
void AsynchronousExplicitImageCache::stopThread()
|
||||
@@ -159,10 +161,35 @@ void AsynchronousExplicitImageCache::stopThread()
|
||||
m_finishing = true;
|
||||
}
|
||||
|
||||
bool AsynchronousExplicitImageCache::isRunning()
|
||||
void AsynchronousExplicitImageCache::ensureThreadIsRunning()
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
return !m_finishing || m_requestEntries.size();
|
||||
if (m_finishing)
|
||||
return;
|
||||
|
||||
if (!m_sleeping)
|
||||
return;
|
||||
|
||||
if (m_backgroundThread.joinable())
|
||||
m_backgroundThread.join();
|
||||
|
||||
m_sleeping = false;
|
||||
|
||||
m_backgroundThread = std::thread{[this] {
|
||||
while (true) {
|
||||
auto [lock, abort] = waitForEntries();
|
||||
if (abort)
|
||||
return;
|
||||
|
||||
if (auto entry = getEntry(std::move(lock)); entry) {
|
||||
request(entry->name,
|
||||
entry->extraId,
|
||||
entry->requestType,
|
||||
std::move(entry->captureCallback),
|
||||
std::move(entry->abortCallback),
|
||||
m_storage);
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -18,23 +18,6 @@ AsynchronousImageCache::AsynchronousImageCache(ImageCacheStorageInterface &stora
|
||||
, m_generator(generator)
|
||||
, m_timeStampProvider(timeStampProvider)
|
||||
{
|
||||
m_backgroundThread = std::thread{[this] {
|
||||
while (isRunning()) {
|
||||
if (auto entry = getEntry(); entry) {
|
||||
request(entry->name,
|
||||
entry->extraId,
|
||||
entry->requestType,
|
||||
std::move(entry->captureCallback),
|
||||
std::move(entry->abortCallback),
|
||||
std::move(entry->auxiliaryData),
|
||||
m_storage,
|
||||
m_generator,
|
||||
m_timeStampProvider);
|
||||
}
|
||||
|
||||
waitForEntries();
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
AsynchronousImageCache::~AsynchronousImageCache()
|
||||
@@ -169,10 +152,10 @@ void AsynchronousImageCache::clean()
|
||||
m_generator.clean();
|
||||
}
|
||||
|
||||
std::optional<AsynchronousImageCache::Entry> AsynchronousImageCache::getEntry()
|
||||
std::optional<AsynchronousImageCache::Entry> AsynchronousImageCache::getEntry(
|
||||
std::unique_lock<std::mutex> lock)
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
|
||||
auto l = std::move(lock);
|
||||
if (m_entries.empty())
|
||||
return {};
|
||||
|
||||
@@ -191,6 +174,8 @@ void AsynchronousImageCache::addEntry(Utils::PathString &&name,
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
|
||||
ensureThreadIsRunning();
|
||||
|
||||
m_entries.emplace_back(std::move(name),
|
||||
std::move(extraId),
|
||||
std::move(captureCallback),
|
||||
@@ -207,11 +192,23 @@ void AsynchronousImageCache::clearEntries()
|
||||
m_entries.clear();
|
||||
}
|
||||
|
||||
void AsynchronousImageCache::waitForEntries()
|
||||
std::tuple<std::unique_lock<std::mutex>, bool> AsynchronousImageCache::waitForEntries()
|
||||
{
|
||||
using namespace std::literals::chrono_literals;
|
||||
std::unique_lock lock{m_mutex};
|
||||
if (m_entries.empty())
|
||||
m_condition.wait(lock, [&] { return m_entries.size() || m_finishing; });
|
||||
if (m_finishing)
|
||||
return {std::move(lock), true};
|
||||
if (m_entries.empty()) {
|
||||
auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] {
|
||||
return m_entries.size() || m_finishing;
|
||||
});
|
||||
|
||||
if (timedOutWithoutEntriesOrFinishing || m_finishing) {
|
||||
m_sleeping = true;
|
||||
return {std::move(lock), true};
|
||||
}
|
||||
}
|
||||
return {std::move(lock), false};
|
||||
}
|
||||
|
||||
void AsynchronousImageCache::stopThread()
|
||||
@@ -220,10 +217,37 @@ void AsynchronousImageCache::stopThread()
|
||||
m_finishing = true;
|
||||
}
|
||||
|
||||
bool AsynchronousImageCache::isRunning()
|
||||
void AsynchronousImageCache::ensureThreadIsRunning()
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
return !m_finishing || m_entries.size();
|
||||
if (m_finishing)
|
||||
return;
|
||||
|
||||
if (!m_sleeping)
|
||||
return;
|
||||
|
||||
if (m_backgroundThread.joinable())
|
||||
m_backgroundThread.join();
|
||||
|
||||
m_sleeping = false;
|
||||
|
||||
m_backgroundThread = std::thread{[this] {
|
||||
while (true) {
|
||||
auto [lock, abort] = waitForEntries();
|
||||
if (abort)
|
||||
return;
|
||||
if (auto entry = getEntry(std::move(lock)); entry) {
|
||||
request(entry->name,
|
||||
entry->extraId,
|
||||
entry->requestType,
|
||||
std::move(entry->captureCallback),
|
||||
std::move(entry->abortCallback),
|
||||
std::move(entry->auxiliaryData),
|
||||
m_storage,
|
||||
m_generator,
|
||||
m_timeStampProvider);
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -17,20 +17,7 @@ AsynchronousImageFactory::AsynchronousImageFactory(ImageCacheStorageInterface &s
|
||||
, m_timeStampProvider(timeStampProvider)
|
||||
, m_collector(collector)
|
||||
{
|
||||
m_backgroundThread = std::thread{[this] {
|
||||
while (isRunning()) {
|
||||
if (auto entry = getEntry(); entry) {
|
||||
request(entry->name,
|
||||
entry->extraId,
|
||||
std::move(entry->auxiliaryData),
|
||||
m_storage,
|
||||
m_timeStampProvider,
|
||||
m_collector);
|
||||
}
|
||||
|
||||
waitForEntries();
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
AsynchronousImageFactory::~AsynchronousImageFactory()
|
||||
@@ -53,25 +40,64 @@ void AsynchronousImageFactory::addEntry(Utils::SmallStringView name,
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
|
||||
ensureThreadIsRunning();
|
||||
|
||||
m_entries.emplace_back(std::move(name), std::move(extraId), std::move(auxiliaryData));
|
||||
}
|
||||
|
||||
bool AsynchronousImageFactory::isRunning()
|
||||
void AsynchronousImageFactory::ensureThreadIsRunning()
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
return !m_finishing || m_entries.size();
|
||||
if (m_finishing)
|
||||
return;
|
||||
|
||||
if (!m_sleeping)
|
||||
return;
|
||||
|
||||
if (m_backgroundThread.joinable())
|
||||
m_backgroundThread.join();
|
||||
|
||||
m_sleeping = false;
|
||||
|
||||
m_backgroundThread = std::thread{[this] {
|
||||
while (true) {
|
||||
auto [lock, abort] = waitForEntries();
|
||||
if (abort)
|
||||
return;
|
||||
if (auto entry = getEntry(std::move(lock)); entry) {
|
||||
request(entry->name,
|
||||
entry->extraId,
|
||||
std::move(entry->auxiliaryData),
|
||||
m_storage,
|
||||
m_timeStampProvider,
|
||||
m_collector);
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
void AsynchronousImageFactory::waitForEntries()
|
||||
std::tuple<std::unique_lock<std::mutex>, bool> AsynchronousImageFactory::waitForEntries()
|
||||
{
|
||||
using namespace std::literals::chrono_literals;
|
||||
std::unique_lock lock{m_mutex};
|
||||
if (m_entries.empty())
|
||||
m_condition.wait(lock, [&] { return m_entries.size() || m_finishing; });
|
||||
if (m_finishing)
|
||||
return {std::move(lock), true};
|
||||
if (m_entries.empty()) {
|
||||
auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] {
|
||||
return m_entries.size() || m_finishing;
|
||||
});
|
||||
|
||||
if (timedOutWithoutEntriesOrFinishing || m_finishing) {
|
||||
m_sleeping = true;
|
||||
return {std::move(lock), true};
|
||||
}
|
||||
}
|
||||
return {std::move(lock), false};
|
||||
}
|
||||
|
||||
std::optional<AsynchronousImageFactory::Entry> AsynchronousImageFactory::getEntry()
|
||||
std::optional<AsynchronousImageFactory::Entry> AsynchronousImageFactory::getEntry(
|
||||
std::unique_lock<std::mutex> lock)
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
auto l = std::move(lock);
|
||||
|
||||
if (m_entries.empty())
|
||||
return {};
|
||||
|
@@ -53,9 +53,9 @@ private:
|
||||
void addEntry(Utils::SmallStringView name,
|
||||
Utils::SmallStringView extraId,
|
||||
ImageCache::AuxiliaryData &&auxiliaryData);
|
||||
bool isRunning();
|
||||
void waitForEntries();
|
||||
std::optional<Entry> getEntry();
|
||||
void ensureThreadIsRunning();
|
||||
[[nodiscard]] std::tuple<std::unique_lock<std::mutex>, bool> waitForEntries();
|
||||
[[nodiscard]] std::optional<Entry> getEntry(std::unique_lock<std::mutex> lock);
|
||||
void request(Utils::SmallStringView name,
|
||||
Utils::SmallStringView extraId,
|
||||
ImageCache::AuxiliaryData auxiliaryData,
|
||||
@@ -75,6 +75,7 @@ private:
|
||||
TimeStampProviderInterface &m_timeStampProvider;
|
||||
ImageCacheCollectorInterface &m_collector;
|
||||
bool m_finishing{false};
|
||||
bool m_sleeping{true};
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -14,10 +14,7 @@ ImageCacheGenerator::ImageCacheGenerator(ImageCacheCollectorInterface &collector
|
||||
ImageCacheStorageInterface &storage)
|
||||
: m_collector{collector}
|
||||
, m_storage(storage)
|
||||
{
|
||||
m_backgroundThread.reset(QThread::create([this]() { startGeneration(); }));
|
||||
m_backgroundThread->start();
|
||||
}
|
||||
{}
|
||||
|
||||
ImageCacheGenerator::~ImageCacheGenerator()
|
||||
{
|
||||
@@ -25,6 +22,22 @@ ImageCacheGenerator::~ImageCacheGenerator()
|
||||
waitForFinished();
|
||||
}
|
||||
|
||||
void ImageCacheGenerator::ensureThreadIsRunning()
|
||||
{
|
||||
if (m_finishing)
|
||||
return;
|
||||
|
||||
if (m_sleeping) {
|
||||
if (m_backgroundThread)
|
||||
m_backgroundThread->wait();
|
||||
|
||||
m_sleeping = false;
|
||||
|
||||
m_backgroundThread.reset(QThread::create([this]() { startGeneration(); }));
|
||||
m_backgroundThread->start();
|
||||
}
|
||||
}
|
||||
|
||||
void ImageCacheGenerator::generateImage(Utils::SmallStringView name,
|
||||
Utils::SmallStringView extraId,
|
||||
Sqlite::TimeStamp timeStamp,
|
||||
@@ -35,6 +48,8 @@ void ImageCacheGenerator::generateImage(Utils::SmallStringView name,
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
|
||||
ensureThreadIsRunning();
|
||||
|
||||
auto found = std::find_if(m_tasks.begin(), m_tasks.end(), [&](const Task &task) {
|
||||
return task.filePath == name && task.extraId == extraId;
|
||||
});
|
||||
@@ -91,18 +106,14 @@ void ImageCacheGenerator::waitForFinished()
|
||||
|
||||
void ImageCacheGenerator::startGeneration()
|
||||
{
|
||||
while (isRunning()) {
|
||||
waitForEntries();
|
||||
|
||||
while (true) {
|
||||
Task task;
|
||||
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
auto [lock, abort] = waitForEntries();
|
||||
|
||||
if (m_finishing && m_tasks.empty()) {
|
||||
m_storage.walCheckpointFull();
|
||||
if (abort)
|
||||
return;
|
||||
}
|
||||
|
||||
task = std::move(m_tasks.front());
|
||||
|
||||
@@ -141,11 +152,23 @@ void ImageCacheGenerator::startGeneration()
|
||||
}
|
||||
}
|
||||
|
||||
void ImageCacheGenerator::waitForEntries()
|
||||
std::tuple<std::unique_lock<std::mutex>, bool> ImageCacheGenerator::waitForEntries()
|
||||
{
|
||||
using namespace std::literals::chrono_literals;
|
||||
std::unique_lock lock{m_mutex};
|
||||
if (m_tasks.empty())
|
||||
m_condition.wait(lock, [&] { return m_tasks.size() || m_finishing; });
|
||||
if (m_finishing)
|
||||
return {std::move(lock), true};
|
||||
if (m_tasks.empty()) {
|
||||
auto timedOutWithoutEntriesOrFinishing = !m_condition.wait_for(lock, 10min, [&] {
|
||||
return m_tasks.size() || m_finishing;
|
||||
});
|
||||
|
||||
if (timedOutWithoutEntriesOrFinishing || m_finishing) {
|
||||
m_sleeping = true;
|
||||
return {std::move(lock), true};
|
||||
}
|
||||
}
|
||||
return {std::move(lock), false};
|
||||
}
|
||||
|
||||
void ImageCacheGenerator::stopThread()
|
||||
@@ -154,10 +177,4 @@ void ImageCacheGenerator::stopThread()
|
||||
m_finishing = true;
|
||||
}
|
||||
|
||||
bool ImageCacheGenerator::isRunning()
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
return !m_finishing || m_tasks.size();
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -67,10 +67,9 @@ private:
|
||||
};
|
||||
|
||||
void startGeneration();
|
||||
|
||||
void waitForEntries();
|
||||
void ensureThreadIsRunning();
|
||||
[[nodiscard]] std::tuple<std::unique_lock<std::mutex>, bool> waitForEntries();
|
||||
void stopThread();
|
||||
bool isRunning();
|
||||
|
||||
private:
|
||||
private:
|
||||
@@ -81,6 +80,7 @@ private:
|
||||
ImageCacheCollectorInterface &m_collector;
|
||||
ImageCacheStorageInterface &m_storage;
|
||||
bool m_finishing{false};
|
||||
bool m_sleeping{true};
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -62,16 +62,16 @@ private:
|
||||
RequestType requestType = RequestType::Image;
|
||||
};
|
||||
|
||||
std::optional<RequestEntry> getEntry();
|
||||
[[nodiscard]] std::optional<RequestEntry> getEntry(std::unique_lock<std::mutex> lock);
|
||||
void addEntry(Utils::PathString &&name,
|
||||
Utils::SmallString &&extraId,
|
||||
ImageCache::CaptureImageCallback &&captureCallback,
|
||||
ImageCache::AbortCallback &&abortCallback,
|
||||
RequestType requestType);
|
||||
void clearEntries();
|
||||
void waitForEntries();
|
||||
[[nodiscard]] std::tuple<std::unique_lock<std::mutex>, bool> waitForEntries();
|
||||
void stopThread();
|
||||
bool isRunning();
|
||||
void ensureThreadIsRunning();
|
||||
static void request(Utils::SmallStringView name,
|
||||
Utils::SmallStringView extraId,
|
||||
AsynchronousExplicitImageCache::RequestType requestType,
|
||||
@@ -89,6 +89,7 @@ private:
|
||||
std::thread m_backgroundThread;
|
||||
ImageCacheStorageInterface &m_storage;
|
||||
bool m_finishing{false};
|
||||
bool m_sleeping{true};
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -73,7 +73,7 @@ private:
|
||||
RequestType requestType = RequestType::Image;
|
||||
};
|
||||
|
||||
std::optional<Entry> getEntry();
|
||||
[[nodiscard]] std::optional<Entry> getEntry(std::unique_lock<std::mutex> lock);
|
||||
void addEntry(Utils::PathString &&name,
|
||||
Utils::SmallString &&extraId,
|
||||
ImageCache::CaptureImageCallback &&captureCallback,
|
||||
@@ -81,9 +81,9 @@ private:
|
||||
ImageCache::AuxiliaryData &&auxiliaryData,
|
||||
RequestType requestType);
|
||||
void clearEntries();
|
||||
void waitForEntries();
|
||||
[[nodiscard]] std::tuple<std::unique_lock<std::mutex>, bool> waitForEntries();
|
||||
void stopThread();
|
||||
bool isRunning();
|
||||
void ensureThreadIsRunning();
|
||||
static void request(Utils::SmallStringView name,
|
||||
Utils::SmallStringView extraId,
|
||||
AsynchronousImageCache::RequestType requestType,
|
||||
@@ -106,6 +106,7 @@ private:
|
||||
ImageCacheGeneratorInterface &m_generator;
|
||||
TimeStampProviderInterface &m_timeStampProvider;
|
||||
bool m_finishing{false};
|
||||
bool m_sleeping{true};
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -391,7 +391,7 @@ TEST_F(ImageCacheGenerator, wait_for_finished)
|
||||
generator.generateImage(
|
||||
"name2", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
|
||||
EXPECT_CALL(imageCallbackMock, Call(_, _, _)).Times(2);
|
||||
EXPECT_CALL(imageCallbackMock, Call(_, _, _)).Times(AtMost(2));
|
||||
|
||||
waitInThread.notify();
|
||||
generator.waitForFinished();
|
||||
|
Reference in New Issue
Block a user