DtkCore
DTK Core module
载入中...
搜索中...
未找到
dthreadutils.h
浏览该文件的文档.
1// SPDX-FileCopyrightText: 2020 - 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
5#ifndef DTHREADUTILS_H
6#define DTHREADUTILS_H
7
8#include <dtkcore_global.h>
9#include <QObject>
10#include <QSemaphore>
11#include <QThread>
12#include <QCoreApplication>
13#include <QPointer>
14#include <QDebug>
15
16#if DTK_VERSION >= DTK_VERSION_CHECK(6, 0, 0, 0)
17#include <QFuture>
18#include <QPromise>
19#include <QEvent>
20#endif
21
22DCORE_BEGIN_NAMESPACE
23
24#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
25namespace DThreadUtil {
26typedef std::function<void()> FunctionType;
27
28class LIBDTKCORESHARED_EXPORT FunctionCallProxy : public QObject
29{
30 Q_OBJECT
31public:
32 explicit FunctionCallProxy(QThread *thread);
33
34 static void proxyCall(QSemaphore *s, QThread *thread, QObject *target, FunctionType fun);
35
36Q_SIGNALS:
37 void callInLiveThread(QSemaphore *s, QPointer<QObject> target, FunctionType *func);
38};
39
40template <typename ReturnType>
41class LIBDTKCORESHARED_EXPORT _TMP
42{
43public:
44 inline static ReturnType runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function<ReturnType()> fun)
45 {
46 ReturnType result;
47 FunctionType proxyFun = [&result, &fun] () {
48 result = fun();
49 };
50
51 FunctionCallProxy::proxyCall(s, thread, target, proxyFun);
52 return result;
53 }
54
55 template <typename T>
56 inline static typename std::enable_if<!std::is_base_of<QObject, T>::value, ReturnType>::type
57 runInThread(QSemaphore *s, QThread *thread, T *, std::function<ReturnType()> fun)
58 {
59 return runInThread(s, thread, static_cast<QObject*>(nullptr), fun);
60 }
61};
62template <>
63class LIBDTKCORESHARED_EXPORT _TMP<void>
64{
65public:
66 inline static void runInThread(QSemaphore *s, QThread *thread, QObject *target, std::function<void()> fun)
67 {
68 FunctionCallProxy::proxyCall(s, thread, target, fun);
69 }
70
71 template <typename T>
72 inline static typename std::enable_if<!std::is_base_of<QObject, T>::value, void>::type
73 runInThread(QSemaphore *s, QThread *thread, T *, std::function<void()> fun)
74 {
75 return runInThread(s, thread, static_cast<QObject*>(nullptr), fun);
76 }
77};
78
79template <typename Fun, typename... Args>
80inline auto runInThread(QSemaphore *s, QThread *thread, QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...))
81{
82 return _TMP<decltype(fun(args...))>::runInThread(s, thread, target, std::bind(fun, std::forward<Args>(args)...));
83}
84template <typename Fun, typename... Args>
85inline auto runInThread(QSemaphore *s, QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...))
86{
87 return runInThread(s, thread, nullptr, fun, std::forward<Args>(args)...);
88}
89template <typename Fun, typename... Args>
90inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
91 runInThread(QSemaphore *s, QThread *thread, QObject *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
92{
93 return _TMP<typename QtPrivate::FunctionPointer<Fun>::ReturnType>::runInThread(s, thread, target, std::bind(fun, obj, std::forward<Args>(args)...));
94}
95template <typename Fun, typename... Args>
96inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
97 runInThread(QSemaphore *s, QThread *thread, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
98{
99 return _TMP<typename QtPrivate::FunctionPointer<Fun>::ReturnType>::runInThread(s, thread, obj, std::bind(fun, obj, std::forward<Args>(args)...));
100}
101
102template <typename Fun, typename... Args>
103inline auto runInThread(QThread *thread, QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...))
104{
105 QSemaphore s;
106
107 return runInThread(&s, thread, target, fun, std::forward<Args>(args)...);
108}
109template <typename Fun, typename... Args>
110inline auto runInThread(QThread *thread, Fun fun, Args&&... args) -> decltype(fun(args...))
111{
112 return runInThread(thread, nullptr, fun, std::forward<Args>(args)...);
113}
114template <typename T, typename Fun, typename... Args>
115inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
116 runInThread(QThread *thread, T *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
117{
118 QSemaphore s;
119
120 return runInThread(&s, thread, target, obj, fun, std::forward<Args>(args)...);
121}
122
123template <typename Fun, typename... Args>
124inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
125 runInThread(QThread *thread, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
126{
127 return runInThread(thread, obj, obj, fun, std::forward<Args>(args)...);
128}
129
130template <typename Fun, typename... Args>
131inline auto runInMainThread(QObject *target, Fun fun, Args&&... args) -> decltype(fun(args...))
132{
133 if (!QCoreApplication::instance()) {
134 return fun(std::forward<Args>(args)...);
135 }
136
137 return runInThread(QCoreApplication::instance()->thread(), target, fun, std::forward<Args>(args)...);
138}
139template <typename Fun, typename... Args>
140inline auto runInMainThread(Fun fun, Args&&... args) -> decltype(fun(args...))
141{
142 return runInMainThread(nullptr, fun, std::forward<Args>(args)...);
143}
144
145template <typename T, typename Fun, typename... Args>
146inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
147 runInMainThread(T *target, typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
148{
149 if (!QCoreApplication::instance()) {
150 return (obj->*fun)(std::forward<Args>(args)...);
151 }
152
153 return runInThread(QCoreApplication::instance()->thread(), target, obj, fun, std::forward<Args>(args)...);
154}
155template <typename Fun, typename... Args>
156inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
157 runInMainThread(typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
158{
159 return runInMainThread(obj, obj, fun, std::forward<Args>(args)...);
160}
161}
162#else
163class LIBDTKCORESHARED_EXPORT DThreadUtils final
164{
165 friend class Caller;
166public:
167 explicit DThreadUtils(QThread *thread);
169
170 static DThreadUtils &gui();
171
172 QThread *thread() const noexcept;
173
174 template <typename Func, typename... Args>
175 inline auto run(QObject *context,typename QtPrivate::FunctionPointer<Func>::Object *obj, Func fun, Args &&...args)
176 {
177 return call(context, fun, *obj, std::forward<Args>(args)...);
178 }
179 template <typename Func, typename... Args>
180 inline auto run(typename QtPrivate::FunctionPointer<Func>::Object *obj, Func fun, Args &&...args)
181 {
182 if constexpr (std::is_base_of<QObject, typename QtPrivate::FunctionPointer<Func>::Object>::value) {
183 return call(obj, fun, *obj, std::forward<Args>(args)...);
184 } else {
185 return call(static_cast<QObject *>(nullptr), fun, *obj, std::forward<Args>(args)...);
186 }
187 }
188 template <typename Func, typename... Args>
189 inline QFuture<std::invoke_result_t<std::decay_t<Func>, Args...>> run(QObject *context, Func fun, Args &&...args)
190 {
191 return call(context, fun, std::forward<Args>(args)...);
192 }
193 template <typename Func, typename... Args>
194 inline QFuture<std::invoke_result_t<std::decay_t<Func>, Args...>> run(Func fun, Args &&...args)
195 {
196 return call(static_cast<QObject *>(nullptr), fun, std::forward<Args>(args)...);
197 }
198 template <typename... T>
199 inline decltype(auto) exec(T &&...args)
200 {
201 auto future = run(std::forward<T>(args)...);
202 if (!thread()->isRunning()) {
203 qWarning() << "The target thread is not running, maybe lead to deadlock.";
204 }
205 future.waitForFinished();
206 if constexpr (std::is_same_v<decltype(future), QFuture<void>>) {
207 return;
208 } else {
209 return future.result();
210 }
211 }
212
213private:
214 class AbstractCallEvent : public QEvent
215 {
216 public:
217 AbstractCallEvent(QEvent::Type type)
218 : QEvent(type)
219 {
220 }
221 virtual void call() = 0;
222 };
223
224 template <typename Func, typename... Args>
225 class Q_DECL_HIDDEN CallEvent : public AbstractCallEvent
226 {
227 using FunInfo = QtPrivate::FunctionPointer<std::decay_t<Func>>;
228 using ReturnType = std::invoke_result_t<std::decay_t<Func>, Args...>;
229
230 public:
231 CallEvent(QEvent::Type type, Func &&fun, Args &&...args)
232 : AbstractCallEvent(type)
233 , function(std::forward<Func>(fun))
234 , arguments(std::forward<Args>(args)...)
235 {
236 }
237
238 QEvent *clone() const override { return nullptr; }
239
240 void call() override
241 {
242 if (promise.isCanceled()) {
243 return;
244 }
245
246 if (contextChecker == context) {
247 promise.start();
248#ifndef QT_NO_EXCEPTIONS
249 try {
250#endif
251 if constexpr (std::is_void_v<ReturnType>) {
252 std::apply(function, arguments);
253 } else {
254 promise.addResult(std::apply(function, arguments));
255 }
256#ifndef QT_NO_EXCEPTIONS
257 } catch (...) {
258 promise.setException(std::current_exception());
259 }
260#endif
261 promise.finish();
262 } else {
263 promise.start();
264 promise.setException(std::make_exception_ptr(std::runtime_error("The context object is destroyed.")));
265 promise.finish();
266 }
267 }
268
269 Func function;
270 const std::tuple<Args...> arguments;
271 QPromise<ReturnType> promise;
272
273 QObject *context{nullptr};
274 QPointer<QObject> contextChecker;
275 };
276
277 template <typename Func, typename... Args>
278 inline auto call(QObject *context, Func fun, Args &&...args)
279 {
280 using FuncInfo = QtPrivate::FunctionPointer<std::decay_t<Func>>;
281 using ReturnType = std::invoke_result_t<std::decay_t<Func>, Args...> ;
282
283 if constexpr (FuncInfo::IsPointerToMemberFunction) {
284 static_assert(std::is_same_v<std::decay_t<typename QtPrivate::List<Args...>::Car>, typename FuncInfo::Object>,
285 "The obj and function are not compatible.");
286 static_assert(
287 QtPrivate::CheckCompatibleArguments<typename QtPrivate::List<Args...>::Cdr, typename FuncInfo::Arguments>::value,
288 "The args and function are not compatible.");
289 } else if constexpr (FuncInfo::ArgumentCount != -1) {
290 static_assert(QtPrivate::CheckCompatibleArguments<QtPrivate::List<Args...>, typename FuncInfo::Arguments>::value,
291 "The args and function are not compatible.");
292 } else { // for lambda and impl operator()
293 static_assert(std::is_invocable_r_v<ReturnType, Func, Args...>,
294 "The callable object can't invoke with supplied args");
295 }
296
297 QPromise<ReturnType> promise;
298 auto future = promise.future();
299
300 if (Q_UNLIKELY(QThread::currentThread() == m_thread)) {
301 promise.start();
302 if constexpr (std::is_void_v<ReturnType>) {
303 std::invoke(fun, std::forward<Args>(args)...);
304 } else {
305 promise.addResult(std::invoke(fun, std::forward<Args>(args)...));
306 }
307 promise.finish();
308 } else {
309 auto event = new CallEvent<Func, Args...>(eventType, std::move(fun), std::forward<Args>(args)...);
310 event->promise = std::move(promise);
311 event->context = context;
312 event->contextChecker = context;
313
314 QCoreApplication::postEvent(ensureThreadContextObject(), event);
315 }
316
317 return future;
318 }
319
320 QObject *ensureThreadContextObject();
321
322 static inline QEvent::Type eventType;
323 QThread *m_thread;
324 QAtomicPointer<QObject> threadContext;
325};
326#endif // version macro end
327DCORE_END_NAMESPACE
328#endif // protect macro end
Definition dthreadutils.h:164
QThread * thread() const noexcept
获取DThreadUtils对应的线程
static DThreadUtils & gui()
获取以GUI线程初始化的静态对象