source: trunk/CORE/arb_progress.h

Last change on this file was 19415, checked in by westram, 10 months ago
  • fix ARB_edge::iteration_count():
    • did report negative counts for broken trees (see previous commit)
    • added tests; fixed behavior: now returns zero in these cases.
  • accept zero as increment amount to arb_progress::inc_by().
File size: 13.5 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#if !defined(DEVEL_RELEASE)
33# define FORCE_WEIGHTED_ESTIMATION // see also ../WINDOW/AW_status.cxx@FORCEWEIGHTEDESTIMATION
34#endif
35
36#define DUMP_PROGRESS // undefine DUMP_PROGRESS again? (when progress bug #807 is fixed)
37
38struct arb_status_implementation;
39
40namespace ArbProgress {
41    typedef long SINT;
42
43    class nestable;   // nestable progresses
44
45    class weightable { // can weight gauge
46        bool weighted;
47
48        double phase1_weight;
49        double phase2_weight() const { return 1.0-phase1_weight; }
50
51    public:
52        weightable() :
53            weighted(false),
54            phase1_weight(0.0)
55        {}
56
57        explicit weightable(double phase1_weight_) :
58            weighted(true),
59            phase1_weight(phase1_weight_)
60        {}
61
62        bool is_weighted() const { return weighted; }
63
64        double get_adjusted_gauge(double linear_gauge) const {
65            arb_assert(linear_gauge>=0);
66            arb_assert(linear_gauge<=1);
67
68            double adjusted_gauge = linear_gauge;
69            if (is_weighted()) {
70                if (linear_gauge<0.5) adjusted_gauge = phase1_weight * linear_gauge * 2;
71                else                  adjusted_gauge = phase1_weight + phase2_weight() * (linear_gauge-0.5)*2;
72            }
73
74            arb_assert(adjusted_gauge>=0);
75            arb_assert(adjusted_gauge<=1);
76
77            return adjusted_gauge;
78        }
79    };
80
81    class counter : virtual Noncopyable {
82    protected:
83        nestable *ownedBy;
84    public:
85
86        counter(nestable *owner)
87            : ownedBy(owner)
88        {
89            arb_assert(owner); // counter w/o owner is not possible
90        }
91        virtual ~counter() {
92            arb_assert(has_correct_ownership());
93        }
94
95        virtual void inc()                              = 0;
96        virtual void implicit_inc()                     = 0;
97        virtual void inc_by(SINT count)                 = 0;
98        virtual void inc_to(SINT x)                     = 0;
99        virtual void inc_to_avoid_overflow(SINT x)      = 0;
100        virtual void child_updates_gauge(double gauge)  = 0;
101        virtual void done()                             = 0;
102        virtual void force_update()                     = 0;
103        virtual void auto_subtitles(const char *prefix) = 0;
104        virtual bool has_auto_subtitles()               = 0;
105
106#if defined(DUMP_PROGRESS)
107        virtual void dump() const = 0;
108#endif
109
110        virtual counter *clone(nestable *owner, SINT overall_count) const = 0;
111
112        bool owned_by(const nestable *by) const { return ownedBy && ownedBy == by; }
113        bool has_correct_ownership() const;
114    };
115
116    const int LEVEL_TITLE    = 0;
117    const int LEVEL_SUBTITLE = 1;
118
119    class nestable : virtual Noncopyable {
120        nestable *prev_recent;   // old value of global 'recent'
121        bool      reuse_allowed; // display may be reused by childs
122
123    protected:
124#if defined(DUMP_PROGRESS)
125        char *name; // dumped name
126#define DUMP_AS(NAME) freedup(name,NAME)
127#else
128#define DUMP_AS(NAME)
129#endif
130
131        bool        has_title;
132        counter    *cntr; // counter used by this progress
133        weightable  speed;
134
135        static nestable                  *recent; // last instance constructed
136        static arb_status_implementation *impl;   // defines implementation to display status
137
138        virtual SmartPtr<nestable> create_child_progress(const char *title, SINT overall_count, const weightable& speed_) = 0;
139
140        nestable(counter *counter_, bool has_title_, const weightable& speed_)
141            : reuse_allowed(false),
142              has_title(has_title_),
143              cntr(counter_),
144              speed(speed_)
145        {
146            prev_recent = recent;
147            recent      = this;
148
149#if defined(DUMP_PROGRESS)
150            name = NULp;
151#endif
152
153#if defined(TEST_COUNTERS)
154            accept_invalid_counters = false;
155#endif
156            arb_assert(is_correct_owner());
157        }
158    public:
159#if defined(TEST_COUNTERS)
160        bool accept_invalid_counters; // if true, do not complain about unfinished counters
161#endif
162
163        virtual ~nestable() {
164            arb_assert(this == recent); // nestable|s should be destroyed in-order!
165
166            delete cntr;
167            recent = prev_recent;
168#if defined(DUMP_PROGRESS)
169            arb_assert(name); // progress was not named (dump() wont show anything useful; use DUMP_AS in ctor of derived class)
170            free(name);
171#endif
172        }
173
174        static SmartPtr<nestable> create(const char *title, SINT overall_count, const double *phase1_fraction);
175        static SmartPtr<nestable> create_suppressor();
176
177#if defined(FORCE_WEIGHTED_ESTIMATION)
178        static bool have_weighted_progress();
179        bool this_or_recent_is_weighted() const {
180            return speed.is_weighted() || (prev_recent && prev_recent->this_or_recent_is_weighted());
181        }
182#endif
183
184        bool aborted() const;
185        virtual void set_text(int level, const char *text) = 0;
186        virtual void update_gauge(double gauge)            = 0;
187
188#if defined(DUMP_PROGRESS)
189        virtual void dump() const;
190#endif
191
192        void child_sets_text(int level, const char *text) {
193            set_text(level+1-reuse_allowed+cntr->has_auto_subtitles(), text);
194        }
195        void allow_title_reuse() { reuse_allowed = true; }
196
197        void update_parent_gauge(double gauge) { cntr->child_updates_gauge(gauge); }
198        void child_terminated() { cntr->implicit_inc(); }
199
200        void initial_update() { cntr->force_update(); }
201        void force_update() { cntr->force_update(); }
202
203        void inc() { cntr->inc(); }
204        void inc_by(SINT count) { arb_assert(count>=0); cntr->inc_by(count); }
205        void inc_to(SINT x) { cntr->inc_to(x); }
206        void inc_to_avoid_overflow(SINT x) { cntr->inc_to_avoid_overflow(x); }
207        void done() { cntr->done(); }
208        void auto_subtitles(const char *prefix) { cntr->auto_subtitles(prefix); }
209
210        static void show_comment(const char *comment) {
211            if (recent) recent->set_text(ArbProgress::LEVEL_SUBTITLE, comment);
212        }
213
214        bool owns(const counter *pwnd) const { return cntr && cntr == pwnd; }
215        bool is_correct_owner() const { return owns(cntr) && cntr->owned_by(this); }
216    };
217
218    inline bool counter::has_correct_ownership() const { return owned_by(ownedBy) && ownedBy->owns(this); }
219
220};
221
222// ----------------------
223//      arb_progress
224
225enum WeightedProgressMarker { WEIGHTED }; // just a marker to emphasize weighted progresses
226
227class arb_progress : virtual Noncopyable {
228    SmartPtr<ArbProgress::nestable> used;
229
230    typedef ArbProgress::SINT PINT;
231
232    void setup(const char *title, PINT overall_count, const double *phase1_fraction = NULp) {
233        bool acceptInvalid = false;
234        if (overall_count<0) { // assume overflow
235            GB_warningf("Warning: progress indicator underflow (%li, %s)\n"
236                        "Please report to devel@arb-home.de\n"
237                        "Estimation of time left is broken :/",
238                        overall_count, title ? title : "<no title>");
239
240#if defined(DEVEL_RALF)
241            arb_assert(0); // overflow?
242#endif
243
244            overall_count = 10L*INT_MAX; // fake sth big
245            acceptInvalid = true;
246        }
247
248        used = ArbProgress::nestable::create(title, overall_count, phase1_fraction);
249        if (acceptInvalid) accept_invalid_counters();
250        used->initial_update();
251    }
252    // cppcheck-suppress functionConst
253    void accept_invalid_counters() {
254#if defined(TEST_COUNTERS)
255        used->accept_invalid_counters = true;
256#endif
257    }
258
259public:
260    // ------------------------------
261    // recommended interface:
262
263    arb_progress(const char *title, PINT overall_count) {
264        // open a progress indicator
265        //
266        // expects to be incremented 'overall_count' times
267        //      incrementation is done either
268        //      - explicitly by calling one of the inc...()-functions below or
269        //      - implicitely by creating another arb_progress while this one remains
270        //
271        // if you can't ahead-determine the exact number of incrementations,
272        // specify an upper-limit and call .done() before dtor.
273        setup(title, overall_count);
274    }
275    explicit arb_progress(const char *title) {
276        // open a wrapping progress indicator
277        //
278        // expects NOT to be incremented explicitly!
279        //      if arb_progresses are created while this exists, they reuse the progress window.
280        //      Useful to avoid spamming the user with numerous popping-up progress windows.
281        setup(title, 0);
282    }
283    explicit arb_progress(PINT overall_count) {
284        // open counting progress (reuses text of parent progress).
285        //
286        // Useful to separate text- from gauge-display or
287        // to summarize several consecutive child-progresses into one.
288        setup(NULp, overall_count);
289    }
290
291
292    arb_progress() {
293        // plain wrapper (avoids multiple implicit increments of its parent).
294        //
295        // usage-conditions:
296        // * caller increments progress in a loop and
297        // * loop calls one or more callees and callees open multiple progress bars.
298        //
299        // in this case the parent progress would be implicitely incremented several times
300        // per loop, resulting in wrong gauge.
301        //
302        // if you know the number of opened progresses, use arb_progress(PINT),
303        // otherwise add one wrapper-progress into the loop.
304        setup(NULp, 0);
305    }
306
307private:
308    // forbid constructing arb_progress with integer types that could overflow in caller code:
309    template <typename NUM>          arb_progress(const char *t, NUM n);
310    template <typename NUM> explicit arb_progress(NUM n);
311public:
312    // explicitely overload wanted ctors:
313    explicit arb_progress(char *title) { setup(title, 0); }
314    explicit arb_progress(unsigned long overall_count) { arb_assert(overall_count<=LONG_MAX); setup(NULp, overall_count); }
315    arb_progress(const char *title, unsigned long overall_count) { arb_assert(overall_count<=LONG_MAX); setup(title, overall_count); }
316    arb_progress(WeightedProgressMarker, const char *title, double phase1_fraction) { setup(title, 2UL, &phase1_fraction); } // a name weighted progress (always has count of 2)
317    arb_progress(WeightedProgressMarker, double phase1_fraction)                    { setup(NULp,  2UL, &phase1_fraction); } // an unnamed  weighted progress (always has count of 2)
318
319    void allow_title_reuse() { used->allow_title_reuse(); }
320
321    void subtitle(const char *stitle) { used->set_text(ArbProgress::LEVEL_SUBTITLE, stitle); }
322
323    GB_ERROR error_if_aborted() {
324        return aborted() ? "Operation aborted on user request" : NULp;
325    }
326
327    GB_ERROR inc_and_error_if_aborted() {
328        inc();
329        return error_if_aborted();
330    }
331
332    void inc_and_check_user_abort(GB_ERROR& error)  { if (!error) error = inc_and_error_if_aborted(); else accept_invalid_counters(); }
333    void inc_and_check_user_abort(ARB_ERROR& error) { if (!error) error = inc_and_error_if_aborted(); else accept_invalid_counters(); }
334
335    bool aborted() {
336        // true if user pressed "Abort"
337        bool aborted_ = used->aborted();
338#if defined(TEST_COUNTERS)
339        if (aborted_) accept_invalid_counters();
340#endif
341        return aborted_;
342    }
343
344    void auto_subtitles(const char *prefix) {
345        // automatically update subtitles ("prefix #/#")
346        // prefix = NULp -> switch off
347        used->auto_subtitles(prefix);
348    }
349    static void show_comment(const char *comment) {
350        // Like subtitle(), but w/o needing to know anything about a eventually open progress
351        // e.g. used to show ARB is connecting to ptserver
352        ArbProgress::nestable::show_comment(comment);
353    }
354
355    // ------------------------------
356    // less recommended interface:
357
358    void inc() { used->inc(); } // increment progress
359    const arb_progress& operator++() { inc(); return *this; } // ++progress
360
361    void inc_by(PINT count) { arb_assert(count>=0); used->inc_by(count); }
362    void inc_to(PINT x) { used->inc_to(x); }
363    void inc_to_avoid_overflow(PINT x) { used->inc_to_avoid_overflow(x); }
364
365    void sub_progress_skipped() { used->child_terminated(); }
366
367    void done() { used->done(); } // set "done" (aka 100%). Useful when exiting some loop early
368#if defined(DUMP_PROGRESS)
369    void dump() const;
370#endif
371    void force_update() { used->force_update(); }
372};
373
374// -------------------------------
375//      arb_suppress_progress
376
377class arb_suppress_progress : virtual Noncopyable {
378    SmartPtr<ArbProgress::nestable> suppressor;
379public:
380    arb_suppress_progress()
381        : suppressor(ArbProgress::nestable::create_suppressor())
382    {}
383};
384
385
386#else
387#error arb_progress.h included twice
388#endif // ARB_PROGRESS_H
Note: See TracBrowser for help on using the repository browser.