Skip to main content

dsi_progress_logger/
lib.rs

1/*
2 * SPDX-FileCopyrightText: 2023 Inria
3 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
4 * SPDX-FileCopyrightText: 2024 Fondation Inria
5 *
6 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
7 */
8
9#![doc = include_str!("../README.md")]
10
11use log::{debug, error, info, trace, warn};
12use num_format::{Locale, ToFormattedString};
13use pluralizer::pluralize;
14use std::fmt::{Arguments, Display, Formatter, Result};
15use std::sync::{Arc, Mutex};
16use std::time::{Duration, Instant};
17use sysinfo::{MemoryRefreshKind, Pid, ProcessRefreshKind, ProcessesToUpdate, RefreshKind, System};
18mod utils;
19pub use utils::*;
20
21/// Logging trait.
22///
23/// To log the progress of an activity, you call [`start`](ProgressLog::start).
24/// Then, each time you want to mark progress, you call
25/// [`update`](ProgressLog::update), which increases the item counter, and will
26/// log progress information if enough time has passed since the last log.
27/// [`light_update`](ProgressLog::light_update) will perform a time check only
28/// on a subset of updates (e.g., for [`ProgressLogger`], multiples of
29/// [`LIGHT_UPDATE_MASK`](ProgressLogger::LIGHT_UPDATE_MASK) + 1); it should be
30/// used when the activity has an extremely low cost that is comparable to that
31/// of the time check (a call to [`Instant::now()`]) itself.
32///
33/// A few setters can be called at any time to customize the logger (e.g.,
34/// [`item_name`](ProgressLog::item_name),
35/// [`log_interval`](ProgressLog::log_interval),
36/// [`expected_updates`](ProgressLog::expected_updates), etc.). The setters take
37/// and return a mutable reference to the logger, so you must first assign the
38/// logger to a variable, and then you can chain-call the setters on the
39/// variable in fluent style. The disadvantage of this approach is that you must
40/// assign the logger to a variable, but the advantage is that you can call any
41/// setter without having to reassign the variable holding the logger.
42///
43/// It is also possible to log used and free memory at each log interval by
44/// calling [`display_memory`](ProgressLog::display_memory). Memory is read from
45/// system data by the [`sysinfo`] crate, and will be updated at each log
46/// interval (note that this will slightly slow down the logging process).
47/// However, never use this feature in a
48/// [`rayon`](https://crates.io/crates/rayon) environment if another crate in
49/// your compilation unit depends on
50/// [`sysinfo`](https://crates.io/crates/sysinfo)'s (default) `multithread`
51/// feature, as [this can lead to a
52/// deadlock](https://github.com/rayon-rs/rayon/issues/592) .
53///
54///
55/// At any time, displaying the progress logger will give you time information
56/// up to the present. However, since it is impossible to update the memory
57/// information from the [`Display::fmt`] implementation, you should call
58/// [`refresh`](ProgressLog::refresh) before displaying the logger on your own.
59///
60/// When the activity is over, you call [`stop`](ProgressLog::stop), which fixes
61/// the final time, and possibly display again the logger.
62/// [`done`](ProgressLog::done) will stop the logger, print `Completed.`, and
63/// display the final stats.
64///
65/// After you finish a run of the progress logger, can call
66/// [`start`](ProgressLog::start) again measure another activity.
67///
68/// As explained in the [crate documentation](crate), we suggest using `&mut
69/// impl ProgressLog` to pass a logger as an argument, to be able to use
70/// optional logging.
71///
72/// # Examples
73///
74/// See the [`ProgressLogger`] documentation.
75pub trait ProgressLog {
76    /// The type returned by [`concurrent`](ProgressLog::concurrent).
77    type Concurrent: ConcurrentProgressLog;
78
79    /// Force a log of `self` assuming `now` is the current time.
80    ///
81    /// This is a low-level method that should not be called directly.
82    fn log(&mut self, now: Instant);
83
84    /// Log `self` if it is time to log.
85    ///
86    /// This is a low-level method that should not be called directly.
87    fn log_if(&mut self, now: Instant);
88
89    /// Set the display of memory information.
90    ///
91    /// Memory information include:
92    /// - the [resident-set size](sysinfo::Process::memory) of the process that
93    ///   created the logger;
94    /// - the [virtual-memory size](sysinfo::Process::virtual_memory) of the
95    ///   process that created the logger;
96    /// - the [available memory](sysinfo::System::available_memory);
97    /// - the [free memory](sysinfo::System::free_memory);
98    /// - the [total amount](sysinfo::System::total_memory) of memory.
99    ///
100    /// Never use this feature in a [`rayon`](https://crates.io/crates/rayon)
101    /// environment if another crate in your compilation unit depends on
102    /// [`sysinfo`](https://crates.io/crates/sysinfo)'s (default) `multithread`
103    /// feature, as [this can lead to a
104    /// deadlock](https://github.com/rayon-rs/rayon/issues/592) .
105    fn display_memory(&mut self, display_memory: bool) -> &mut Self;
106
107    /// Set the name of an item.
108    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self;
109
110    /// Set the log interval.
111    fn log_interval(&mut self, log_interval: Duration) -> &mut Self;
112
113    /// Set the expected number of updates.
114    ///
115    /// If not [`None`], the logger will display the percentage of completion
116    /// and an estimate of the time to completion.
117    fn expected_updates(&mut self, expected_updates: Option<usize>) -> &mut Self;
118
119    /// Set the time unit to use for speed.
120    ///
121    /// If not [`None`], the logger will always display the speed in this unit
122    /// instead of making a choice of readable unit based on the elapsed time.
123    /// Moreover, large numbers will not be thousands separated. This behavior
124    /// is useful when the output of the logger must be parsed.
125    fn time_unit(&mut self, time_unit: Option<TimeUnit>) -> &mut Self;
126
127    /// Set whether to display additionally the speed achieved during the last
128    /// log interval.
129    fn local_speed(&mut self, local_speed: bool) -> &mut Self;
130
131    /// Set the [`log`] target.
132    ///
133    /// This should often be the path of the module logging progress, which is
134    /// obtained with [`std::module_path!`].
135    ///
136    /// Note that the macro [`progress_logger!`] sets this field automatically
137    /// to [`std::module_path!`].
138    ///
139    /// # Examples
140    ///
141    /// ```rust
142    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
143    /// use dsi_progress_logger::prelude::*;
144    ///
145    ///    env_logger::builder()
146    ///        .filter_level(log::LevelFilter::Info)
147    ///        .try_init()?;
148    ///
149    /// let mut pl = ProgressLogger::default();
150    /// pl.item_name("pumpkin");
151    /// pl.log_target(std::module_path!());
152    /// pl.start("Smashing pumpkins from a module...");
153    /// for _ in 0..100 {
154    ///    // do something on each pumpkin
155    ///    pl.update();
156    /// }
157    /// pl.done();
158    /// #     Ok(())
159    /// # }
160    /// ```
161    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self;
162
163    /// Add a value to the counter.
164    ///
165    /// This method is mainly useful for wrappers or to implement a custom
166    /// update strategy.
167    fn add_to_count(&mut self, count: usize);
168
169    /// Start the logger, displaying the given message.
170    ///
171    /// You can pass the empty string to display nothing.
172    fn start(&mut self, msg: impl AsRef<str>);
173
174    /// Increase the count and check whether it is time to log.
175    fn update(&mut self);
176
177    /// Set the count and check whether it is time to log.
178    fn update_with_count(&mut self, count: usize) {
179        self.update_with_count_and_time(count, Instant::now());
180    }
181
182    /// Set the count and check whether it is time to log, given the current
183    /// time.
184    ///
185    /// This method is mainly useful for wrappers that want to avoid unnecessary
186    /// calls to [`Instant::now`].
187    fn update_with_count_and_time(&mut self, count: usize, now: Instant);
188
189    /// Increase the count but checks whether it is time to log only after an
190    /// implementation-defined number of calls.
191    ///
192    /// Useful for very short activities with respect to which checking the
193    /// time is expensive.
194    fn light_update(&mut self);
195
196    /// Increase the count and forces a log.
197    fn update_and_display(&mut self);
198
199    /// Stop the logger, fixing the final time.
200    fn stop(&mut self);
201
202    /// Stop the logger, print `Completed.`, and display the final stats. The
203    /// number of expected updates will be cleared.
204    fn done(&mut self);
205
206    /// Stop the logger, sets the count, prints `Completed.`, and displays the
207    /// final stats. The number of expected updates will be cleared.
208    ///
209    /// This method is particularly useful in two circumstances:
210    /// * you have updated the logger with some approximate values (e.g., in a
211    ///   multicore computation) but before printing the final stats you want
212    ///   the internal counter to contain an exact value;
213    /// * you have used the logger as a handy timer, calling just
214    ///   [`start`](ProgressLog::start) and this method.
215    fn done_with_count(&mut self, count: usize);
216
217    /// Return the elapsed time since the logger was started, or `None` if the
218    /// logger has not been started.
219    fn elapsed(&self) -> Option<Duration>;
220
221    /// Return the last count the logger has been set to.
222    ///
223    /// Note that you can call this method even after the logger has been
224    /// [stopped](ProgressLog::stop).
225    fn count(&self) -> usize;
226
227    /// Refresh memory information, if previously requested with
228    /// [`display_memory`](#method.display_memory). You do not need to call this
229    /// method unless you display the logger manually.
230    fn refresh(&mut self);
231
232    /// Output the given message at the [trace](`log::Level::Trace`) level.
233    ///
234    /// See [`info`](ProgressLog::info) for an example.
235    fn trace(&self, args: Arguments<'_>);
236
237    /// Output the given message at the [debug](`log::Level::Debug`) level.
238    ///
239    /// See [`info`](ProgressLog::info) for an example.
240    fn debug(&self, args: Arguments<'_>);
241
242    /// Output the given message at the [info](`log::Level::Info`) level.
243    ///
244    /// For maximum flexibility, this method takes as argument the result of a
245    /// [`std::format_args!`] macro. Note that there will be no output if the
246    /// logger is [`None`].
247    ///
248    /// # Examples
249    ///
250    /// ```rust
251    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
252    /// use dsi_progress_logger::*;
253    ///
254    /// env_logger::builder()
255    ///     .filter_level(log::LevelFilter::Info)
256    ///     .try_init()?;
257    ///
258    /// let logger_name = "my_logger";
259    /// let mut pl = progress_logger![];
260    /// pl.info(format_args!("My logger named {}", logger_name));
261    /// #     Ok(())
262    /// # }
263    /// ```
264    fn info(&self, args: Arguments<'_>);
265
266    /// Output the given message at the [warn](`log::Level::Warn`) level.
267    ///
268    /// See [`info`](ProgressLog::info) for an example.
269    fn warn(&self, args: Arguments<'_>);
270
271    /// Output the given message at the [error](`log::Level::Error`) level.
272    ///
273    /// See [`info`](ProgressLog::info) for an example.
274    fn error(&self, args: Arguments<'_>);
275
276    /// Return a concurrent copy of the logger.
277    ///
278    /// Some methods require both sequential and concurrent logging. To keep
279    /// optional logging efficient, we suggest in this cases to use `&mut impl
280    /// ProgressLog` to pass a logger as an argument, and then creating a
281    /// concurrent copy of the logger with this method. If the original logger
282    /// is `None`, the concurrent copy will be `None` as well.
283    ///
284    /// Note that the result of the method is a copy—it will not share the state
285    /// of the original logger.
286    ///
287    /// Concurrent logger implementations can just return a duplicate of
288    /// themselves. [`dup`](ConcurrentProgressLog::dup).
289    fn concurrent(&self) -> Self::Concurrent;
290}
291
292/// Concurrent logging trait.
293///
294/// This trait extends [`ProgressLog`] by adding a
295/// [`dup`](ConcurrentProgressLog::dup) method that duplicates the logger and
296/// adding the [`Clone`], [`Sync`], and [`Send`] traits.
297///
298/// By contract, [`Clone`] implementations must return a new logger updating the
299/// same internal state, so you can easily use a [`ConcurrentProgressLog`] in
300/// methods like
301/// [`rayon::ParallelIterator::for_each_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.for_each_with),
302/// [`rayon::ParallelIterator::map_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.map_with),
303/// and so on. In a [`rayon`](https://docs.rs/rayon) environment, however, you
304/// cannot use [`display_memory`](ProgressLog::display_memory) if another crate
305/// in your compilation unit depends on
306/// [`sysinfo`](https://crates.io/crates/sysinfo)'s (default) `multithread`
307/// feature, as [this can lead to a
308/// deadlock](https://github.com/rayon-rs/rayon/issues/592) .
309///
310/// Note that [`ProgressLogger`]'s [`Clone`
311/// implementation](ProgressLogger#impl-Clone-for-ProgressLogger) has a
312/// completely different semantics.
313///
314/// As explained in the [crate documentation](crate), we suggest using `&mut
315/// Self::Concurrent` to pass a concurrent logger as an argument, to be able to
316/// use optional logging.
317///
318/// # Examples
319///
320/// See the [`ConcurrentWrapper`] documentation.
321pub trait ConcurrentProgressLog: ProgressLog + Sync + Send + Clone {
322    /// The type returned by [`dup`](ConcurrentProgressLog::dup).
323    type Duplicated: ConcurrentProgressLog;
324
325    /// Duplicate the concurrent progress logger, obtaining a new one.
326    ///
327    /// Note that the this method has the same semantics of [`ProgressLogger`'s
328    /// `Clone` implementation](ProgressLogger#impl-Clone-for-ProgressLogger),
329    /// but in a [`ConcurrentProgressLog`] by contract [cloning must generate
330    /// copies with the same underlying logger](ConcurrentProgressLog).
331    fn dup(&self) -> Self::Duplicated;
332}
333
334impl<P: ProgressLog> ProgressLog for &mut P {
335    type Concurrent = P::Concurrent;
336
337    fn log(&mut self, now: Instant) {
338        (**self).log(now);
339    }
340
341    fn log_if(&mut self, now: Instant) {
342        (**self).log_if(now);
343    }
344
345    fn add_to_count(&mut self, count: usize) {
346        (**self).add_to_count(count);
347    }
348
349    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
350        (**self).display_memory(display_memory);
351        self
352    }
353
354    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
355        (**self).item_name(item_name);
356        self
357    }
358
359    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
360        (**self).log_interval(log_interval);
361        self
362    }
363
364    fn expected_updates(&mut self, expected_updates: Option<usize>) -> &mut Self {
365        (**self).expected_updates(expected_updates);
366        self
367    }
368
369    fn time_unit(&mut self, time_unit: Option<TimeUnit>) -> &mut Self {
370        (**self).time_unit(time_unit);
371        self
372    }
373
374    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
375        (**self).local_speed(local_speed);
376        self
377    }
378
379    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
380        (**self).log_target(target);
381        self
382    }
383
384    fn start(&mut self, msg: impl AsRef<str>) {
385        (**self).start(msg);
386    }
387
388    fn update(&mut self) {
389        (**self).update();
390    }
391
392    fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
393        (**self).update_with_count_and_time(count, now);
394    }
395
396    fn light_update(&mut self) {
397        (**self).light_update();
398    }
399
400    fn update_and_display(&mut self) {
401        (**self).update_and_display();
402    }
403
404    fn stop(&mut self) {
405        (**self).stop();
406    }
407
408    fn done(&mut self) {
409        (**self).done();
410    }
411
412    fn done_with_count(&mut self, count: usize) {
413        (**self).done_with_count(count);
414    }
415
416    fn elapsed(&self) -> Option<Duration> {
417        (**self).elapsed()
418    }
419
420    fn count(&self) -> usize {
421        (**self).count()
422    }
423
424    fn refresh(&mut self) {
425        (**self).refresh();
426    }
427
428    fn trace(&self, args: Arguments<'_>) {
429        (**self).trace(args);
430    }
431
432    fn debug(&self, args: Arguments<'_>) {
433        (**self).debug(args);
434    }
435
436    fn info(&self, args: Arguments<'_>) {
437        (**self).info(args);
438    }
439
440    fn warn(&self, args: Arguments<'_>) {
441        (**self).warn(args);
442    }
443
444    fn error(&self, args: Arguments<'_>) {
445        (**self).error(args);
446    }
447
448    fn concurrent(&self) -> Self::Concurrent {
449        (**self).concurrent()
450    }
451}
452
453impl<P: ProgressLog> ProgressLog for Option<P> {
454    type Concurrent = Option<P::Concurrent>;
455
456    fn log(&mut self, now: Instant) {
457        if let Some(pl) = self {
458            pl.log(now);
459        }
460    }
461
462    fn log_if(&mut self, now: Instant) {
463        if let Some(pl) = self {
464            pl.log_if(now);
465        }
466    }
467
468    fn add_to_count(&mut self, count: usize) {
469        if let Some(pl) = self {
470            pl.add_to_count(count);
471        }
472    }
473
474    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
475        if let Some(pl) = self {
476            pl.display_memory(display_memory);
477        }
478        self
479    }
480
481    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
482        if let Some(pl) = self {
483            pl.item_name(item_name);
484        }
485        self
486    }
487
488    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
489        if let Some(pl) = self {
490            pl.log_interval(log_interval);
491        }
492        self
493    }
494
495    fn expected_updates(&mut self, expected_updates: Option<usize>) -> &mut Self {
496        if let Some(pl) = self {
497            pl.expected_updates(expected_updates);
498        }
499        self
500    }
501
502    fn time_unit(&mut self, time_unit: Option<TimeUnit>) -> &mut Self {
503        if let Some(pl) = self {
504            pl.time_unit(time_unit);
505        }
506        self
507    }
508
509    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
510        if let Some(pl) = self {
511            pl.local_speed(local_speed);
512        }
513        self
514    }
515
516    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
517        if let Some(pl) = self {
518            pl.log_target(target);
519        }
520        self
521    }
522
523    fn start(&mut self, msg: impl AsRef<str>) {
524        if let Some(pl) = self {
525            pl.start(msg);
526        }
527    }
528
529    fn update(&mut self) {
530        if let Some(pl) = self {
531            pl.update();
532        }
533    }
534
535    fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
536        if let Some(pl) = self {
537            pl.update_with_count_and_time(count, now);
538        }
539    }
540
541    fn light_update(&mut self) {
542        if let Some(pl) = self {
543            pl.light_update();
544        }
545    }
546
547    fn update_and_display(&mut self) {
548        if let Some(pl) = self {
549            pl.update_and_display();
550        }
551    }
552
553    fn stop(&mut self) {
554        if let Some(pl) = self {
555            pl.stop();
556        }
557    }
558
559    fn done(&mut self) {
560        if let Some(pl) = self {
561            pl.done();
562        }
563    }
564
565    fn done_with_count(&mut self, count: usize) {
566        if let Some(pl) = self {
567            pl.done_with_count(count);
568        }
569    }
570
571    fn elapsed(&self) -> Option<Duration> {
572        self.as_ref().and_then(|pl| pl.elapsed())
573    }
574
575    fn count(&self) -> usize {
576        self.as_ref().map(|pl| pl.count()).unwrap_or(0)
577    }
578
579    fn refresh(&mut self) {
580        if let Some(pl) = self {
581            pl.refresh();
582        }
583    }
584
585    fn trace(&self, args: Arguments<'_>) {
586        if let Some(pl) = self {
587            pl.trace(args);
588        }
589    }
590
591    fn debug(&self, args: Arguments<'_>) {
592        if let Some(pl) = self {
593            pl.debug(args);
594        }
595    }
596
597    fn info(&self, args: Arguments<'_>) {
598        if let Some(pl) = self {
599            pl.info(args);
600        }
601    }
602
603    fn warn(&self, args: Arguments<'_>) {
604        if let Some(pl) = self {
605            pl.warn(args);
606        }
607    }
608
609    fn error(&self, args: Arguments<'_>) {
610        if let Some(pl) = self {
611            pl.error(args);
612        }
613    }
614
615    fn concurrent(&self) -> Self::Concurrent {
616        self.as_ref().map(|pl| pl.concurrent())
617    }
618}
619
620impl<P: ConcurrentProgressLog> ConcurrentProgressLog for Option<P> {
621    type Duplicated = Option<P::Duplicated>;
622
623    fn dup(&self) -> Self::Duplicated {
624        self.as_ref().map(|pl| pl.dup())
625    }
626}
627
628/// An implementation of [`ProgressLog`] with output generated using the
629/// [`log`](https://docs.rs/log) crate at the `info` level.
630///
631/// Instances can be created by using fluent setters, or by using the
632/// [`progress_logger`] macro.
633///
634/// You can [clone](#impl-Clone-for-ProgressLogger) a logger to create a new one
635/// with the same setup but with all the counters reset. This behavior is useful
636/// when you want to configure a logger and then use its configuration for other
637/// loggers.
638///
639/// # Examples
640///
641/// A typical call sequence to a progress logger is as follows:
642///
643/// ```rust
644/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
645/// use dsi_progress_logger::prelude::*;
646///
647/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
648///
649/// let mut pl = ProgressLogger::default();
650/// pl.item_name("pumpkin");
651/// pl.start("Smashing pumpkins...");
652/// for _ in 0..100 {
653///    // do something on each pumpkin
654///    pl.update();
655/// }
656/// pl.done();
657/// #     Ok(())
658/// # }
659/// ```
660///
661/// The [`progress_logger`] macro will create the progress logger for you and
662/// set its [`log_target`](ProgressLog::log_target) to [`std::module_path!()`],
663/// which is usually what you want. You can also call any setter with a
664/// key-value syntax:
665///
666/// ```rust
667/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
668/// use dsi_progress_logger::prelude::*;
669///
670/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
671///
672/// let mut pl = progress_logger![item_name="pumpkin"];
673/// pl.start("Smashing pumpkins...");
674/// for _ in 0..100 {
675///    // do something on each pumpkin
676///    pl.update();
677/// }
678/// pl.done();
679/// #     Ok(())
680/// # }
681/// ```
682///
683/// A progress logger can also be used as a handy timer:
684///
685/// ```rust
686/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
687/// use dsi_progress_logger::prelude::*;
688///
689/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
690///
691/// let mut pl = progress_logger![item_name="pumpkin"];
692/// pl.start("Smashing pumpkins...");
693/// for _ in 0..100 {
694///    // do something on each pumpkin
695/// }
696/// pl.done_with_count(100);
697/// #     Ok(())
698/// # }
699/// ```
700///
701/// This progress logger will display information about memory usage:
702///
703/// ```rust
704/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
705/// use dsi_progress_logger::prelude::*;
706///
707/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
708///
709/// let mut pl = progress_logger![display_memory=true];
710/// #     Ok(())
711/// # }
712/// ```
713pub struct ProgressLogger {
714    /// The name of an item. Defaults to `item`.
715    item_name: String,
716    /// The pluralized name of an item. Defaults to `items`. It is quite
717    /// expensive to compute with [`pluralize`], hence the caching.
718    items_name: String,
719    /// The log interval. Defaults to 10 seconds.
720    log_interval: Duration,
721    /// The expected number of updates. If set, the logger will display the percentage of completion and
722    /// an estimate of the time to completion.
723    expected_updates: Option<usize>,
724    /// The time unit to use for speed. If set, the logger will always display the speed in this unit
725    /// instead of making a choice of readable unit based on the elapsed time. Moreover, large numbers
726    /// will not be thousands separated. This is useful when the output of the logger must be parsed.
727    time_unit: Option<TimeUnit>,
728    /// Display additionally the speed achieved during the last log interval.
729    local_speed: bool,
730    /// [`log`] target
731    ///
732    /// This is often the path of the module logging progress.
733    log_target: String,
734    /// When the logger was started.
735    start_time: Option<Instant>,
736    /// The last time we logged the activity (to compute speed).
737    last_log_time: Instant,
738    /// The next time we will log the activity.
739    next_log_time: Instant,
740    /// When the logger was stopped.
741    stop_time: Option<Instant>,
742    /// The number of items.
743    count: usize,
744    /// The number of items at the last log (to compute speed).
745    last_count: usize,
746    /// Display additionally the amount of used and free memory using this [`sysinfo::System`]
747    system: Option<System>,
748    /// The pid of the current process
749    pid: Pid,
750}
751
752impl Default for ProgressLogger {
753    /// Create a default [`ProgressLogger`] with a log interval of 10 seconds and
754    /// item name set to “item”.
755    fn default() -> Self {
756        Self {
757            item_name: "item".into(),
758            items_name: "items".into(),
759            log_interval: Duration::from_secs(10),
760            expected_updates: None,
761            time_unit: None,
762            local_speed: false,
763            log_target: std::env::current_exe()
764                .ok()
765                .and_then(|path| {
766                    path.file_name()
767                        .and_then(|s| s.to_owned().into_string().ok())
768                })
769                .unwrap_or_else(|| "main".to_string()),
770            start_time: None,
771            last_log_time: Instant::now(),
772            next_log_time: Instant::now(),
773            stop_time: None,
774            count: 0,
775            last_count: 0,
776            system: None,
777            pid: Pid::from(std::process::id() as usize),
778        }
779    }
780}
781
782impl Clone for ProgressLogger {
783    /// Clone the logger, returning a logger with the same setup but with all the
784    /// counters reset.
785    #[allow(clippy::manual_map)]
786    fn clone(&self) -> Self {
787        Self {
788            item_name: self.item_name.clone(),
789            items_name: self.items_name.clone(),
790            log_interval: self.log_interval,
791            time_unit: self.time_unit,
792            local_speed: self.local_speed,
793            system: match self.system {
794                Some(_) => Some(System::new_with_specifics(
795                    RefreshKind::nothing().with_memory(MemoryRefreshKind::nothing().with_ram()),
796                )),
797                None => None,
798            },
799            ..ProgressLogger::default()
800        }
801    }
802}
803
804/// Macro to create a [`ProgressLogger`] with default log target set to
805/// [`std::module_path!`], and key-value pairs instead of setters.
806///
807/// # Examples
808///
809///
810/// ```rust
811/// use dsi_progress_logger::prelude::*;
812///
813/// let mut pl = progress_logger![item_name="pumpkin", display_memory=true];
814/// ```
815#[macro_export]
816macro_rules! progress_logger {
817    ($($method:ident = $arg:expr),* $(,)?) => {
818        {
819            let mut pl = $crate::ProgressLogger::default();
820            $crate::ProgressLog::log_target(&mut pl, ::std::module_path!());
821            $(
822                $crate::ProgressLog::$method(&mut pl, $arg);
823            )*
824            pl
825        }
826    }
827}
828
829impl ProgressLogger {
830    /// Calls to [light_update](ProgressLog::light_update) will cause a call to
831    /// [`Instant::now`] only if the current count is a multiple of this mask
832    /// plus one.
833    pub const LIGHT_UPDATE_MASK: usize = (1 << 20) - 1;
834
835    fn fmt_timing_speed(&self, f: &mut Formatter<'_>, seconds_per_item: f64) -> Result {
836        let items_per_second = 1.0 / seconds_per_item;
837
838        let time_unit_timing = self
839            .time_unit
840            .unwrap_or_else(|| TimeUnit::nice_time_unit(seconds_per_item));
841
842        let time_unit_speed = self
843            .time_unit
844            .unwrap_or_else(|| TimeUnit::nice_speed_unit(seconds_per_item));
845
846        f.write_fmt(format_args!(
847            "{:.2} {}/{}, {:.2} {}/{}",
848            items_per_second * time_unit_speed.as_seconds(),
849            self.items_name,
850            time_unit_speed.label(),
851            seconds_per_item / time_unit_timing.as_seconds(),
852            time_unit_timing.label(),
853            self.item_name
854        ))?;
855
856        Ok(())
857    }
858}
859
860impl ProgressLog for ProgressLogger {
861    type Concurrent = ConcurrentWrapper<Self>;
862
863    fn log(&mut self, now: Instant) {
864        self.refresh();
865        info!(target: &self.log_target, "{}", self);
866        self.last_count = self.count;
867        self.last_log_time = now;
868        self.next_log_time = now + self.log_interval;
869    }
870
871    fn log_if(&mut self, now: Instant) {
872        if self.next_log_time <= now {
873            self.log(now);
874        }
875    }
876
877    fn add_to_count(&mut self, count: usize) {
878        self.count += count;
879    }
880
881    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
882        match (display_memory, &self.system) {
883            (true, None) => {
884                self.system = Some(System::new_with_specifics(
885                    RefreshKind::nothing().with_memory(MemoryRefreshKind::nothing().with_ram()),
886                ));
887            }
888            (false, Some(_)) => {
889                self.system = None;
890            }
891            _ => (),
892        }
893        self
894    }
895
896    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
897        self.item_name = item_name.as_ref().into();
898        self.items_name = pluralize(item_name.as_ref(), 2, false);
899        self
900    }
901
902    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
903        self.log_interval = log_interval;
904        self
905    }
906
907    fn expected_updates(&mut self, expected_updates: Option<usize>) -> &mut Self {
908        self.expected_updates = expected_updates;
909        self
910    }
911
912    fn time_unit(&mut self, time_unit: Option<TimeUnit>) -> &mut Self {
913        self.time_unit = time_unit;
914        self
915    }
916
917    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
918        self.local_speed = local_speed;
919        self
920    }
921
922    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
923        self.log_target = target.as_ref().into();
924        self
925    }
926
927    fn start(&mut self, msg: impl AsRef<str>) {
928        let now = Instant::now();
929        self.start_time = Some(now);
930        self.stop_time = None;
931        self.count = 0;
932        self.last_count = 0;
933        self.last_log_time = now;
934        self.next_log_time = now + self.log_interval;
935        if !msg.as_ref().is_empty() {
936            info!(target: &self.log_target, "{}", msg.as_ref());
937        }
938    }
939
940    fn refresh(&mut self) {
941        if let Some(system) = &mut self.system {
942            system.refresh_processes_specifics(
943                ProcessesToUpdate::Some(&[self.pid]),
944                false,
945                ProcessRefreshKind::nothing().with_memory(),
946            );
947        }
948    }
949
950    fn update(&mut self) {
951        self.count += 1;
952        self.log_if(Instant::now());
953    }
954
955    fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
956        self.count += count;
957        self.log_if(now);
958    }
959
960    /// Increases the count and, once every
961    /// [`LIGHT_UPDATE_MASK`](#fields.LIGHT_UPDATE_MASK) + 1 calls, check
962    /// whether it is time to log.
963    #[inline(always)]
964    fn light_update(&mut self) {
965        self.count += 1;
966        if (self.count & Self::LIGHT_UPDATE_MASK) == 0 {
967            self.log_if(Instant::now());
968        }
969    }
970
971    fn update_and_display(&mut self) {
972        self.count += 1;
973        self.log(Instant::now());
974    }
975
976    fn stop(&mut self) {
977        self.stop_time = Some(Instant::now());
978        // just to avoid wrong reuses
979        self.expected_updates = None;
980    }
981
982    fn done(&mut self) {
983        self.stop();
984        info!(target: &self.log_target, "Completed.");
985        self.refresh();
986        info!(target: &self.log_target, "{}", self);
987    }
988
989    fn done_with_count(&mut self, count: usize) {
990        self.count = count;
991        self.done();
992    }
993
994    fn elapsed(&self) -> Option<Duration> {
995        let start_time = self.start_time?;
996        Some(self.stop_time.unwrap_or_else(Instant::now) - start_time)
997    }
998
999    fn count(&self) -> usize {
1000        self.count
1001    }
1002
1003    fn trace(&self, args: Arguments<'_>) {
1004        trace!(target: &self.log_target, "{}", std::fmt::format(args));
1005    }
1006
1007    fn debug(&self, args: Arguments<'_>) {
1008        debug!(target: &self.log_target, "{}", std::fmt::format(args));
1009    }
1010
1011    fn info(&self, args: Arguments<'_>) {
1012        info!(target: &self.log_target, "{}", std::fmt::format(args));
1013    }
1014
1015    fn warn(&self, args: Arguments<'_>) {
1016        warn!(target: &self.log_target, "{}", std::fmt::format(args));
1017    }
1018
1019    fn error(&self, args: Arguments<'_>) {
1020        error!(target: &self.log_target, "{}", std::fmt::format(args));
1021    }
1022
1023    fn concurrent(&self) -> Self::Concurrent {
1024        ConcurrentWrapper::wrap(self.clone())
1025    }
1026}
1027
1028impl Display for ProgressLogger {
1029    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1030        if let Some(start_time) = self.start_time {
1031            let count_fmtd = if self.time_unit.is_none() {
1032                self.count.to_formatted_string(&Locale::en)
1033            } else {
1034                self.count.to_string()
1035            };
1036
1037            if let Some(stop_time) = self.stop_time {
1038                let elapsed = stop_time - start_time;
1039                let seconds_per_item = elapsed.as_secs_f64() / self.count as f64;
1040
1041                f.write_fmt(format_args!(
1042                    "Elapsed: {}",
1043                    TimeUnit::pretty_print(elapsed.as_millis())
1044                ))?;
1045
1046                if self.count != 0 {
1047                    f.write_fmt(format_args!(
1048                        " [{} {}, ",
1049                        count_fmtd,
1050                        if self.count == 1 {
1051                            &self.item_name
1052                        } else {
1053                            &self.items_name
1054                        }
1055                    ))?;
1056                    self.fmt_timing_speed(f, seconds_per_item)?;
1057                    f.write_fmt(format_args!("]"))?
1058                }
1059            } else {
1060                let now = Instant::now();
1061
1062                let elapsed = now - start_time;
1063
1064                f.write_fmt(format_args!(
1065                    "{} {}, {}, ",
1066                    count_fmtd,
1067                    if self.count == 1 {
1068                        &self.item_name
1069                    } else {
1070                        &self.items_name
1071                    },
1072                    TimeUnit::pretty_print(elapsed.as_millis()),
1073                ))?;
1074
1075                let seconds_per_item = elapsed.as_secs_f64() / self.count as f64;
1076                self.fmt_timing_speed(f, seconds_per_item)?;
1077
1078                if let Some(expected_updates) = self.expected_updates {
1079                    let millis_to_end: u128 = (expected_updates.saturating_sub(self.count) as u128
1080                        * elapsed.as_millis())
1081                        / (self.count as u128 + 1);
1082                    f.write_fmt(format_args!(
1083                        "; {:.2}% done, {} to end",
1084                        100.0 * self.count as f64 / expected_updates as f64,
1085                        TimeUnit::pretty_print(millis_to_end)
1086                    ))?;
1087                }
1088
1089                if self.local_speed && self.stop_time.is_none() {
1090                    f.write_fmt(format_args!(" ["))?;
1091
1092                    let elapsed = now - self.last_log_time;
1093                    let seconds_per_item =
1094                        elapsed.as_secs_f64() / (self.count - self.last_count) as f64;
1095                    self.fmt_timing_speed(f, seconds_per_item)?;
1096
1097                    f.write_fmt(format_args!("]"))?;
1098                }
1099            }
1100
1101            // It would be ideal to refresh self.system here, but this operation
1102            // would require an &mut self reference.
1103            if let Some(system) = &self.system {
1104                f.write_fmt(format_args!(
1105                    "; res/vir/avail/free/total mem {}/{}/{}B/{}B/{}B",
1106                    system
1107                        .process(self.pid)
1108                        .map(|process| humanize(process.memory() as _) + "B")
1109                        .unwrap_or("N/A".to_string()),
1110                    system
1111                        .process(self.pid)
1112                        .map(|process| humanize(process.virtual_memory() as _) + "B")
1113                        .unwrap_or("N/A".to_string()),
1114                    humanize(system.available_memory() as _),
1115                    humanize(system.free_memory() as _),
1116                    humanize(system.total_memory() as _)
1117                ))?;
1118            }
1119
1120            Ok(())
1121        } else {
1122            write!(f, "ProgressLogger not started")
1123        }
1124    }
1125}
1126
1127/// A [`ConcurrentProgressLog`] implementation that wraps a [`ProgressLog`] in
1128/// an [`Arc`]/[`Mutex`].
1129///
1130/// The methods [`update`](ProgressLog::update) and
1131/// [`update_with_count`](ProgressLog::update_with_count) buffer the increment
1132/// and add it to the underlying logger only when the buffer reaches a
1133/// threshold; this prevents locking the underlying logger too often. The
1134/// threshold is set at creation using the methods
1135/// [`with_threshold`](Self::with_threshold) and
1136/// [`wrap_with_threshold`](Self::wrap_with_threshold), or by calling the method
1137/// [`threshold`](Self::threshold).
1138///
1139/// The method [`light_update`](ProgressLog::light_update), as in the case of
1140/// [`ProgressLogger`], further delays updates using an even faster check.
1141///
1142/// # Examples
1143///
1144/// In this example, we manually spawn processes:
1145///
1146/// ```rust
1147/// use dsi_progress_logger::prelude::*;
1148/// use std::thread;
1149///
1150/// let mut cpl = concurrent_progress_logger![item_name = "pumpkin"];
1151/// cpl.start("Smashing pumpkins (using many threads)...");
1152///
1153/// std::thread::scope(|s| {
1154///     for i in 0..100 {
1155///         let mut pl = cpl.clone();
1156///         s.spawn(move || {
1157///             for _ in 0..100000 {
1158///                 // do something on each pumpkin
1159///                 pl.update();
1160///             }
1161///         });
1162///     }
1163/// });
1164///
1165/// cpl.done();
1166/// ```
1167///
1168/// You can obtain the same behavior with
1169/// [`rayon`](https://crates.io/crates/rayon) using methods such as
1170/// [`for_each_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.for_each_with)
1171/// and
1172/// [`map_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.map_with):
1173///
1174/// ```rust
1175/// use dsi_progress_logger::prelude::*;
1176/// use rayon::prelude::*;
1177///
1178/// let mut cpl = concurrent_progress_logger![item_name = "pumpkin"];
1179/// cpl.start("Smashing pumpkins (using many threads)...");
1180///
1181/// (0..1000000).into_par_iter().
1182///     with_min_len(1000). // optional, might reduce the amount of cloning
1183///     for_each_with(cpl.clone(), |pl, i| {
1184///         // do something on each pumpkin
1185///         pl.update();
1186///     }
1187/// );
1188///
1189/// cpl.done();
1190/// ```
1191///
1192/// Note that you have to pass `cpl.clone()` to avoid a move that would make the
1193/// call to [`done`](ProgressLog::done) impossible. Also, since
1194/// [`for_each_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.for_each_with)
1195/// might perform excessive cloning if jobs are too short, you can use
1196/// [`with_min_len`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.with_min_len)
1197/// to reduce the amount of cloning.
1198pub struct ConcurrentWrapper<P: ProgressLog = ProgressLogger> {
1199    /// Underlying logger
1200    inner: Arc<Mutex<P>>,
1201    /// The number of items processed by the current thread.
1202    local_count: u32,
1203    /// The threshold for updating the underlying logger.
1204    threshold: u32,
1205}
1206
1207impl Default for ConcurrentWrapper {
1208    /// Create a new [`ConcurrentWrapper`] based on a default
1209    /// [`ProgressLogger`], with a threshold of
1210    /// [`DEFAULT_THRESHOLD`](Self::DEFAULT_THRESHOLD).
1211    fn default() -> Self {
1212        Self {
1213            inner: Arc::new(Mutex::new(ProgressLogger::default())),
1214            local_count: 0,
1215            threshold: Self::DEFAULT_THRESHOLD,
1216        }
1217    }
1218}
1219
1220impl<P: ProgressLog + Clone> Clone for ConcurrentWrapper<P> {
1221    /// Clone the concurrent wrapper, obtaining a new one with the same
1222    /// threshold, with a local count of zero, and with the same inner
1223    /// [`ProgressLog`].
1224    fn clone(&self) -> Self {
1225        Self {
1226            inner: self.inner.clone(),
1227            local_count: 0,
1228            threshold: self.threshold,
1229        }
1230    }
1231}
1232
1233/// Macro to create a [`ConcurrentWrapper`] based on a
1234/// [`ProgressLogger`], with default log target set to [`std::module_path!`],
1235/// and key-value pairs instead of setters.
1236///
1237/// # Examples
1238///
1239/// ```rust
1240/// use dsi_progress_logger::prelude::*;
1241///
1242/// let mut pl = concurrent_progress_logger![item_name="pumpkin", display_memory=true];
1243/// ```
1244#[macro_export]
1245macro_rules! concurrent_progress_logger {
1246    ($($method:ident = $arg:expr),* $(,)?) => {
1247        {
1248            let mut cpl = $crate::ConcurrentWrapper::default();
1249            $crate::ProgressLog::log_target(&mut cpl, ::std::module_path!());
1250            $(
1251                $crate::ProgressLog::$method(&mut cpl, $arg);
1252            )*
1253            cpl
1254        }
1255    }
1256}
1257
1258impl ConcurrentWrapper {
1259    /// Create a new [`ConcurrentWrapper`] based on a default
1260    /// [`ProgressLogger`], using the [default
1261    /// threshold](Self::DEFAULT_THRESHOLD).
1262    pub fn new() -> Self {
1263        Self::with_threshold(Self::DEFAULT_THRESHOLD)
1264    }
1265
1266    /// Create a new [`ConcurrentWrapper`] wrapping a default
1267    /// [`ProgressLogger`], using the given threshold.
1268    pub fn with_threshold(threshold: u32) -> Self {
1269        Self {
1270            inner: Arc::new(Mutex::new(ProgressLogger::default())),
1271            local_count: 0,
1272            threshold,
1273        }
1274    }
1275}
1276
1277impl<P: ProgressLog> ConcurrentWrapper<P> {
1278    /// The default threshold for updating the underlying logger.
1279    pub const DEFAULT_THRESHOLD: u32 = 1 << 15;
1280
1281    /// Calls to [`light_update`](ProgressLog::light_update) will cause a call
1282    /// to [`update_with_count`](ProgressLog::update_with_count) only if the
1283    /// current local count is a multiple of this mask plus one.
1284    ///
1285    /// Note that this constant is significantly smaller than the one used in
1286    /// [`ProgressLogger`], as updates will be further delayed by the threshold
1287    /// mechanism.
1288    pub const LIGHT_UPDATE_MASK: u32 = (1 << 10) - 1;
1289
1290    /// Set the threshold for updating the underlying logger.
1291    ///
1292    /// Note that concurrent loggers with the same underlying logger
1293    /// have independent thresholds.
1294    pub fn threshold(&mut self, threshold: u32) -> &mut Self {
1295        self.threshold = threshold;
1296        self
1297    }
1298
1299    /// Wrap a given [`ProgressLog`] in a [`ConcurrentWrapper`]
1300    /// using the [default threshold](Self::DEFAULT_THRESHOLD).
1301    pub fn wrap(inner: P) -> Self {
1302        Self {
1303            inner: Arc::new(Mutex::new(inner)),
1304            local_count: 0,
1305            threshold: Self::DEFAULT_THRESHOLD,
1306        }
1307    }
1308
1309    /// Wrap a given [`ProgressLog`] in a [`ConcurrentWrapper`] using a
1310    /// given threshold.
1311    pub fn wrap_with_threshold(inner: P, threshold: u32) -> Self {
1312        Self {
1313            inner: Arc::new(Mutex::new(inner)),
1314            local_count: 0,
1315            threshold,
1316        }
1317    }
1318
1319    /// Force an update of the underlying logger with the current local count.
1320    pub fn flush(&mut self) {
1321        self.inner
1322            .lock()
1323            .unwrap()
1324            .update_with_count(self.local_count as _);
1325        self.local_count = 0;
1326    }
1327}
1328
1329impl<P: ProgressLog + Clone> ConcurrentWrapper<P> {
1330    /// Duplicates the concurrent wrapper, obtaining a new one with the same
1331    /// threshold, with a local count of zero, and with an inner
1332    /// [`ProgressLog`] that is a clone of the original one.
1333    pub fn dup(&self) -> Self {
1334        Self {
1335            inner: Arc::new(Mutex::new(self.inner.lock().unwrap().clone())),
1336            local_count: 0,
1337            threshold: self.threshold,
1338        }
1339    }
1340}
1341
1342impl<P: ProgressLog + Clone + Send> ConcurrentProgressLog for ConcurrentWrapper<P> {
1343    type Duplicated = ConcurrentWrapper<P>;
1344    fn dup(&self) -> Self {
1345        Self {
1346            inner: Arc::new(Mutex::new(self.inner.lock().unwrap().clone())),
1347            local_count: 0,
1348            threshold: self.threshold,
1349        }
1350    }
1351}
1352
1353impl<P: ProgressLog + Clone + Send> ProgressLog for ConcurrentWrapper<P> {
1354    type Concurrent = Self;
1355
1356    fn log(&mut self, now: Instant) {
1357        self.inner.lock().unwrap().log(now);
1358    }
1359
1360    fn log_if(&mut self, now: Instant) {
1361        self.inner.lock().unwrap().log_if(now);
1362    }
1363
1364    fn add_to_count(&mut self, count: usize) {
1365        self.inner.lock().unwrap().add_to_count(count);
1366    }
1367
1368    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
1369        self.inner.lock().unwrap().display_memory(display_memory);
1370        self
1371    }
1372
1373    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
1374        self.inner.lock().unwrap().item_name(item_name);
1375        self
1376    }
1377
1378    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
1379        self.inner.lock().unwrap().log_interval(log_interval);
1380        self
1381    }
1382
1383    fn expected_updates(&mut self, expected_updates: Option<usize>) -> &mut Self {
1384        self.inner
1385            .lock()
1386            .unwrap()
1387            .expected_updates(expected_updates);
1388        self
1389    }
1390
1391    fn time_unit(&mut self, time_unit: Option<TimeUnit>) -> &mut Self {
1392        self.inner.lock().unwrap().time_unit(time_unit);
1393        self
1394    }
1395
1396    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
1397        self.inner.lock().unwrap().local_speed(local_speed);
1398        self
1399    }
1400
1401    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
1402        self.inner.lock().unwrap().log_target(target);
1403        self
1404    }
1405
1406    fn start(&mut self, msg: impl AsRef<str>) {
1407        self.inner.lock().unwrap().start(msg);
1408        self.local_count = 0;
1409    }
1410
1411    #[inline]
1412    fn update(&mut self) {
1413        self.update_with_count(1)
1414    }
1415
1416    #[inline]
1417    fn update_with_count_and_time(&mut self, count: usize, _now: Instant) {
1418        self.update_with_count(count);
1419    }
1420
1421    #[inline]
1422    fn update_with_count(&mut self, count: usize) {
1423        match (self.local_count as usize).checked_add(count) {
1424            None => {
1425                // Sum overflows, update in two steps
1426                {
1427                    let now = Instant::now();
1428                    let mut inner = self.inner.lock().unwrap();
1429                    inner.update_with_count_and_time(self.local_count as _, now);
1430                    inner.update_with_count_and_time(count, now);
1431                }
1432                self.local_count = 0;
1433            }
1434            Some(total_count) => {
1435                if total_count >= self.threshold as usize {
1436                    self.local_count = 0;
1437                    // Threshold reached, time to flush to the inner ProgressLog
1438                    let now = Instant::now();
1439                    self.inner
1440                        .lock()
1441                        .unwrap()
1442                        .update_with_count_and_time(total_count, now);
1443                } else {
1444                    // total_count is lower than self.threshold, which is a u32;
1445                    // so total_count fits in u32.
1446                    self.local_count = total_count as u32;
1447                }
1448            }
1449        }
1450    }
1451
1452    #[inline]
1453    fn light_update(&mut self) {
1454        self.local_count += 1;
1455        if (self.local_count & Self::LIGHT_UPDATE_MASK) == 0 && self.local_count >= self.threshold {
1456            // Threshold reached, time to flush to the inner ProgressLog
1457            let local_count = self.local_count as usize;
1458            self.local_count = 0;
1459            let now = Instant::now();
1460            self.inner
1461                .lock()
1462                .unwrap()
1463                .update_with_count_and_time(local_count, now);
1464        }
1465    }
1466
1467    fn update_and_display(&mut self) {
1468        {
1469            let mut inner = self.inner.lock().unwrap();
1470            inner.add_to_count(self.local_count as _);
1471            inner.update_and_display();
1472        }
1473        self.local_count = 0;
1474    }
1475
1476    fn stop(&mut self) {
1477        self.inner.lock().unwrap().stop();
1478    }
1479
1480    fn done(&mut self) {
1481        self.inner.lock().unwrap().done();
1482    }
1483
1484    fn done_with_count(&mut self, count: usize) {
1485        self.inner.lock().unwrap().done_with_count(count);
1486    }
1487
1488    fn elapsed(&self) -> Option<Duration> {
1489        self.inner.lock().unwrap().elapsed()
1490    }
1491
1492    fn count(&self) -> usize {
1493        self.inner.lock().unwrap().count()
1494    }
1495
1496    fn refresh(&mut self) {
1497        self.inner.lock().unwrap().refresh();
1498    }
1499
1500    fn trace(&self, args: Arguments<'_>) {
1501        self.inner.lock().unwrap().trace(args);
1502    }
1503
1504    fn debug(&self, args: Arguments<'_>) {
1505        self.inner.lock().unwrap().debug(args);
1506    }
1507
1508    fn info(&self, args: Arguments<'_>) {
1509        self.inner.lock().unwrap().info(args);
1510    }
1511
1512    fn warn(&self, args: Arguments<'_>) {
1513        self.inner.lock().unwrap().warn(args);
1514    }
1515
1516    fn error(&self, args: Arguments<'_>) {
1517        self.inner.lock().unwrap().error(args);
1518    }
1519
1520    fn concurrent(&self) -> Self::Concurrent {
1521        self.dup()
1522    }
1523}
1524
1525/// This implementation just calls [`flush`](ConcurrentWrapper::flush),
1526/// to guarantee that all updates are correctly passed to the underlying logger.
1527impl<P: ProgressLog> Drop for ConcurrentWrapper<P> {
1528    fn drop(&mut self) {
1529        self.flush();
1530    }
1531}
1532
1533impl Display for ConcurrentWrapper {
1534    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1535        self.inner.lock().unwrap().fmt(f)
1536    }
1537}
1538
1539/// Convenience macro specifying that no (concurrent) logging should be
1540/// performed.
1541#[macro_export]
1542macro_rules! no_logging {
1543    () => {
1544        &mut Option::<$crate::ConcurrentWrapper<$crate::ProgressLogger>>::None
1545    };
1546}
1547
1548pub mod prelude {
1549    pub use super::{
1550        ConcurrentProgressLog, ConcurrentWrapper, ProgressLog, ProgressLogger, TimeUnit,
1551        concurrent_progress_logger, no_logging, progress_logger,
1552    };
1553}