aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/QtTaskTree/qbarriertask.h
blob: 0eef1551af4d40e062ebf0f2f1f0b47e90b5e8f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// 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 <QtTaskTree/qttasktreeglobal.h>

#include <QtTaskTree/qtasktree.h>

#include <QtCore/QSharedData>

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<QtTaskTree::DoneResult> result() const;

Q_SIGNALS:
    void done(QtTaskTree::DoneResult success, QPrivateSignal);
};

using QBarrierTask = QCustomTask<QBarrier>;

template <int Limit = 1>
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 <int Limit = 1>
using QStoredMultiBarrier = QtTaskTree::Storage<QStartedBarrier<Limit>>;
using QStoredBarrier = QStoredMultiBarrier<1>;

namespace QtTaskTree {

template <int Limit>
ExecutableItem barrierAwaiterTask(const QStoredMultiBarrier<Limit> &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<DoneResult> 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 <typename Signal>
ExecutableItem signalAwaiterTask(const typename QtPrivate::FunctionPointer<Signal>::Object *sender,
                                 Signal signal)
{
    return QBarrierTask([sender, signal](QBarrier &barrier) {
        QObject::connect(sender, signal, &barrier, &QBarrier::advance, Qt::SingleShotConnection);
    });
}

using BarrierKickerGetter = std::function<ExecutableItem(const QStoredBarrier &)>;

class WhenPrivate;

class When final
{
public:
    Q_TASKTREE_EXPORT explicit When(const BarrierKickerGetter &kicker,
                                    WorkflowPolicy policy = WorkflowPolicy::StopOnError);

    template <typename Task, typename Adapter, typename Deleter, typename Signal>
    explicit When(const QCustomTask<Task, Adapter, Deleter> &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 <typename Task, typename Adapter, typename Deleter, typename Signal>
    static BarrierKickerGetter kickerForSignal(const QCustomTask<Task, Adapter, Deleter> &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<Task, Adapter, Deleter>::TaskAdapter;
                TaskAdapter *adapter = static_cast<TaskAdapter *>(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<WhenPrivate> d;
};

} // namespace QtTaskTree

QT_END_NAMESPACE

#endif // QBARRIERTASK_H