source: branches/properties/CORE/arb_progress.cxx

Last change on this file was 18959, checked in by westram, 3 years ago
File size: 14.1 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                last_auto_counter   = count; // important to set BEFORE calling set_text (which may recurse!)
120                const char *autosub = GBS_global_string("%s #%li/%li", auto_subtitle_prefix, count, maxcount);
121                ownedBy->set_text(LEVEL_SUBTITLE, autosub);
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
205inline const char *spaced_weightable(const weightable& speed) {
206    if (!speed.is_weighted()) return "";
207    double phase1_weight = speed.get_adjusted_gauge(0.5);
208    return GBS_global_string(" (weighted: %.3f%%:%.3f%%)", phase1_weight*100, (1-phase1_weight)*100);
209}
210
211static counter *make_counter(nestable *owner, SINT overall_count) {
212    if (overall_count) return new concrete_counter(owner, overall_count);
213    return new no_counter(owner);
214}
215
216class child_progress FINAL_TYPE : public nestable { // derived from a Noncopyable
217    nestable *parent;
218    static child_progress *child_triggering_update;
219
220public:
221    child_progress(nestable *parent_, const char *title, SINT overall_count, const weightable& speed_) :
222        nestable(make_counter(this, overall_count), title, speed_),
223        parent(parent_)
224    {
225        set_text(LEVEL_TITLE, title);
226        DUMP_AS(GBS_global_string("child: %s%s", title ? title : "<untitled>", spaced_weightable(speed_)));
227    }
228    ~child_progress() OVERRIDE {
229        parent->child_terminated();
230    }
231
232    SmartPtr<nestable> create_child_progress(const char *title, SINT overall_count, const weightable& speed_) OVERRIDE {
233        return new child_progress(this, title, overall_count, speed_);
234    }
235
236#if defined(DUMP_PROGRESS)
237    void dump() const OVERRIDE {
238        nestable::dump();
239        fprintf(stderr, "is child of\n");
240        parent->dump();
241    }
242#endif
243
244    void set_text(int level, const char *text) OVERRIDE { parent->child_sets_text(level+has_title-1, text); }
245
246    void update_gauge(double gauge) OVERRIDE {
247        double adjusted_gauge = speed.get_adjusted_gauge(gauge);
248        if (!child_triggering_update) {
249            LocallyModify<child_progress*> storeUpdater(child_triggering_update, this);
250            parent->update_parent_gauge(adjusted_gauge);
251        }
252        else {
253            parent->update_parent_gauge(adjusted_gauge);
254        }
255    }
256
257    static const child_progress *calling_child_progress() { return child_triggering_update; }
258};
259
260child_progress *child_progress::child_triggering_update = NULp;
261
262static void warn_and_dump_counter_or_calling_progress(const char *warnmsg, const counter *fallback) {
263    GB_warning(warnmsg);
264    fprintf(stderr, "-------------------- warn_and_dump_counter_or_calling_progress [start]\n");
265    fputs(warnmsg, stderr);
266    fputc('\n', stderr);
267#if defined(DUMP_PROGRESS)
268    const child_progress *updater = child_progress::calling_child_progress();
269    if (updater) {
270        updater->dump();
271    }
272    else { // assume this counter has been updated directly
273        fallback->dump();
274    }
275#endif
276    fprintf(stderr, "-------------------- warn_and_dump_counter_or_calling_progress [end]\n");
277}
278
279class initial_progress: public nestable {
280
281public:
282    initial_progress(const char *title, counter *counter_, const weightable& speed_)
283        : nestable(counter_, title, speed_)
284    {
285        DUMP_AS(GBS_global_string("initial: %s%s", title ? title : "<untitled>", spaced_weightable(speed_)));
286        if (!title) title = "..."; // use fake title (nestable got no title, so it will be replaced by child title)
287        impl->openstatus(title);
288    }
289    ~initial_progress() OVERRIDE {
290        update_gauge(1.0); // due to numeric issues it often only counts up to 99.999%
291        impl->closestatus();
292    }
293
294    SmartPtr<nestable> create_child_progress(const char *title, SINT overall_count, const weightable& speed_) OVERRIDE {
295        return new child_progress(this, title, overall_count, speed_);
296    }
297
298    void set_text(int level, const char *text) OVERRIDE {
299        if (!text) return;
300        switch (level+has_title-1) {
301            case LEVEL_TITLE: impl->set_title(text); break;
302            case LEVEL_SUBTITLE:
303#if defined(FORCE_WEIGHTED_ESTIMATION)
304                if (speed.is_weighted() || have_weighted_progress()) {
305                    cntr->force_update(); // send most recent gauge
306                    impl->set_subtitle("REQUEST_ESTIMATION"); // hack: force estimation
307                }
308#endif
309                impl->set_subtitle(text);
310                break;
311        }
312    }
313
314    void update_gauge(double gauge) OVERRIDE {
315        arb_assert(gauge>=0);
316        arb_assert(gauge<=1);
317
318        double adjusted_gauge = speed.get_adjusted_gauge(gauge);
319        impl->set_gauge(adjusted_gauge);
320    }
321};
322
323struct initial_selfRef_progress : public initial_progress {
324    initial_selfRef_progress(const char *title, SINT overall_count, const weightable& speed_) :
325        initial_progress(title, make_counter(this, overall_count), speed_)
326    {}
327};
328
329class null_progress: public nestable {
330    null_progress(counter *counter_to_clone, SINT overall_count, const weightable& speed_) : // only used by create_child_progress
331        nestable(counter_to_clone->clone(this, overall_count), false, speed_)
332    {
333        DUMP_AS("null_progress (cloned)");
334    }
335public:
336    explicit null_progress() : // creates a suppressor
337        nestable(new null_counter(this), false, weightable())
338    {
339        DUMP_AS("null_progress (suppressor)");
340    }
341
342    SmartPtr<nestable> create_child_progress(const char*, SINT overall_count, const weightable& speed_) OVERRIDE {
343        return new null_progress(cntr, overall_count, speed_);
344    }
345    void set_text(int,const char*) OVERRIDE {}
346    void update_gauge(double) OVERRIDE {
347        arb_assert(!speed.is_weighted()); // you can't weight nothing!
348    }
349};
350
351// -------------------------
352//      progress factory
353
354nestable                  *nestable::recent = NULp;
355arb_status_implementation *nestable::impl   = NULp; // defines implementation to display status
356
357SmartPtr<nestable> nestable::create(const char *title, SINT overall_count, const double *phase1_fraction) {
358    weightable weighted = phase1_fraction ? weightable(*phase1_fraction) : weightable();
359    arb_assert(implicated(weighted.is_weighted(), overall_count == 2UL));
360
361    if (recent) {
362        return recent->create_child_progress(title, overall_count, weighted);
363    }
364    impl = &active_arb_handlers->status;
365    return new initial_selfRef_progress(title, overall_count, weighted);
366}
367
368SmartPtr<nestable> nestable::create_suppressor() {
369    return new null_progress;
370}
371
372#if defined(FORCE_WEIGHTED_ESTIMATION)
373bool nestable::have_weighted_progress() {
374    return recent->this_or_recent_is_weighted();
375}
376#endif
377
378bool nestable::aborted() const {
379    if (impl) return impl->user_abort();
380    return false;
381}
382
383
384// --------------------------
385//      progress dumpers
386
387#if defined(DUMP_PROGRESS)
388
389// not inlined in header (otherwise they are missing while debugging)
390
391void nestable::dump() const {
392    fprintf(stderr, "progress %s\n", name);
393    fprintf(stderr, "counter: ");
394    cntr->dump();
395}
396
397void arb_progress::dump() const {
398    fprintf(stderr, "--------------------\n");
399    used->dump();
400}
401
402#endif
403
Note: See TracBrowser for help on using the repository browser.