summaryrefslogtreecommitdiffstats
path: root/src/async/progress.h
blob: 120f03b28ae67441fa6eea786106aee5055118cd (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
// SPDX-License-Identifier: GPL-2.0-or-later
/** \file Progress
 * Interface for reporting progress and checking cancellation.
 */
#ifndef INKSCAPE_ASYNC_PROGRESS_H
#define INKSCAPE_ASYNC_PROGRESS_H

#include <chrono>

namespace Inkscape {
namespace Async {

class CancelledException {};

/**
 * An interface for tasks to report progress and check for cancellation.
 * Not supported:
 *  - Error reporting - use exceptions!
 *  - Thread-safety - overrides should provide this if needed using e.g. BackgroundProgress.
 */
template <typename... T>
class Progress
{
public:
    /// Report a progress value, returning false if cancelled.
    bool report(T const &... progress) { return _report(progress...); }

    /// Report a progress value, throwing CancelledException if cancelled.
    void report_or_throw(T const &... progress) { if (!_report(progress...)) throw CancelledException(); }

    /// Return whether not cancelled.
    bool keepgoing() const { return _keepgoing(); }

    /// Throw CancelledException if cancelled.
    void throw_if_cancelled() const { if (!_keepgoing()) throw CancelledException(); }

    /// Convenience function - same as check().
    operator bool() const { return _keepgoing(); }

protected:
    ~Progress() = default;
    virtual bool _keepgoing() const = 0;
    virtual bool _report(T const &... progress) = 0;
};

/**
 * A Progress object representing a sub-task of another Progress.
 */
template <typename T, typename... S>
class SubProgress final
    : public Progress<T, S...>
{
public:
    /// Construct a progress object for a sub-task.
    SubProgress(Progress<T, S...> &parent, T from, T amount)
    {
        if (auto p = dynamic_cast<SubProgress*>(&parent)) {
            _root = p->_root;
            _from = p->_from + p->_amount * from;
            _amount = p->_amount * amount;
        } else {
            _root = &parent;
            _from = from;
            _amount = amount;
        }
    }

private:
    Progress<T, S...> *_root;
    T _from, _amount;

    bool _keepgoing() const override { return _root->keepgoing(); }
    bool _report(T const &progress, S const &... aux) override { return _root->report(_from + _amount * progress, aux...); }
};

/**
 * A Progress object that throttles reports to a given step size.
 */
template <typename T, typename... S>
class ProgressStepThrottler final
    : public Progress<T, S...>
{
public:
    ProgressStepThrottler(Progress<T, S...> &parent, T step)
        : parent(&parent), step(step) {}

private:
    Progress<T, S...> *parent;
    T step;
    T last = 0;

    bool _keepgoing() const override { return parent->keepgoing(); }

    bool _report(T const &progress, S const &... aux) override
    {
        if (progress - last < step) {
            return parent->keepgoing();
        } else {
            last = progress;
            return parent->report(progress, aux...);
        }
    }
};

/**
 * A Progress object that throttles reports to a given time interval.
 */
template <typename... T>
class ProgressTimeThrottler final
    : public Progress<T...>
{
    using clock = std::chrono::steady_clock;
    using time_point = clock::time_point;

public:
    using duration = clock::duration;

    ProgressTimeThrottler(Progress<T...> &parent, duration interval)
        : parent(&parent), interval(interval) {}

private:
    Progress<T...> *parent;
    duration interval;
    time_point last = clock::now();

    bool _keepgoing() const override { return parent->keepgoing(); }

    bool _report(T const &... progress) override
    {
        auto now = clock::now();
        if (now - last < interval) {
            return parent->keepgoing();
        } else {
            last = now;
            return parent->report(progress...);
        }
    }
};

/**
 * A dummy Progress object that never reports cancellation.
 */
template <typename... T>
class ProgressAlways final
    : public Progress<T...>
{
private:
    bool _keepgoing() const override { return true; }
    bool _report(T const &...) override { return true; }
};

} // namespace Async
} // namespace Inkscape

#endif // INKSCAPE_ASYNC_PROGRESS_H