1 | // Copyright (C) 2020 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QFUTURE_H |
5 | #error Do not include qfuture_impl.h directly |
6 | #endif |
7 | |
8 | #if 0 |
9 | #pragma qt_sync_skip_header_check |
10 | #pragma qt_sync_stop_processing |
11 | #endif |
12 | |
13 | #include <QtCore/qglobal.h> |
14 | #include <QtCore/qfutureinterface.h> |
15 | #include <QtCore/qthreadpool.h> |
16 | #include <QtCore/qexception.h> |
17 | #include <QtCore/qpromise.h> |
18 | |
19 | #include <memory> |
20 | |
21 | QT_BEGIN_NAMESPACE |
22 | |
23 | // |
24 | // forward declarations |
25 | // |
26 | template<class T> |
27 | class QFuture; |
28 | template<class T> |
29 | class QFutureInterface; |
30 | template<class T> |
31 | class QPromise; |
32 | |
33 | namespace QtFuture { |
34 | |
35 | enum class Launch { Sync, Async, Inherit }; |
36 | |
37 | template<class T> |
38 | struct WhenAnyResult |
39 | { |
40 | qsizetype index = -1; |
41 | QFuture<T> future; |
42 | }; |
43 | |
44 | // Deduction guide |
45 | template<class T> |
46 | WhenAnyResult(qsizetype, const QFuture<T> &) -> WhenAnyResult<T>; |
47 | } |
48 | |
49 | namespace QtPrivate { |
50 | |
51 | template<class T> |
52 | using EnableForVoid = std::enable_if_t<std::is_same_v<T, void>>; |
53 | |
54 | template<class T> |
55 | using EnableForNonVoid = std::enable_if_t<!std::is_same_v<T, void>>; |
56 | |
57 | template<typename F, typename Arg, typename Enable = void> |
58 | struct ResultTypeHelper |
59 | { |
60 | }; |
61 | |
62 | // The callable takes an argument of type Arg |
63 | template<typename F, typename Arg> |
64 | struct ResultTypeHelper< |
65 | F, Arg, typename std::enable_if_t<!std::is_invocable_v<std::decay_t<F>, QFuture<Arg>>>> |
66 | { |
67 | using ResultType = std::invoke_result_t<std::decay_t<F>, std::decay_t<Arg>>; |
68 | }; |
69 | |
70 | // The callable takes an argument of type QFuture<Arg> |
71 | template<class F, class Arg> |
72 | struct ResultTypeHelper< |
73 | F, Arg, typename std::enable_if_t<std::is_invocable_v<std::decay_t<F>, QFuture<Arg>>>> |
74 | { |
75 | using ResultType = std::invoke_result_t<std::decay_t<F>, QFuture<Arg>>; |
76 | }; |
77 | |
78 | // The callable takes an argument of type QFuture<void> |
79 | template<class F> |
80 | struct ResultTypeHelper< |
81 | F, void, typename std::enable_if_t<std::is_invocable_v<std::decay_t<F>, QFuture<void>>>> |
82 | { |
83 | using ResultType = std::invoke_result_t<std::decay_t<F>, QFuture<void>>; |
84 | }; |
85 | |
86 | // The callable doesn't take argument |
87 | template<class F> |
88 | struct ResultTypeHelper< |
89 | F, void, typename std::enable_if_t<!std::is_invocable_v<std::decay_t<F>, QFuture<void>>>> |
90 | { |
91 | using ResultType = std::invoke_result_t<std::decay_t<F>>; |
92 | }; |
93 | |
94 | // Helpers to remove QPrivateSignal argument from the list of arguments |
95 | |
96 | template<class T, class Enable = void> |
97 | inline constexpr bool IsPrivateSignalArg = false; |
98 | |
99 | template<class T> |
100 | inline constexpr bool IsPrivateSignalArg<T, typename std::enable_if_t< |
101 | // finds injected-class-name, the 'class' avoids falling into the rules of [class.qual]/2: |
102 | std::is_class_v<class T::QPrivateSignal> |
103 | >> = true; |
104 | |
105 | template<class Tuple, std::size_t... I> |
106 | auto cutTuple(Tuple &&t, std::index_sequence<I...>) |
107 | { |
108 | return std::make_tuple(std::get<I>(t)...); |
109 | } |
110 | |
111 | template<class Arg, class... Args> |
112 | auto createTuple(Arg &&arg, Args &&... args) |
113 | { |
114 | using TupleType = std::tuple<std::decay_t<Arg>, std::decay_t<Args>...>; |
115 | constexpr auto Size = sizeof...(Args); // One less than the size of all arguments |
116 | if constexpr (QtPrivate::IsPrivateSignalArg<std::tuple_element_t<Size, TupleType>>) { |
117 | if constexpr (Size == 1) { |
118 | return std::forward<Arg>(arg); |
119 | } else { |
120 | return cutTuple(std::make_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...), |
121 | std::make_index_sequence<Size>()); |
122 | } |
123 | } else { |
124 | return std::make_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...); |
125 | } |
126 | } |
127 | |
128 | // Helpers to resolve argument types of callables. |
129 | |
130 | template<class Arg, class... Args> |
131 | using FilterLastPrivateSignalArg = |
132 | std::conditional_t<(sizeof...(Args) > 0), |
133 | std::invoke_result_t<decltype(createTuple<Arg, Args...>), Arg, Args...>, |
134 | std::conditional_t<IsPrivateSignalArg<Arg>, void, Arg>>; |
135 | |
136 | template<typename...> |
137 | struct ArgsType; |
138 | |
139 | template<typename Arg, typename... Args> |
140 | struct ArgsType<Arg, Args...> |
141 | { |
142 | using First = Arg; |
143 | using PromiseType = void; |
144 | using IsPromise = std::false_type; |
145 | static const bool HasExtraArgs = (sizeof...(Args) > 0); |
146 | using AllArgs = FilterLastPrivateSignalArg<std::decay_t<Arg>, std::decay_t<Args>...>; |
147 | |
148 | template<class Class, class Callable> |
149 | static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class, Arg, Args...>; |
150 | }; |
151 | |
152 | template<typename Arg, typename... Args> |
153 | struct ArgsType<QPromise<Arg> &, Args...> |
154 | { |
155 | using First = QPromise<Arg> &; |
156 | using PromiseType = Arg; |
157 | using IsPromise = std::true_type; |
158 | static const bool HasExtraArgs = (sizeof...(Args) > 0); |
159 | using AllArgs = FilterLastPrivateSignalArg<QPromise<Arg>, std::decay_t<Args>...>; |
160 | |
161 | template<class Class, class Callable> |
162 | static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class, QPromise<Arg> &, Args...>; |
163 | }; |
164 | |
165 | template<> |
166 | struct ArgsType<> |
167 | { |
168 | using First = void; |
169 | using PromiseType = void; |
170 | using IsPromise = std::false_type; |
171 | static const bool HasExtraArgs = false; |
172 | using AllArgs = void; |
173 | |
174 | template<class Class, class Callable> |
175 | static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class>; |
176 | }; |
177 | |
178 | template<typename F> |
179 | struct ArgResolver : ArgResolver<decltype(&std::decay_t<F>::operator())> |
180 | { |
181 | }; |
182 | |
183 | template<typename F> |
184 | struct ArgResolver<std::reference_wrapper<F>> : ArgResolver<decltype(&std::decay_t<F>::operator())> |
185 | { |
186 | }; |
187 | |
188 | template<typename R, typename... Args> |
189 | struct ArgResolver<R(Args...)> : public ArgsType<Args...> |
190 | { |
191 | }; |
192 | |
193 | template<typename R, typename... Args> |
194 | struct ArgResolver<R (*)(Args...)> : public ArgsType<Args...> |
195 | { |
196 | }; |
197 | |
198 | template<typename R, typename... Args> |
199 | struct ArgResolver<R (*&)(Args...)> : public ArgsType<Args...> |
200 | { |
201 | }; |
202 | |
203 | template<typename R, typename... Args> |
204 | struct ArgResolver<R (* const)(Args...)> : public ArgsType<Args...> |
205 | { |
206 | }; |
207 | |
208 | template<typename R, typename... Args> |
209 | struct ArgResolver<R (&)(Args...)> : public ArgsType<Args...> |
210 | { |
211 | }; |
212 | |
213 | template<typename Class, typename R, typename... Args> |
214 | struct ArgResolver<R (Class::*)(Args...)> : public ArgsType<Args...> |
215 | { |
216 | }; |
217 | |
218 | template<typename Class, typename R, typename... Args> |
219 | struct ArgResolver<R (Class::*)(Args...) noexcept> : public ArgsType<Args...> |
220 | { |
221 | }; |
222 | |
223 | template<typename Class, typename R, typename... Args> |
224 | struct ArgResolver<R (Class::*)(Args...) const> : public ArgsType<Args...> |
225 | { |
226 | }; |
227 | |
228 | template<typename Class, typename R, typename... Args> |
229 | struct ArgResolver<R (Class::*)(Args...) const noexcept> : public ArgsType<Args...> |
230 | { |
231 | }; |
232 | |
233 | template<typename Class, typename R, typename... Args> |
234 | struct ArgResolver<R (Class::* const)(Args...) const> : public ArgsType<Args...> |
235 | { |
236 | }; |
237 | |
238 | template<typename Class, typename R, typename... Args> |
239 | struct ArgResolver<R (Class::* const)(Args...) const noexcept> : public ArgsType<Args...> |
240 | { |
241 | }; |
242 | |
243 | template<class Class, class Callable> |
244 | using EnableIfInvocable = std::enable_if_t< |
245 | QtPrivate::ArgResolver<Callable>::template CanInvokeWithArgs<Class, Callable>>; |
246 | |
247 | template<class T> |
248 | inline constexpr bool isQFutureV = false; |
249 | |
250 | template<class T> |
251 | inline constexpr bool isQFutureV<QFuture<T>> = true; |
252 | |
253 | template<class T> |
254 | using isQFuture = std::bool_constant<isQFutureV<T>>; |
255 | |
256 | template<class T> |
257 | struct Future |
258 | { |
259 | }; |
260 | |
261 | template<class T> |
262 | struct Future<QFuture<T>> |
263 | { |
264 | using type = T; |
265 | }; |
266 | |
267 | template<class... Args> |
268 | using NotEmpty = std::bool_constant<(sizeof...(Args) > 0)>; |
269 | |
270 | template<class Sequence> |
271 | using IsRandomAccessible = |
272 | std::is_convertible<typename std::iterator_traits<std::decay_t<decltype( |
273 | std::begin(std::declval<Sequence>()))>>::iterator_category, |
274 | std::random_access_iterator_tag>; |
275 | |
276 | template<class Sequence> |
277 | using HasInputIterator = |
278 | std::is_convertible<typename std::iterator_traits<std::decay_t<decltype( |
279 | std::begin(std::declval<Sequence>()))>>::iterator_category, |
280 | std::input_iterator_tag>; |
281 | |
282 | template<class Iterator> |
283 | using IsForwardIterable = |
284 | std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, |
285 | std::forward_iterator_tag>; |
286 | |
287 | template<typename Function, typename ResultType, typename ParentResultType> |
288 | class Continuation |
289 | { |
290 | Q_DISABLE_COPY_MOVE(Continuation) |
291 | public: |
292 | template<typename F = Function> |
293 | Continuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p) |
294 | : promise(std::move(p)), parentFuture(f), function(std::forward<F>(func)) |
295 | { |
296 | } |
297 | virtual ~Continuation() = default; |
298 | |
299 | bool execute(); |
300 | |
301 | template<typename F = Function> |
302 | static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi, |
303 | QtFuture::Launch policy); |
304 | |
305 | template<typename F = Function> |
306 | static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi, |
307 | QThreadPool *pool); |
308 | |
309 | template<typename F = Function> |
310 | static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi, |
311 | QObject *context); |
312 | |
313 | private: |
314 | void fulfillPromiseWithResult(); |
315 | void fulfillVoidPromise(); |
316 | void fulfillPromiseWithVoidResult(); |
317 | |
318 | template<class... Args> |
319 | void fulfillPromise(Args &&... args); |
320 | |
321 | protected: |
322 | virtual void runImpl() = 0; |
323 | |
324 | void runFunction(); |
325 | |
326 | protected: |
327 | QPromise<ResultType> promise; |
328 | QFuture<ParentResultType> parentFuture; |
329 | Function function; |
330 | }; |
331 | |
332 | template<typename Function, typename ResultType, typename ParentResultType> |
333 | class SyncContinuation final : public Continuation<Function, ResultType, ParentResultType> |
334 | { |
335 | public: |
336 | template<typename F = Function> |
337 | SyncContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p) |
338 | : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f, |
339 | std::move(p)) |
340 | { |
341 | } |
342 | |
343 | ~SyncContinuation() override = default; |
344 | |
345 | private: |
346 | void runImpl() override { this->runFunction(); } |
347 | }; |
348 | |
349 | template<typename Function, typename ResultType, typename ParentResultType> |
350 | class AsyncContinuation final : public QRunnable, |
351 | public Continuation<Function, ResultType, ParentResultType> |
352 | { |
353 | public: |
354 | template<typename F = Function> |
355 | AsyncContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p, |
356 | QThreadPool *pool = nullptr) |
357 | : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f, |
358 | std::move(p)), |
359 | threadPool(pool) |
360 | { |
361 | } |
362 | |
363 | ~AsyncContinuation() override = default; |
364 | |
365 | private: |
366 | void runImpl() override // from Continuation |
367 | { |
368 | QThreadPool *pool = threadPool ? threadPool : QThreadPool::globalInstance(); |
369 | pool->start(this); |
370 | } |
371 | |
372 | void run() override // from QRunnable |
373 | { |
374 | this->runFunction(); |
375 | } |
376 | |
377 | private: |
378 | QThreadPool *threadPool; |
379 | }; |
380 | |
381 | #ifndef QT_NO_EXCEPTIONS |
382 | |
383 | template<class Function, class ResultType> |
384 | class FailureHandler |
385 | { |
386 | public: |
387 | template<typename F = Function> |
388 | static void create(F &&function, QFuture<ResultType> *future, |
389 | const QFutureInterface<ResultType> &fi); |
390 | |
391 | template<typename F = Function> |
392 | static void create(F &&function, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi, |
393 | QObject *context); |
394 | |
395 | template<typename F = Function> |
396 | FailureHandler(F &&func, const QFuture<ResultType> &f, QPromise<ResultType> &&p) |
397 | : promise(std::move(p)), parentFuture(f), handler(std::forward<F>(func)) |
398 | { |
399 | } |
400 | |
401 | public: |
402 | void run(); |
403 | |
404 | private: |
405 | template<class ArgType> |
406 | void handleException(); |
407 | void handleAllExceptions(); |
408 | |
409 | private: |
410 | QPromise<ResultType> promise; |
411 | QFuture<ResultType> parentFuture; |
412 | Function handler; |
413 | }; |
414 | |
415 | #endif |
416 | |
417 | template<typename Function, typename ResultType, typename ParentResultType> |
418 | void Continuation<Function, ResultType, ParentResultType>::runFunction() |
419 | { |
420 | promise.start(); |
421 | |
422 | Q_ASSERT(parentFuture.isFinished()); |
423 | |
424 | #ifndef QT_NO_EXCEPTIONS |
425 | try { |
426 | #endif |
427 | if constexpr (!std::is_void_v<ResultType>) { |
428 | if constexpr (std::is_void_v<ParentResultType>) { |
429 | fulfillPromiseWithVoidResult(); |
430 | } else if constexpr (std::is_invocable_v<Function, ParentResultType>) { |
431 | fulfillPromiseWithResult(); |
432 | } else { |
433 | // This assert normally should never fail, this is to make sure |
434 | // that nothing unexpected happened. |
435 | static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>, |
436 | "The continuation is not invocable with the provided arguments"); |
437 | fulfillPromise(parentFuture); |
438 | } |
439 | } else { |
440 | if constexpr (std::is_void_v<ParentResultType>) { |
441 | if constexpr (std::is_invocable_v<Function, QFuture<void>>) |
442 | function(parentFuture); |
443 | else |
444 | function(); |
445 | } else if constexpr (std::is_invocable_v<Function, ParentResultType>) { |
446 | fulfillVoidPromise(); |
447 | } else { |
448 | // This assert normally should never fail, this is to make sure |
449 | // that nothing unexpected happened. |
450 | static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>, |
451 | "The continuation is not invocable with the provided arguments"); |
452 | function(parentFuture); |
453 | } |
454 | } |
455 | #ifndef QT_NO_EXCEPTIONS |
456 | } catch (...) { |
457 | promise.setException(std::current_exception()); |
458 | } |
459 | #endif |
460 | promise.finish(); |
461 | } |
462 | |
463 | template<typename Function, typename ResultType, typename ParentResultType> |
464 | bool Continuation<Function, ResultType, ParentResultType>::execute() |
465 | { |
466 | Q_ASSERT(parentFuture.isFinished()); |
467 | |
468 | if (parentFuture.d.isChainCanceled()) { |
469 | #ifndef QT_NO_EXCEPTIONS |
470 | if (parentFuture.d.hasException()) { |
471 | // If the continuation doesn't take a QFuture argument, propagate the exception |
472 | // to the caller, by reporting it. If the continuation takes a QFuture argument, |
473 | // the user may want to catch the exception inside the continuation, to not |
474 | // interrupt the continuation chain, so don't report anything yet. |
475 | if constexpr (!std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) { |
476 | promise.start(); |
477 | promise.setException(parentFuture.d.exceptionStore().exception()); |
478 | promise.finish(); |
479 | return false; |
480 | } |
481 | } else |
482 | #endif |
483 | { |
484 | promise.start(); |
485 | promise.future().cancel(); |
486 | promise.finish(); |
487 | return false; |
488 | } |
489 | } |
490 | |
491 | runImpl(); |
492 | return true; |
493 | } |
494 | |
495 | // Workaround for keeping move-only lambdas inside std::function |
496 | template<class Function> |
497 | struct ContinuationWrapper |
498 | { |
499 | ContinuationWrapper(Function &&f) : function(std::move(f)) { } |
500 | ContinuationWrapper(const ContinuationWrapper &other) |
501 | : function(std::move(const_cast<ContinuationWrapper &>(other).function)) |
502 | { |
503 | Q_ASSERT_X(false, "QFuture", "Continuation shouldn't be copied"); |
504 | } |
505 | ContinuationWrapper(ContinuationWrapper &&other) = default; |
506 | ContinuationWrapper &operator=(ContinuationWrapper &&) = default; |
507 | |
508 | void operator()(const QFutureInterfaceBase &parentData) { function(parentData); } |
509 | |
510 | private: |
511 | Function function; |
512 | }; |
513 | |
514 | template<typename Function, typename ResultType, typename ParentResultType> |
515 | template<typename F> |
516 | void Continuation<Function, ResultType, ParentResultType>::create(F &&func, |
517 | QFuture<ParentResultType> *f, |
518 | QFutureInterface<ResultType> &fi, |
519 | QtFuture::Launch policy) |
520 | { |
521 | Q_ASSERT(f); |
522 | |
523 | QThreadPool *pool = nullptr; |
524 | |
525 | bool launchAsync = (policy == QtFuture::Launch::Async); |
526 | if (policy == QtFuture::Launch::Inherit) { |
527 | launchAsync = f->d.launchAsync(); |
528 | |
529 | // If the parent future was using a custom thread pool, inherit it as well. |
530 | if (launchAsync && f->d.threadPool()) { |
531 | pool = f->d.threadPool(); |
532 | fi.setThreadPool(pool); |
533 | } |
534 | } |
535 | |
536 | fi.setLaunchAsync(launchAsync); |
537 | |
538 | auto continuation = [func = std::forward<F>(func), fi, promise_ = QPromise(fi), pool, |
539 | launchAsync](const QFutureInterfaceBase &parentData) mutable { |
540 | const auto parent = QFutureInterface<ParentResultType>(parentData).future(); |
541 | Continuation<Function, ResultType, ParentResultType> *continuationJob = nullptr; |
542 | if (launchAsync) { |
543 | auto asyncJob = new AsyncContinuation<Function, ResultType, ParentResultType>( |
544 | std::forward<Function>(func), parent, std::move(promise_), pool); |
545 | fi.setRunnable(asyncJob); |
546 | continuationJob = asyncJob; |
547 | } else { |
548 | continuationJob = new SyncContinuation<Function, ResultType, ParentResultType>( |
549 | std::forward<Function>(func), parent, std::move(promise_)); |
550 | } |
551 | |
552 | bool isLaunched = continuationJob->execute(); |
553 | // If continuation is successfully launched, AsyncContinuation will be deleted |
554 | // by the QThreadPool which has started it. Synchronous continuation will be |
555 | // executed immediately, so it's safe to always delete it here. |
556 | if (!(launchAsync && isLaunched)) { |
557 | delete continuationJob; |
558 | continuationJob = nullptr; |
559 | } |
560 | }; |
561 | f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d); |
562 | } |
563 | |
564 | template<typename Function, typename ResultType, typename ParentResultType> |
565 | template<typename F> |
566 | void Continuation<Function, ResultType, ParentResultType>::create(F &&func, |
567 | QFuture<ParentResultType> *f, |
568 | QFutureInterface<ResultType> &fi, |
569 | QThreadPool *pool) |
570 | { |
571 | Q_ASSERT(f); |
572 | |
573 | fi.setLaunchAsync(true); |
574 | fi.setThreadPool(pool); |
575 | |
576 | auto continuation = [func = std::forward<F>(func), promise_ = QPromise(fi), |
577 | pool](const QFutureInterfaceBase &parentData) mutable { |
578 | const auto parent = QFutureInterface<ParentResultType>(parentData).future(); |
579 | auto continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>( |
580 | std::forward<Function>(func), parent, std::move(promise_), pool); |
581 | bool isLaunched = continuationJob->execute(); |
582 | // If continuation is successfully launched, AsyncContinuation will be deleted |
583 | // by the QThreadPool which has started it. |
584 | if (!isLaunched) { |
585 | delete continuationJob; |
586 | continuationJob = nullptr; |
587 | } |
588 | }; |
589 | f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d); |
590 | } |
591 | |
592 | template <typename Continuation> |
593 | void watchContinuation(const QObject *context, Continuation &&c, QFutureInterfaceBase &fi) |
594 | { |
595 | using Prototype = typename QtPrivate::Callable<Continuation>::Function; |
596 | watchContinuationImpl(context, |
597 | QtPrivate::makeCallableObject<Prototype>(std::forward<Continuation>(c)), |
598 | fi); |
599 | } |
600 | |
601 | template<typename Function, typename ResultType, typename ParentResultType> |
602 | template<typename F> |
603 | void Continuation<Function, ResultType, ParentResultType>::create(F &&func, |
604 | QFuture<ParentResultType> *f, |
605 | QFutureInterface<ResultType> &fi, |
606 | QObject *context) |
607 | { |
608 | Q_ASSERT(f); |
609 | Q_ASSERT(context); |
610 | |
611 | // When the context object is destroyed, the signal-slot connection is broken and the |
612 | // continuation callback is destroyed. The promise that is created in the capture list is |
613 | // destroyed and, if it is not yet finished, cancelled. |
614 | auto continuation = [func = std::forward<F>(func), parent = *f, |
615 | promise_ = QPromise(fi)]() mutable { |
616 | SyncContinuation<Function, ResultType, ParentResultType> continuationJob( |
617 | std::forward<Function>(func), parent, std::move(promise_)); |
618 | continuationJob.execute(); |
619 | }; |
620 | |
621 | QtPrivate::watchContinuation(context, std::move(continuation), f->d); |
622 | } |
623 | |
624 | template<typename Function, typename ResultType, typename ParentResultType> |
625 | void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithResult() |
626 | { |
627 | if constexpr (std::is_copy_constructible_v<ParentResultType>) |
628 | fulfillPromise(parentFuture.result()); |
629 | else |
630 | fulfillPromise(parentFuture.takeResult()); |
631 | } |
632 | |
633 | template<typename Function, typename ResultType, typename ParentResultType> |
634 | void Continuation<Function, ResultType, ParentResultType>::fulfillVoidPromise() |
635 | { |
636 | if constexpr (std::is_copy_constructible_v<ParentResultType>) |
637 | function(parentFuture.result()); |
638 | else |
639 | function(parentFuture.takeResult()); |
640 | } |
641 | |
642 | template<typename Function, typename ResultType, typename ParentResultType> |
643 | void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithVoidResult() |
644 | { |
645 | if constexpr (std::is_invocable_v<Function, QFuture<void>>) |
646 | fulfillPromise(parentFuture); |
647 | else |
648 | fulfillPromise(); |
649 | } |
650 | |
651 | template<typename Function, typename ResultType, typename ParentResultType> |
652 | template<class... Args> |
653 | void Continuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &&... args) |
654 | { |
655 | promise.addResult(std::invoke(function, std::forward<Args>(args)...)); |
656 | } |
657 | |
658 | template<class T> |
659 | void fulfillPromise(QPromise<T> &promise, QFuture<T> &future) |
660 | { |
661 | if constexpr (!std::is_void_v<T>) { |
662 | if constexpr (std::is_copy_constructible_v<T>) |
663 | promise.addResult(future.result()); |
664 | else |
665 | promise.addResult(future.takeResult()); |
666 | } |
667 | } |
668 | |
669 | template<class T, class Function> |
670 | void fulfillPromise(QPromise<T> &promise, Function &&handler) |
671 | { |
672 | if constexpr (std::is_void_v<T>) |
673 | handler(); |
674 | else |
675 | promise.addResult(handler()); |
676 | } |
677 | |
678 | #ifndef QT_NO_EXCEPTIONS |
679 | |
680 | template<class Function, class ResultType> |
681 | template<class F> |
682 | void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future, |
683 | const QFutureInterface<ResultType> &fi) |
684 | { |
685 | Q_ASSERT(future); |
686 | |
687 | auto failureContinuation = [function = std::forward<F>(function), promise_ = QPromise(fi)]( |
688 | const QFutureInterfaceBase &parentData) mutable { |
689 | const auto parent = QFutureInterface<ResultType>(parentData).future(); |
690 | FailureHandler<Function, ResultType> failureHandler(std::forward<Function>(function), |
691 | parent, std::move(promise_)); |
692 | failureHandler.run(); |
693 | }; |
694 | |
695 | future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation))); |
696 | } |
697 | |
698 | template<class Function, class ResultType> |
699 | template<class F> |
700 | void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future, |
701 | QFutureInterface<ResultType> &fi, |
702 | QObject *context) |
703 | { |
704 | Q_ASSERT(future); |
705 | Q_ASSERT(context); |
706 | auto failureContinuation = [function = std::forward<F>(function), |
707 | parent = *future, promise_ = QPromise(fi)]() mutable { |
708 | FailureHandler<Function, ResultType> failureHandler( |
709 | std::forward<Function>(function), parent, std::move(promise_)); |
710 | failureHandler.run(); |
711 | }; |
712 | |
713 | QtPrivate::watchContinuation(context, std::move(failureContinuation), future->d); |
714 | } |
715 | |
716 | template<class Function, class ResultType> |
717 | void FailureHandler<Function, ResultType>::run() |
718 | { |
719 | Q_ASSERT(parentFuture.isFinished()); |
720 | |
721 | promise.start(); |
722 | |
723 | if (parentFuture.d.hasException()) { |
724 | using ArgType = typename QtPrivate::ArgResolver<Function>::First; |
725 | if constexpr (std::is_void_v<ArgType>) { |
726 | handleAllExceptions(); |
727 | } else { |
728 | handleException<ArgType>(); |
729 | } |
730 | } else if (parentFuture.d.isChainCanceled()) { |
731 | promise.future().cancel(); |
732 | } else { |
733 | QtPrivate::fulfillPromise(promise, parentFuture); |
734 | } |
735 | promise.finish(); |
736 | } |
737 | |
738 | template<class Function, class ResultType> |
739 | template<class ArgType> |
740 | void FailureHandler<Function, ResultType>::handleException() |
741 | { |
742 | try { |
743 | Q_ASSERT(parentFuture.d.hasException()); |
744 | parentFuture.d.exceptionStore().rethrowException(); |
745 | } catch (const ArgType &e) { |
746 | try { |
747 | // Handle exceptions matching with the handler's argument type |
748 | if constexpr (std::is_void_v<ResultType>) |
749 | handler(e); |
750 | else |
751 | promise.addResult(handler(e)); |
752 | } catch (...) { |
753 | promise.setException(std::current_exception()); |
754 | } |
755 | } catch (...) { |
756 | // Exception doesn't match with handler's argument type, propagate |
757 | // the exception to be handled later. |
758 | promise.setException(std::current_exception()); |
759 | } |
760 | } |
761 | |
762 | template<class Function, class ResultType> |
763 | void FailureHandler<Function, ResultType>::handleAllExceptions() |
764 | { |
765 | try { |
766 | Q_ASSERT(parentFuture.d.hasException()); |
767 | parentFuture.d.exceptionStore().rethrowException(); |
768 | } catch (...) { |
769 | try { |
770 | QtPrivate::fulfillPromise(promise, std::forward<Function>(handler)); |
771 | } catch (...) { |
772 | promise.setException(std::current_exception()); |
773 | } |
774 | } |
775 | } |
776 | |
777 | #endif // QT_NO_EXCEPTIONS |
778 | |
779 | template<class Function, class ResultType> |
780 | class CanceledHandler |
781 | { |
782 | public: |
783 | template<class F = Function> |
784 | static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi) |
785 | { |
786 | Q_ASSERT(future); |
787 | |
788 | auto canceledContinuation = [promise = QPromise(fi), handler = std::forward<F>(handler)]( |
789 | const QFutureInterfaceBase &parentData) mutable { |
790 | auto parentFuture = QFutureInterface<ResultType>(parentData).future(); |
791 | run(std::forward<F>(handler), parentFuture, std::move(promise)); |
792 | }; |
793 | future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation))); |
794 | } |
795 | |
796 | template<class F = Function> |
797 | static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi, |
798 | QObject *context) |
799 | { |
800 | Q_ASSERT(future); |
801 | Q_ASSERT(context); |
802 | auto canceledContinuation = [handler = std::forward<F>(handler), |
803 | parentFuture = *future, promise = QPromise(fi)]() mutable { |
804 | run(std::forward<F>(handler), parentFuture, std::move(promise)); |
805 | }; |
806 | |
807 | QtPrivate::watchContinuation(context, std::move(canceledContinuation), future->d); |
808 | } |
809 | |
810 | template<class F = Function> |
811 | static void run(F &&handler, QFuture<ResultType> &parentFuture, QPromise<ResultType> &&promise) |
812 | { |
813 | promise.start(); |
814 | |
815 | if (parentFuture.isCanceled()) { |
816 | #ifndef QT_NO_EXCEPTIONS |
817 | if (parentFuture.d.hasException()) { |
818 | // Propagate the exception to the result future |
819 | promise.setException(parentFuture.d.exceptionStore().exception()); |
820 | } else { |
821 | try { |
822 | #endif |
823 | QtPrivate::fulfillPromise(promise, std::forward<F>(handler)); |
824 | #ifndef QT_NO_EXCEPTIONS |
825 | } catch (...) { |
826 | promise.setException(std::current_exception()); |
827 | } |
828 | } |
829 | #endif |
830 | } else { |
831 | QtPrivate::fulfillPromise(promise, parentFuture); |
832 | } |
833 | |
834 | promise.finish(); |
835 | } |
836 | }; |
837 | |
838 | struct UnwrapHandler |
839 | { |
840 | template<class T> |
841 | static auto unwrapImpl(T *outer) |
842 | { |
843 | Q_ASSERT(outer); |
844 | |
845 | using ResultType = typename QtPrivate::Future<std::decay_t<T>>::type; |
846 | using NestedType = typename QtPrivate::Future<ResultType>::type; |
847 | QFutureInterface<NestedType> promise(QFutureInterfaceBase::State::Pending); |
848 | |
849 | outer->then([promise](const QFuture<ResultType> &outerFuture) mutable { |
850 | // We use the .then([](QFuture<ResultType> outerFuture) {...}) version |
851 | // (where outerFuture == *outer), to propagate the exception if the |
852 | // outer future has failed. |
853 | Q_ASSERT(outerFuture.isFinished()); |
854 | #ifndef QT_NO_EXCEPTIONS |
855 | if (outerFuture.d.hasException()) { |
856 | promise.reportStarted(); |
857 | promise.reportException(outerFuture.d.exceptionStore().exception()); |
858 | promise.reportFinished(); |
859 | return; |
860 | } |
861 | #endif |
862 | |
863 | promise.reportStarted(); |
864 | ResultType nestedFuture = outerFuture.result(); |
865 | |
866 | nestedFuture.then([promise] (const QFuture<NestedType> &nested) mutable { |
867 | #ifndef QT_NO_EXCEPTIONS |
868 | if (nested.d.hasException()) { |
869 | promise.reportException(nested.d.exceptionStore().exception()); |
870 | } else |
871 | #endif |
872 | { |
873 | if constexpr (!std::is_void_v<NestedType>) |
874 | promise.reportResults(nested.results()); |
875 | } |
876 | promise.reportFinished(); |
877 | }).onCanceled([promise] () mutable { |
878 | promise.reportCanceled(); |
879 | promise.reportFinished(); |
880 | }); |
881 | }).onCanceled([promise]() mutable { |
882 | // propagate the cancellation of the outer future |
883 | promise.reportStarted(); |
884 | promise.reportCanceled(); |
885 | promise.reportFinished(); |
886 | }); |
887 | return promise.future(); |
888 | } |
889 | }; |
890 | |
891 | template<typename ValueType> |
892 | QFuture<ValueType> makeReadyRangeFutureImpl(const QList<ValueType> &values) |
893 | { |
894 | QFutureInterface<ValueType> promise; |
895 | promise.reportStarted(); |
896 | promise.reportResults(values); |
897 | promise.reportFinished(); |
898 | return promise.future(); |
899 | } |
900 | |
901 | } // namespace QtPrivate |
902 | |
903 | namespace QtFuture { |
904 | |
905 | template<class Signal> |
906 | using ArgsType = typename QtPrivate::ArgResolver<Signal>::AllArgs; |
907 | |
908 | template<class Sender, class Signal, typename = QtPrivate::EnableIfInvocable<Sender, Signal>> |
909 | static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal) |
910 | { |
911 | using ArgsType = ArgsType<Signal>; |
912 | QFutureInterface<ArgsType> promise; |
913 | promise.reportStarted(); |
914 | if (!sender) { |
915 | promise.reportCanceled(); |
916 | promise.reportFinished(); |
917 | return promise.future(); |
918 | } |
919 | |
920 | using Connections = std::pair<QMetaObject::Connection, QMetaObject::Connection>; |
921 | auto connections = std::make_shared<Connections>(); |
922 | |
923 | if constexpr (std::is_void_v<ArgsType>) { |
924 | connections->first = |
925 | QObject::connect(sender, signal, sender, [promise, connections]() mutable { |
926 | QObject::disconnect(connections->first); |
927 | QObject::disconnect(connections->second); |
928 | promise.reportFinished(); |
929 | }); |
930 | } else if constexpr (QtPrivate::ArgResolver<Signal>::HasExtraArgs) { |
931 | connections->first = QObject::connect(sender, signal, sender, |
932 | [promise, connections](auto... values) mutable { |
933 | QObject::disconnect(connections->first); |
934 | QObject::disconnect(connections->second); |
935 | promise.reportResult(QtPrivate::createTuple( |
936 | std::move(values)...)); |
937 | promise.reportFinished(); |
938 | }); |
939 | } else { |
940 | connections->first = QObject::connect(sender, signal, sender, |
941 | [promise, connections](ArgsType value) mutable { |
942 | QObject::disconnect(connections->first); |
943 | QObject::disconnect(connections->second); |
944 | promise.reportResult(value); |
945 | promise.reportFinished(); |
946 | }); |
947 | } |
948 | |
949 | if (!connections->first) { |
950 | promise.reportCanceled(); |
951 | promise.reportFinished(); |
952 | return promise.future(); |
953 | } |
954 | |
955 | connections->second = |
956 | QObject::connect(sender, &QObject::destroyed, sender, [promise, connections]() mutable { |
957 | QObject::disconnect(connections->first); |
958 | QObject::disconnect(connections->second); |
959 | promise.reportCanceled(); |
960 | promise.reportFinished(); |
961 | }); |
962 | |
963 | return promise.future(); |
964 | } |
965 | |
966 | template<typename Container> |
967 | using if_container_with_input_iterators = |
968 | std::enable_if_t<QtPrivate::HasInputIterator<Container>::value, bool>; |
969 | |
970 | template<typename Container> |
971 | using ContainedType = |
972 | typename std::iterator_traits<decltype( |
973 | std::cbegin(std::declval<Container&>()))>::value_type; |
974 | |
975 | template<typename Container, if_container_with_input_iterators<Container> = true> |
976 | static QFuture<ContainedType<Container>> makeReadyRangeFuture(Container &&container) |
977 | { |
978 | // handle QList<T> separately, because reportResults() takes a QList |
979 | // as an input |
980 | using ValueType = ContainedType<Container>; |
981 | if constexpr (std::is_convertible_v<q20::remove_cvref_t<Container>, QList<ValueType>>) { |
982 | return QtPrivate::makeReadyRangeFutureImpl(container); |
983 | } else { |
984 | return QtPrivate::makeReadyRangeFutureImpl(QList<ValueType>{std::cbegin(container), |
985 | std::cend(container)}); |
986 | } |
987 | } |
988 | |
989 | template<typename ValueType> |
990 | static QFuture<ValueType> makeReadyRangeFuture(std::initializer_list<ValueType> values) |
991 | { |
992 | return QtPrivate::makeReadyRangeFutureImpl(QList<ValueType>{values}); |
993 | } |
994 | |
995 | template<typename T> |
996 | static QFuture<std::decay_t<T>> makeReadyValueFuture(T &&value) |
997 | { |
998 | QFutureInterface<std::decay_t<T>> promise; |
999 | promise.reportStarted(); |
1000 | promise.reportResult(std::forward<T>(value)); |
1001 | promise.reportFinished(); |
1002 | |
1003 | return promise.future(); |
1004 | } |
1005 | |
1006 | Q_CORE_EXPORT QFuture<void> makeReadyVoidFuture(); // implemented in qfutureinterface.cpp |
1007 | |
1008 | #if QT_DEPRECATED_SINCE(6, 10) |
1009 | template<typename T, typename = QtPrivate::EnableForNonVoid<T>> |
1010 | QT_DEPRECATED_VERSION_X(6, 10, "Use makeReadyValueFuture() instead.") |
1011 | static QFuture<std::decay_t<T>> makeReadyFuture(T &&value) |
1012 | { |
1013 | return makeReadyValueFuture(std::forward<T>(value)); |
1014 | } |
1015 | |
1016 | // the void specialization is moved to the end of qfuture.h, because it now |
1017 | // uses makeReadyVoidFuture() and required QFuture<void> to be defined. |
1018 | |
1019 | template<typename T> |
1020 | QT_DEPRECATED_VERSION_X(6, 10, "Use makeReadyRangeFuture() instead.") |
1021 | static QFuture<T> makeReadyFuture(const QList<T> &values) |
1022 | { |
1023 | return makeReadyRangeFuture(values); |
1024 | } |
1025 | #endif // QT_DEPRECATED_SINCE(6, 10) |
1026 | |
1027 | #ifndef QT_NO_EXCEPTIONS |
1028 | |
1029 | template<typename T = void> |
1030 | static QFuture<T> makeExceptionalFuture(std::exception_ptr exception) |
1031 | { |
1032 | QFutureInterface<T> promise; |
1033 | promise.reportStarted(); |
1034 | promise.reportException(exception); |
1035 | promise.reportFinished(); |
1036 | |
1037 | return promise.future(); |
1038 | } |
1039 | |
1040 | template<typename T = void> |
1041 | static QFuture<T> makeExceptionalFuture(const QException &exception) |
1042 | { |
1043 | try { |
1044 | exception.raise(); |
1045 | } catch (...) { |
1046 | return makeExceptionalFuture<T>(std::current_exception()); |
1047 | } |
1048 | Q_UNREACHABLE(); |
1049 | } |
1050 | |
1051 | #endif // QT_NO_EXCEPTIONS |
1052 | |
1053 | } // namespace QtFuture |
1054 | |
1055 | namespace QtPrivate { |
1056 | |
1057 | template<typename ResultFutures> |
1058 | struct WhenAllContext |
1059 | { |
1060 | using ValueType = typename ResultFutures::value_type; |
1061 | |
1062 | explicit WhenAllContext(qsizetype size) : remaining(size) {} |
1063 | |
1064 | template<typename T = ValueType> |
1065 | void checkForCompletion(qsizetype index, T &&future) |
1066 | { |
1067 | futures[index] = std::forward<T>(future); |
1068 | const auto oldRemaining = remaining.fetchAndSubRelaxed(valueToAdd: 1); |
1069 | Q_ASSERT(oldRemaining > 0); |
1070 | if (oldRemaining <= 1) { // that was the last one |
1071 | promise.addResult(futures); |
1072 | promise.finish(); |
1073 | } |
1074 | } |
1075 | |
1076 | QAtomicInteger<qsizetype> remaining; |
1077 | QPromise<ResultFutures> promise; |
1078 | ResultFutures futures; |
1079 | }; |
1080 | |
1081 | template<typename ResultType> |
1082 | struct WhenAnyContext |
1083 | { |
1084 | using ValueType = ResultType; |
1085 | |
1086 | template<typename T = ResultType, typename = EnableForNonVoid<T>> |
1087 | void checkForCompletion(qsizetype, T &&result) |
1088 | { |
1089 | if (!ready.fetchAndStoreRelaxed(newValue: true)) { |
1090 | promise.addResult(std::forward<T>(result)); |
1091 | promise.finish(); |
1092 | } |
1093 | } |
1094 | |
1095 | QAtomicInt ready = false; |
1096 | QPromise<ResultType> promise; |
1097 | }; |
1098 | |
1099 | template<qsizetype Index, typename ContextType, typename... Ts> |
1100 | void addCompletionHandlersImpl(const std::shared_ptr<ContextType> &context, |
1101 | const std::tuple<Ts...> &t) |
1102 | { |
1103 | auto future = std::get<Index>(t); |
1104 | using ResultType = typename ContextType::ValueType; |
1105 | // Need context=context so that the compiler does not infer the captured variable's type as 'const' |
1106 | future.then([context=context](const std::tuple_element_t<Index, std::tuple<Ts...>> &f) { |
1107 | context->checkForCompletion(Index, ResultType { std::in_place_index<Index>, f }); |
1108 | }).onCanceled([context=context, future]() { |
1109 | context->checkForCompletion(Index, ResultType { std::in_place_index<Index>, future }); |
1110 | }); |
1111 | |
1112 | if constexpr (Index != 0) |
1113 | addCompletionHandlersImpl<Index - 1, ContextType, Ts...>(context, t); |
1114 | } |
1115 | |
1116 | template<typename ContextType, typename... Ts> |
1117 | void addCompletionHandlers(const std::shared_ptr<ContextType> &context, const std::tuple<Ts...> &t) |
1118 | { |
1119 | constexpr qsizetype size = std::tuple_size<std::tuple<Ts...>>::value; |
1120 | addCompletionHandlersImpl<size - 1, ContextType, Ts...>(context, t); |
1121 | } |
1122 | |
1123 | template<typename OutputSequence, typename InputIt, typename ValueType> |
1124 | QFuture<OutputSequence> whenAllImpl(InputIt first, InputIt last) |
1125 | { |
1126 | const qsizetype size = std::distance(first, last); |
1127 | if (size == 0) |
1128 | return QtFuture::makeReadyValueFuture(OutputSequence()); |
1129 | |
1130 | const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size); |
1131 | context->futures.resize(size); |
1132 | context->promise.start(); |
1133 | |
1134 | qsizetype idx = 0; |
1135 | for (auto it = first; it != last; ++it, ++idx) { |
1136 | // Need context=context so that the compiler does not infer the captured variable's type as 'const' |
1137 | it->then([context=context, idx](const ValueType &f) { |
1138 | context->checkForCompletion(idx, f); |
1139 | }).onCanceled([context=context, idx, f = *it] { |
1140 | context->checkForCompletion(idx, f); |
1141 | }); |
1142 | } |
1143 | return context->promise.future(); |
1144 | } |
1145 | |
1146 | template<typename OutputSequence, typename... Futures> |
1147 | QFuture<OutputSequence> whenAllImpl(Futures &&... futures) |
1148 | { |
1149 | constexpr qsizetype size = sizeof...(Futures); |
1150 | const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size); |
1151 | context->futures.resize(size); |
1152 | context->promise.start(); |
1153 | |
1154 | QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward<Futures>(futures)...)); |
1155 | |
1156 | return context->promise.future(); |
1157 | } |
1158 | |
1159 | template<typename InputIt, typename ValueType> |
1160 | QFuture<QtFuture::WhenAnyResult<typename Future<ValueType>::type>> whenAnyImpl(InputIt first, |
1161 | InputIt last) |
1162 | { |
1163 | using PackagedType = typename Future<ValueType>::type; |
1164 | using ResultType = QtFuture::WhenAnyResult<PackagedType>; |
1165 | |
1166 | const qsizetype size = std::distance(first, last); |
1167 | if (size == 0) { |
1168 | return QtFuture::makeReadyValueFuture( |
1169 | QtFuture::WhenAnyResult { qsizetype(-1), QFuture<PackagedType>() }); |
1170 | } |
1171 | |
1172 | const auto context = std::make_shared<QtPrivate::WhenAnyContext<ResultType>>(); |
1173 | context->promise.start(); |
1174 | |
1175 | qsizetype idx = 0; |
1176 | for (auto it = first; it != last; ++it, ++idx) { |
1177 | // Need context=context so that the compiler does not infer the captured variable's type as 'const' |
1178 | it->then([context=context, idx](const ValueType &f) { |
1179 | context->checkForCompletion(idx, QtFuture::WhenAnyResult { idx, f }); |
1180 | }).onCanceled([context=context, idx, f = *it] { |
1181 | context->checkForCompletion(idx, QtFuture::WhenAnyResult { idx, f }); |
1182 | }); |
1183 | } |
1184 | return context->promise.future(); |
1185 | } |
1186 | |
1187 | template<typename... Futures> |
1188 | QFuture<std::variant<std::decay_t<Futures>...>> whenAnyImpl(Futures &&... futures) |
1189 | { |
1190 | using ResultType = std::variant<std::decay_t<Futures>...>; |
1191 | |
1192 | const auto context = std::make_shared<QtPrivate::WhenAnyContext<ResultType>>(); |
1193 | context->promise.start(); |
1194 | |
1195 | QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward<Futures>(futures)...)); |
1196 | |
1197 | return context->promise.future(); |
1198 | } |
1199 | |
1200 | } // namespace QtPrivate |
1201 | |
1202 | QT_END_NAMESPACE |
1203 |
Definitions
- Launch
- WhenAnyResult
- ResultTypeHelper
- ResultTypeHelper
- ResultTypeHelper
- ResultTypeHelper
- ResultTypeHelper
- IsPrivateSignalArg
- IsPrivateSignalArg
- cutTuple
- createTuple
- ArgsType
- ArgsType
- ArgsType
- ArgResolver
- ArgResolver
- ArgResolver
- ArgResolver
- ArgResolver
- ArgResolver
- ArgResolver
- ArgResolver
- ArgResolver
- ArgResolver
- ArgResolver
- ArgResolver
- ArgResolver
- isQFutureV
- isQFutureV
- Future
- Future
- Continuation
- Continuation
- Continuation
- Continuation
- ~Continuation
- SyncContinuation
- SyncContinuation
- ~SyncContinuation
- runImpl
- AsyncContinuation
- AsyncContinuation
- ~AsyncContinuation
- runImpl
- run
- runFunction
- execute
- ContinuationWrapper
- ContinuationWrapper
- ContinuationWrapper
- ContinuationWrapper
- operator=
- operator()
- create
- create
- watchContinuation
- create
- fulfillPromiseWithResult
- fulfillVoidPromise
- fulfillPromiseWithVoidResult
- fulfillPromise
- fulfillPromise
- fulfillPromise
- CanceledHandler
- create
- create
- run
- UnwrapHandler
- unwrapImpl
- makeReadyRangeFutureImpl
- connect
- makeReadyRangeFuture
- makeReadyRangeFuture
- makeReadyValueFuture
- makeReadyFuture
- makeReadyFuture
- WhenAllContext
- WhenAllContext
- checkForCompletion
- WhenAnyContext
- checkForCompletion
- addCompletionHandlersImpl
- addCompletionHandlers
- whenAllImpl
- whenAllImpl
- whenAnyImpl
Learn Advanced QML with KDAB
Find out more