Skip to content
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

feat: MultiBar is now thread-safe; Create new bars dynamically #92

Merged
merged 1 commit into from
Dec 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ license = "MIT"

[dependencies]
libc = "0.2"
time = "0.1.35"
time = "0.1"
crossbeam-channel = "0.4"

[target.'cfg(target_os = "windows")'.dependencies.winapi]
version = "0.3"
features = ["wincon", "processenv", "winbase"]

[target.'cfg(target_os = "redox")'.dependencies]
termion = "1.4"
termion = "1.5"

[dev-dependencies]
rand = "0.5"
rand = "0.7"
2 changes: 1 addition & 1 deletion examples/multi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::thread;
use std::time::Duration;

fn main() {
let mut mb = MultiBar::new();
let mb = MultiBar::new();
mb.println("Your Application Header:");
mb.println("");

Expand Down
47 changes: 47 additions & 0 deletions examples/multi_bg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
extern crate pbr;

use pbr::MultiBar;
use std::{
sync::{atomic::{AtomicBool, Ordering}, Arc},
thread,
time::Duration,
};

fn main() {
let complete = Arc::new(AtomicBool::new(false));
let progress = Arc::new(MultiBar::new());

thread::spawn({
let complete = Arc::clone(&complete);
let progress = Arc::clone(&progress);
move || {
for task in 1..=10 {
thread::spawn({
let progress = Arc::clone(&progress);
move || {
let mut bar = progress.create_bar(100);
bar.message(&format!("Task {}: ", task));

for _ in 0..100 {
thread::sleep(Duration::from_millis(50));
bar.inc();
}

bar.finish_print(&format!("Task {} Complete", task));
}
});

thread::sleep(Duration::from_millis(1000));
}

complete.store(true, Ordering::SeqCst);
}
});

while !complete.load(Ordering::SeqCst) {
let _ = progress.listen();
thread::sleep(Duration::from_millis(1000));
}

let _ = progress.listen();
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ macro_rules! printfl {
}}
}

extern crate crossbeam_channel;
extern crate time;
mod multi;
mod pb;
Expand Down
75 changes: 45 additions & 30 deletions src/multi.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
use crossbeam_channel::{unbounded, Sender, Receiver};
use pb::ProgressBar;
use std::io::{Result, Stdout, Write};
use std::str::from_utf8;
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, Ordering};
use tty::move_cursor_up;

pub struct MultiBar<T: Write> {
nlines: usize,

lines: Vec<String>,

nbars: usize,

state: Mutex<State<T>>,
chan: (Sender<WriteMsg>, Receiver<WriteMsg>),
nbars: AtomicUsize,
}

handle: T,
struct State<T: Write> {
lines: Vec<String>,
nlines: usize,
handle: T
}

impl MultiBar<Stdout> {
Expand Down Expand Up @@ -79,11 +80,13 @@ impl<T: Write> MultiBar<T> {
/// ```
pub fn on(handle: T) -> MultiBar<T> {
MultiBar {
nlines: 0,
nbars: 0,
lines: Vec::new(),
chan: mpsc::channel(),
handle: handle,
state: Mutex::new(State {
lines: Vec::new(),
handle: handle,
nlines: 0,
}),
chan: unbounded(),
nbars: AtomicUsize::new(0),
}
}

Expand Down Expand Up @@ -114,9 +117,10 @@ impl<T: Write> MultiBar<T> {
/// // ...
/// mb.listen();
/// ```
pub fn println(&mut self, s: &str) {
self.lines.push(s.to_owned());
self.nlines += 1;
pub fn println(&self, s: &str) {
let mut state = self.state.lock().unwrap();
state.lines.push(s.to_owned());
state.nlines += 1;
}

/// create_bar creates new `ProgressBar` with `Pipe` as the writer.
Expand Down Expand Up @@ -150,16 +154,22 @@ impl<T: Write> MultiBar<T> {
/// // ...
/// mb.listen();
/// ```
pub fn create_bar(&mut self, total: u64) -> ProgressBar<Pipe> {
self.println("");
self.nbars += 1;
pub fn create_bar(&self, total: u64) -> ProgressBar<Pipe> {
let mut state = self.state.lock().unwrap();

state.lines.push(String::new());
state.nlines += 1;

self.nbars.fetch_add(1, Ordering::SeqCst);

let mut p = ProgressBar::on(
Pipe {
level: self.nlines - 1,
level: state.nlines - 1,
chan: self.chan.0.clone(),
},
total,
);

p.is_multibar = true;
p.add(0);
p
Expand Down Expand Up @@ -193,29 +203,34 @@ impl<T: Write> MultiBar<T> {
///
/// // ...
/// ```
pub fn listen(&mut self) {
pub fn listen(&self) {
let mut first = true;
let mut nbars = self.nbars;
while nbars > 0 {
let mut out = String::new();

while self.nbars.load(Ordering::SeqCst) > 0 {
// receive message
let msg = self.chan.1.recv().unwrap();
if msg.done {
nbars -= 1;
self.nbars.fetch_sub(1, Ordering::SeqCst);
continue;
}
self.lines[msg.level] = msg.string;

out.clear();
let mut state = self.state.lock().unwrap();
state.lines[msg.level] = msg.string;

// and draw
let mut out = String::new();
if !first {
out += &move_cursor_up(self.nlines);
out += &move_cursor_up(state.nlines);
} else {
first = false;
}
for l in self.lines.iter() {

for l in state.lines.iter() {
out.push_str(&format!("\r{}\n", l));
}
printfl!(self.handle, "{}", out);

printfl!(state.handle, "{}", out);
}
}
}
Expand Down