source: branches/help/CORE/arb_progress.h

Last change on this file was 17942, checked in by westram, 5 years ago
  • progress overflow bug (#807):
    • improved progress overflow message.
    • in case of progress overflow:
      • dump progress/counter-cascade (to stderr, i.e. to session log).
      • may slow down progress in general a bit (see DUMP_PROGRESS which is now defined in RELEASE version).
    • dump() shall not modify object → become const.
File size: 11.2 KB
Line 
1// ================================================================ //
2//                                                                  //
3//   File      : arb_progress.h                                     //
4//   Purpose   :                                                    //
5//                                                                  //
6//   Coded by Ralf Westram (coder@reallysoft.de) in November 2010   //
7//   Institute of Microbiology (Technical University Munich)        //
8//   http://www.arb-home.de/                                        //
9//                                                                  //
10// ================================================================ //
11
12#ifndef ARB_PROGRESS_H
13#define ARB_PROGRESS_H
14
15#ifndef ARB_ASSERT_H
16#include <arb_assert.h>
17#endif
18#ifndef ARB_ERROR_H
19#include <arb_error.h>
20#endif
21#ifndef ARBTOOLS_H
22#include <arbtools.h>
23#endif
24#ifndef ARB_MSG_H
25#include "arb_msg.h"
26#endif
27
28#if defined(DEBUG)
29#define TEST_COUNTERS
30#endif
31
32#define DUMP_PROGRESS // undefine DUMP_PROGRESS again? (when progress bug #807 is fixed)
33
34struct arb_status_implementation;
35
36namespace ArbProgress {
37    typedef long SINT;
38
39    class nestable; // nestable progresses
40
41    class counter : virtual Noncopyable {
42    protected:
43        nestable *ownedBy;
44    public:
45
46        counter(nestable *owner)
47            : ownedBy(owner)
48        {
49            arb_assert(owner); // counter w/o owner is not possible
50        }
51        virtual ~counter() {
52            arb_assert(has_correct_ownership());
53        }
54
55        virtual void inc()                              = 0;
56        virtual void implicit_inc()                     = 0;
57        virtual void inc_by(SINT count)                 = 0;
58        virtual void inc_to(SINT x)                     = 0;
59        virtual void inc_to_avoid_overflow(SINT x)      = 0;
60        virtual void child_updates_gauge(double gauge)  = 0;
61        virtual void done()                             = 0;
62        virtual void force_update()                     = 0;
63        virtual void auto_subtitles(const char *prefix) = 0;
64        virtual bool has_auto_subtitles()               = 0;
65
66#if defined(DUMP_PROGRESS)
67        virtual void dump() const = 0;
68#endif
69
70        virtual counter *clone(nestable *owner, SINT overall_count) const = 0;
71
72        bool owned_by(const nestable *by) const { return ownedBy && ownedBy == by; }
73        bool has_correct_ownership() const;
74    };
75
76    const int LEVEL_TITLE    = 0;
77    const int LEVEL_SUBTITLE = 1;
78
79    class nestable : virtual Noncopyable {
80        nestable *prev_recent;   // old value of global 'recent'
81        bool      reuse_allowed; // display may be reused by childs
82
83    protected:
84#if defined(DUMP_PROGRESS)
85        char *name; // dumped name
86#define DUMP_AS(NAME) freedup(name,NAME)
87#else
88#define DUMP_AS(NAME)
89#endif
90
91        bool     has_title;
92        counter *cntr; // counter used by this progress
93
94        static nestable       *recent; // last instance constructed
95        static arb_status_implementation *impl;   // defines implementation to display status
96
97        virtual SmartPtr<nestable> create_child_progress(const char *title, SINT overall_count) = 0;
98
99        nestable(counter *counter_, bool has_title_)
100            : reuse_allowed(false),
101              has_title(has_title_),
102              cntr(counter_)
103        {
104            prev_recent = recent;
105            recent      = this;
106
107#if defined(DUMP_PROGRESS)
108            name = NULp;
109#endif
110
111#if defined(TEST_COUNTERS)
112            accept_invalid_counters = false;
113#endif
114            arb_assert(is_correct_owner());
115        }
116    public:
117#if defined(TEST_COUNTERS)
118        bool accept_invalid_counters; // if true, do not complain about unfinished counters
119#endif
120
121        virtual ~nestable() {
122            delete cntr;
123            recent = prev_recent;
124#if defined(DUMP_PROGRESS)
125            arb_assert(name); // progress was not named (dump() wont show anything useful; use DUMP_AS in ctor of derived class)
126            free(name);
127#endif
128        }
129
130        static SmartPtr<nestable> create(const char *title, SINT overall_count);
131        static SmartPtr<nestable> create_suppressor();
132
133        bool aborted() const;
134        virtual void set_text(int level, const char *text) = 0;
135        virtual void update_gauge(double gauge)            = 0;
136
137#if defined(DUMP_PROGRESS)
138        virtual void dump() const;
139#endif
140
141        void child_sets_text(int level, const char *text) {
142            set_text(level+1-reuse_allowed+cntr->has_auto_subtitles(), text);
143        }
144        void allow_title_reuse() { reuse_allowed = true; }
145
146        void update_parent_gauge(double gauge) { cntr->child_updates_gauge(gauge); }
147        void child_terminated() { cntr->implicit_inc(); }
148
149        void initial_update() { cntr->force_update(); }
150        void force_update() { cntr->force_update(); }
151
152        void inc() { cntr->inc(); }
153        void inc_by(SINT count) { arb_assert(count>0); cntr->inc_by(count); }
154        void inc_to(SINT x) { cntr->inc_to(x); }
155        void inc_to_avoid_overflow(SINT x) { cntr->inc_to_avoid_overflow(x); }
156        void done() { cntr->done(); }
157        void auto_subtitles(const char *prefix) { cntr->auto_subtitles(prefix); }
158
159        static void show_comment(const char *comment) {
160            if (recent) recent->set_text(ArbProgress::LEVEL_SUBTITLE, comment);
161        }
162
163        bool owns(const counter *pwnd) const { return cntr && cntr == pwnd; }
164        bool is_correct_owner() const { return owns(cntr) && cntr->owned_by(this); }
165    };
166
167    inline bool counter::has_correct_ownership() const { return owned_by(ownedBy) && ownedBy->owns(this); }
168
169};
170
171class arb_progress {
172    SmartPtr<ArbProgress::nestable> used;
173
174    typedef ArbProgress::SINT PINT;
175
176    void setup(const char *title, PINT overall_count) {
177        bool acceptInvalid = false;
178        if (overall_count<0) { // assume overflow
179            GB_warningf("Warning: progress indicator underflow (%li, %s)\n"
180                        "Please report to devel@arb-home.de\n"
181                        "Estimation of time left is broken :/",
182                        overall_count, title ? title : "<no title>");
183
184#if defined(DEVEL_RALF)
185            arb_assert(0); // overflow?
186#endif
187
188            overall_count = 10L*INT_MAX; // fake sth big
189            acceptInvalid = true;
190        }
191
192        used = ArbProgress::nestable::create(title, overall_count);
193        if (acceptInvalid) accept_invalid_counters();
194        used->initial_update();
195    }
196    // cppcheck-suppress functionConst
197    void accept_invalid_counters() {
198#if defined(TEST_COUNTERS)
199        used->accept_invalid_counters = true;
200#endif
201    }
202
203public:
204    // ------------------------------
205    // recommended interface:
206
207    arb_progress(const char *title, PINT overall_count) {
208        // open a progress indicator
209        //
210        // expects to be incremented 'overall_count' times
211        //      incrementation is done either
212        //      - explicitly by calling one of the inc...()-functions below or
213        //      - implicitely by creating another arb_progress while this one remains
214        //
215        // if you can't ahead-determine the exact number of incrementations,
216        // specify an upper-limit and call .done() before dtor.
217        setup(title, overall_count);
218    }
219    explicit arb_progress(const char *title) {
220        // open a wrapping progress indicator
221        //
222        // expects NOT to be incremented explicitly!
223        //      if arb_progresses are created while this exists, they reuse the progress window.
224        //      Useful to avoid spamming the user with numerous popping-up progress windows.
225        setup(title, 0);
226    }
227    explicit arb_progress(PINT overall_count) {
228        // open counting progress (reuses text of parent progress).
229        //
230        // Useful to separate text- from gauge-display or
231        // to summarize several consecutive child-progresses into one.
232        setup(NULp, overall_count);
233    }
234
235
236    arb_progress() {
237        // plain wrapper (avoids multiple implicit increments of its parent).
238        //
239        // usage-conditions:
240        // * caller increments progress in a loop and
241        // * loop calls one or more callees and callees open multiple progress bars.
242        //
243        // in this case the parent progress would be implicitely incremented several times
244        // per loop, resulting in wrong gauge.
245        //
246        // if you know the number of opened progresses, use arb_progress(PINT),
247        // otherwise add one wrapper-progress into the loop.
248        setup(NULp, 0);
249    }
250
251private:
252    // forbid constructing arb_progress with integer types that could overflow in caller code:
253    template <typename NUM>          arb_progress(const char *t, NUM n);
254    template <typename NUM> explicit arb_progress(NUM n);
255public:
256    // explicitely overload wanted ctors:
257    explicit arb_progress(char *title) { setup(title, 0); }
258    arb_progress(const char *title, unsigned long overall_count) { arb_assert(overall_count<=LONG_MAX); setup(title, overall_count); }
259    explicit arb_progress(unsigned long overall_count) { arb_assert(overall_count<=LONG_MAX); setup(NULp, overall_count); }
260
261    void allow_title_reuse() { used->allow_title_reuse(); }
262
263    void subtitle(const char *stitle) { used->set_text(ArbProgress::LEVEL_SUBTITLE, stitle); }
264
265    GB_ERROR error_if_aborted() {
266        return aborted() ? "Operation aborted on user request" : NULp;
267    }
268
269    GB_ERROR inc_and_error_if_aborted() {
270        inc();
271        return error_if_aborted();
272    }
273
274    void inc_and_check_user_abort(GB_ERROR& error)  { if (!error) error = inc_and_error_if_aborted(); else accept_invalid_counters(); }
275    void inc_and_check_user_abort(ARB_ERROR& error) { if (!error) error = inc_and_error_if_aborted(); else accept_invalid_counters(); }
276
277    bool aborted() {
278        // true if user pressed "Abort"
279        bool aborted_ = used->aborted();
280#if defined(TEST_COUNTERS)
281        if (aborted_) accept_invalid_counters();
282#endif
283        return aborted_;
284    }
285
286    void auto_subtitles(const char *prefix) {
287        // automatically update subtitles ("prefix #/#")
288        // prefix = NULp -> switch off
289        used->auto_subtitles(prefix);
290    }
291    static void show_comment(const char *comment) {
292        // Like subtitle(), but w/o needing to know anything about a eventually open progress
293        // e.g. used to show ARB is connecting to ptserver
294        ArbProgress::nestable::show_comment(comment);
295    }
296
297    // ------------------------------
298    // less recommended interface:
299
300    void inc() { used->inc(); } // increment progress
301    const arb_progress& operator++() { inc(); return *this; } // ++progress
302
303    void inc_by(PINT count) { arb_assert(count>0); used->inc_by(count); }
304    void inc_to(PINT x) { used->inc_to(x); }
305    void inc_to_avoid_overflow(PINT x) { used->inc_to_avoid_overflow(x); }
306
307    void sub_progress_skipped() { used->child_terminated(); }
308
309    void done() { used->done(); } // set "done" (aka 100%). Useful when exiting some loop early
310#if defined(DUMP_PROGRESS)
311    void dump() const;
312#endif
313    void force_update() { used->force_update(); }
314};
315
316class arb_suppress_progress {
317    SmartPtr<ArbProgress::nestable> suppressor;
318public:
319    arb_suppress_progress()
320        : suppressor(ArbProgress::nestable::create_suppressor())
321    {}
322};
323
324#else
325#error arb_progress.h included twice
326#endif // ARB_PROGRESS_H
Note: See TracBrowser for help on using the repository browser.