Repository: a8m/pb (language: rust)

Issue:
when used in rayon par_iter, I got this compile error message.
```
`std::sync::mpsc::Sender<pbr::multi::WriteMsg>` cannot be shared between threads safely

`std::sync::mpsc::Sender<pbr::multi::WriteMsg>` cannot be shared between threads safely

help: within `[closure@src/main.rs:262:25: 276:14 mb:&mut pbr::multi::MultiBar<std::io::Stdout>]`, the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Sender<pbr::multi::WriteMsg>`
note: required because it appears within the type `(std::sync::mpsc::Sender<pbr::multi::WriteMsg>, std::sync::mpsc::Receiver<pbr::multi::WriteMsg>)`
note: required because it appears within the type `pbr::multi::MultiBar<std::io::Stdout>`
note: required because it appears within the type `&mut pbr::multi::MultiBar<std::io::Stdout>`
note: required because it appears within the type `[closure@src/main.rs:262:25: 276:14 mb:&mut pbr::multi::MultiBar<std::io::Stdout>]`
```
how can i get rid of this error? thank you.


Discussion / hints:

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 2d1cd684..987ee483 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/examples/multi.rs b/examples/multi.rs
index 3416045b..ee99de58 100644
--- a/examples/multi.rs
+++ b/examples/multi.rs
@@ -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("");
 
diff --git a/examples/multi_bg.rs b/examples/multi_bg.rs
new file mode 100644
index 00000000..bb57cd25
--- /dev/null
+++ b/examples/multi_bg.rs
@@ -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();
+}
diff --git a/src/lib.rs b/src/lib.rs
index 908e76c2..0e4317ad 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -111,6 +111,7 @@ macro_rules! printfl {
     }}
 }
 
+extern crate crossbeam_channel;
 extern crate time;
 mod multi;
 mod pb;
diff --git a/src/multi.rs b/src/multi.rs
index a2afa50c..9a60b632 100644
--- a/src/multi.rs
+++ b/src/multi.rs
@@ -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> {
@@ -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),
         }
     }
 
@@ -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.
@@ -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
@@ -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);
         }
     }
 }


Test patch: