GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/progress.cpp
Date: 2026-09-07 02:25:23
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 37 void begin_task(const ProgressTask* task) {
65
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 task_canceled_ = false;
66 progress_tasks_.push(task);
67
68
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 if(progress_client_) {
69 37 progress_client_->begin();
70 }
71 37 }
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 14 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 1004 void task_progress(index_t step, index_t percent) {
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1004 times.
1004 if(task_canceled_) {
102 throw TaskCanceled();
103 }
104
105
1/2
✓ Branch 0 taken 1004 times.
✗ Branch 1 not taken.
1004 if(progress_client_) {
106 1004 progress_client_->progress(step, percent);
107 }
108 1004 }
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 37 times.
37 void end_task(const ProgressTask* task) {
123
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
37 geo_assert(!progress_tasks_.empty());
124
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
37 geo_assert(progress_tasks_.top() == task);
125
126
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 if(progress_client_) {
127 37 progress_client_->end(task_canceled_);
128 }
129
130 progress_tasks_.pop();
131
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 if(progress_tasks_.empty()) {
132 37 task_canceled_ = false;
133 }
134 37 }
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 249 class TerminalProgressClient : public ProgressClient {
143 public:
144 /** \copydoc GEO::ProgressClient::begin() */
145 37 void begin() override {
146 37 const ProgressTask* task = Progress::current_progress_task();
147 37 CmdLine::ui_progress(task->task_name(), 0, 0);
148 37 }
149
150 /** \copydoc GEO::ProgressClient::progress(index_t,index_t) */
151 1004 void progress(index_t step, index_t percent) override {
152 1004 const ProgressTask* task = Progress::current_progress_task();
153 1004 CmdLine::ui_progress(task->task_name(), step, percent);
154 1004 }
155
156 /** \copydoc GEO::ProgressClient::end(bool) */
157 37 void end(bool canceled) override {
158 37 const ProgressTask* task = Progress::current_progress_task();
159 37 double elapsed = Stopwatch::now() - task->start_time();
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 if(canceled) {
161 CmdLine::ui_progress_canceled(
162 task->task_name(), elapsed, task->percent()
163 );
164 } else {
165 37 CmdLine::ui_progress_time(task->task_name(), elapsed);
166 }
167 37 }
168
169 protected:
170 /** \brief TerminalProgressClient destructor */
171 498 ~TerminalProgressClient() override {
172 498 }
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 249 void initialize() {
189 249 set_client(new TerminalProgressClient());
190 249 }
191
192 249 void terminate() {
193 249 set_client(nullptr);
194 249 }
195
196 498 void set_client(ProgressClient* client) {
197 498 progress_client_ = client;
198 498 }
199
200
1/2
✓ Branch 0 taken 1078 times.
✗ Branch 1 not taken.
1078 const ProgressTask* current_progress_task() {
201
1/2
✓ Branch 0 taken 1078 times.
✗ Branch 1 not taken.
2156 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 498 ProgressClient::~ProgressClient() {
222 498 }
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 17 ProgressTask::ProgressTask(
242 const std::string& task_name, index_t max_steps
243 17 ) :
244 17 task_name_(task_name),
245
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
17 start_time_(Stopwatch::now()),
246
2/4
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
✗ Branch 4 not taken.
17 quiet_(Logger::instance()->is_quiet()),
247 17 max_steps_(std::max(index_t(1), max_steps)),
248 17 step_(0),
249
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 percent_(0)
250 {
251
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if(!quiet_) {
252
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
17 begin_task(this);
253 }
254 17 }
255
256
257 74 ProgressTask::~ProgressTask() {
258
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
74 if(!quiet_) {
259 74 end_task(this);
260 }
261 74 }
262
263 14 void ProgressTask::reset() {
264 14 start_time_ = Stopwatch::now();
265 reset_task(this);
266 14 progress(0);
267 14 }
268
269 14 void ProgressTask::reset(index_t max_steps) {
270 14 max_steps_ = std::max(index_t(1), max_steps);
271 14 reset();
272 14 }
273
274 403 void ProgressTask::next() {
275 403 step_++;
276 403 step_ = std::min(step_, max_steps_);
277 403 update();
278 403 }
279
280 730 void ProgressTask::progress(index_t step) {
281
2/2
✓ Branch 0 taken 716 times.
✓ Branch 1 taken 14 times.
730 if(step != step_) {
282 step_ = step;
283 716 step_ = std::min(step_, max_steps_);
284 716 update();
285 }
286 730 }
287
288 702 bool ProgressTask::is_canceled() const {
289 702 return task_canceled_;
290 }
291
292 1119 void ProgressTask::update() {
293 index_t new_percent =
294
2/2
✓ Branch 0 taken 1004 times.
✓ Branch 1 taken 115 times.
1119 std::min(index_t(100), index_t(step_ * 100 / max_steps_));
295
2/2
✓ Branch 0 taken 1004 times.
✓ Branch 1 taken 115 times.
1119 if(new_percent != percent_) {
296 1004 percent_ = new_percent;
297
1/2
✓ Branch 0 taken 1004 times.
✗ Branch 1 not taken.
1004 if(!quiet_) {
298 1004 task_progress(step_, percent_);
299 }
300 }
301 1119 }
302 }
303