aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/QtTaskTree/qtasktreerunner.cpp
blob: 6896dfe5965f7c6aa78b542d3d01de7f77f10734 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
// Copyright (C) 2025 Jarek Kobus
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include <QtTaskTree/qtasktreerunner.h>

#include <QtTaskTree/qtasktree.h>

#include <QtCore/private/qobject_p.h>

QT_BEGIN_NAMESPACE

using namespace QtTaskTree;

class QAbstractTaskTreeRunnerPrivate : public QObjectPrivate
{
public:
    std::unique_ptr<QTaskTree> m_taskTree;
};

/*!
    \class QAbstractTaskTreeRunner
    \inheaderfile qtasktreerunner.h
    \inmodule TaskTree
    \brief An abstract base class for various task tree controllers.
    \reentrant

    The task tree runner manages the lifetime
    of the underlying QTaskTree used to execute the given recipe.

    The following table summarizes the differences between various
    QAbstractTaskTreeRunner subclasses:

    \table
    \header
        \li Class name
        \li Description
    \row
        \li QSingleTaskTreeRunner
        \li Manages single task tree execution.
            The QSingleTaskTreeRunner::start() method unconditionally starts
            the passed recipe, resetting any task tree that might be
            running. Only one task tree can be executing at a time.
    \row
        \li QSequentialTaskTreeRunner
        \li Manages sequential task tree executions.
            The QSequentialTaskTreeRunner::enqueue() method starts
            the passed recipe if the task tree runner is idle.
            Otherwise, the recipe is enqueued. When the current
            task finishes, the runner executes the dequeued recipe
            sequentially. Only one task tree can be executing at a time.
    \row
        \li QParallelTaskTreeRunner
        \li Manages parallel task tree executions.
            The QParallelTaskTreeRunner::start() method unconditionally starts
            the passed recipe and keeps any possibly
            running task trees in parallel.
    \row
        \li QMappedTaskTreeRunner
        \li Manages mapped task tree executions.
            The QMappedTaskTreeRunner::start() method unconditionally starts
            the passed recipe for a given key,
            resetting any possibly running task tree with the same key.
            Task trees with different keys are unaffected and continue
            their execution.
    \endtable
*/

/*!
    \typealias QAbstractTaskTreeRunner::TreeSetupHandler

    Type alias for std::function<void(QTaskTree &)>.

    The \c TreeSetupHandler is an optional argument of
    task tree runners' functions scheduling the task tree execution.
    The function is called when the task tree runner is about
    to execute it's recipe. The access to the QTaskTree is via
    the passed argument.

    The task tree runners accept also handlers in a shortened form
    of \c std::function<void(void)>.
*/

/*!
    \typealias QAbstractTaskTreeRunner::TreeDoneHandler

    Type alias for std::function<void(const QTaskTree &, QtTaskTree::DoneWith)>.

    The \c TreeDoneHandler is an optional argument of
    task tree runners' functions scheduling the task tree execution.
    The function is called when the task tree runner's recipe
    finished the execution. The access to the QTaskTree and recipe's
    result is via the passed arguments.

    The task tree runners accept also handlers in a shortened form of
    \c std::function<void(const QTaskTree &)>,
    \c std::function<void(QtTaskTree::DoneWith)>, or
    \c std::function<void(void)>.
*/

/*!
    Constructs an abstract task tree runner for the given \a parent.
*/
QAbstractTaskTreeRunner::QAbstractTaskTreeRunner(QObject *parent)
    : QObject(*new QAbstractTaskTreeRunnerPrivate, parent)
{}

QAbstractTaskTreeRunner::QAbstractTaskTreeRunner(QAbstractTaskTreeRunnerPrivate &dd, QObject *parent)
    : QObject(dd, parent)
{}

/*!
    Destroys the abstract task tree runner. Any possibly running
    task tree is deleted and any scheduled task tree execution
    is skipped.

    \sa {QTaskTree::~QTaskTree()} {~QTaskTree()}
*/
QAbstractTaskTreeRunner::~QAbstractTaskTreeRunner() = default;

/*!
    \fn bool QAbstractTaskTreeRunner::isRunning() const

    Returns whether the task tree runner is currently
    executing any task tree.
*/

/*!
    \fn void QAbstractTaskTreeRunner::cancel()

    Cancels all running task trees. Calls task trees' done
    handlers and emits done() signals with
    \l {QtTaskTree::} {DoneWith::Cancel}.
    Any scheduled and not started task tree executions are removed.
*/

/*!
    \fn void QAbstractTaskTreeRunner::reset()

    Resets all running task trees. No task tree's done
    handlers are called nor done() signals are emitted.
    Any scheduled and not started task tree executions are removed.
*/

/*!
    \fn void QAbstractTaskTreeRunner::aboutToStart(QTaskTree *taskTree)

    This signal is emitted whenever task tree runner is about to
    start the \a taskTree.

    \sa QTaskTree::started()
*/

/*!
    \fn void QAbstractTaskTreeRunner::done(QtTaskTree::DoneWith result, QTaskTree *taskTree)

    This signal is emitted whenever task tree runner finishes the execution
    of the \a taskTree with a \a result.

    \sa QTaskTree::done()
*/

class QSingleTaskTreeRunnerPrivate : public QAbstractTaskTreeRunnerPrivate
{
public:
    std::unique_ptr<QTaskTree> m_taskTree;
};

/*!
    \class QSingleTaskTreeRunner
    \inheaderfile qtasktreerunner.h
    \inmodule TaskTree
    \brief A single task tree execution controller.

    Manages single task tree execution.
    Use the start() method to execute a given recipe,
    resetting any possibly running task tree.
    It's guaranteed that at most one task tree is executing at any given time.
*/

/*!
    Constructs a single task tree runner for the given \a parent.
*/
QSingleTaskTreeRunner::QSingleTaskTreeRunner(QObject *parent)
    : QAbstractTaskTreeRunner(*new QSingleTaskTreeRunnerPrivate, parent)
{}

/*!
    Destroys the single task tree runner. A possibly running
    task tree is deleted. No task tree's done handler is called nor
    done() signal is emitted.

    \sa {QTaskTree::~QTaskTree()} {~QTaskTree()}
*/
QSingleTaskTreeRunner::~QSingleTaskTreeRunner() = default;

/*!
    \reimp

    Returns whether the single task tree runner is currently
    executing a task tree.
*/
bool QSingleTaskTreeRunner::isRunning() const { return bool(d_func()->m_taskTree); }

/*!
    \reimp

    Cancels the running task tree. Calls task tree' done
    handler and emits done() signal with
    \l {QtTaskTree::} {DoneWith::Cancel}.
*/
void QSingleTaskTreeRunner::cancel()
{
    if (d_func()->m_taskTree)
        d_func()->m_taskTree->cancel();
}

/*!
    \reimp

    Resets the running task tree. No task tree's done
    handler is called nor done() signal is emitted.
*/
void QSingleTaskTreeRunner::reset()
{
    d_func()->m_taskTree.reset();
}

/*!
    \fn template <typename SetupHandler = TreeSetupHandler, typename DoneHandler = TreeDoneHandler> void QSingleTaskTreeRunner::start(const QtTaskTree::Group &recipe, SetupHandler &&setupHandler = {}, DoneHandler &&doneHandler = {}, QtTaskTree::CallDoneFlags callDone = QtTaskTree::CallDone::Always)

    Starts the \a recipe unconditionally, resetting any possibly
    running task tree.
    Calls \a setupHandler when new task tree is about to be started.
    Calls \a doneHandler when the task tree is finished.
    The \a doneHandler is called according to the passed \a callDone.
*/

void QSingleTaskTreeRunner::startImpl(const Group &recipe,
                                      const TreeSetupHandler &setupHandler,
                                      const TreeDoneHandler &doneHandler,
                                      CallDoneFlags callDone)
{
    Q_D(QSingleTaskTreeRunner);
    d->m_taskTree.reset(new QTaskTree(recipe));
    connect(d->m_taskTree.get(), &QTaskTree::done, this, [this, doneHandler, callDone](DoneWith result) {
        Q_D(QSingleTaskTreeRunner);
        QTaskTree *taskTree = d->m_taskTree.release();
        taskTree->deleteLater();
        if (doneHandler && shouldCallDone(callDone, result))
            doneHandler(*taskTree, result);
        Q_EMIT done(result, taskTree);
    });
    if (setupHandler)
        setupHandler(*d->m_taskTree);
    Q_EMIT aboutToStart(d->m_taskTree.get());
    d->m_taskTree->start();
}

namespace QtTaskTree {

struct TreeData
{
    Group recipe;
    QAbstractTaskTreeRunner::TreeSetupHandler setupHandler;
    QAbstractTaskTreeRunner::TreeDoneHandler doneHandler;
    CallDoneFlags callDone;
};

} // namespace QtTaskTree

class QSequentialTaskTreeRunnerPrivate : public QAbstractTaskTreeRunnerPrivate
{
public:
    void startNext();

    QList<TreeData> m_treeDataQueue;
    QSingleTaskTreeRunner m_taskTreeRunner;
};

/*!
    \class QSequentialTaskTreeRunner
    \inheaderfile qtasktreerunner.h
    \inmodule TaskTree
    \brief A sequential task tree execution controller.

    Manages sequential task tree execution.
    Use the enqueue() method to schedule the execution of a given recipe.
    It's guaranteed that at most one task tree is executing at any given time.
*/

void QSequentialTaskTreeRunnerPrivate::startNext()
{
    if (m_treeDataQueue.isEmpty())
        return;

    const auto data = m_treeDataQueue.takeFirst();
    m_taskTreeRunner.start(data.recipe, data.setupHandler, data.doneHandler, data.callDone);
}

/*!
    Constructs a sequential task tree runner for the given \a parent.
*/
QSequentialTaskTreeRunner::QSequentialTaskTreeRunner(QObject *parent)
    : QAbstractTaskTreeRunner(*new QSequentialTaskTreeRunnerPrivate, parent)
{
    Q_D(QSequentialTaskTreeRunner);
    connect(&d->m_taskTreeRunner, &QSingleTaskTreeRunner::aboutToStart,
            this, &QSequentialTaskTreeRunner::aboutToStart);
    connect(&d->m_taskTreeRunner, &QSingleTaskTreeRunner::done,
            this, [this](DoneWith result, QTaskTree *taskTree) {
        Q_EMIT done(result, taskTree);
        d_func()->startNext();
    });
}

/*!
    Destroys the sequential task tree runner. A possibly running
    task tree is deleted and enqueued tasks are removed.
    No task tree's done handler is called nor done() signal is emitted.

    \sa {QTaskTree::~QTaskTree()} {~QTaskTree()}
*/
QSequentialTaskTreeRunner::~QSequentialTaskTreeRunner() = default;

/*!
    \reimp

    Returns whether the sequential task tree runner is currently
    executing a task tree.
*/
bool QSequentialTaskTreeRunner::isRunning() const
{
    Q_D(const QSequentialTaskTreeRunner);
    return !d->m_treeDataQueue.isEmpty() || d->m_taskTreeRunner.isRunning();
}

/*!
    \reimp

    Cancels the running task tree. Calls task tree' done
    handler and emits done() signal with
    \l {QtTaskTree::} {DoneWith::Cancel}.
    All queued tasks are removed.
*/
void QSequentialTaskTreeRunner::cancel()
{
    Q_D(QSequentialTaskTreeRunner);
    d->m_treeDataQueue.clear();
    d->m_taskTreeRunner.cancel();
}

/*!
    \reimp

    Resets the running task tree. No task tree's done
    handler is called nor done() signal is emitted.
    All queued tasks are removed.
*/
void QSequentialTaskTreeRunner::reset()
{
    Q_D(QSequentialTaskTreeRunner);
    d->m_treeDataQueue.clear();
    d->m_taskTreeRunner.reset();
}

/*!
    Cancels the running task tree. Calls task tree' done
    handler and emits done() signal with
    \l {QtTaskTree::} {DoneWith::Cancel}.
    If there are any enqueued recipes, the dequeued recipe is started.
*/
void QSequentialTaskTreeRunner::cancelCurrent()
{
    Q_D(QSequentialTaskTreeRunner);
    d->m_taskTreeRunner.cancel();
}

/*!
    Resets the running task tree. No task tree's done
    handler is called nor done() signal is emitted.
    If there are any enqueued recipes, the dequeued recipe is started.
*/
void QSequentialTaskTreeRunner::resetCurrent()
{
    Q_D(QSequentialTaskTreeRunner);
    d->m_taskTreeRunner.reset();
    d->startNext();
}

/*!
    \fn template <typename SetupHandler = TreeSetupHandler, typename DoneHandler = TreeDoneHandler> void QSequentialTaskTreeRunner::enqueue(const QtTaskTree::Group &recipe, SetupHandler &&setupHandler = {}, DoneHandler &&doneHandler = {}, QtTaskTree::CallDoneFlags callDone = QtTaskTree::CallDone::Always)

    Schedules the \a recipe execution. If no task tree is executing,
    the runner starts a new task tree synchronously, otherwise
    the \a recipe is enqueued. When the currently executing task tree
    finished, the runner starts a new task tree with a dequeued recipe.
    Calls \a setupHandler when new task tree is about to be started.
    Calls \a doneHandler when the task tree is finished.
    The \a doneHandler is called according to the passed \a callDone.
*/

void QSequentialTaskTreeRunner::enqueueImpl(const Group &recipe,
                                            const TreeSetupHandler &setupHandler,
                                            const TreeDoneHandler &doneHandler,
                                            CallDoneFlags callDone)
{
    Q_D(QSequentialTaskTreeRunner);
    d->m_treeDataQueue.append({recipe, setupHandler, doneHandler, callDone});
    if (!d->m_taskTreeRunner.isRunning())
        d->startNext();
}

class QParallelTaskTreeRunnerPrivate : public QAbstractTaskTreeRunnerPrivate
{
public:
    std::unordered_map<QTaskTree *, std::unique_ptr<QTaskTree>> m_taskTrees;
};

/*!
    \class QParallelTaskTreeRunner
    \inheaderfile qtasktreerunner.h
    \inmodule TaskTree
    \brief A parallel task tree execution controller.

    Manages parallel task tree execution.
    Use the start() method to execute a given recipe instantly,
    keeping other possibly running task trees in parallel.
*/

/*!
    Constructs a parallel task tree runner for the given \a parent.
*/
QParallelTaskTreeRunner::QParallelTaskTreeRunner(QObject *parent)
    : QAbstractTaskTreeRunner(*new QParallelTaskTreeRunnerPrivate, parent)
{}

/*!
    Destroys the parallel task tree runner. All running
    task trees are deleted. No task trees' done handlers are called nor
    done() signals are emitted.

    \sa {QTaskTree::~QTaskTree()} {~QTaskTree()}
*/
QParallelTaskTreeRunner::~QParallelTaskTreeRunner() = default;

/*!
    \reimp

    Returns whether the parallel task tree runner is currently
    executing at least one task tree.
*/
bool QParallelTaskTreeRunner::isRunning() const { return !d_func()->m_taskTrees.empty(); }

/*!
    \reimp

    Cancels all running task trees. Calls task trees' done
    handlers and emits done() signals with
    \l {QtTaskTree::} {DoneWith::Cancel}.
    The order of task trees' cancellation is random.
*/
void QParallelTaskTreeRunner::cancel()
{
    Q_D(QParallelTaskTreeRunner);
    while (!d->m_taskTrees.empty())
        d->m_taskTrees.begin()->second->cancel();
}

/*!
    \reimp

    Resets all running task trees. No task trees' done
    handlers are called nor done() signals are emitted.
*/
void QParallelTaskTreeRunner::reset()
{
    d_func()->m_taskTrees.clear();
}

/*!
    \fn template <typename SetupHandler = TreeSetupHandler, typename DoneHandler = TreeDoneHandler> void QParallelTaskTreeRunner::start(const QtTaskTree::Group &recipe, SetupHandler &&setupHandler = {}, DoneHandler &&doneHandler = {}, QtTaskTree::CallDoneFlags callDone = QtTaskTree::CallDone::Always)

    Starts the \a recipe instantly and keeps other possibly running
    task trees in parallel.
    Calls \a setupHandler when new task tree is about to be started.
    Calls \a doneHandler when the task tree is finished.
    The \a doneHandler is called according to the passed \a callDone.
*/

void QParallelTaskTreeRunner::startImpl(const Group &recipe,
                                       const TreeSetupHandler &setupHandler,
                                       const TreeDoneHandler &doneHandler,
                                       CallDoneFlags callDone)
{
    Q_D(QParallelTaskTreeRunner);
    QTaskTree *taskTree = new QTaskTree(recipe);
    connect(taskTree, &QTaskTree::done, this, [this, taskTree, doneHandler, callDone](DoneWith result) {
        Q_D(QParallelTaskTreeRunner);
        const auto it = d->m_taskTrees.find(taskTree);
        it->second.release()->deleteLater();
        d->m_taskTrees.erase(it);
        if (doneHandler && shouldCallDone(callDone, result))
            doneHandler(*taskTree, result);
        Q_EMIT done(result, taskTree);
    });
    d->m_taskTrees.emplace(taskTree, taskTree);
    if (setupHandler)
        setupHandler(*taskTree);
    Q_EMIT aboutToStart(taskTree);
    taskTree->start();
}

/*!
    \class QMappedTaskTreeRunner
    \inheaderfile qtasktreerunner.h
    \inmodule TaskTree
    \brief A mapped task tree execution controller with a given Key type.

    Manages mapped task tree execution using \c Key type.
    Use the start() method to execute a recipe for a given key unconditionally,
    resetting a possibly running task tree with the same key,
    and keeping other possibly running task trees with different keys
    in parallel.
*/

/*!
    \fn template <typename Key> QMappedTaskTreeRunner<Key>::QMappedTaskTreeRunner(QObject *parent = nullptr)

    Constructs a mapped task tree runner for the given \a parent.
    The \c Key type is used for task tree mapping.
*/

/*!
    \fn template <typename Key> QMappedTaskTreeRunner<Key>::~QMappedTaskTreeRunner()

    Destroys the mapped task tree runner. All running
    task trees are deleted. No task trees' done handlers are called nor
    done() signals are emitted.

    \sa {QTaskTree::~QTaskTree()} {~QTaskTree()}
*/

/*!
    \fn template <typename Key> bool QMappedTaskTreeRunner<Key>::isRunning() const
    \reimp

    Returns whether the mapped task tree runner is currently
    executing at least one task tree.
*/

/*!
    \fn template <typename Key> void QMappedTaskTreeRunner<Key>::cancel()
    \reimp

    Cancels all running task trees. Calls task trees' done
    handlers and emits done() signals with
    \l {QtTaskTree::} {DoneWith::Cancel}.
    The order of task trees' cancellation is random.
*/

/*!
    \fn template <typename Key> void QMappedTaskTreeRunner<Key>::reset()
    \reimp

    Resets all running task trees. No task trees' done
    handlers are called nor done() signals are emitted.
*/

/*!
    \fn template <typename Key> bool QMappedTaskTreeRunner<Key>::isKeyRunning(const Key &key) const

    Returns whether the mapped task tree runner is currently
    executing a task tree that was started with the \a key.
*/

/*!
    \fn template <typename Key> void QMappedTaskTreeRunner<Key>::cancelKey(const Key &key)

    Cancels a potentially running task tree that was started with the \a key.
    Calls task tree's done handler and emits done() signal with
    \l {QtTaskTree::} {DoneWith::Cancel}.
*/

/*!
    \fn template <typename Key> void QMappedTaskTreeRunner<Key>::resetKey(const Key &key)

    Resets a potentially running task tree that was started with the \a key.
    No task tree's done handlers is called nor done() signal is emitted.
*/

/*!
    \fn template <typename Key> template <typename SetupHandler = TreeSetupHandler, typename DoneHandler = TreeDoneHandler> void QMappedTaskTreeRunner<Key>::start(const Key &key, const QtTaskTree::Group &recipe, SetupHandler &&setupHandler = {}, DoneHandler &&doneHandler = {}, QtTaskTree::CallDoneFlags callDone = QtTaskTree::CallDone::Always)

    Starts the \a recipe for a given \a key unconditionally,
    resetting any possibly running task tree with the same key,
    and keeping other possibly running task trees with different keys
    in parallel.
    Calls \a setupHandler when new task tree is about to be started.
    Calls \a doneHandler when the task tree is finished.
    The \a doneHandler is called according to the passed \a callDone.
*/

QT_END_NAMESPACE