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_CONCURRENTCALL_H
6#define TASKING_CONCURRENTCALL_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 "tasktree.h"
20
21#include <QtConcurrent/QtConcurrent>
22
23QT_BEGIN_NAMESPACE
24
25namespace Tasking {
26
27// This class introduces the dependency to Qt::Concurrent, otherwise Tasking namespace
28// is independent on Qt::Concurrent.
29// Possibly, it could be placed inside Qt::Concurrent library, as a wrapper around
30// QtConcurrent::run() call.
31
32template <typename ResultType>
33class ConcurrentCall
34{
35 Q_DISABLE_COPY_MOVE(ConcurrentCall)
36
37public:
38 ConcurrentCall() = default;
39 template <typename Function, typename ...Args>
40 void setConcurrentCallData(Function &&function, Args &&...args)
41 {
42 return wrapConcurrent(std::forward<Function>(function), std::forward<Args>(args)...);
43 }
44 void setThreadPool(QThreadPool *pool) { m_threadPool = pool; }
45 ResultType result() const
46 {
47 return m_future.resultCount() ? m_future.result() : ResultType();
48 }
49 QList<ResultType> results() const
50 {
51 return m_future.results();
52 }
53 QFuture<ResultType> future() const { return m_future; }
54
55private:
56 template <typename Function, typename ...Args>
57 void wrapConcurrent(Function &&function, Args &&...args)
58 {
59 m_startHandler = [this, function = std::forward<Function>(function), args...] {
60 QThreadPool *threadPool = m_threadPool ? m_threadPool : QThreadPool::globalInstance();
61 return QtConcurrent::run(threadPool, function, args...);
62 };
63 }
64
65 template <typename Function, typename ...Args>
66 void wrapConcurrent(std::reference_wrapper<const Function> &&wrapper, Args &&...args)
67 {
68 m_startHandler = [this, wrapper = std::forward<std::reference_wrapper<const Function>>(wrapper), args...] {
69 QThreadPool *threadPool = m_threadPool ? m_threadPool : QThreadPool::globalInstance();
70 return QtConcurrent::run(threadPool, std::forward<const Function>(wrapper.get()),
71 args...);
72 };
73 }
74
75 template <typename T>
76 friend class ConcurrentCallTaskAdapter;
77
78 std::function<QFuture<ResultType>()> m_startHandler;
79 QThreadPool *m_threadPool = nullptr;
80 QFuture<ResultType> m_future;
81};
82
83template <typename ResultType>
84class ConcurrentCallTaskAdapter : public TaskAdapter<ConcurrentCall<ResultType>>
85{
86public:
87 ~ConcurrentCallTaskAdapter() {
88 if (m_watcher) {
89 m_watcher->cancel();
90 m_watcher->waitForFinished();
91 }
92 }
93
94 void start() final {
95 if (!this->task()->m_startHandler) {
96 emit this->done(DoneResult::Error); // TODO: Add runtime assert
97 return;
98 }
99 m_watcher.reset(new QFutureWatcher<ResultType>);
100 this->connect(m_watcher.get(), &QFutureWatcherBase::finished, this, [this] {
101 emit this->done(toDoneResult(!m_watcher->isCanceled()));
102 m_watcher.release()->deleteLater();
103 });
104 this->task()->m_future = this->task()->m_startHandler();
105 m_watcher->setFuture(this->task()->m_future);
106 }
107
108private:
109 std::unique_ptr<QFutureWatcher<ResultType>> m_watcher;
110};
111
112template <typename T>
113using ConcurrentCallTask = CustomTask<ConcurrentCallTaskAdapter<T>>;
114
115} // namespace Tasking
116
117QT_END_NAMESPACE
118
119#endif // TASKING_CONCURRENTCALL_H
120

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