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

Add DocFS layer to rustdoc #60971

Merged
merged 3 commits into from
Jun 22, 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
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3254,6 +3254,7 @@ dependencies = [
"minifier 0.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pulldown-cmark 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]

Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ path = "lib.rs"
[dependencies]
pulldown-cmark = { version = "0.5.2", default-features = false }
minifier = "0.0.30"
rayon = { version = "0.2.0", package = "rustc-rayon" }
tempfile = "3"
parking_lot = "0.7"
116 changes: 116 additions & 0 deletions src/librustdoc/docfs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//! Rustdoc's FileSystem abstraction module.
//!
//! On Windows this indirects IO into threads to work around performance issues
//! with Defender (and other similar virus scanners that do blocking operations).
//! On other platforms this is a thin shim to fs.
//!
//! Only calls needed to permit this workaround have been abstracted: thus
//! fs::read is still done directly via the fs module; if in future rustdoc
//! needs to read-after-write from a file, then it would be added to this
//! abstraction.

use errors;

use std::fs;
use std::io;
use std::path::Path;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver, Sender};

macro_rules! try_err {
($e:expr, $file:expr) => {{
match $e {
Ok(e) => e,
Err(e) => return Err(E::new(e, $file)),
}
}};
}

pub trait PathError {
fn new<P: AsRef<Path>>(e: io::Error, path: P) -> Self;
}

pub struct ErrorStorage {
sender: Option<Sender<Option<String>>>,
receiver: Receiver<Option<String>>,
}

impl ErrorStorage {
pub fn new() -> ErrorStorage {
let (sender, receiver) = channel();
ErrorStorage {
sender: Some(sender),
receiver,
}
}

/// Prints all stored errors. Returns the number of printed errors.
pub fn write_errors(&mut self, diag: &errors::Handler) -> usize {
let mut printed = 0;
// In order to drop the sender part of the channel.
self.sender = None;

for msg in self.receiver.iter() {
if let Some(ref error) = msg {
diag.struct_err(&error).emit();
printed += 1;
}
}
printed
}
}

pub struct DocFS {
sync_only: bool,
errors: Arc<ErrorStorage>,
}

impl DocFS {
pub fn new(errors: &Arc<ErrorStorage>) -> DocFS {
DocFS {
sync_only: false,
errors: Arc::clone(errors),
}
}

pub fn set_sync_only(&mut self, sync_only: bool) {
self.sync_only = sync_only;
}

pub fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
// For now, dir creation isn't a huge time consideration, do it
// synchronously, which avoids needing ordering between write() actions
// and directory creation.
fs::create_dir_all(path)
}

pub fn write<P, C, E>(&self, path: P, contents: C) -> Result<(), E>
where
P: AsRef<Path>,
C: AsRef<[u8]>,
E: PathError,
{
if !self.sync_only && cfg!(windows) {
// A possible future enhancement after more detailed profiling would
// be to create the file sync so errors are reported eagerly.
let contents = contents.as_ref().to_vec();
let path = path.as_ref().to_path_buf();
let sender = self.errors.sender.clone().unwrap();
rayon::spawn(move || {
match fs::write(&path, &contents) {
Ok(_) => {
sender.send(None)
.expect(&format!("failed to send error on \"{}\"", path.display()));
}
Err(e) => {
sender.send(Some(format!("\"{}\": {}", path.display(), e)))
.expect(&format!("failed to send non-error on \"{}\"", path.display()));
}
}
});
Ok(())
} else {
Ok(try_err!(fs::write(&path, contents), path))
}
}
}
Loading