|
2 | 2 | ///
|
3 | 3 | /// Use for diagnosing bugs or working around any unexpected issues with the
|
4 | 4 | /// 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 | +} |
6 | 30 |
|
7 |
| -#[derive(Default)] |
8 |
| -pub struct ImmediateUnpacker {} |
9 | 31 | impl ImmediateUnpacker {
|
10 | 32 | 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 | + } |
12 | 65 | }
|
13 | 66 | }
|
14 | 67 |
|
15 | 68 | 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() |
19 | 107 | }
|
20 | 108 |
|
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() |
23 | 111 | }
|
24 | 112 |
|
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 | + } |
27 | 204 | }
|
28 | 205 | }
|
0 commit comments