1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2020 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtCore module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #ifndef QFUTURE_H |
41 | #error Do not include qfuture_impl.h directly |
42 | #endif |
43 | |
44 | #if 0 |
45 | #pragma qt_sync_skip_header_check |
46 | #pragma qt_sync_stop_processing |
47 | #endif |
48 | |
49 | #include <QtCore/qglobal.h> |
50 | #include <QtCore/qfutureinterface.h> |
51 | #include <QtCore/qthreadpool.h> |
52 | |
53 | QT_BEGIN_NAMESPACE |
54 | |
55 | // |
56 | // forward declarations |
57 | // |
58 | template<class T> |
59 | class QFuture; |
60 | template<class T> |
61 | class QFutureInterface; |
62 | |
63 | namespace QtFuture { |
64 | enum class Launch { Sync, Async, Inherit }; |
65 | } |
66 | |
67 | namespace QtPrivate { |
68 | |
69 | template<class T, class U> |
70 | using EnableIfSameOrConvertible = std::enable_if_t<std::is_same_v<T, U> |
71 | || std::is_convertible_v<T, U>>; |
72 | |
73 | template<class T> |
74 | using EnableForVoid = std::enable_if_t<std::is_same_v<T, void>>; |
75 | |
76 | template<class T> |
77 | using EnableForNonVoid = std::enable_if_t<!std::is_same_v<T, void>>; |
78 | |
79 | template<typename F, typename Arg, typename Enable = void> |
80 | struct ResultTypeHelper |
81 | { |
82 | }; |
83 | |
84 | // The callable takes an argument of type Arg |
85 | template<typename F, typename Arg> |
86 | struct ResultTypeHelper< |
87 | F, Arg, typename std::enable_if_t<!std::is_invocable_v<std::decay_t<F>, QFuture<Arg>>>> |
88 | { |
89 | using ResultType = std::invoke_result_t<std::decay_t<F>, std::decay_t<Arg>>; |
90 | }; |
91 | |
92 | // The callable takes an argument of type QFuture<Arg> |
93 | template<class F, class Arg> |
94 | struct ResultTypeHelper< |
95 | F, Arg, typename std::enable_if_t<std::is_invocable_v<std::decay_t<F>, QFuture<Arg>>>> |
96 | { |
97 | using ResultType = std::invoke_result_t<std::decay_t<F>, QFuture<Arg>>; |
98 | }; |
99 | |
100 | // The callable takes an argument of type QFuture<void> |
101 | template<class F> |
102 | struct ResultTypeHelper< |
103 | F, void, typename std::enable_if_t<std::is_invocable_v<std::decay_t<F>, QFuture<void>>>> |
104 | { |
105 | using ResultType = std::invoke_result_t<std::decay_t<F>, QFuture<void>>; |
106 | }; |
107 | |
108 | // The callable doesn't take argument |
109 | template<class F> |
110 | struct ResultTypeHelper< |
111 | F, void, typename std::enable_if_t<!std::is_invocable_v<std::decay_t<F>, QFuture<void>>>> |
112 | { |
113 | using ResultType = std::invoke_result_t<std::decay_t<F>>; |
114 | }; |
115 | |
116 | // Helpers to resolve argument types of callables. |
117 | template<typename...> |
118 | struct ArgsType; |
119 | |
120 | template<typename Arg, typename... Args> |
121 | struct ArgsType<Arg, Args...> |
122 | { |
123 | using First = Arg; |
124 | static const bool = (sizeof...(Args) > 0); |
125 | using AllArgs = |
126 | std::conditional_t<HasExtraArgs, std::tuple<std::decay_t<Arg>, std::decay_t<Args>...>, |
127 | std::decay_t<Arg>>; |
128 | |
129 | template<class Class, class Callable> |
130 | static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class, Arg, Args...>; |
131 | }; |
132 | |
133 | template<> |
134 | struct ArgsType<> |
135 | { |
136 | using First = void; |
137 | static const bool = false; |
138 | using AllArgs = void; |
139 | |
140 | template<class Class, class Callable> |
141 | static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class>; |
142 | }; |
143 | |
144 | template<typename F> |
145 | struct ArgResolver : ArgResolver<decltype(&std::decay_t<F>::operator())> |
146 | { |
147 | }; |
148 | |
149 | template<typename R, typename... Args> |
150 | struct ArgResolver<R(Args...)> : public ArgsType<Args...> |
151 | { |
152 | }; |
153 | |
154 | template<typename R, typename... Args> |
155 | struct ArgResolver<R (*)(Args...)> : public ArgsType<Args...> |
156 | { |
157 | }; |
158 | |
159 | template<typename R, typename... Args> |
160 | struct ArgResolver<R (&)(Args...)> : public ArgsType<Args...> |
161 | { |
162 | }; |
163 | |
164 | template<typename Class, typename R, typename... Args> |
165 | struct ArgResolver<R (Class::*)(Args...)> : public ArgsType<Args...> |
166 | { |
167 | }; |
168 | |
169 | template<typename Class, typename R, typename... Args> |
170 | struct ArgResolver<R (Class::*)(Args...) noexcept> : public ArgsType<Args...> |
171 | { |
172 | }; |
173 | |
174 | template<typename Class, typename R, typename... Args> |
175 | struct ArgResolver<R (Class::*)(Args...) const> : public ArgsType<Args...> |
176 | { |
177 | }; |
178 | |
179 | template<typename Class, typename R, typename... Args> |
180 | struct ArgResolver<R (Class::*)(Args...) const noexcept> : public ArgsType<Args...> |
181 | { |
182 | }; |
183 | |
184 | template<class Class, class Callable> |
185 | using EnableIfInvocable = std::enable_if_t< |
186 | QtPrivate::ArgResolver<Callable>::template CanInvokeWithArgs<Class, Callable>>; |
187 | |
188 | template<class> |
189 | struct isTuple : std::false_type |
190 | { |
191 | }; |
192 | template<class... T> |
193 | struct isTuple<std::tuple<T...>> : std::true_type |
194 | { |
195 | }; |
196 | template<class T> |
197 | inline constexpr bool isTupleV = isTuple<T>::value; |
198 | |
199 | template<typename Function, typename ResultType, typename ParentResultType> |
200 | class Continuation |
201 | { |
202 | public: |
203 | Continuation(Function &&func, const QFuture<ParentResultType> &f, |
204 | const QFutureInterface<ResultType> &p) |
205 | : promise(p), parentFuture(f), function(std::forward<Function>(func)) |
206 | { |
207 | } |
208 | virtual ~Continuation() = default; |
209 | |
210 | bool execute(); |
211 | |
212 | static void create(Function &&func, QFuture<ParentResultType> *f, |
213 | QFutureInterface<ResultType> &p, QtFuture::Launch policy); |
214 | |
215 | static void create(Function &&func, QFuture<ParentResultType> *f, |
216 | QFutureInterface<ResultType> &p, QThreadPool *pool); |
217 | |
218 | private: |
219 | void fulfillPromiseWithResult(); |
220 | void fulfillVoidPromise(); |
221 | void fulfillPromiseWithVoidResult(); |
222 | |
223 | template<class... Args> |
224 | void fulfillPromise(Args &&... args); |
225 | |
226 | protected: |
227 | virtual void runImpl() = 0; |
228 | |
229 | void runFunction(); |
230 | |
231 | protected: |
232 | QFutureInterface<ResultType> promise; |
233 | QFuture<ParentResultType> parentFuture; |
234 | Function function; |
235 | }; |
236 | |
237 | template<typename Function, typename ResultType, typename ParentResultType> |
238 | class SyncContinuation final : public Continuation<Function, ResultType, ParentResultType> |
239 | { |
240 | public: |
241 | SyncContinuation(Function &&func, const QFuture<ParentResultType> &f, |
242 | const QFutureInterface<ResultType> &p) |
243 | : Continuation<Function, ResultType, ParentResultType>(std::forward<Function>(func), f, p) |
244 | { |
245 | } |
246 | |
247 | ~SyncContinuation() override = default; |
248 | |
249 | private: |
250 | void runImpl() override { this->runFunction(); } |
251 | }; |
252 | |
253 | template<typename Function, typename ResultType, typename ParentResultType> |
254 | class AsyncContinuation final : public QRunnable, |
255 | public Continuation<Function, ResultType, ParentResultType> |
256 | { |
257 | public: |
258 | AsyncContinuation(Function &&func, const QFuture<ParentResultType> &f, |
259 | const QFutureInterface<ResultType> &p, QThreadPool *pool = nullptr) |
260 | : Continuation<Function, ResultType, ParentResultType>(std::forward<Function>(func), f, p), |
261 | threadPool(pool) |
262 | { |
263 | this->promise.setRunnable(this); |
264 | } |
265 | |
266 | ~AsyncContinuation() override = default; |
267 | |
268 | private: |
269 | void runImpl() override // from Continuation |
270 | { |
271 | QThreadPool *pool = threadPool ? threadPool : QThreadPool::globalInstance(); |
272 | pool->start(this); |
273 | } |
274 | |
275 | void run() override // from QRunnable |
276 | { |
277 | this->runFunction(); |
278 | } |
279 | |
280 | private: |
281 | QThreadPool *threadPool; |
282 | }; |
283 | |
284 | #ifndef QT_NO_EXCEPTIONS |
285 | |
286 | template<class Function, class ResultType> |
287 | class FailureHandler |
288 | { |
289 | public: |
290 | static void create(Function &&function, QFuture<ResultType> *future, |
291 | const QFutureInterface<ResultType> &promise); |
292 | |
293 | FailureHandler(Function &&func, const QFuture<ResultType> &f, |
294 | const QFutureInterface<ResultType> &p) |
295 | : promise(p), parentFuture(f), handler(std::forward<Function>(func)) |
296 | { |
297 | } |
298 | |
299 | public: |
300 | void run(); |
301 | |
302 | private: |
303 | template<class ArgType> |
304 | void handleException(); |
305 | void handleAllExceptions(); |
306 | |
307 | private: |
308 | QFutureInterface<ResultType> promise; |
309 | QFuture<ResultType> parentFuture; |
310 | Function handler; |
311 | }; |
312 | |
313 | #endif |
314 | |
315 | template<typename Function, typename ResultType, typename ParentResultType> |
316 | void Continuation<Function, ResultType, ParentResultType>::runFunction() |
317 | { |
318 | promise.reportStarted(); |
319 | |
320 | Q_ASSERT(parentFuture.isFinished()); |
321 | |
322 | #ifndef QT_NO_EXCEPTIONS |
323 | try { |
324 | #endif |
325 | if constexpr (!std::is_void_v<ResultType>) { |
326 | if constexpr (std::is_void_v<ParentResultType>) { |
327 | fulfillPromiseWithVoidResult(); |
328 | } else if constexpr (std::is_invocable_v<Function, ParentResultType>) { |
329 | fulfillPromiseWithResult(); |
330 | } else { |
331 | // This assert normally should never fail, this is to make sure |
332 | // that nothing unexpected happend. |
333 | static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>, |
334 | "The continuation is not invocable with the provided arguments" ); |
335 | fulfillPromise(parentFuture); |
336 | } |
337 | } else { |
338 | if constexpr (std::is_void_v<ParentResultType>) { |
339 | if constexpr (std::is_invocable_v<Function, QFuture<void>>) |
340 | function(parentFuture); |
341 | else |
342 | function(); |
343 | } else if constexpr (std::is_invocable_v<Function, ParentResultType>) { |
344 | fulfillVoidPromise(); |
345 | } else { |
346 | // This assert normally should never fail, this is to make sure |
347 | // that nothing unexpected happend. |
348 | static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>, |
349 | "The continuation is not invocable with the provided arguments" ); |
350 | function(parentFuture); |
351 | } |
352 | } |
353 | #ifndef QT_NO_EXCEPTIONS |
354 | } catch (...) { |
355 | promise.reportException(std::current_exception()); |
356 | } |
357 | #endif |
358 | promise.reportFinished(); |
359 | } |
360 | |
361 | template<typename Function, typename ResultType, typename ParentResultType> |
362 | bool Continuation<Function, ResultType, ParentResultType>::execute() |
363 | { |
364 | Q_ASSERT(parentFuture.isFinished()); |
365 | |
366 | if (parentFuture.isCanceled()) { |
367 | #ifndef QT_NO_EXCEPTIONS |
368 | if (parentFuture.d.exceptionStore().hasException()) { |
369 | // If the continuation doesn't take a QFuture argument, propagate the exception |
370 | // to the caller, by reporting it. If the continuation takes a QFuture argument, |
371 | // the user may want to catch the exception inside the continuation, to not |
372 | // interrupt the continuation chain, so don't report anything yet. |
373 | if constexpr (!std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) { |
374 | promise.reportStarted(); |
375 | promise.reportException(parentFuture.d.exceptionStore().exception()); |
376 | promise.reportFinished(); |
377 | return false; |
378 | } |
379 | } else |
380 | #endif |
381 | { |
382 | promise.reportStarted(); |
383 | promise.reportCanceled(); |
384 | promise.reportFinished(); |
385 | return false; |
386 | } |
387 | } |
388 | |
389 | runImpl(); |
390 | return true; |
391 | } |
392 | |
393 | template<typename Function, typename ResultType, typename ParentResultType> |
394 | void Continuation<Function, ResultType, ParentResultType>::create(Function &&func, |
395 | QFuture<ParentResultType> *f, |
396 | QFutureInterface<ResultType> &p, |
397 | QtFuture::Launch policy) |
398 | { |
399 | Q_ASSERT(f); |
400 | |
401 | QThreadPool *pool = nullptr; |
402 | |
403 | bool launchAsync = (policy == QtFuture::Launch::Async); |
404 | if (policy == QtFuture::Launch::Inherit) { |
405 | launchAsync = f->d.launchAsync(); |
406 | |
407 | // If the parent future was using a custom thread pool, inherit it as well. |
408 | if (launchAsync && f->d.threadPool()) { |
409 | pool = f->d.threadPool(); |
410 | p.setThreadPool(pool); |
411 | } |
412 | } |
413 | |
414 | Continuation<Function, ResultType, ParentResultType> *continuationJob = nullptr; |
415 | if (launchAsync) { |
416 | continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>( |
417 | std::forward<Function>(func), *f, p, pool); |
418 | } else { |
419 | continuationJob = new SyncContinuation<Function, ResultType, ParentResultType>( |
420 | std::forward<Function>(func), *f, p); |
421 | } |
422 | |
423 | p.setLaunchAsync(launchAsync); |
424 | |
425 | auto continuation = [continuationJob, launchAsync]() mutable { |
426 | bool isLaunched = continuationJob->execute(); |
427 | // If continuation is successfully launched, AsyncContinuation will be deleted |
428 | // by the QThreadPool which has started it. Synchronous continuation will be |
429 | // executed immediately, so it's safe to always delete it here. |
430 | if (!(launchAsync && isLaunched)) { |
431 | delete continuationJob; |
432 | continuationJob = nullptr; |
433 | } |
434 | }; |
435 | |
436 | f->d.setContinuation(std::move(continuation)); |
437 | } |
438 | |
439 | template<typename Function, typename ResultType, typename ParentResultType> |
440 | void Continuation<Function, ResultType, ParentResultType>::create(Function &&func, |
441 | QFuture<ParentResultType> *f, |
442 | QFutureInterface<ResultType> &p, |
443 | QThreadPool *pool) |
444 | { |
445 | Q_ASSERT(f); |
446 | |
447 | auto continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>( |
448 | std::forward<Function>(func), *f, p, pool); |
449 | p.setLaunchAsync(true); |
450 | p.setThreadPool(pool); |
451 | |
452 | auto continuation = [continuationJob]() mutable { |
453 | bool isLaunched = continuationJob->execute(); |
454 | // If continuation is successfully launched, AsyncContinuation will be deleted |
455 | // by the QThreadPool which has started it. |
456 | if (!isLaunched) { |
457 | delete continuationJob; |
458 | continuationJob = nullptr; |
459 | } |
460 | }; |
461 | |
462 | f->d.setContinuation(continuation); |
463 | } |
464 | |
465 | template<typename Function, typename ResultType, typename ParentResultType> |
466 | void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithResult() |
467 | { |
468 | if constexpr (std::is_copy_constructible_v<ParentResultType>) |
469 | fulfillPromise(parentFuture.result()); |
470 | else |
471 | fulfillPromise(parentFuture.takeResult()); |
472 | } |
473 | |
474 | template<typename Function, typename ResultType, typename ParentResultType> |
475 | void Continuation<Function, ResultType, ParentResultType>::fulfillVoidPromise() |
476 | { |
477 | if constexpr (std::is_copy_constructible_v<ParentResultType>) |
478 | function(parentFuture.result()); |
479 | else |
480 | function(parentFuture.takeResult()); |
481 | } |
482 | |
483 | template<typename Function, typename ResultType, typename ParentResultType> |
484 | void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithVoidResult() |
485 | { |
486 | if constexpr (std::is_invocable_v<Function, QFuture<void>>) |
487 | fulfillPromise(parentFuture); |
488 | else |
489 | fulfillPromise(); |
490 | } |
491 | |
492 | template<typename Function, typename ResultType, typename ParentResultType> |
493 | template<class... Args> |
494 | void Continuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &&... args) |
495 | { |
496 | if constexpr (std::is_copy_constructible_v<ResultType>) |
497 | promise.reportResult(std::invoke(function, std::forward<Args>(args)...)); |
498 | else |
499 | promise.reportAndMoveResult(std::invoke(function, std::forward<Args>(args)...)); |
500 | } |
501 | |
502 | template<class T> |
503 | void fulfillPromise(QFutureInterface<T> &promise, QFuture<T> &future) |
504 | { |
505 | if constexpr (!std::is_void_v<T>) { |
506 | if constexpr (std::is_copy_constructible_v<T>) |
507 | promise.reportResult(future.result()); |
508 | else |
509 | promise.reportAndMoveResult(future.takeResult()); |
510 | } |
511 | } |
512 | |
513 | template<class T, class Function> |
514 | void fulfillPromise(QFutureInterface<T> &promise, Function &&handler) |
515 | { |
516 | if constexpr (std::is_void_v<T>) |
517 | handler(); |
518 | else if constexpr (std::is_copy_constructible_v<T>) |
519 | promise.reportResult(handler()); |
520 | else |
521 | promise.reportAndMoveResult(handler()); |
522 | } |
523 | |
524 | #ifndef QT_NO_EXCEPTIONS |
525 | |
526 | template<class Function, class ResultType> |
527 | void FailureHandler<Function, ResultType>::create(Function &&function, QFuture<ResultType> *future, |
528 | const QFutureInterface<ResultType> &promise) |
529 | { |
530 | Q_ASSERT(future); |
531 | |
532 | FailureHandler<Function, ResultType> *failureHandler = new FailureHandler<Function, ResultType>( |
533 | std::forward<Function>(function), *future, promise); |
534 | |
535 | auto failureContinuation = [failureHandler]() mutable { |
536 | failureHandler->run(); |
537 | delete failureHandler; |
538 | }; |
539 | |
540 | future->d.setContinuation(std::move(failureContinuation)); |
541 | } |
542 | |
543 | template<class Function, class ResultType> |
544 | void FailureHandler<Function, ResultType>::run() |
545 | { |
546 | Q_ASSERT(parentFuture.isFinished()); |
547 | |
548 | promise.reportStarted(); |
549 | |
550 | if (parentFuture.d.exceptionStore().hasException()) { |
551 | using ArgType = typename QtPrivate::ArgResolver<Function>::First; |
552 | if constexpr (std::is_void_v<ArgType>) { |
553 | handleAllExceptions(); |
554 | } else { |
555 | handleException<ArgType>(); |
556 | } |
557 | } else { |
558 | QtPrivate::fulfillPromise(promise, parentFuture); |
559 | } |
560 | promise.reportFinished(); |
561 | } |
562 | |
563 | template<class Function, class ResultType> |
564 | template<class ArgType> |
565 | void FailureHandler<Function, ResultType>::handleException() |
566 | { |
567 | try { |
568 | parentFuture.d.exceptionStore().throwPossibleException(); |
569 | } catch (const ArgType &e) { |
570 | try { |
571 | // Handle exceptions matching with the handler's argument type |
572 | if constexpr (std::is_void_v<ResultType>) { |
573 | handler(e); |
574 | } else { |
575 | if constexpr (std::is_copy_constructible_v<ResultType>) |
576 | promise.reportResult(handler(e)); |
577 | else |
578 | promise.reportAndMoveResult(handler(e)); |
579 | } |
580 | } catch (...) { |
581 | promise.reportException(std::current_exception()); |
582 | } |
583 | } catch (...) { |
584 | // Exception doesn't match with handler's argument type, propagate |
585 | // the exception to be handled later. |
586 | promise.reportException(std::current_exception()); |
587 | } |
588 | } |
589 | |
590 | template<class Function, class ResultType> |
591 | void FailureHandler<Function, ResultType>::handleAllExceptions() |
592 | { |
593 | try { |
594 | parentFuture.d.exceptionStore().throwPossibleException(); |
595 | } catch (...) { |
596 | try { |
597 | QtPrivate::fulfillPromise(promise, std::forward<Function>(handler)); |
598 | } catch (...) { |
599 | promise.reportException(std::current_exception()); |
600 | } |
601 | } |
602 | } |
603 | |
604 | #endif // QT_NO_EXCEPTIONS |
605 | |
606 | template<class Function, class ResultType> |
607 | class CanceledHandler |
608 | { |
609 | public: |
610 | static QFuture<ResultType> create(Function &&handler, QFuture<ResultType> *future, |
611 | QFutureInterface<ResultType> promise) |
612 | { |
613 | Q_ASSERT(future); |
614 | |
615 | auto canceledContinuation = [parentFuture = *future, promise, |
616 | handler = std::move(handler)]() mutable { |
617 | promise.reportStarted(); |
618 | |
619 | if (parentFuture.isCanceled()) { |
620 | #ifndef QT_NO_EXCEPTIONS |
621 | if (parentFuture.d.exceptionStore().hasException()) { |
622 | // Propagate the exception to the result future |
623 | promise.reportException(parentFuture.d.exceptionStore().exception()); |
624 | } else { |
625 | try { |
626 | #endif |
627 | QtPrivate::fulfillPromise(promise, std::forward<Function>(handler)); |
628 | #ifndef QT_NO_EXCEPTIONS |
629 | } catch (...) { |
630 | promise.reportException(std::current_exception()); |
631 | } |
632 | } |
633 | #endif |
634 | } else { |
635 | QtPrivate::fulfillPromise(promise, parentFuture); |
636 | } |
637 | |
638 | promise.reportFinished(); |
639 | }; |
640 | future->d.setContinuation(std::move(canceledContinuation)); |
641 | return promise.future(); |
642 | } |
643 | }; |
644 | |
645 | } // namespace QtPrivate |
646 | |
647 | namespace QtFuture { |
648 | |
649 | template<class Signal> |
650 | using ArgsType = typename QtPrivate::ArgResolver<Signal>::AllArgs; |
651 | |
652 | template<class Sender, class Signal, typename = QtPrivate::EnableIfInvocable<Sender, Signal>> |
653 | static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal) |
654 | { |
655 | using ArgsType = ArgsType<Signal>; |
656 | QFutureInterface<ArgsType> promise; |
657 | promise.reportStarted(); |
658 | |
659 | using Connections = std::pair<QMetaObject::Connection, QMetaObject::Connection>; |
660 | auto connections = std::make_shared<Connections>(); |
661 | |
662 | if constexpr (std::is_void_v<ArgsType>) { |
663 | connections->first = |
664 | QObject::connect(sender, signal, sender, [promise, connections]() mutable { |
665 | promise.reportFinished(); |
666 | QObject::disconnect(connections->first); |
667 | QObject::disconnect(connections->second); |
668 | }); |
669 | } else if constexpr (QtPrivate::isTupleV<ArgsType>) { |
670 | connections->first = QObject::connect(sender, signal, sender, |
671 | [promise, connections](auto... values) mutable { |
672 | promise.reportResult(std::make_tuple(values...)); |
673 | promise.reportFinished(); |
674 | QObject::disconnect(connections->first); |
675 | QObject::disconnect(connections->second); |
676 | }); |
677 | } else { |
678 | connections->first = QObject::connect(sender, signal, sender, |
679 | [promise, connections](ArgsType value) mutable { |
680 | promise.reportResult(value); |
681 | promise.reportFinished(); |
682 | QObject::disconnect(connections->first); |
683 | QObject::disconnect(connections->second); |
684 | }); |
685 | } |
686 | |
687 | connections->second = |
688 | QObject::connect(sender, &QObject::destroyed, sender, [promise, connections]() mutable { |
689 | promise.reportCanceled(); |
690 | promise.reportFinished(); |
691 | QObject::disconnect(connections->first); |
692 | QObject::disconnect(connections->second); |
693 | }); |
694 | |
695 | return promise.future(); |
696 | } |
697 | |
698 | } // namespace QtFuture |
699 | |
700 | QT_END_NAMESPACE |
701 | |