summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJarek Kobus <jaroslaw.kobus@qt.io>2023-04-29 19:53:40 +0200
committerJarek Kobus <jaroslaw.kobus@qt.io>2023-05-02 05:55:55 +0000
commite46a4eba8dbe757903c10fd043bf03142836994a (patch)
tree1a8623c8b8b7ebe4d06caa8f6d1415a244bcff3c
parent56b0d77c8276c8c3aa122409643deaaddb075bfb (diff)
downloadqt-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.txt1
-rw-r--r--src/libs/utils/barrier.cpp47
-rw-r--r--src/libs/utils/barrier.h97
-rw-r--r--src/libs/utils/utils.qbs2
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",