-
Notifications
You must be signed in to change notification settings - Fork 245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement ProgressBar::suspend #333
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! How hard would it be to add some kind of test for this?
is_finished has always been ORed with force_draw, and removing it allows the logic to be simplified.
Actually I'm thinking about an alternative way to implement this: instead of taking a closure, we return a The reason is that the
If we used the closure-style API then we would need to wrap the Or maybe we could expose both. BTW I found that it might be useful for us to pass on the return value from the closure so I'll add it in an upcoming commit. As for automated testing, I don't have a good idea how it could be implemented yet. Any suggestions? |
d0d17d2
to
a6072c1
Compare
I think using a drop guard makes sense for this. Unless there's a good reason I'd prefer not to have both. |
I realized the closure one is better after all, as the drop guard will have a lifetime specifier, making various things harder to do. Especially when dealing with a static Let's go with the closure one. It's kinda ugly to rely on the implementation detail, but with the current tracing-subscriber implementation it isn't less efficient than the drop guard implementation since the write is performed with a single Code I'm using in my prototype, for referencestruct SuspendProgressBarWriter<T: Write>(T);
impl<T: Write> SuspendProgressBarWriter<T> {
fn suspend<F: FnOnce(&mut Self) -> R, R>(&mut self, f: F) -> R {
let handle = PROGRESS_BAR.lock().unwrap().upgrade();
if let Some(mut p) = handle {
p.suspend(|| f(self))
} else {
f(self)
}
}
}
static PROGRESS_BAR: Lazy<Mutex<WeakProgressBar>> = Lazy::new(|| Mutex::new(WeakProgressBar::default()));
impl<T: Write> Write for SuspendProgressBarWriter<T> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.suspend(|this| this.0.write(buf))
}
#[inline]
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
self.suspend(|this| this.0.write_vectored(bufs))
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.suspend(|this| this.0.flush())
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.suspend(|this| this.0.write_all(buf))
}
#[inline]
fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> {
self.suspend(|this| this.0.write_fmt(fmt))
}
} |
a6072c1
to
3364cc4
Compare
Patch updated:
|
Thanks! |
@ishitatsuyuki could you update your message to be properly formatted and/or is there another example (/documentation) of working with this function for the |
I fixed the formatting. For env_logger, I think using |
thanks for the response and the prototype, though for now i use a similar logger / crate: for anyone that wants to use a "improved" version of the prototype from #333 (comment), with some paths from sentry-cli's logging: use std::io::Write;
use indicatif::{
ProgressBar,
WeakProgressBar,
};
use parking_lot::RwLock;
// Utils file, may contain various small helper functions for main binary
#[derive(Debug)]
pub struct ProgressHandler<T: Write + Send>(T);
lazy_static::lazy_static! {
/// Stores the reference to the current Progressbar, if there even is one
/// uses "parking_lot::RwLock", because it is faster than the standart libraries and to allow many readers (read-only access to the variable)
static ref CURRENT_PROGRESS_BAR: RwLock<Option<WeakProgressBar>> = RwLock::new(None);
}
impl<T: Write + Send> ProgressHandler<T> {
/// Helper function to execute everything before running "inner_function"
fn handle<F: FnOnce(&mut Self) -> R, R>(&mut self, inner_function: F) -> R {
let handle = get_progress_bar();
return match handle {
Some(pb) => pb.suspend(|| return inner_function(self)),
None => inner_function(self),
};
}
/// Create a new instance of "Self"
pub fn new(pipe: T) -> Self {
return Self(pipe);
}
}
impl<T: Write + Send> Write for ProgressHandler<T> {
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
return self.handle(|this| this.0.write(buf));
}
#[inline]
fn flush(&mut self) -> std::io::Result<()> {
return self.handle(|this| this.0.flush());
}
}
/// Sets the reference to the current ProgressBar for the writer
pub fn set_progress_bar(pb: Option<WeakProgressBar>) {
*CURRENT_PROGRESS_BAR.write() = pb;
}
/// Returns the Progressbar, if it exists, otherwise "None"
fn get_progress_bar() -> Option<ProgressBar> {
return CURRENT_PROGRESS_BAR.read().as_ref()?.upgrade();
} and then the use simplelog::*;
WriteLogger::init(
LevelFilter::Debug,
ConfigBuilder::new().set_thread_mode(ThreadLogMode::Both).build(),
ProgressHandler::new(std::io::stdout()),
)
.expect("Failed to create Logger"); |
In that case, it's also possible to use |
The workaround is problematic because if the PB is either finished or hidden then it also discards all log messages. This is impossible to get around because if pb.is_hidden() || pb.is_finished() { // Race condition on the ||
println!(...); // Race condition because the if check is not necessarily valid anymore
} else {
pb.println(...);
}
Perhaps it'd be better to make a Personally, I'm tempted to either patch the crate locally or get the private mutex with some trickery, lock it, do my things, unlock it. This is just my two cents, once 0.17 is released and |
Is this a problem for you? I thought that you won't need to access the progress bar inside |
No, it's not a problem for me, you're good. I was unsure whether |
This function hides the progress bar temporarily, execute
f
, then redraws the progress bar, which is useful for external code that writes to the standard output.The idea is originally raised in #92.
It is planned to eventually create a global function like
ProgressBar::instance
that allows callingsuspend
from e.g. a log handler, but this currently doesn't generalize toMultiProgressBar
which could be an obstacle.