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
52class TASKING_EXPORT BarrierTaskAdapter : public TaskAdapter<Barrier>
53{
54public:
55 BarrierTaskAdapter() { connect(sender: task(), signal: &Barrier::done, context: this, slot: &TaskInterface::done); }
56 void start() final { task()->start(); }
57};
58
59using BarrierTask = CustomTask<BarrierTaskAdapter>;
60
61template <int Limit = 1>
62class SharedBarrier
63{
64public:
65 static_assert(Limit > 0, "SharedBarrier's limit should be 1 or more.");
66 SharedBarrier() : m_barrier(new Barrier) {
67 m_barrier->setLimit(Limit);
68 m_barrier->start();
69 }
70 Barrier *barrier() const { return m_barrier.get(); }
71
72private:
73 std::shared_ptr<Barrier> m_barrier;
74};
75
76template <int Limit = 1>
77using MultiBarrier = Storage<SharedBarrier<Limit>>;
78
79// Can't write: "MultiBarrier barrier;". Only "MultiBarrier<> barrier;" would work.
80// Can't have one alias with default type in C++17, getting the following error:
81// alias template deduction only available with C++20.
82using SingleBarrier = MultiBarrier<1>;
83
84template <int Limit>
85ExecutableItem waitForBarrierTask(const MultiBarrier<Limit> &sharedBarrier)
86{
87 return BarrierTask([sharedBarrier](Barrier &barrier) {
88 SharedBarrier<Limit> *activeBarrier = sharedBarrier.activeStorage();
89 if (!activeBarrier) {
90 qWarning(msg: "The barrier referenced from WaitForBarrier element "
91 "is not reachable in the running tree. "
92 "It is possible that no barrier was added to the tree, "
93 "or the storage is not reachable from where it is referenced. "
94 "The WaitForBarrier task finishes with an error. ");
95 return SetupResult::StopWithError;
96 }
97 Barrier *activeSharedBarrier = activeBarrier->barrier();
98 const std::optional<DoneResult> result = activeSharedBarrier->result();
99 if (result.has_value()) {
100 return result.value() == DoneResult::Success ? SetupResult::StopWithSuccess
101 : SetupResult::StopWithError;
102 }
103 QObject::connect(sender: activeSharedBarrier, signal: &Barrier::done, context: &barrier, slot: &Barrier::stopWithResult);
104 return SetupResult::Continue;
105 });
106}
107
108} // namespace Tasking
109
110QT_END_NAMESPACE
111
112#endif // TASKING_BARRIER_H
113

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