// Copyright (C) 2025 Jarek Kobus // Copyright (C) 2025 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QBARRIERTASK_H #define QBARRIERTASK_H #include #include #include QT_BEGIN_NAMESPACE class QBarrierPrivate; class Q_TASKTREE_EXPORT QBarrier : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QBarrier) public: QBarrier(QObject *parent = nullptr); ~QBarrier() override; void setLimit(int value); int limit() const; void start(); void advance(); void stopWithResult(QtTaskTree::DoneResult result); bool isRunning() const; int current() const; std::optional result() const; Q_SIGNALS: void done(QtTaskTree::DoneResult success, QPrivateSignal); }; using QBarrierTask = QCustomTask; template class QStartedBarrier : public QBarrier { public: static_assert(Limit > 0, "StartedBarrier's limit should be 1 or more."); QStartedBarrier(QObject *parent = nullptr) : QBarrier(parent) { setLimit(Limit); start(); } }; template using QStoredMultiBarrier = QtTaskTree::Storage>; using QStoredBarrier = QStoredMultiBarrier<1>; namespace QtTaskTree { template ExecutableItem barrierAwaiterTask(const QStoredMultiBarrier &storedBarrier) { return QBarrierTask([storedBarrier](QBarrier &barrier) { QBarrier *activeBarrier = storedBarrier.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 barrier is not reachable from where it is referenced. " "The WaitForBarrier task finishes with an error. "); return SetupResult::StopWithError; } const std::optional result = activeBarrier->result(); if (result.has_value()) { return *result == DoneResult::Success ? SetupResult::StopWithSuccess : SetupResult::StopWithError; } QObject::connect(activeBarrier, &QBarrier::done, &barrier, &QBarrier::stopWithResult); return SetupResult::Continue; }); } template ExecutableItem signalAwaiterTask(const typename QtPrivate::FunctionPointer::Object *sender, Signal signal) { return QBarrierTask([sender, signal](QBarrier &barrier) { QObject::connect(sender, signal, &barrier, &QBarrier::advance, Qt::SingleShotConnection); }); } using BarrierKickerGetter = std::function; class WhenPrivate; class When final { public: Q_TASKTREE_EXPORT explicit When(const BarrierKickerGetter &kicker, WorkflowPolicy policy = WorkflowPolicy::StopOnError); template explicit When(const QCustomTask &customTask, Signal signal, WorkflowPolicy policy = WorkflowPolicy::StopOnError) : When(kickerForSignal(customTask, signal), policy) {} Q_TASKTREE_EXPORT ~When(); Q_TASKTREE_EXPORT When(const When &other); Q_TASKTREE_EXPORT When(When &&other) noexcept; Q_TASKTREE_EXPORT When &operator=(const When &other); QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(When) void swap(When &other) noexcept { d.swap(other.d); } private: Q_TASKTREE_EXPORT friend Group operator>>(const When &whenItem, const Do &doItem); template static BarrierKickerGetter kickerForSignal(const QCustomTask &task, Signal signal) { return [taskHandler = task.taskHandler(), signal](const QStoredBarrier &barrier) { auto handler = std::move(taskHandler); const auto wrappedSetupHandler = [originalSetupHandler = std::move(handler.m_taskAdapterSetupHandler), barrier, signal](void *taskAdapter) { const SetupResult setupResult = std::invoke(originalSetupHandler, taskAdapter); using TaskAdapter = typename QCustomTask::TaskAdapter; TaskAdapter *adapter = static_cast(taskAdapter); QObject::connect(adapter->task.get(), signal, barrier.activeStorage(), &QBarrier::advance); return setupResult; }; handler.m_taskAdapterSetupHandler = std::move(wrappedSetupHandler); return ExecutableItem(std::move(handler)); }; } QExplicitlySharedDataPointer d; }; } // namespace QtTaskTree QT_END_NAMESPACE #endif // QBARRIERTASK_H