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 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 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 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. type Concurrent =
321///     Option<P::Concurrent>;
322///
323pub trait ConcurrentProgressLog: ProgressLog + Sync + Send + Clone {
324    /// The type returned by [`dup`](ConcurrentProgressLog::dup).
325    type Duplicated: ConcurrentProgressLog;
326
327    /// Duplicate the concurrent progress logger, obtaining a new one.
328    ///
329    /// Note that the this method has the same semantics of [`ProgressLogger`'s
330    /// `Clone` implementation](ProgressLogger#impl-Clone-for-ProgressLogger),
331    /// but in a [`ConcurrentProgressLog`] by contract [cloning must generate
332    /// copies with the same underlying logger](ConcurrentProgressLog).
333    fn dup(&self) -> Self::Duplicated;
334}
335
336impl<P: ProgressLog> ProgressLog for &mut P {
337    type Concurrent = P::Concurrent;
338
339    fn log(&mut self, now: Instant) {
340        (**self).log(now);
341    }
342
343    fn log_if(&mut self, now: Instant) {
344        (**self).log_if(now);
345    }
346
347    fn add_to_count(&mut self, count: usize) {
348        (**self).add_to_count(count);
349    }
350
351    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
352        (**self).display_memory(display_memory);
353        self
354    }
355
356    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
357        (**self).item_name(item_name);
358        self
359    }
360
361    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
362        (**self).log_interval(log_interval);
363        self
364    }
365
366    fn expected_updates(&mut self, expected_updates: Option<usize>) -> &mut Self {
367        (**self).expected_updates(expected_updates);
368        self
369    }
370
371    fn time_unit(&mut self, time_unit: Option<TimeUnit>) -> &mut Self {
372        (**self).time_unit(time_unit);
373        self
374    }
375
376    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
377        (**self).local_speed(local_speed);
378        self
379    }
380
381    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
382        (**self).log_target(target);
383        self
384    }
385
386    fn start(&mut self, msg: impl AsRef<str>) {
387        (**self).start(msg);
388    }
389
390    fn update(&mut self) {
391        (**self).update();
392    }
393
394    fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
395        (**self).update_with_count_and_time(count, now);
396    }
397
398    fn light_update(&mut self) {
399        (**self).light_update();
400    }
401
402    fn update_and_display(&mut self) {
403        (**self).update_and_display();
404    }
405
406    fn stop(&mut self) {
407        (**self).stop();
408    }
409
410    fn done(&mut self) {
411        (**self).done();
412    }
413
414    fn done_with_count(&mut self, count: usize) {
415        (**self).done_with_count(count);
416    }
417
418    fn elapsed(&self) -> Option<Duration> {
419        (**self).elapsed()
420    }
421
422    fn count(&self) -> usize {
423        (**self).count()
424    }
425
426    fn refresh(&mut self) {
427        (**self).refresh();
428    }
429
430    fn trace(&self, args: Arguments<'_>) {
431        (**self).trace(args);
432    }
433
434    fn debug(&self, args: Arguments<'_>) {
435        (**self).debug(args);
436    }
437
438    fn info(&self, args: Arguments<'_>) {
439        (**self).info(args);
440    }
441
442    fn warn(&self, args: Arguments<'_>) {
443        (**self).warn(args);
444    }
445
446    fn error(&self, args: Arguments<'_>) {
447        (**self).error(args);
448    }
449
450    fn concurrent(&self) -> Self::Concurrent {
451        (**self).concurrent()
452    }
453}
454
455impl<P: ProgressLog> ProgressLog for Option<P> {
456    type Concurrent = Option<P::Concurrent>;
457
458    fn log(&mut self, now: Instant) {
459        if let Some(pl) = self {
460            pl.log(now);
461        }
462    }
463
464    fn log_if(&mut self, now: Instant) {
465        if let Some(pl) = self {
466            pl.log_if(now);
467        }
468    }
469
470    fn add_to_count(&mut self, count: usize) {
471        if let Some(pl) = self {
472            pl.add_to_count(count);
473        }
474    }
475
476    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
477        if let Some(pl) = self {
478            pl.display_memory(display_memory);
479        }
480        self
481    }
482
483    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
484        if let Some(pl) = self {
485            pl.item_name(item_name);
486        }
487        self
488    }
489
490    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
491        if let Some(pl) = self {
492            pl.log_interval(log_interval);
493        }
494        self
495    }
496
497    fn expected_updates(&mut self, expected_updates: Option<usize>) -> &mut Self {
498        if let Some(pl) = self {
499            pl.expected_updates(expected_updates);
500        }
501        self
502    }
503
504    fn time_unit(&mut self, time_unit: Option<TimeUnit>) -> &mut Self {
505        if let Some(pl) = self {
506            pl.time_unit(time_unit);
507        }
508        self
509    }
510
511    /// Sets whether to display additionally the speed achieved during the last
512    /// log interval.
513    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
514        if let Some(pl) = self {
515            pl.local_speed(local_speed);
516        }
517        self
518    }
519
520    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
521        if let Some(pl) = self {
522            pl.log_target(target);
523        }
524        self
525    }
526
527    fn start(&mut self, msg: impl AsRef<str>) {
528        if let Some(pl) = self {
529            pl.start(msg);
530        }
531    }
532
533    fn update(&mut self) {
534        if let Some(pl) = self {
535            pl.update();
536        }
537    }
538
539    fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
540        if let Some(pl) = self {
541            pl.update_with_count_and_time(count, now);
542        }
543    }
544
545    fn light_update(&mut self) {
546        if let Some(pl) = self {
547            pl.light_update();
548        }
549    }
550
551    fn update_and_display(&mut self) {
552        if let Some(pl) = self {
553            pl.update_and_display();
554        }
555    }
556
557    fn stop(&mut self) {
558        if let Some(pl) = self {
559            pl.stop();
560        }
561    }
562
563    fn done(&mut self) {
564        if let Some(pl) = self {
565            pl.done();
566        }
567    }
568
569    fn done_with_count(&mut self, count: usize) {
570        if let Some(pl) = self {
571            pl.done_with_count(count);
572        }
573    }
574
575    fn elapsed(&self) -> Option<Duration> {
576        self.as_ref().and_then(|pl| pl.elapsed())
577    }
578
579    fn count(&self) -> usize {
580        self.as_ref().map(|pl| pl.count()).unwrap_or(0)
581    }
582
583    fn refresh(&mut self) {
584        if let Some(pl) = self {
585            pl.refresh();
586        }
587    }
588
589    fn trace(&self, args: Arguments<'_>) {
590        if let Some(pl) = self {
591            pl.trace(args);
592        }
593    }
594
595    fn debug(&self, args: Arguments<'_>) {
596        if let Some(pl) = self {
597            pl.debug(args);
598        }
599    }
600
601    fn info(&self, args: Arguments<'_>) {
602        if let Some(pl) = self {
603            pl.info(args);
604        }
605    }
606
607    fn warn(&self, args: Arguments<'_>) {
608        if let Some(pl) = self {
609            pl.warn(args);
610        }
611    }
612
613    fn error(&self, args: Arguments<'_>) {
614        if let Some(pl) = self {
615            pl.error(args);
616        }
617    }
618
619    fn concurrent(&self) -> Self::Concurrent {
620        self.as_ref().map(|pl| pl.concurrent())
621    }
622}
623
624impl<P: ConcurrentProgressLog> ConcurrentProgressLog for Option<P> {
625    type Duplicated = Option<P::Duplicated>;
626
627    fn dup(&self) -> Self::Duplicated {
628        self.as_ref().map(|pl| pl.dup())
629    }
630}
631
632/// An implementation of [`ProgressLog`] with output generated using the
633/// [`log`](https://docs.rs/log) crate at the `info` level.
634///
635/// Instances can be created by using fluent setters, or by using the
636/// [`progress_logger`] macro.
637///
638/// You can [clone](#impl-Clone-for-ProgressLogger) a logger to create a new one
639/// with the same setup but with all the counters reset. This behavior is useful
640/// when you want to configure a logger and then use its configuration for other
641/// loggers.
642///
643/// # Examples
644///
645/// A typical call sequence to a progress logger is as follows:
646///
647/// ```rust
648/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
649/// use dsi_progress_logger::prelude::*;
650///
651/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
652///
653/// let mut pl = ProgressLogger::default();
654/// pl.item_name("pumpkin");
655/// pl.start("Smashing pumpkins...");
656/// for _ in 0..100 {
657///    // do something on each pumpkin
658///    pl.update();
659/// }
660/// pl.done();
661/// #     Ok(())
662/// # }
663/// ```
664///
665/// The [`progress_logger`] macro will create the progress logger for you and
666/// set its [`log_target`](ProgressLog::log_target) to [`std::module_path!()`],
667/// which is usually what you want. You can also call any setter with a
668/// key-value syntax:
669///
670/// ```rust
671/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
672/// use dsi_progress_logger::prelude::*;
673///
674/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
675///
676/// let mut pl = progress_logger![item_name="pumpkin"];
677/// pl.start("Smashing pumpkins...");
678/// for _ in 0..100 {
679///    // do something on each pumpkin
680///    pl.update();
681/// }
682/// pl.done();
683/// #     Ok(())
684/// # }
685/// ```
686///
687/// A progress logger can also be used as a handy timer:
688///
689/// ```rust
690/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
691/// use dsi_progress_logger::prelude::*;
692///
693/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
694///
695/// let mut pl = progress_logger![item_name="pumpkin"];
696/// pl.start("Smashing pumpkins...");
697/// for _ in 0..100 {
698///    // do something on each pumpkin
699/// }
700/// pl.done_with_count(100);
701/// #     Ok(())
702/// # }
703/// ```
704///
705/// This progress logger will display information about  memory usage:
706///
707/// ```rust
708/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
709/// use dsi_progress_logger::prelude::*;
710///
711/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
712///
713/// let mut pl = progress_logger![display_memory=true];
714/// #     Ok(())
715/// # }
716/// ```
717pub struct ProgressLogger {
718    /// The name of an item. Defaults to `item`.
719    item_name: String,
720    /// The pluralized name of an item. Defaults to `items`. It is quite
721    /// expensive to compute with [`pluralize`](pluralizer::pluralize), hence
722    /// the caching.
723    items_name: String,
724    /// The log interval. Defaults to 10 seconds.
725    log_interval: Duration,
726    /// The expected number of updates. If set, the logger will display the percentage of completion and
727    /// an estimate of the time to completion.
728    expected_updates: Option<usize>,
729    /// The time unit to use for speed. If set, the logger will always display the speed in this unit
730    /// instead of making a choice of readable unit based on the elapsed time. Moreover, large numbers
731    /// will not be thousands separated. This is useful when the output of the logger must be parsed.
732    time_unit: Option<TimeUnit>,
733    /// Display additionally the speed achieved during the last log interval.
734    local_speed: bool,
735    /// [`log`] target
736    ///
737    /// This is often the path of the module logging progress.
738    log_target: String,
739    /// When the logger was started.
740    start_time: Option<Instant>,
741    /// The last time we logged the activity (to compute speed).
742    last_log_time: Instant,
743    /// The next time we will log the activity.
744    next_log_time: Instant,
745    /// When the logger was stopped.
746    stop_time: Option<Instant>,
747    /// The number of items.
748    count: usize,
749    /// The number of items at the last log (to compute speed).
750    last_count: usize,
751    /// Display additionally the amount of used and free memory using this [`sysinfo::System`]
752    system: Option<System>,
753    /// The pid of the current process
754    pid: Pid,
755}
756
757impl Default for ProgressLogger {
758    /// Create a default [`ProgressLogger`] with a log interval of 10 seconds and
759    /// item name set to “item”.
760    fn default() -> Self {
761        Self {
762            item_name: "item".into(),
763            items_name: "items".into(),
764            log_interval: Duration::from_secs(10),
765            expected_updates: None,
766            time_unit: None,
767            local_speed: false,
768            log_target: std::env::current_exe()
769                .ok()
770                .and_then(|path| {
771                    path.file_name()
772                        .and_then(|s| s.to_owned().into_string().ok())
773                })
774                .unwrap_or_else(|| "main".to_string()),
775            start_time: None,
776            last_log_time: Instant::now(),
777            next_log_time: Instant::now(),
778            stop_time: None,
779            count: 0,
780            last_count: 0,
781            system: None,
782            pid: Pid::from(std::process::id() as usize),
783        }
784    }
785}
786
787impl Clone for ProgressLogger {
788    /// Clone the logger, returning a logger with the same setup but with all the
789    /// counters reset.
790    #[allow(clippy::manual_map)]
791    fn clone(&self) -> Self {
792        Self {
793            item_name: self.item_name.clone(),
794            items_name: self.items_name.clone(),
795            log_interval: self.log_interval,
796            time_unit: self.time_unit,
797            local_speed: self.local_speed,
798            system: match self.system {
799                Some(_) => Some(System::new_with_specifics(
800                    RefreshKind::nothing().with_memory(MemoryRefreshKind::nothing().with_ram()),
801                )),
802                None => None,
803            },
804            ..ProgressLogger::default()
805        }
806    }
807}
808
809/// Macro to create a [`ProgressLogger`] with default log target set to
810/// [`std::module_path!`], and key-value pairs instead of setters.
811///
812/// # Examples
813///
814///
815/// ```rust
816/// use dsi_progress_logger::prelude::*;
817///
818/// let mut pl = progress_logger![item_name="pumpkin", display_memory=true];
819/// ```
820#[macro_export]
821macro_rules! progress_logger {
822    ($($method:ident = $arg:expr),* $(,)?) => {
823        {
824            let mut pl = ::dsi_progress_logger::ProgressLogger::default();
825            ::dsi_progress_logger::ProgressLog::log_target(&mut pl, ::std::module_path!());
826            $(
827                ::dsi_progress_logger::ProgressLog::$method(&mut pl, $arg);
828            )*
829            pl
830        }
831    }
832}
833
834impl ProgressLogger {
835    /// Calls to [light_update](ProgressLog::light_update) will cause a call to
836    /// [`Instant::now`] only if the current count is a multiple of this mask
837    /// plus one.
838    pub const LIGHT_UPDATE_MASK: usize = (1 << 20) - 1;
839
840    fn fmt_timing_speed(&self, f: &mut Formatter<'_>, seconds_per_item: f64) -> Result {
841        let items_per_second = 1.0 / seconds_per_item;
842
843        let time_unit_timing = self
844            .time_unit
845            .unwrap_or_else(|| TimeUnit::nice_time_unit(seconds_per_item));
846
847        let time_unit_speed = self
848            .time_unit
849            .unwrap_or_else(|| TimeUnit::nice_speed_unit(seconds_per_item));
850
851        f.write_fmt(format_args!(
852            "{:.2} {}/{}, {:.2} {}/{}",
853            items_per_second * time_unit_speed.as_seconds(),
854            self.items_name,
855            time_unit_speed.label(),
856            seconds_per_item / time_unit_timing.as_seconds(),
857            time_unit_timing.label(),
858            self.item_name
859        ))?;
860
861        Ok(())
862    }
863}
864
865impl ProgressLog for ProgressLogger {
866    type Concurrent = ConcurrentWrapper<Self>;
867
868    fn log(&mut self, now: Instant) {
869        self.refresh();
870        info!(target: &self.log_target, "{}", self);
871        self.last_count = self.count;
872        self.last_log_time = now;
873        self.next_log_time = now + self.log_interval;
874    }
875
876    fn log_if(&mut self, now: Instant) {
877        if self.next_log_time <= now {
878            self.log(now);
879        }
880    }
881
882    fn add_to_count(&mut self, count: usize) {
883        self.count += count;
884    }
885
886    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
887        match (display_memory, &self.system) {
888            (true, None) => {
889                self.system = Some(System::new_with_specifics(
890                    RefreshKind::nothing().with_memory(MemoryRefreshKind::nothing().with_ram()),
891                ));
892            }
893            (false, Some(_)) => {
894                self.system = None;
895            }
896            _ => (),
897        }
898        self
899    }
900
901    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
902        self.item_name = item_name.as_ref().into();
903        self.items_name = pluralize(item_name.as_ref(), 2, false);
904        self
905    }
906
907    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
908        self.log_interval = log_interval;
909        self
910    }
911
912    fn expected_updates(&mut self, expected_updates: Option<usize>) -> &mut Self {
913        self.expected_updates = expected_updates;
914        self
915    }
916
917    fn time_unit(&mut self, time_unit: Option<TimeUnit>) -> &mut Self {
918        self.time_unit = time_unit;
919        self
920    }
921
922    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
923        self.local_speed = local_speed;
924        self
925    }
926
927    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
928        self.log_target = target.as_ref().into();
929        self
930    }
931
932    fn start(&mut self, msg: impl AsRef<str>) {
933        let now = Instant::now();
934        self.start_time = Some(now);
935        self.stop_time = None;
936        self.count = 0;
937        self.last_count = 0;
938        self.last_log_time = now;
939        self.next_log_time = now + self.log_interval;
940        if !msg.as_ref().is_empty() {
941            info!(target: &self.log_target, "{}", msg.as_ref());
942        }
943    }
944
945    fn refresh(&mut self) {
946        if let Some(system) = &mut self.system {
947            system.refresh_processes_specifics(
948                ProcessesToUpdate::Some(&[self.pid]),
949                false,
950                ProcessRefreshKind::nothing().with_memory(),
951            );
952        }
953    }
954
955    fn update(&mut self) {
956        self.count += 1;
957        self.log_if(Instant::now());
958    }
959
960    fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
961        self.count += count;
962        self.log_if(now);
963    }
964
965    /// Increases the count and, once every
966    /// [`LIGHT_UPDATE_MASK`](#fields.LIGHT_UPDATE_MASK) + 1 calls, check
967    /// whether it is time to log.
968    #[inline(always)]
969    fn light_update(&mut self) {
970        self.count += 1;
971        if (self.count & Self::LIGHT_UPDATE_MASK) == 0 {
972            self.log_if(Instant::now());
973        }
974    }
975
976    fn update_and_display(&mut self) {
977        self.count += 1;
978        self.log(Instant::now());
979    }
980
981    fn stop(&mut self) {
982        self.stop_time = Some(Instant::now());
983        self.expected_updates = None;
984    }
985
986    fn done(&mut self) {
987        self.stop();
988        info!(target: &self.log_target, "Completed.");
989        // just to avoid wrong reuses
990        self.expected_updates = None;
991        self.refresh();
992        info!(target: &self.log_target, "{}", self);
993    }
994
995    fn done_with_count(&mut self, count: usize) {
996        self.count = count;
997        self.done();
998    }
999
1000    fn elapsed(&self) -> Option<Duration> {
1001        self.start_time?.elapsed().into()
1002    }
1003
1004    fn count(&self) -> usize {
1005        self.count
1006    }
1007
1008    fn trace(&self, args: Arguments<'_>) {
1009        trace!(target: &self.log_target, "{}", std::fmt::format(args));
1010    }
1011
1012    fn debug(&self, args: Arguments<'_>) {
1013        debug!(target: &self.log_target, "{}", std::fmt::format(args));
1014    }
1015
1016    fn info(&self, args: Arguments<'_>) {
1017        info!(target: &self.log_target, "{}", std::fmt::format(args));
1018    }
1019
1020    fn warn(&self, args: Arguments<'_>) {
1021        warn!(target: &self.log_target, "{}", std::fmt::format(args));
1022    }
1023
1024    fn error(&self, args: Arguments<'_>) {
1025        error!(target: &self.log_target, "{}", std::fmt::format(args));
1026    }
1027
1028    fn concurrent(&self) -> Self::Concurrent {
1029        ConcurrentWrapper::wrap(self.clone())
1030    }
1031}
1032
1033impl Display for ProgressLogger {
1034    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1035        if let Some(start_time) = self.start_time {
1036            let count_fmtd = if self.time_unit.is_none() {
1037                self.count.to_formatted_string(&Locale::en)
1038            } else {
1039                self.count.to_string()
1040            };
1041
1042            if let Some(stop_time) = self.stop_time {
1043                let elapsed = stop_time - start_time;
1044                let seconds_per_item = elapsed.as_secs_f64() / self.count as f64;
1045
1046                f.write_fmt(format_args!(
1047                    "Elapsed: {}",
1048                    TimeUnit::pretty_print(elapsed.as_millis())
1049                ))?;
1050
1051                if self.count != 0 {
1052                    f.write_fmt(format_args!(
1053                        " [{} {}, ",
1054                        count_fmtd,
1055                        if self.count == 1 {
1056                            &self.item_name
1057                        } else {
1058                            &self.items_name
1059                        }
1060                    ))?;
1061                    self.fmt_timing_speed(f, seconds_per_item)?;
1062                    f.write_fmt(format_args!("]"))?
1063                }
1064            } else {
1065                let now = Instant::now();
1066
1067                let elapsed = now - start_time;
1068
1069                f.write_fmt(format_args!(
1070                    "{} {}, {}, ",
1071                    count_fmtd,
1072                    if self.count == 1 {
1073                        &self.item_name
1074                    } else {
1075                        &self.items_name
1076                    },
1077                    TimeUnit::pretty_print(elapsed.as_millis()),
1078                ))?;
1079
1080                let seconds_per_item = elapsed.as_secs_f64() / self.count as f64;
1081                self.fmt_timing_speed(f, seconds_per_item)?;
1082
1083                if let Some(expected_updates) = self.expected_updates {
1084                    let millis_to_end: u128 = (expected_updates.saturating_sub(self.count) as u128
1085                        * elapsed.as_millis())
1086                        / (self.count as u128 + 1);
1087                    f.write_fmt(format_args!(
1088                        "; {:.2}% done, {} to end",
1089                        100.0 * self.count as f64 / expected_updates as f64,
1090                        TimeUnit::pretty_print(millis_to_end)
1091                    ))?;
1092                }
1093
1094                if self.local_speed && self.stop_time.is_none() {
1095                    f.write_fmt(format_args!(" ["))?;
1096
1097                    let elapsed = now - self.last_log_time;
1098                    let seconds_per_item =
1099                        elapsed.as_secs_f64() / (self.count - self.last_count) as f64;
1100                    self.fmt_timing_speed(f, seconds_per_item)?;
1101
1102                    f.write_fmt(format_args!("]"))?;
1103                }
1104            }
1105
1106            // It would be ideal to refresh self.system here, but this operation
1107            // would require an &mut self reference.
1108            if let Some(system) = &self.system {
1109                f.write_fmt(format_args!(
1110                    "; res/vir/avail/free/total mem {}/{}/{}B/{}B/{}B",
1111                    system
1112                        .process(self.pid)
1113                        .map(|process| humanize(process.memory() as _) + "B")
1114                        .unwrap_or("N/A".to_string()),
1115                    system
1116                        .process(self.pid)
1117                        .map(|process| humanize(process.virtual_memory() as _) + "B")
1118                        .unwrap_or("N/A".to_string()),
1119                    humanize(system.available_memory() as _),
1120                    humanize(system.free_memory() as _),
1121                    humanize(system.total_memory() as _)
1122                ))?;
1123            }
1124
1125            Ok(())
1126        } else {
1127            write!(f, "ProgressLogger not started")
1128        }
1129    }
1130}
1131
1132/// A [`ConcurrentProgressLog`] implementation that wraps a [`ProgressLog`] in
1133/// an [`Arc`]/[`Mutex`].
1134///
1135/// The methods [`update`](ProgressLog::update) and
1136/// [`update_with_count`](ProgressLog::update_with_count) buffer the increment
1137/// and add it to the underlying logger only when the buffer reaches a
1138/// threshold; this prevents locking the underlying logger too often. The
1139/// threshold is set at creation using the methods
1140/// [`with_threshold`](Self::with_threshold) and
1141/// [`wrap_with_threshold`](Self::wrap_with_threshold), or by calling the method
1142/// [`threshold`](Self::threshold).
1143///
1144/// The method [`light_update`](ProgressLog::light_update), as in the case of
1145/// [`ProgressLogger`], further delays updates using an even faster check.
1146///
1147/// # Examples
1148///
1149/// In this example, we manually spawn processes:
1150///
1151/// ```rust
1152/// use dsi_progress_logger::prelude::*;
1153/// use std::thread;
1154///
1155/// let mut cpl = concurrent_progress_logger![item_name = "pumpkin"];
1156/// cpl.start("Smashing pumpkins (using many threads)...");
1157///
1158/// std::thread::scope(|s| {
1159///     for i in 0..100 {
1160///         let mut pl = cpl.clone();
1161///         s.spawn(move || {
1162///             for _ in 0..100000 {
1163///                 // do something on each pumpkin
1164///                 pl.update();
1165///             }
1166///         });
1167///     }
1168/// });
1169///
1170/// cpl.done();
1171/// ```
1172///
1173/// You can obtain the same behavior with
1174/// [`rayon`](https://crates.io/crates/rayon) using methods such as
1175/// [`for_each_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.for_each_with)
1176/// and
1177/// [`map_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.map_with):
1178///
1179/// ```rust
1180/// use dsi_progress_logger::prelude::*;
1181/// use rayon::prelude::*;
1182///
1183/// let mut cpl = concurrent_progress_logger![item_name = "pumpkin"];
1184/// cpl.start("Smashing pumpkins (using many threads)...");
1185///
1186/// (0..1000000).into_par_iter().
1187///     with_min_len(1000). // optional, might reduce the amount of cloning
1188///     for_each_with(cpl.clone(), |pl, i| {
1189///         // do something on each pumpkin
1190///         pl.update();
1191///     }
1192/// );
1193///
1194/// cpl.done();
1195/// ```
1196///
1197/// Note that you have to pass `cpl.clone()` to avoid a move that would make the
1198/// call to [`done`](ProgressLog::done) impossible. Also, since
1199/// [`for_each_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.for_each_with)
1200/// might perform excessive cloning if jobs are too short, you can use
1201/// [`with_min_len`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.with_min_len)
1202/// to reduce the amount of cloning.
1203pub struct ConcurrentWrapper<P: ProgressLog = ProgressLogger> {
1204    /// Underlying logger
1205    inner: Arc<Mutex<P>>,
1206    /// The number of items processed by the current thread.
1207    local_count: u32,
1208    /// The threshold for updating the underlying logger.
1209    threshold: u32,
1210}
1211
1212impl Default for ConcurrentWrapper {
1213    /// Create a new [`ConcurrentWrapper`] based on a default
1214    /// [`ProgressLogger`], with a threshold of
1215    /// [`DEFAULT_THRESHOLD`](Self::DEFAULT_THRESHOLD).
1216    fn default() -> Self {
1217        Self {
1218            inner: Arc::new(Mutex::new(ProgressLogger::default())),
1219            local_count: 0,
1220            threshold: Self::DEFAULT_THRESHOLD,
1221        }
1222    }
1223}
1224
1225impl<P: ProgressLog + Clone> Clone for ConcurrentWrapper<P> {
1226    /// Clone the concurrent wrapper, obtaining a new one with the same
1227    /// threshold, with a local count of zero, and with the same inner
1228    /// [`ProgressLog`].
1229    fn clone(&self) -> Self {
1230        Self {
1231            inner: self.inner.clone(),
1232            local_count: 0,
1233            threshold: self.threshold,
1234        }
1235    }
1236}
1237
1238/// Macro to create a [`ConcurrentWrapper`] based on a
1239/// [`ProgressLogger`], with default log target set to [`std::module_path!`],
1240/// and key-value pairs instead of setters.
1241///
1242/// # Examples
1243///
1244/// ```rust
1245/// use dsi_progress_logger::prelude::*;
1246///
1247/// let mut pl = concurrent_progress_logger![item_name="pumpkin", display_memory=true];
1248/// ```
1249#[macro_export]
1250macro_rules! concurrent_progress_logger {
1251    ($($method:ident = $arg:expr),* $(,)?) => {
1252        {
1253            let mut cpl = ::dsi_progress_logger::ConcurrentWrapper::default();
1254            ::dsi_progress_logger::ProgressLog::log_target(&mut cpl, ::std::module_path!());
1255            $(
1256                ::dsi_progress_logger::ProgressLog::$method(&mut cpl, $arg);
1257            )*
1258            cpl
1259        }
1260    }
1261}
1262
1263impl ConcurrentWrapper {
1264    /// Create a new [`ConcurrentWrapper`] based on a default
1265    /// [`ProgressLogger`], using the [default
1266    /// threshold](Self::DEFAULT_THRESHOLD).
1267    pub fn new() -> Self {
1268        Self::with_threshold(Self::DEFAULT_THRESHOLD)
1269    }
1270
1271    /// Create a new [`ConcurrentWrapper`] wrapping a default
1272    /// [`ProgressLogger`], using the given threshold.
1273    pub fn with_threshold(threshold: u32) -> Self {
1274        Self {
1275            inner: Arc::new(Mutex::new(ProgressLogger::default())),
1276            local_count: 0,
1277            threshold,
1278        }
1279    }
1280}
1281
1282impl<P: ProgressLog> ConcurrentWrapper<P> {
1283    /// The default threshold for updating the underlying logger.
1284    pub const DEFAULT_THRESHOLD: u32 = 1 << 15;
1285
1286    /// Calls to [`light_update`](ProgressLog::light_update) will cause a call
1287    /// to [`update_with_count`](ProgressLog::update_with_count) only if the
1288    /// current local count is a multiple of this mask plus one.
1289    ///
1290    /// Note that this constant is significantly smaller than the one used in
1291    /// [`ProgressLogger`], as updates will be further delayed by the threshold
1292    /// mechanism.
1293    pub const LIGHT_UPDATE_MASK: u32 = (1 << 10) - 1;
1294
1295    /// Set the threshold for updating the underlying logger.
1296    ///
1297    /// Note that concurrent loggers with the same underlying logger
1298    /// have independent thresholds.
1299    pub fn threshold(&mut self, threshold: u32) -> &mut Self {
1300        self.threshold = threshold;
1301        self
1302    }
1303
1304    /// Wrap a given [`ProgressLog`] in a [`ConcurrentWrapper`]
1305    /// using the [default threshold](Self::DEFAULT_THRESHOLD).
1306    pub fn wrap(inner: P) -> Self {
1307        Self {
1308            inner: Arc::new(Mutex::new(inner)),
1309            local_count: 0,
1310            threshold: Self::DEFAULT_THRESHOLD,
1311        }
1312    }
1313
1314    /// Wrap a given [`ProgressLog`] in a [`ConcurrentWrapper`] using a
1315    /// given threshold.
1316    pub fn wrap_with_threshold(inner: P, threshold: u32) -> Self {
1317        Self {
1318            inner: Arc::new(Mutex::new(inner)),
1319            local_count: 0,
1320            threshold,
1321        }
1322    }
1323
1324    /// Force an update of the underlying logger with the current local count.
1325    pub fn flush(&mut self) {
1326        self.inner
1327            .lock()
1328            .unwrap()
1329            .update_with_count(self.local_count as _);
1330        self.local_count = 0;
1331    }
1332}
1333
1334impl<P: ProgressLog + Clone> ConcurrentWrapper<P> {
1335    /// Duplicates the concurrent wrapper, obtaining a new one with the same
1336    /// threshold, with a local count of zero, and with an inner
1337    /// [`ProgressLog`] that is a clone of the original one.
1338    pub fn dup(&self) -> Self {
1339        Self {
1340            inner: Arc::new(Mutex::new(self.inner.lock().unwrap().clone())),
1341            local_count: 0,
1342            threshold: self.threshold,
1343        }
1344    }
1345}
1346
1347impl<P: ProgressLog + Clone + Send> ConcurrentProgressLog for ConcurrentWrapper<P> {
1348    type Duplicated = ConcurrentWrapper<P>;
1349    fn dup(&self) -> Self {
1350        Self {
1351            inner: Arc::new(Mutex::new(self.inner.lock().unwrap().clone())),
1352            local_count: 0,
1353            threshold: self.threshold,
1354        }
1355    }
1356}
1357
1358impl<P: ProgressLog + Clone + Send> ProgressLog for ConcurrentWrapper<P> {
1359    type Concurrent = Self;
1360
1361    fn log(&mut self, now: Instant) {
1362        self.inner.lock().unwrap().log(now);
1363    }
1364
1365    fn log_if(&mut self, now: Instant) {
1366        self.inner.lock().unwrap().log_if(now);
1367    }
1368
1369    fn add_to_count(&mut self, count: usize) {
1370        self.inner.lock().unwrap().add_to_count(count);
1371    }
1372
1373    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
1374        self.inner.lock().unwrap().display_memory(display_memory);
1375        self
1376    }
1377
1378    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
1379        self.inner.lock().unwrap().item_name(item_name);
1380        self
1381    }
1382
1383    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
1384        self.inner.lock().unwrap().log_interval(log_interval);
1385        self
1386    }
1387
1388    fn expected_updates(&mut self, expected_updates: Option<usize>) -> &mut Self {
1389        self.inner
1390            .lock()
1391            .unwrap()
1392            .expected_updates(expected_updates);
1393        self
1394    }
1395
1396    fn time_unit(&mut self, time_unit: Option<TimeUnit>) -> &mut Self {
1397        self.inner.lock().unwrap().time_unit(time_unit);
1398        self
1399    }
1400
1401    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
1402        self.inner.lock().unwrap().local_speed(local_speed);
1403        self
1404    }
1405
1406    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
1407        self.inner.lock().unwrap().log_target(target);
1408        self
1409    }
1410
1411    fn start(&mut self, msg: impl AsRef<str>) {
1412        self.inner.lock().unwrap().start(msg);
1413        self.local_count = 0;
1414    }
1415
1416    #[inline]
1417    fn update(&mut self) {
1418        self.update_with_count(1)
1419    }
1420
1421    #[inline]
1422    fn update_with_count_and_time(&mut self, count: usize, _now: Instant) {
1423        self.update_with_count(count);
1424    }
1425
1426    #[inline]
1427    fn update_with_count(&mut self, count: usize) {
1428        match (self.local_count as usize).checked_add(count) {
1429            None => {
1430                // Sum overflows, update in two steps
1431                {
1432                    let now = Instant::now();
1433                    let mut inner = self.inner.lock().unwrap();
1434                    inner.update_with_count_and_time(self.local_count as _, now);
1435                    inner.update_with_count_and_time(count, now);
1436                }
1437                self.local_count = 0;
1438            }
1439            Some(total_count) => {
1440                if total_count >= self.threshold as usize {
1441                    self.local_count = 0;
1442                    // Threshold reached, time to flush to the inner ProgressLog
1443                    let now = Instant::now();
1444                    self.inner
1445                        .lock()
1446                        .unwrap()
1447                        .update_with_count_and_time(total_count, now);
1448                } else {
1449                    // total_count is lower than self.threshold, which is a u32;
1450                    // so total_count fits in u32.
1451                    self.local_count = total_count as u32;
1452                }
1453            }
1454        }
1455    }
1456
1457    #[inline]
1458    fn light_update(&mut self) {
1459        self.local_count += 1;
1460        if (self.local_count & Self::LIGHT_UPDATE_MASK) == 0 && self.local_count >= self.threshold {
1461            // Threshold reached, time to flush to the inner ProgressLog
1462            let local_count = self.local_count as usize;
1463            self.local_count = 0;
1464            let now = Instant::now();
1465            self.inner
1466                .lock()
1467                .unwrap()
1468                .update_with_count_and_time(local_count, now);
1469        }
1470    }
1471
1472    fn update_and_display(&mut self) {
1473        {
1474            let mut inner = self.inner.lock().unwrap();
1475            inner.add_to_count(self.local_count as _);
1476            inner.update_and_display();
1477        }
1478        self.local_count = 0;
1479    }
1480
1481    fn stop(&mut self) {
1482        self.inner.lock().unwrap().stop();
1483    }
1484
1485    fn done(&mut self) {
1486        self.inner.lock().unwrap().done();
1487    }
1488
1489    fn done_with_count(&mut self, count: usize) {
1490        self.inner.lock().unwrap().done_with_count(count);
1491    }
1492
1493    fn elapsed(&self) -> Option<Duration> {
1494        self.inner.lock().unwrap().elapsed()
1495    }
1496
1497    fn count(&self) -> usize {
1498        self.inner.lock().unwrap().count()
1499    }
1500
1501    fn refresh(&mut self) {
1502        self.inner.lock().unwrap().refresh();
1503    }
1504
1505    fn trace(&self, args: Arguments<'_>) {
1506        self.inner.lock().unwrap().trace(args);
1507    }
1508
1509    fn debug(&self, args: Arguments<'_>) {
1510        self.inner.lock().unwrap().debug(args);
1511    }
1512
1513    fn info(&self, args: Arguments<'_>) {
1514        self.inner.lock().unwrap().info(args);
1515    }
1516
1517    fn warn(&self, args: Arguments<'_>) {
1518        self.inner.lock().unwrap().warn(args);
1519    }
1520
1521    fn error(&self, args: Arguments<'_>) {
1522        self.inner.lock().unwrap().error(args);
1523    }
1524
1525    fn concurrent(&self) -> Self::Concurrent {
1526        self.dup()
1527    }
1528}
1529
1530/// This implementation just calls [`flush`](ConcurrentWrapper::flush),
1531///     type Concurrent = Option<P::Concurrent>;
1532///
1533/// to guarantee that all updates are correctly passed to the underlying logger.
1534impl<P: ProgressLog> Drop for ConcurrentWrapper<P> {
1535    fn drop(&mut self) {
1536        self.flush();
1537    }
1538}
1539
1540impl Display for ConcurrentWrapper {
1541    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1542        self.inner.lock().unwrap().fmt(f)
1543    }
1544}
1545
1546/// Convenience macro specifying that no (concurrent) logging should be
1547/// performed.
1548#[macro_export]
1549macro_rules! no_logging {
1550    () => {
1551        &mut Option::<dsi_progress_logger::ConcurrentWrapper::<dsi_progress_logger::ProgressLogger>>::None
1552    };
1553}
1554
1555pub mod prelude {
1556    pub use super::{
1557        concurrent_progress_logger, no_logging, progress_logger, ConcurrentProgressLog,
1558        ConcurrentWrapper, ProgressLog, ProgressLogger,
1559    };
1560}