|
9 | 9 | // except according to those terms. |
10 | 10 |
|
11 | 11 | use std::fs::File; |
12 | | -use std::io::Write; |
| 12 | +use std::io::{self, Write, BufWriter}; |
13 | 13 | use std::path::Path; |
14 | | -use std::sync::Arc; |
15 | | -use std::thread; |
16 | 14 |
|
17 | 15 | use flate2; |
18 | 16 | use flate2::write::GzEncoder; |
| 17 | +use rayon; |
19 | 18 | use tar::{Builder, Header}; |
20 | 19 | use walkdir::WalkDir; |
21 | 20 | use xz2::write::XzEncoder; |
@@ -57,41 +56,42 @@ impl Tarballer { |
57 | 56 | .chain_err(|| "failed to collect file paths")?; |
58 | 57 | files.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev())); |
59 | 58 |
|
60 | | - // Write the tar into both encoded files. We write all directories |
61 | | - // first, so files may be directly created. (see rustup.rs#1092) |
62 | | - let mut builder = Builder::new(Vec::new()); |
63 | | - for path in dirs { |
64 | | - let src = Path::new(&self.work_dir).join(&path); |
65 | | - builder.append_dir(&path, &src) |
66 | | - .chain_err(|| format!("failed to tar dir '{}'", src.display()))?; |
67 | | - } |
68 | | - for path in files { |
69 | | - let src = Path::new(&self.work_dir).join(&path); |
70 | | - let file = open_file(&src)?; |
71 | | - builder.append_data(&mut header(&src, &file)?, &path, &file) |
72 | | - .chain_err(|| format!("failed to tar file '{}'", src.display()))?; |
73 | | - } |
74 | | - let contents = builder.into_inner() |
75 | | - .chain_err(|| "failed to finish writing .tar stream")?; |
76 | | - let contents = Arc::new(contents); |
77 | | - |
78 | 59 | // Prepare the .tar.gz file |
79 | | - let contents2 = contents.clone(); |
80 | | - let t = thread::spawn(move || { |
81 | | - let mut gz = GzEncoder::new(create_new_file(tar_gz)?, |
82 | | - flate2::Compression::best()); |
83 | | - gz.write_all(&contents2).chain_err(|| "failed to write .gz")?; |
84 | | - gz.finish().chain_err(|| "failed to finish .gz") |
85 | | - }); |
| 60 | + let gz = GzEncoder::new(create_new_file(tar_gz)?, flate2::Compression::best()); |
86 | 61 |
|
87 | 62 | // Prepare the .tar.xz file |
88 | | - let mut xz = XzEncoder::new(create_new_file(tar_xz)?, 9); |
89 | | - xz.write_all(&contents).chain_err(|| "failed to write .xz")?; |
90 | | - xz.finish().chain_err(|| "failed to finish .xz")?; |
| 63 | + let xz = XzEncoder::new(create_new_file(tar_xz)?, 9); |
91 | 64 |
|
92 | | - t.join().unwrap()?; |
93 | | - |
94 | | - Ok(()) |
| 65 | + // Write the tar into both encoded files. We write all directories |
| 66 | + // first, so files may be directly created. (see rustup.rs#1092) |
| 67 | + let tee = RayonTee(xz, gz); |
| 68 | + let buf = BufWriter::with_capacity(1024 * 1024, tee); |
| 69 | + let mut builder = Builder::new(buf); |
| 70 | + |
| 71 | + let pool = rayon::Configuration::new().num_threads(2).build().unwrap(); |
| 72 | + pool.install(move || { |
| 73 | + for path in dirs { |
| 74 | + let src = Path::new(&self.work_dir).join(&path); |
| 75 | + builder.append_dir(&path, &src) |
| 76 | + .chain_err(|| format!("failed to tar dir '{}'", src.display()))?; |
| 77 | + } |
| 78 | + for path in files { |
| 79 | + let src = Path::new(&self.work_dir).join(&path); |
| 80 | + let file = open_file(&src)?; |
| 81 | + builder.append_data(&mut header(&src, &file)?, &path, &file) |
| 82 | + .chain_err(|| format!("failed to tar file '{}'", src.display()))?; |
| 83 | + } |
| 84 | + let RayonTee(xz, gz) = builder.into_inner() |
| 85 | + .chain_err(|| "failed to finish writing .tar stream")? |
| 86 | + .into_inner().ok().unwrap(); |
| 87 | + |
| 88 | + // Finish both encoded files |
| 89 | + let (rxz, rgz) = rayon::join( |
| 90 | + || xz.finish().chain_err(|| "failed to finish .tar.xz file"), |
| 91 | + || gz.finish().chain_err(|| "failed to finish .tar.gz file"), |
| 92 | + ); |
| 93 | + rxz.and(rgz).and(Ok(())) |
| 94 | + }) |
95 | 95 | } |
96 | 96 | } |
97 | 97 |
|
@@ -138,3 +138,24 @@ fn get_recursive_paths<P, Q>(root: P, name: Q) -> Result<(Vec<String>, Vec<Strin |
138 | 138 | } |
139 | 139 | Ok((dirs, files)) |
140 | 140 | } |
| 141 | + |
| 142 | +struct RayonTee<A, B>(A, B); |
| 143 | + |
| 144 | +impl<A: Write + Send, B: Write + Send> Write for RayonTee<A, B> { |
| 145 | + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
| 146 | + self.write_all(buf)?; |
| 147 | + Ok(buf.len()) |
| 148 | + } |
| 149 | + |
| 150 | + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
| 151 | + let (a, b) = (&mut self.0, &mut self.1); |
| 152 | + let (ra, rb) = rayon::join(|| a.write_all(buf), || b.write_all(buf)); |
| 153 | + ra.and(rb) |
| 154 | + } |
| 155 | + |
| 156 | + fn flush(&mut self) -> io::Result<()> { |
| 157 | + let (a, b) = (&mut self.0, &mut self.1); |
| 158 | + let (ra, rb) = rayon::join(|| a.flush(), || b.flush()); |
| 159 | + ra.and(rb) |
| 160 | + } |
| 161 | +} |
0 commit comments