Skip to content

Commit b132a85

Browse files
authored
Merge pull request #2707 from rbtcollins/chunkedio
Stream large files during unpacking
2 parents 84974df + 8ad8b74 commit b132a85

17 files changed

+633
-134
lines changed

src/cli/self_update.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ pub fn install(
413413

414414
fn rustc_or_cargo_exists_in_path() -> Result<()> {
415415
// Ignore rustc and cargo if present in $HOME/.cargo/bin or a few other directories
416+
#[allow(clippy::ptr_arg)]
416417
fn ignore_paths(path: &PathBuf) -> bool {
417418
!path
418419
.components()

src/cli/self_update/windows.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ fn _apply_new_path(new_path: Option<Vec<u16>>) -> Result<()> {
180180
}
181181

182182
// Tell other processes to update their environment
183+
#[allow(clippy::unnecessary_cast)]
183184
unsafe {
184185
SendMessageTimeoutA(
185186
HWND_BROADCAST,

src/cli/topical_doc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fn index_html(doc: &DocData<'_>, wpath: &Path) -> Option<PathBuf> {
2222
}
2323
}
2424

25-
fn dir_into_vec(dir: &PathBuf) -> Result<Vec<OsString>> {
25+
fn dir_into_vec(dir: &Path) -> Result<Vec<OsString>> {
2626
let entries = fs::read_dir(dir).chain_err(|| format!("Opening directory {:?}", dir))?;
2727
let mut v = Vec::new();
2828
for entry in entries {

src/config.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,12 @@ impl<'a> OverrideCfg<'a> {
110110
|| file.toolchain.components.is_some()
111111
|| file.toolchain.profile.is_some()
112112
{
113-
return Err(ErrorKind::CannotSpecifyPathAndOptions(path.into()).into());
113+
return Err(ErrorKind::CannotSpecifyPathAndOptions(path).into());
114114
}
115115
Some(Toolchain::from_path(cfg, cfg_path, &path)?)
116116
}
117117
(Some(channel), Some(path)) => {
118-
return Err(ErrorKind::CannotSpecifyChannelAndPath(channel, path.into()).into())
118+
return Err(ErrorKind::CannotSpecifyChannelAndPath(channel, path).into())
119119
}
120120
(None, None) => None,
121121
},
@@ -172,8 +172,7 @@ impl PgpPublicKey {
172172
Ok(ret)
173173
}
174174
use pgp::types::KeyTrait;
175-
let mut ret = Vec::new();
176-
ret.push(format!("from {}", self));
175+
let mut ret = vec![format!("from {}", self)];
177176
let key = self.key();
178177
let keyid = format_hex(&key.key_id().to_vec(), "-", 4)?;
179178
let algo = key.algorithm();

src/diskio/immediate.rs

Lines changed: 188 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,204 @@
22
///
33
/// Use for diagnosing bugs or working around any unexpected issues with the
44
/// threaded code paths.
5-
use super::{perform, Executor, Item};
5+
use std::{
6+
fmt::Debug,
7+
fs::{File, OpenOptions},
8+
io::{self, Write},
9+
path::Path,
10+
sync::{Arc, Mutex},
11+
time::Instant,
12+
};
13+
14+
use super::{CompletedIo, Executor, Item};
15+
16+
#[derive(Debug)]
17+
pub struct _IncrementalFileState {
18+
completed_chunks: Vec<usize>,
19+
err: Option<io::Result<()>>,
20+
item: Option<Item>,
21+
finished: bool,
22+
}
23+
24+
pub(super) type IncrementalFileState = Arc<Mutex<Option<_IncrementalFileState>>>;
25+
26+
#[derive(Default, Debug)]
27+
pub struct ImmediateUnpacker {
28+
incremental_state: IncrementalFileState,
29+
}
630

7-
#[derive(Default)]
8-
pub struct ImmediateUnpacker {}
931
impl ImmediateUnpacker {
1032
pub fn new() -> Self {
11-
Self {}
33+
Self {
34+
..Default::default()
35+
}
36+
}
37+
38+
fn deque(&self) -> Box<dyn Iterator<Item = CompletedIo>> {
39+
let mut guard = self.incremental_state.lock().unwrap();
40+
// incremental file in progress
41+
if let Some(ref mut state) = *guard {
42+
// Case 1: pending errors
43+
if state.finished {
44+
let mut item = state.item.take().unwrap();
45+
if state.err.is_some() {
46+
let err = state.err.take().unwrap();
47+
item.result = err;
48+
}
49+
item.finish = item
50+
.start
51+
.map(|s| Instant::now().saturating_duration_since(s));
52+
if state.finished {
53+
*guard = None;
54+
}
55+
Box::new(Some(CompletedIo::Item(item)).into_iter())
56+
} else {
57+
// Case 2: pending chunks (which might be empty)
58+
let mut completed_chunks = vec![];
59+
completed_chunks.append(&mut state.completed_chunks);
60+
Box::new(completed_chunks.into_iter().map(CompletedIo::Chunk))
61+
}
62+
} else {
63+
Box::new(None.into_iter())
64+
}
1265
}
1366
}
1467

1568
impl Executor for ImmediateUnpacker {
16-
fn dispatch(&self, mut item: Item) -> Box<dyn Iterator<Item = Item> + '_> {
17-
perform(&mut item);
18-
Box::new(Some(item).into_iter())
69+
fn dispatch(&self, mut item: Item) -> Box<dyn Iterator<Item = CompletedIo> + '_> {
70+
item.result = match &mut item.kind {
71+
super::Kind::Directory => super::create_dir(&item.full_path),
72+
super::Kind::File(ref contents) => {
73+
super::write_file(&item.full_path, &contents, item.mode)
74+
}
75+
super::Kind::IncrementalFile(_incremental_file) => {
76+
return {
77+
// If there is a pending error, return it, otherwise stash the
78+
// Item for eventual return when the file is finished.
79+
let mut guard = self.incremental_state.lock().unwrap();
80+
if let Some(ref mut state) = *guard {
81+
if state.err.is_some() {
82+
let err = state.err.take().unwrap();
83+
item.result = err;
84+
item.finish = item
85+
.start
86+
.map(|s| Instant::now().saturating_duration_since(s));
87+
*guard = None;
88+
Box::new(Some(CompletedIo::Item(item)).into_iter())
89+
} else {
90+
state.item = Some(item);
91+
Box::new(None.into_iter())
92+
}
93+
} else {
94+
unreachable!();
95+
}
96+
};
97+
}
98+
};
99+
item.finish = item
100+
.start
101+
.map(|s| Instant::now().saturating_duration_since(s));
102+
Box::new(Some(CompletedIo::Item(item)).into_iter())
103+
}
104+
105+
fn join(&mut self) -> Box<dyn Iterator<Item = CompletedIo>> {
106+
self.deque()
19107
}
20108

21-
fn join(&mut self) -> Box<dyn Iterator<Item = Item>> {
22-
Box::new(None.into_iter())
109+
fn completed(&self) -> Box<dyn Iterator<Item = CompletedIo>> {
110+
self.deque()
23111
}
24112

25-
fn completed(&self) -> Box<dyn Iterator<Item = Item>> {
26-
Box::new(None.into_iter())
113+
fn incremental_file_state(&self) -> super::IncrementalFileState {
114+
let mut state = self.incremental_state.lock().unwrap();
115+
if state.is_some() {
116+
unreachable!();
117+
} else {
118+
*state = Some(_IncrementalFileState {
119+
completed_chunks: vec![],
120+
err: None,
121+
item: None,
122+
finished: false,
123+
});
124+
super::IncrementalFileState::Immediate(self.incremental_state.clone())
125+
}
126+
}
127+
}
128+
129+
/// The non-shared state for writing a file incrementally
130+
#[derive(Debug)]
131+
pub(super) struct IncrementalFileWriter {
132+
state: IncrementalFileState,
133+
file: Option<File>,
134+
path_display: String,
135+
}
136+
137+
impl IncrementalFileWriter {
138+
#[allow(unused_variables)]
139+
pub fn new<P: AsRef<Path>>(
140+
path: P,
141+
mode: u32,
142+
state: IncrementalFileState,
143+
) -> std::result::Result<Self, io::Error> {
144+
let mut opts = OpenOptions::new();
145+
#[cfg(unix)]
146+
{
147+
use std::os::unix::fs::OpenOptionsExt;
148+
opts.mode(mode);
149+
}
150+
let path = path.as_ref();
151+
let path_display = format!("{}", path.display());
152+
let file = Some({
153+
trace_scoped!("creat", "name": path_display);
154+
opts.write(true).create(true).truncate(true).open(path)?
155+
});
156+
Ok(IncrementalFileWriter {
157+
file,
158+
state,
159+
path_display,
160+
})
161+
}
162+
163+
pub fn chunk_submit(&mut self, chunk: Vec<u8>) -> bool {
164+
if (self.state.lock().unwrap()).is_none() {
165+
return false;
166+
}
167+
match self.write(chunk) {
168+
Ok(v) => v,
169+
Err(e) => {
170+
let mut state = self.state.lock().unwrap();
171+
if let Some(ref mut state) = *state {
172+
state.err.replace(Err(e));
173+
state.finished = true;
174+
false
175+
} else {
176+
false
177+
}
178+
}
179+
}
180+
}
181+
182+
fn write(&mut self, chunk: Vec<u8>) -> std::result::Result<bool, io::Error> {
183+
let mut state = self.state.lock().unwrap();
184+
if let Some(ref mut state) = *state {
185+
if let Some(ref mut file) = (&mut self.file).as_mut() {
186+
// Length 0 vector is used for clean EOF signalling.
187+
if chunk.is_empty() {
188+
trace_scoped!("close", "name:": self.path_display);
189+
drop(std::mem::take(&mut self.file));
190+
state.finished = true;
191+
} else {
192+
trace_scoped!("write_segment", "name": self.path_display, "len": chunk.len());
193+
file.write_all(&chunk)?;
194+
195+
state.completed_chunks.push(chunk.len());
196+
}
197+
Ok(true)
198+
} else {
199+
Ok(false)
200+
}
201+
} else {
202+
unreachable!();
203+
}
27204
}
28205
}

0 commit comments

Comments
 (0)