GCC Code Coverage Report


Directory: ./
File: basic/progress.cpp
Date: 2026-09-27 03:10:11
Exec Total Coverage
Lines: 102 115 88.7%
Functions: 21 25 84.0%
Branches: 31 68 45.6%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram/basic/progress.h>
41 #include <geogram/basic/command_line.h>
42 #include <geogram/basic/assert.h>
43 #include <geogram/basic/stopwatch.h>
44 #include <stack>
45
46 namespace {
47
48 using namespace GEO;
49
50 ProgressClient_var progress_client_;
51 std::stack<const ProgressTask*> progress_tasks_;
52 bool task_canceled_ = false;
53
54 /**
55 * \brief Notifies clients that a task has started.
56 * \details This function is called when a new ProgressTask is
57 * constructed. It pushes the given \p task to the task stack and notifies
58 * the registered LoggerClient to start listening to the progress of the
59 * task.
60 * \param[in] task the task being started
61 * \see ProgressTask::ProgressTask()
62 * \see LoggerClient::begin()
63 */
64 38 void begin_task(const ProgressTask* task) {
65
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 task_canceled_ = false;
66 progress_tasks_.push(task);
67
68
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 if(progress_client_) {
69 38 progress_client_->begin();
70 }
71 38 }
72
73 /**
74 * \brief Notifies clients that a task has restarted.
75 * \details This function is called when a new ProgressTask is reset.
76 * In some contexts, the same ProgressTask instance is reused several
77 * times, and the progress is restarted by calling ProgressTask::reset().
78 * We must consider this situation as if a new ProgressTask had been
79 * created.
80 * \note It does \b not push the given \p task to the task stack and does
81 * \b not notify the registered LoggerClient to start listening to the
82 * progress of the task.
83 * \param[in] task the task being restarted
84 * \see ProgressTask::reset()
85 */
86 void reset_task(const ProgressTask* task) {
87 geo_argused(task);
88 15 task_canceled_ = false;
89 }
90
91 /**
92 * \brief Notifies clients that a task progresses.
93 * \details This function is called by ProgressTask::progress() to
94 * notify registered LoggerClient%s that the execution step has
95 * changed.
96 * \param[in] step the current progress step
97 * \param[in] percent the percentage of completion of the task
98 * \see LoggerClient::progress()
99 */
100 1044 void task_progress(index_t step, index_t percent) {
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1044 times.
1044 if(task_canceled_) {
102 ✗ throw TaskCanceled();
103 }
104
105
1/2
✓ Branch 0 taken 1044 times.
✗ Branch 1 not taken.
1044 if(progress_client_) {
106 1044 progress_client_->progress(step, percent);
107 }
108 1044 }
109
110 /**
111 * \brief Notifies clients that a task has ended.
112 * \details This function is called by the ProgressTask destructor. It
113 * restores the previous task from the task stack and notifies registered
114 * LoggerClient%s that the current task has ended.
115 * \param[in] task the task being terminated
116 * \note The parameter \p task is not strictly necessary here, but we want
117 * to make sure that the ProgressTask being destroyed is really the one at
118 * the top of the stack.
119 * \see ProgressTask::~ProgressTask()
120 * \see LoggerClient::end()
121 */
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 void end_task(const ProgressTask* task) {
123
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
38 geo_assert(!progress_tasks_.empty());
124
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
38 geo_assert(progress_tasks_.top() == task);
125
126
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 if(progress_client_) {
127 38 progress_client_->end(task_canceled_);
128 }
129
130 progress_tasks_.pop();
131
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 if(progress_tasks_.empty()) {
132 38 task_canceled_ = false;
133 }
134 38 }
135
136 /**
137 * \brief Logs task progress to the console
138 * \details TerminalProgressClient is an implementation of LoggerClient
139 * that logs progress to the console using console progress functions.
140 * \see CmdLine
141 */
142 253 class TerminalProgressClient : public ProgressClient {
143 public:
144 /** \copydoc GEO::ProgressClient::begin() */
145 38 void begin() override {
146 38 const ProgressTask* task = Progress::current_progress_task();
147 38 CmdLine::ui_progress(task->task_name(), 0, 0);
148 38 }
149
150 /** \copydoc GEO::ProgressClient::progress(index_t,index_t) */
151 1044 void progress(index_t step, index_t percent) override {
152 1044 const ProgressTask* task = Progress::current_progress_task();
153 1044 CmdLine::ui_progress(task->task_name(), step, percent);
154 1044 }
155
156 /** \copydoc GEO::ProgressClient::end(bool) */
157 38 void end(bool canceled) override {
158 38 const ProgressTask* task = Progress::current_progress_task();
159 38 double elapsed = Stopwatch::now() - task->start_time();
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if(canceled) {
161 ✗ CmdLine::ui_progress_canceled(
162 task->task_name(), elapsed, task->percent()
163 );
164 } else {
165 38 CmdLine::ui_progress_time(task->task_name(), elapsed);
166 }
167 38 }
168
169 protected:
170 /** \brief TerminalProgressClient destructor */
171 506 ~TerminalProgressClient() override {
172 506 }
173 };
174 }
175
176 /****************************************************************************/
177
178 namespace GEO {
179
180 ✗ const char* TaskCanceled::what() const GEO_NOEXCEPT {
181 ✗ return "Task canceled";
182 }
183
184 /************************************************************************/
185
186 namespace Progress {
187
188 253 void initialize() {
189 253 set_client(new TerminalProgressClient());
190 253 }
191
192 253 void terminate() {
193 253 set_client(nullptr);
194 253 }
195
196 506 void set_client(ProgressClient* client) {
197 506 progress_client_ = client;
198 506 }
199
200
1/2
✓ Branch 0 taken 1120 times.
✗ Branch 1 not taken.
1120 const ProgressTask* current_progress_task() {
201
1/2
✓ Branch 0 taken 1120 times.
✗ Branch 1 not taken.
2240 return progress_tasks_.empty() ? nullptr : progress_tasks_.top();
202 }
203
204 ✗ void cancel() {
205 ✗ if(!progress_tasks_.empty()) {
206 ✗ task_canceled_ = true;
207 }
208 ✗ }
209
210 ✗ bool is_canceled() {
211 ✗ return task_canceled_;
212 }
213
214 ✗ void clear_canceled() {
215 ✗ task_canceled_ = false;
216 ✗ }
217 }
218
219 /************************************************************************/
220
221 506 ProgressClient::~ProgressClient() {
222 506 }
223
224 /************************************************************************/
225
226 20 ProgressTask::ProgressTask(
227 const std::string& task_name, index_t max_steps, bool quiet
228 20 ) :
229 20 task_name_(task_name),
230
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 start_time_(Stopwatch::now()),
231
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 quiet_(quiet),
232 20 max_steps_(std::max(index_t(1), max_steps)),
233 20 step_(0),
234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 percent_(0)
235 {
236
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if(!quiet_) {
237
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 begin_task(this);
238 }
239 20 }
240
241 18 ProgressTask::ProgressTask(
242 const std::string& task_name, index_t max_steps
243 18 ) :
244 18 task_name_(task_name),
245
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 start_time_(Stopwatch::now()),
246
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
✗ Branch 4 not taken.
18 quiet_(Logger::instance()->is_quiet()),
247 18 max_steps_(std::max(index_t(1), max_steps)),
248 18 step_(0),
249
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 percent_(0)
250 {
251
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if(!quiet_) {
252
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 begin_task(this);
253 }
254 18 }
255
256
257 76 ProgressTask::~ProgressTask() {
258
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
76 if(!quiet_) {
259 76 end_task(this);
260 }
261 76 }
262
263 15 void ProgressTask::reset() {
264 15 start_time_ = Stopwatch::now();
265 reset_task(this);
266 15 progress(0);
267 15 }
268
269 15 void ProgressTask::reset(index_t max_steps) {
270 15 max_steps_ = std::max(index_t(1), max_steps);
271 15 reset();
272 15 }
273
274 443 void ProgressTask::next() {
275 443 step_++;
276 443 step_ = std::min(step_, max_steps_);
277 443 update();
278 443 }
279
280 731 void ProgressTask::progress(index_t step) {
281
2/2
✓ Branch 0 taken 716 times.
✓ Branch 1 taken 15 times.
731 if(step != step_) {
282 step_ = step;
283 716 step_ = std::min(step_, max_steps_);
284 716 update();
285 }
286 731 }
287
288 702 bool ProgressTask::is_canceled() const {
289 702 return task_canceled_;
290 }
291
292 1159 void ProgressTask::update() {
293 index_t new_percent =
294
2/2
✓ Branch 0 taken 1044 times.
✓ Branch 1 taken 115 times.
1159 std::min(index_t(100), index_t(step_ * 100 / max_steps_));
295
2/2
✓ Branch 0 taken 1044 times.
✓ Branch 1 taken 115 times.
1159 if(new_percent != percent_) {
296 1044 percent_ = new_percent;
297
1/2
✓ Branch 0 taken 1044 times.
✗ Branch 1 not taken.
1044 if(!quiet_) {
298 1044 task_progress(step_, percent_);
299 }
300 }
301 1159 }
302 }
303