diff options
author | Jarek Kobus <jaroslaw.kobus@qt.io> | 2023-04-29 19:53:40 +0200 |
---|---|---|
committer | Jarek Kobus <jaroslaw.kobus@qt.io> | 2023-05-02 05:55:55 +0000 |
commit | e46a4eba8dbe757903c10fd043bf03142836994a (patch) | |
tree | 1a8623c8b8b7ebe4d06caa8f6d1415a244bcff3c | |
parent | 56b0d77c8276c8c3aa122409643deaaddb075bfb (diff) | |
download | qt-creator-e46a4eba8dbe757903c10fd043bf03142836994a.tar.gz |
Utils: Introduce Barrier primitive
This primitive is going to replace the TaskTree's built-in
mechanism consisting of Wait, Condition and ConditionActivator
elements.
When combining 2 barriers, one placed in a custom storage,
and the other in a tree, it's possible to fully substitute
the Wait, Condition and ConditionActivator with the comparable
amount of code.
However, the Barrier is much more versatile, since it
makes it possible to:
1. distribute the decision about the ultimate barrier pass on the
whole tree.
In order to utilize it, increase the limit of the shared barrier
with setLimit() to the expected number of places that participate
in the decision about the ultimate barrier pass and use advance()
from multiple places in the tree. When the number of calls
to advance() reaches the limit(), the shared barrier passes
automatically.
Whenever some participant failed, so that the shared barrier
can not be passed, it may call stopWithResult(false).
Whenever some other participant decided that all the needed
data are already collected, so that the barrier may pass early,
it may call stopWithResult(true), making the remaining calls to
advance no-op.
2. wait for the same barrier from multiple places.
Before, only one WaitFor was possible for a single Condition.
3. insert multiple Barriers into one Group element.
Before, only one WaitFor could be placed in a single Group.
Provide ready-made SingleCondition and waitFor() helpers.
With the new approach, the following equivalents are provided:
- SingleBarrier (substitutes old Condition)
- WaitForBarrier() (substitutes old WaitFor)
- Barrier (substitutes old ConditionActivator)
This change replaces the mechanism introduced in
29f634a8caf69a374edb00df7a19146352a14d6f.
This change conforms to the naming scheme proposed in QTCREATORBUG-29102.
Task-number: QTCREATORBUG-29102
Change-Id: I48b3e2ee723c3b9fe73a59a25eb7facc72940c3b
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
-rw-r--r-- | src/libs/utils/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/libs/utils/barrier.cpp | 47 | ||||
-rw-r--r-- | src/libs/utils/barrier.h | 97 | ||||
-rw-r--r-- | src/libs/utils/utils.qbs | 2 |
4 files changed, 147 insertions, 0 deletions
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 69fa8769a9..8660e65b84 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -13,6 +13,7 @@ add_qtc_library(Utils archive.cpp archive.h aspects.cpp aspects.h asynctask.cpp asynctask.h + barrier.cpp barrier.h basetreeview.cpp basetreeview.h benchmarker.cpp benchmarker.h buildablehelperlibrary.cpp buildablehelperlibrary.h diff --git a/src/libs/utils/barrier.cpp b/src/libs/utils/barrier.cpp new file mode 100644 index 0000000000..76cc351088 --- /dev/null +++ b/src/libs/utils/barrier.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "barrier.h" + +#include "qtcassert.h" + +namespace Utils { + +void Barrier::setLimit(int value) +{ + QTC_ASSERT(!isRunning(), return); + QTC_ASSERT(value > 0, return); + + m_limit = value; +} + +void Barrier::start() +{ + QTC_ASSERT(!isRunning(), return); + m_current = 0; + m_result = {}; +} + +void Barrier::advance() +{ + // Calling advance on finished is OK + QTC_ASSERT(isRunning() || m_result, return); + if (!isRunning()) // no-op + return; + ++m_current; + if (m_current == m_limit) + stopWithResult(true); +} + +void Barrier::stopWithResult(bool success) +{ + // Calling stopWithResult on finished is OK when the same success is passed + QTC_ASSERT(isRunning() || (m_result && *m_result == success), return); + if (!isRunning()) // no-op + return; + m_current = -1; + m_result = success; + emit done(success); +} + +} // namespace Utils diff --git a/src/libs/utils/barrier.h b/src/libs/utils/barrier.h new file mode 100644 index 0000000000..f61b2ef2b8 --- /dev/null +++ b/src/libs/utils/barrier.h @@ -0,0 +1,97 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "utils_global.h" + +#include "tasktree.h" + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT Barrier final : public QObject +{ + Q_OBJECT + +public: + void setLimit(int value); + int limit() const { return m_limit; } + + void start(); + void advance(); // If limit reached, stops with true + void stopWithResult(bool success); // Ignores limit + + bool isRunning() const { return m_current >= 0; } + int current() const { return m_current; } + std::optional<bool> result() const { return m_result; } + +signals: + void done(bool success); + +private: + std::optional<bool> m_result = {}; + int m_limit = 1; + int m_current = -1; +}; + +class QTCREATOR_UTILS_EXPORT BarrierAdapter : public Tasking::TaskAdapter<Barrier> +{ +public: + BarrierAdapter() { connect(task(), &Barrier::done, this, &TaskInterface::done); } + void start() final { task()->start(); } +}; + +} // namespace Utils + +QTC_DECLARE_CUSTOM_TASK(BarrierTask, Utils::BarrierAdapter); + +namespace Utils::Tasking { + +template <int Limit = 1> +class SharedBarrier +{ +public: + static_assert(Limit > 0, "SharedBarrier's limit should be 1 or more."); + SharedBarrier() : m_barrier(new Barrier) { + m_barrier->setLimit(Limit); + m_barrier->start(); + } + Barrier *barrier() const { return m_barrier.get(); } + +private: + std::shared_ptr<Barrier> m_barrier; +}; + +template <int Limit = 1> +using MultiBarrier = TreeStorage<SharedBarrier<Limit>>; + +// Can't write: "MultiBarrier barrier;". Only "MultiBarrier<> barrier;" would work. +// Can't have one alias with default type in C++17, getting the following error: +// alias template deduction only available with C++20. +using SingleBarrier = MultiBarrier<1>; + +class WaitForBarrier : public BarrierTask +{ +public: + template <int Limit> + WaitForBarrier(const MultiBarrier<Limit> &sharedBarrier) + : BarrierTask([sharedBarrier](Barrier &barrier) { + SharedBarrier<Limit> *activeBarrier = sharedBarrier.activeStorage(); + if (!activeBarrier) { + qWarning("The barrier referenced from WaitForBarrier element " + "is not reachable in the running tree. " + "It is possible that no barrier was added to the tree, " + "or the storage is not reachable from where it is referenced. " + "The WaitForBarrier task will finish with error. "); + return TaskAction::StopWithError; + } + Barrier *activeSharedBarrier = activeBarrier->barrier(); + const std::optional<bool> result = activeSharedBarrier->result(); + if (result.has_value()) + return result.value() ? TaskAction::StopWithDone : TaskAction::StopWithError; + QObject::connect(activeSharedBarrier, &Barrier::done, &barrier, &Barrier::stopWithResult); + return TaskAction::Continue; + }) {} +}; + +} // namespace Utils::Tasking diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 210a623031..eb8c2a9f56 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -51,6 +51,8 @@ Project { "aspects.h", "asynctask.cpp", "asynctask.h", + "barrier.cpp", + "barrier.h", "basetreeview.cpp", "basetreeview.h", "benchmarker.cpp", |