GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/progress.cpp
Date: 2026-09-07 02:37:58
Exec Total Coverage
Lines: 108 121 89.3%
Functions: 22 26 84.6%
Branches: 24 56 42.9%

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