source: branches/help/CORE/arb_progress.cxx

Last change on this file was 18082, checked in by westram, 5 years ago
  • declare child_progress final.
File size: 12.6 KB
Line 
1// ================================================================ //
2//                                                                  //
3//   File      : arb_progress.cxx                                   //
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#include <arb_progress.h>
13#include <arb_handlers.h>
14#include <algorithm>
15
16using namespace ArbProgress;
17
18// ----------------
19//      counter
20
21struct null_counter: public counter {
22    null_counter(nestable *owner) : counter(owner) {}
23
24    void inc() OVERRIDE {}
25    void implicit_inc() OVERRIDE {}
26    void inc_by(SINT) OVERRIDE {}
27    void inc_to(SINT) OVERRIDE {}
28    void inc_to_avoid_overflow(SINT) OVERRIDE {}
29    void done() OVERRIDE {}
30    void force_update() OVERRIDE {}
31    void auto_subtitles(const char *) OVERRIDE {}
32    bool has_auto_subtitles() OVERRIDE { return false; }
33    void child_updates_gauge(double ) OVERRIDE {
34        arb_assert(0); // wont
35    }
36
37#if defined(DUMP_PROGRESS)
38    void dump() const OVERRIDE {
39        fprintf(stderr, "null_counter\n");
40    }
41#endif
42
43    counter *clone(nestable *owner, SINT ) const OVERRIDE { return new null_counter(owner); }
44};
45
46struct no_counter : public null_counter {
47    no_counter(nestable *owner) : null_counter(owner) {}
48    void inc() OVERRIDE {
49        arb_assert(0); // this is no_counter - so explicit inc() is prohibited!
50    }
51    void child_updates_gauge(double gauge) OVERRIDE { ownedBy->update_gauge(gauge); }
52
53#if defined(DUMP_PROGRESS)
54    void dump() const OVERRIDE {
55        fprintf(stderr, "no_counter (=wrapped null_counter)\n");
56    }
57#endif
58
59    counter *clone(nestable *, SINT ) const OVERRIDE {
60        arb_assert(0); // this method previously was not present => previously was cloned as null_counter!
61        return NULL;
62    }
63};
64
65static void warn_and_dump_counter_or_calling_progress(const char *warnmsg, const counter *fallback);
66
67class concrete_counter FINAL_TYPE : public counter { // derived from a Noncopyable
68    SINT    explicit_counter; // incremented by calls to inc() etc.
69    SINT    implicit_counter; // incremented by child_done()
70    SINT    maxcount;         // == 0 -> does not really count (just a wrapper for child progresses)
71    double  autoUpdateEvery;
72    double  nextAutoUpdate;
73    char   *auto_subtitle_prefix;
74    SINT    last_auto_counter;
75
76    SINT dispositive_counter() const { return std::max(implicit_counter, explicit_counter); }
77
78    void init(SINT overallCount) {
79        arb_assert(overallCount>0);
80
81        implicit_counter  = 0;
82        explicit_counter  = 0;
83        maxcount          = overallCount;
84        autoUpdateEvery   = overallCount/500.0; // update status approx. 500 times
85        nextAutoUpdate    = 0;
86    }
87
88    bool refresh_if_needed(double my_counter) { // (double needed when called for child_gauge)
89        arb_assert(my_counter>=0);
90        if (my_counter<nextAutoUpdate) return false;
91
92        {
93            double gauge = my_counter/maxcount;
94            if (gauge>=1.05) { // 5% overflow detected
95                SINT new_maxcount = maxcount*1.2+0.5;
96
97                char *warnmsg = GBS_global_string_copy("Warning: progress indicator overflow (%.1f%%/%li)\n"
98                                                       "Estimation of time left is broken (assuming new maximum: +20%% -> %li)\n"
99                                                       "Please report this problem to devel@arb-home.de\n"
100                                                       "(please mention the full text shown in the progress bar window\n"
101                                                       "AND include the newest session log (stored after quitting arb),\n"
102                                                       "otherwise we'll not be able to fix this problem)",
103                                                       (gauge-1)*100, maxcount, new_maxcount);
104
105                warn_and_dump_counter_or_calling_progress(warnmsg, this);
106                free(warnmsg);
107
108                arb_assert(0); // progress overflow
109
110                maxcount = new_maxcount;
111                gauge    = my_counter/maxcount;
112            }
113            ownedBy->update_gauge(gauge);
114        }
115
116        if (auto_subtitle_prefix) {
117            SINT count = SINT(my_counter+1);
118            if (count>last_auto_counter && count <= maxcount) {
119                const char *autosub = GBS_global_string("%s #%li/%li", auto_subtitle_prefix, count, maxcount);
120                ownedBy->set_text(LEVEL_SUBTITLE, autosub);
121                last_auto_counter   = count;
122            }
123        }
124        nextAutoUpdate += autoUpdateEvery;
125        return true;
126    }
127    void update_display_if_needed() {
128        refresh_if_needed(dispositive_counter());
129    }
130
131    void force_update() OVERRIDE {
132        double oldNext = nextAutoUpdate;
133        nextAutoUpdate = 0;
134        update_display_if_needed();
135        nextAutoUpdate = oldNext;
136    }
137
138  public:
139    concrete_counter(nestable *owner, SINT overall_count) :
140        counter(owner),
141        auto_subtitle_prefix(NULp),
142        last_auto_counter(0)
143    {
144        arb_assert(overall_count>0);
145        init(overall_count);
146    }
147    ~concrete_counter() OVERRIDE {
148        free(auto_subtitle_prefix);
149#if defined(TEST_COUNTERS)
150        if (!ownedBy->accept_invalid_counters) {
151            arb_assert(implicit_counter || explicit_counter); // progress was never incremented
152
153            arb_assert(implicit_counter <= maxcount); // overflow
154            arb_assert(explicit_counter <= maxcount); // overflow
155
156            arb_assert(dispositive_counter() == maxcount); // progress did not finish
157        }
158#endif
159    }
160
161#if defined(DUMP_PROGRESS)
162    void dump() const OVERRIDE {
163        fprintf(stderr,
164                "concrete_counter: explicit=%li, implicit=%li, maxcount=%li\n",
165                explicit_counter, implicit_counter, maxcount);
166    }
167#endif
168
169    void auto_subtitles(const char *prefix) OVERRIDE {
170        // activates use of a prefixed counter as subtitle: "prefix #some/all"
171        arb_assert(!auto_subtitle_prefix);
172        freedup(auto_subtitle_prefix, prefix);
173        force_update();
174    }
175    bool has_auto_subtitles() OVERRIDE { return auto_subtitle_prefix; }
176
177    void inc()          OVERRIDE { explicit_counter += 1; update_display_if_needed(); }
178    void implicit_inc() OVERRIDE { implicit_counter += 1; update_display_if_needed(); }
179
180    void inc_to(SINT x) OVERRIDE {
181        explicit_counter = std::max(explicit_counter, x);
182        update_display_if_needed();
183    }
184    void inc_to_avoid_overflow(SINT x) OVERRIDE {
185        inc_to(std::min(maxcount, x));
186    }
187    void inc_by(SINT count) { inc_to(explicit_counter+count); }
188
189    void done() OVERRIDE {
190        implicit_counter = explicit_counter = maxcount;
191        force_update();
192    }
193
194    counter *clone(nestable *owner, SINT overall_count) const OVERRIDE {
195        return new concrete_counter(owner, overall_count);
196    }
197    void child_updates_gauge(double child_gauge) OVERRIDE {
198        refresh_if_needed(dispositive_counter()+child_gauge);
199    }
200};
201
202// -----------------
203//      progress
204
205static counter *make_counter(nestable *owner, SINT overall_count) {
206    if (overall_count) return new concrete_counter(owner, overall_count);
207    return new no_counter(owner);
208}
209
210class child_progress FINAL_TYPE : public nestable { // derived from a Noncopyable
211    nestable *parent;
212    static child_progress *child_triggering_update;
213
214public:
215    child_progress(nestable *parent_, const char *title, SINT overall_count) :
216        nestable(make_counter(this, overall_count), title),
217        parent(parent_)
218    {
219        set_text(LEVEL_TITLE, title);
220        DUMP_AS(GBS_global_string("child: %s", title ? title : "<untitled>"));
221    }
222    ~child_progress() OVERRIDE {
223        parent->child_terminated();
224    }
225
226    SmartPtr<nestable> create_child_progress(const char *title, SINT overall_count) OVERRIDE {
227        return new child_progress(this, title, overall_count);
228    }
229
230#if defined(DUMP_PROGRESS)
231    void dump() const OVERRIDE {
232        nestable::dump();
233        fprintf(stderr, "is child of\n");
234        parent->dump();
235    }
236#endif
237
238    void set_text(int level, const char *text) OVERRIDE { parent->child_sets_text(level+has_title-1, text); }
239
240    void update_gauge(double gauge) OVERRIDE {
241        if (!child_triggering_update) {
242            LocallyModify<child_progress*> storeUpdater(child_triggering_update, this);
243            parent->update_parent_gauge(gauge);
244        }
245        else {
246            parent->update_parent_gauge(gauge);
247        }
248    }
249
250    static const child_progress *calling_child_progress() { return child_triggering_update; }
251};
252
253child_progress *child_progress::child_triggering_update = NULp;
254
255static void warn_and_dump_counter_or_calling_progress(const char *warnmsg, const counter *fallback) {
256    GB_warning(warnmsg);
257    fprintf(stderr, "-------------------- warn_and_dump_counter_or_calling_progress [start]\n");
258    fputs(warnmsg, stderr);
259    fputc('\n', stderr);
260#if defined(DUMP_PROGRESS)
261    const child_progress *updater = child_progress::calling_child_progress();
262    if (updater) {
263        updater->dump();
264    }
265    else { // assume this counter has been updated directly
266        fallback->dump();
267    }
268#endif
269    fprintf(stderr, "-------------------- warn_and_dump_counter_or_calling_progress [end]\n");
270}
271
272class initial_progress: public nestable {
273
274public:
275    initial_progress(const char *title, counter *counter_)
276        : nestable(counter_, title)
277    {
278        DUMP_AS(GBS_global_string("initial: %s", title ? title : "<untitled>"));
279        if (!title) title = "..."; // use fake title (nestable got no title, so it will be replaced by child title)
280        impl->openstatus(title);
281    }
282    ~initial_progress() OVERRIDE {
283        update_gauge(1.0); // due to numeric issues it often only counts up to 99.999%
284        impl->closestatus();
285    }
286
287    SmartPtr<nestable> create_child_progress(const char *title, SINT overall_count) OVERRIDE {
288        return new child_progress(this, title, overall_count);
289    }
290
291    void set_text(int level, const char *text) OVERRIDE {
292        if (!text) return;
293        switch (level+has_title-1) {
294            case LEVEL_TITLE: impl->set_title(text); break;
295            case LEVEL_SUBTITLE: impl->set_subtitle(text); break;
296        }
297    }
298
299    void update_gauge(double gauge) OVERRIDE {
300        arb_assert(gauge>=0);
301        arb_assert(gauge<=1);
302        impl->set_gauge(gauge);
303    }
304};
305
306struct initial_selfRef_progress : public initial_progress {
307    initial_selfRef_progress(const char *title, SINT overall_count) :
308        initial_progress(title, make_counter(this, overall_count))
309    {}
310};
311
312class null_progress: public nestable {
313    null_progress(counter *counter_to_clone, SINT overall_count) : // only used by create_child_progress
314        nestable(counter_to_clone->clone(this, overall_count), false)
315    {
316        DUMP_AS("null_progress (cloned)");
317    }
318public:
319    explicit null_progress() : // creates a suppressor
320        nestable(new null_counter(this), false)
321    {
322        DUMP_AS("null_progress (suppressor)");
323    }
324
325    SmartPtr<nestable> create_child_progress(const char*, SINT overall_count) OVERRIDE {
326        return new null_progress(cntr, overall_count);
327    }
328    void set_text(int,const char*) OVERRIDE {}
329    void update_gauge(double) OVERRIDE {}
330};
331
332// -------------------------
333//      progress factory
334
335nestable       *nestable::recent = NULp;
336arb_status_implementation *nestable::impl   = NULp; // defines implementation to display status
337
338SmartPtr<nestable> nestable::create(const char *title, SINT overall_count) {
339    if (recent) {
340        return recent->create_child_progress(title, overall_count);
341    }
342    impl = &active_arb_handlers->status;
343    return new initial_selfRef_progress(title, overall_count);
344}
345
346SmartPtr<nestable> nestable::create_suppressor() {
347    return new null_progress;
348}
349
350bool nestable::aborted() const {
351    if (impl) return impl->user_abort();
352    return false;
353}
354
355// --------------------------
356//      progress dumpers
357
358#if defined(DUMP_PROGRESS)
359
360// not inlined in header (otherwise they are missing while debugging)
361
362void nestable::dump() const {
363    fprintf(stderr, "progress '%s'\n", name);
364    fprintf(stderr, "counter: ");
365    cntr->dump();
366}
367void arb_progress::dump() const {
368    fprintf(stderr, "--------------------\n");
369    used->dump();
370}
371
372#endif
373
Note: See TracBrowser for help on using the repository browser.