@@ -104,27 +104,22 @@ class ProgressBarContextManager
104
104
{
105
105
std::lock_guard<std::mutex> lock (m_mutex);
106
106
107
- // To avoid display_all() being executed after calling mark_pbar_as_completed() in some race conditions
107
+ // If the progress bars needs to be updated after completion, move the cursor up to the beginning
108
108
if (m_is_completed)
109
109
{
110
- return ;
110
+ move_cursor_up (m_progress_bars. size ()) ;
111
111
}
112
112
113
- // A bit of hack here to make the font settings work. Indicators enables the font options only if the bars are
114
- // output to standard streams (see is_colorized() in <indicators/termcolor.hpp>), but since we are still using
115
- // the ostream (m_stdout_os) that is connected to the console terminal, the font options should be enabled.
116
- // The internal function here is used to manually enable the font display.
117
- m_stdout_os.iword (termcolor::_internal::colorize_index ()) = 1 ;
113
+ display_all_impl ();
118
114
119
- for (auto & pbar : m_progress_bars)
115
+ // If all the progress bars are completed, keep the cursor position as it is
116
+ if (m_is_completed)
120
117
{
121
- pbar->print_progress (true );
122
- m_stdout_os << termcolor::reset; // The font option only works for the current bar
123
- m_stdout_os << std::endl;
118
+ return ;
124
119
}
125
120
126
- // After each round of display , move cursor up ("\033[A") to the beginning of the first bar
127
- m_stdout_os << " \033 [ " << m_progress_bars.size () << " A " << std::flush ;
121
+ // Otherwise , move cursor up to the beginning after each round of display
122
+ move_cursor_up ( m_progress_bars.size ()) ;
128
123
}
129
124
130
125
void mark_pbar_as_completed (size_t bar_id)
@@ -145,17 +140,9 @@ class ProgressBarContextManager
145
140
}
146
141
if (all_pbars_completed)
147
142
{
148
- // Move the cursor down to the bottom of the last progress bar
149
- // Doing this here instead of the destructor to avoid a race condition with the pipeline's
150
- // "====Pipeline Complete====" log message.
151
- // Using a string stream to ensure other logs are not interleaved.
152
- std::ostringstream new_lines;
153
- for (std::size_t i = 0 ; i < m_progress_bars.size (); ++i)
154
- {
155
- new_lines << " \n " ;
156
- }
143
+ // Display again when completed to avoid progress bars being covered by other logs
144
+ display_all_impl ();
157
145
158
- m_stdout_os << new_lines.str () << std::flush;
159
146
m_is_completed = true ;
160
147
}
161
148
}
@@ -199,6 +186,34 @@ class ProgressBarContextManager
199
186
return std::move (progress_bar);
200
187
}
201
188
189
+ void display_all_impl ()
190
+ {
191
+ // A bit of hack here to make the font settings work. Indicators enables the font options only if the bars are
192
+ // output to standard streams (see is_colorized() in <indicators/termcolor.hpp>), but since we are still using
193
+ // the ostream (m_stdout_os) that is connected to the console terminal, the font options should be enabled.
194
+ // The internal function here is used to manually enable the font display.
195
+ m_stdout_os.iword (termcolor::_internal::colorize_index ()) = 1 ;
196
+
197
+ for (auto & pbar : m_progress_bars)
198
+ {
199
+ pbar->print_progress (true );
200
+ m_stdout_os << termcolor::reset; // The font option only works for the current bar
201
+ m_stdout_os << std::endl;
202
+ }
203
+ }
204
+
205
+ void move_cursor_up (size_t lines)
206
+ {
207
+ // "\033[<n>A" means moving the cursor up for n lines
208
+ m_stdout_os << " \033 [" << lines << " A" << std::flush;
209
+ }
210
+
211
+ void move_cursor_down (size_t lines)
212
+ {
213
+ // "\033[<n>B" means moving the cursor down for n lines
214
+ m_stdout_os << " \033 [" << lines << " B" << std::flush;
215
+ }
216
+
202
217
indicators::DynamicProgress<indicators::IndeterminateProgressBar> m_dynamic_progress_bars;
203
218
std::vector<std::unique_ptr<indicators::IndeterminateProgressBar>> m_progress_bars;
204
219
std::mutex m_mutex;
@@ -227,8 +242,8 @@ class MonitorController
227
242
* @param unit : the unit of message count
228
243
* @param determine_count_fn : A function that computes the count for each incoming message
229
244
*/
230
- MonitorController (const std::string& description,
231
- std::string unit = " messages" ,
245
+ MonitorController (const std::string& description = " Progress " ,
246
+ const std::string& unit = " messages" ,
232
247
indicators::Color text_color = indicators::Color::cyan,
233
248
indicators::FontStyle font_style = indicators::FontStyle::bold ,
234
249
std::optional<std::function<size_t (MessageT)>> determine_count_fn = std::nullopt);
@@ -239,23 +254,26 @@ class MonitorController
239
254
void sink_on_completed ();
240
255
241
256
private:
242
- static std::string format_duration (std::chrono::seconds duration);
243
- static std::string format_throughput (std::chrono::seconds duration, size_t count, const std::string& unit);
257
+ static std::string format_duration (std::chrono::microseconds duration);
258
+ static std::string format_throughput (std::chrono::microseconds duration, size_t count, const std::string& unit);
244
259
245
260
size_t m_bar_id;
261
+ const std::string m_description;
246
262
const std::string m_unit;
247
263
std::optional<std::function<size_t (MessageT)>> m_determine_count_fn;
248
264
size_t m_count{0 };
249
265
time_point_t m_start_time;
250
266
bool m_is_started{false }; // Set to true after the first call to progress_sink()
267
+ bool m_is_completed{false };
251
268
};
252
269
253
270
template <typename MessageT>
254
271
MonitorController<MessageT>::MonitorController(const std::string& description,
255
- std::string unit,
272
+ const std::string& unit,
256
273
indicators::Color text_color,
257
274
indicators::FontStyle font_style,
258
275
std::optional<std::function<size_t (MessageT)>> determine_count_fn) :
276
+ m_description (std::move(description)),
259
277
m_unit(std::move(unit)),
260
278
m_determine_count_fn(determine_count_fn)
261
279
{
@@ -268,7 +286,7 @@ MonitorController<MessageT>::MonitorController(const std::string& description,
268
286
}
269
287
}
270
288
271
- m_bar_id = ProgressBarContextManager::get_instance ().add_progress_bar (description , text_color, font_style);
289
+ m_bar_id = ProgressBarContextManager::get_instance ().add_progress_bar (m_description , text_color, font_style);
272
290
}
273
291
274
292
template <typename MessageT>
@@ -280,13 +298,15 @@ MessageT MonitorController<MessageT>::progress_sink(MessageT msg)
280
298
m_is_started = true ;
281
299
}
282
300
m_count += (*m_determine_count_fn)(msg);
283
- auto duration = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now () - m_start_time);
301
+ auto duration =
302
+ std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now () - m_start_time);
284
303
285
304
auto & manager = ProgressBarContextManager::get_instance ();
286
305
auto & pbar = manager.progress_bars ()[m_bar_id];
287
306
288
307
// Update the progress bar
289
308
pbar->set_option (indicators::option::PostfixText{format_throughput (duration, m_count, m_unit)});
309
+ pbar->set_option (indicators::option::PrefixText{m_description});
290
310
pbar->tick ();
291
311
292
312
manager.display_all ();
@@ -298,14 +318,19 @@ template <typename MessageT>
298
318
void MonitorController<MessageT>::sink_on_completed()
299
319
{
300
320
auto & manager = ProgressBarContextManager::get_instance ();
321
+ auto & pbar = manager.progress_bars ()[m_bar_id];
322
+
323
+ pbar->set_option (indicators::option::PrefixText{" [Completed]" + m_description});
324
+ pbar->set_option (indicators::option::ForegroundColor{indicators::Color::green});
325
+
301
326
manager.mark_pbar_as_completed (m_bar_id);
302
327
}
303
328
304
329
template <typename MessageT>
305
- std::string MonitorController<MessageT>::format_duration(std::chrono::seconds duration)
330
+ std::string MonitorController<MessageT>::format_duration(std::chrono::microseconds duration)
306
331
{
307
332
auto minutes = std::chrono::duration_cast<std::chrono::minutes>(duration);
308
- auto seconds = duration - minutes;
333
+ auto seconds = std::chrono::duration_cast<std::chrono::seconds>( duration - minutes) ;
309
334
310
335
std::ostringstream oss;
311
336
oss << std::setw (2 ) << std::setfill (' 0' ) << minutes.count () << " m:" << std::setw (2 ) << std::setfill (' 0' )
@@ -314,11 +339,12 @@ std::string MonitorController<MessageT>::format_duration(std::chrono::seconds du
314
339
}
315
340
316
341
template <typename MessageT>
317
- std::string MonitorController<MessageT>::format_throughput(std::chrono::seconds duration,
342
+ std::string MonitorController<MessageT>::format_throughput(std::chrono::microseconds duration,
318
343
size_t count,
319
344
const std::string& unit)
320
345
{
321
- double throughput = static_cast <double >(count) / duration.count ();
346
+ double time_in_seconds = std::chrono::duration_cast<std::chrono::duration<double >>(duration).count ();
347
+ double throughput = static_cast <double >(count) / time_in_seconds;
322
348
std::ostringstream oss;
323
349
oss << count << " " << unit << " in " << format_duration (duration) << " , "
324
350
<< " Throughput: " << std::fixed << std::setprecision (2 ) << throughput << " " << unit << " /s" ;
0 commit comments