1// Copyright (C) 2024 Jarek Kobus
2// Copyright (C) 2024 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#ifndef TASKING_BARRIER_H
6#define TASKING_BARRIER_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include "tasking_global.h"
20
21#include "tasktree.h"
22
23QT_BEGIN_NAMESPACE
24
25namespace Tasking {
26
27class TASKING_EXPORT Barrier final : public QObject
28{
29 Q_OBJECT
30
31public:
32 void setLimit(int value);
33 int limit() const { return m_limit; }
34
35 void start();
36 void advance(); // If limit reached, stops with true
37 void stopWithResult(DoneResult result); // Ignores limit
38
39 bool isRunning() const { return m_current >= 0; }
40 int current() const { return m_current; }
41 std::optional<DoneResult> result() const { return m_result; }
42
43Q_SIGNALS:
44 void done(DoneResult success);
45
46private:
47 std::optional<DoneResult> m_result = {};
48 int m_limit = 1;
49 int m_current = -1;
50};
51
52using BarrierTask = SimpleCustomTask<Barrier>;
53
54template <int Limit = 1>
55class SharedBarrier
56{
57public:
58 static_assert(Limit > 0, "SharedBarrier's limit should be 1 or more.");
59 SharedBarrier() : m_barrier(new Barrier) {
60 m_barrier->setLimit(Limit);
61 m_barrier->start();
62 }
63 Barrier *barrier() const { return m_barrier.get(); }
64
65private:
66 std::shared_ptr<Barrier> m_barrier;
67};
68
69template <int Limit = 1>
70using MultiBarrier = Storage<SharedBarrier<Limit>>;
71
72// Can't write: "MultiBarrier barrier;". Only "MultiBarrier<> barrier;" would work.
73// Can't have one alias with default type in C++17, getting the following error:
74// alias template deduction only available with C++20.
75using SingleBarrier = MultiBarrier<1>;
76
77template <int Limit>
78ExecutableItem waitForBarrierTask(const MultiBarrier<Limit> &sharedBarrier)
79{
80 return BarrierTask([sharedBarrier](Barrier &barrier) {
81 SharedBarrier<Limit> *activeBarrier = sharedBarrier.activeStorage();
82 if (!activeBarrier) {
83 qWarning(msg: "The barrier referenced from WaitForBarrier element "
84 "is not reachable in the running tree. "
85 "It is possible that no barrier was added to the tree, "
86 "or the barrier is not reachable from where it is referenced. "
87 "The WaitForBarrier task finishes with an error. ");
88 return SetupResult::StopWithError;
89 }
90 Barrier *activeSharedBarrier = activeBarrier->barrier();
91 const std::optional<DoneResult> result = activeSharedBarrier->result();
92 if (result.has_value()) {
93 return *result == DoneResult::Success ? SetupResult::StopWithSuccess
94 : SetupResult::StopWithError;
95 }
96 QObject::connect(sender: activeSharedBarrier, signal: &Barrier::done, context: &barrier, slot: &Barrier::stopWithResult);
97 return SetupResult::Continue;
98 });
99}
100
101template <typename Signal>
102ExecutableItem signalAwaiter(const typename QtPrivate::FunctionPointer<Signal>::Object *sender, Signal signal)
103{
104 return BarrierTask([sender, signal](Barrier &barrier) {
105 QObject::connect(sender, signal, &barrier, &Barrier::advance, Qt::SingleShotConnection);
106 });
107}
108
109using BarrierKickerGetter = std::function<ExecutableItem(const SingleBarrier &)>;
110
111class TASKING_EXPORT When final
112{
113public:
114 explicit When(const BarrierKickerGetter &kicker) : m_barrierKicker(kicker) {}
115
116private:
117 TASKING_EXPORT friend Group operator>>(const When &whenItem, const Do &doItem);
118
119 BarrierKickerGetter m_barrierKicker;
120};
121
122} // namespace Tasking
123
124QT_END_NAMESPACE
125
126#endif // TASKING_BARRIER_H
127

source code of qtbase/src/assets/downloader/tasking/barrier.h