diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 69fa8769a9d..8660e65b84f 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 00000000000..76cc351088a --- /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 00000000000..f61b2ef2b85 --- /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 result() const { return m_result; } + +signals: + void done(bool success); + +private: + std::optional m_result = {}; + int m_limit = 1; + int m_current = -1; +}; + +class QTCREATOR_UTILS_EXPORT BarrierAdapter : public Tasking::TaskAdapter +{ +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 +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 m_barrier; +}; + +template +using MultiBarrier = TreeStorage>; + +// 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 + WaitForBarrier(const MultiBarrier &sharedBarrier) + : BarrierTask([sharedBarrier](Barrier &barrier) { + SharedBarrier *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 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 210a6230318..eb8c2a9f563 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",