Skip to content

Commit

Permalink
Avoid an unnecessary thread creation at rustdoc startup.
Browse files Browse the repository at this point in the history
rustdoc's `main()` immediately spawns a thread, M, with a large stack
(16MiB or 32MiB) on which it runs `main_args()`. `main_args()` does a
small amount of options processing and then calls
`setup_callbacks_and_run_in_default_thread_pool_with_globals()`, which
spawns it own thread, and M is not used further.

So, thread M seems unnecessary. However, it does serve a purpose: if the
options processing in `main_args()` panics, that panic is caught when M
is joined. So M can't simply be removed.

However, `main_options()`, which is called by `main_args()`, has a
`catch_fatal_errors()` call within it. We can move that call to `main()`
and change it to the very similar `catch_with_exit_code()`. With that in
place, M can be removed, and panics from options processing will still
be caught appropriately.

Even better, this makes rustdoc's `main()` match rustc's `main()`, which
also uses `catch_with_exit_code()`.

(Also note that the use of a 16MiB/32MiB stack was eliminated from rustc
in #55617.)
  • Loading branch information
nnethercote committed Aug 5, 2020
1 parent 8244b1b commit 5301407
Showing 1 changed file with 46 additions and 55 deletions.
101 changes: 46 additions & 55 deletions src/librustdoc/lib.rs
Expand Up @@ -47,9 +47,9 @@ extern crate log;

use std::default::Default;
use std::env;
use std::panic;
use std::process;

use rustc_errors::ErrorReported;
use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup};
use rustc_session::getopts;
use rustc_session::{early_error, early_warn};
Expand Down Expand Up @@ -82,22 +82,14 @@ struct Output {
}

pub fn main() {
let thread_stack_size: usize = if cfg!(target_os = "haiku") {
16_000_000 // 16MB on Haiku
} else {
32_000_000 // 32MB on other platforms
};
rustc_driver::set_sigpipe_handler();
rustc_driver::install_ice_hook();
rustc_driver::init_env_logger("RUSTDOC_LOG");

let res = std::thread::Builder::new()
.stack_size(thread_stack_size)
.spawn(move || get_args().map(|args| main_args(&args)).unwrap_or(1))
.unwrap()
.join()
.unwrap_or(rustc_driver::EXIT_FAILURE);
process::exit(res);
let exit_code = rustc_driver::catch_with_exit_code(|| match get_args() {
Some(args) => main_args(&args),
_ => Err(ErrorReported),
});
process::exit(exit_code);
}

fn get_args() -> Option<Vec<String>> {
Expand Down Expand Up @@ -418,7 +410,10 @@ fn usage(argv0: &str) {
println!("{}", options.usage(&format!("{} [options] <input>", argv0)));
}

fn main_args(args: &[String]) -> i32 {
/// A result type used by several functions under `main()`.
type MainResult = Result<(), ErrorReported>;

fn main_args(args: &[String]) -> MainResult {
let mut options = getopts::Options::new();
for option in opts() {
(option.apply)(&mut options);
Expand All @@ -429,24 +424,27 @@ fn main_args(args: &[String]) -> i32 {
early_error(ErrorOutputType::default(), &err.to_string());
}
};

// Note that we discard any distinction between different non-zero exit
// codes from `from_matches` here.
let options = match config::Options::from_matches(&matches) {
Ok(opts) => opts,
Err(code) => return code,
Err(code) => return if code == 0 { Ok(()) } else { Err(ErrorReported) },
};
rustc_interface::interface::setup_callbacks_and_run_in_default_thread_pool_with_globals(
options.edition,
move || main_options(options),
)
}

fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> i32 {
fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> MainResult {
match res {
Ok(()) => 0,
Ok(()) => Ok(()),
Err(err) => {
if !err.is_empty() {
diag.struct_err(&err).emit();
}
1
Err(ErrorReported)
}
}
}
Expand All @@ -457,9 +455,9 @@ fn run_renderer<T: formats::FormatRenderer>(
render_info: config::RenderInfo,
diag: &rustc_errors::Handler,
edition: rustc_span::edition::Edition,
) -> i32 {
) -> MainResult {
match formats::run_format::<T>(krate, renderopts, render_info, &diag, edition) {
Ok(_) => rustc_driver::EXIT_SUCCESS,
Ok(_) => Ok(()),
Err(e) => {
let mut msg = diag.struct_err(&format!("couldn't generate documentation: {}", e.error));
let file = e.file.display().to_string();
Expand All @@ -468,12 +466,12 @@ fn run_renderer<T: formats::FormatRenderer>(
} else {
msg.note(&format!("failed to create or modify \"{}\"", file)).emit()
}
rustc_driver::EXIT_FAILURE
Err(ErrorReported)
}
}
}

fn main_options(options: config::Options) -> i32 {
fn main_options(options: config::Options) -> MainResult {
let diag = core::new_handler(options.error_format, None, &options.debugging_options);

match (options.should_test, options.markdown_input()) {
Expand All @@ -500,44 +498,37 @@ fn main_options(options: config::Options) -> i32 {
// compiler all the way through the analysis passes. The rustdoc output is
// then generated from the cleaned AST of the crate. This runs all the
// plug/cleaning passes.
let result = rustc_driver::catch_fatal_errors(move || {
let crate_name = options.crate_name.clone();
let crate_version = options.crate_version.clone();
let output_format = options.output_format;
let (mut krate, renderinfo, renderopts) = core::run_core(options);
let crate_name = options.crate_name.clone();
let crate_version = options.crate_version.clone();
let output_format = options.output_format;
let (mut krate, renderinfo, renderopts) = core::run_core(options);

info!("finished with rustc");
info!("finished with rustc");

if let Some(name) = crate_name {
krate.name = name
}
if let Some(name) = crate_name {
krate.name = name
}

krate.version = crate_version;
krate.version = crate_version;

let out = Output { krate, renderinfo, renderopts };
let out = Output { krate, renderinfo, renderopts };

if show_coverage {
// if we ran coverage, bail early, we don't need to also generate docs at this point
// (also we didn't load in any of the useful passes)
return rustc_driver::EXIT_SUCCESS;
}
if show_coverage {
// if we ran coverage, bail early, we don't need to also generate docs at this point
// (also we didn't load in any of the useful passes)
return Ok(());
}

let Output { krate, renderinfo, renderopts } = out;
info!("going to format");
let (error_format, edition, debugging_options) = diag_opts;
let diag = core::new_handler(error_format, None, &debugging_options);
match output_format {
None | Some(config::OutputFormat::Html) => {
run_renderer::<html::render::Context>(krate, renderopts, renderinfo, &diag, edition)
}
Some(config::OutputFormat::Json) => {
run_renderer::<json::JsonRenderer>(krate, renderopts, renderinfo, &diag, edition)
}
let Output { krate, renderinfo, renderopts } = out;
info!("going to format");
let (error_format, edition, debugging_options) = diag_opts;
let diag = core::new_handler(error_format, None, &debugging_options);
match output_format {
None | Some(config::OutputFormat::Html) => {
run_renderer::<html::render::Context>(krate, renderopts, renderinfo, &diag, edition)
}
Some(config::OutputFormat::Json) => {
run_renderer::<json::JsonRenderer>(krate, renderopts, renderinfo, &diag, edition)
}
});

match result {
Ok(output) => output,
Err(_) => panic::resume_unwind(Box::new(rustc_errors::FatalErrorMarker)),
}
}

0 comments on commit 5301407

Please sign in to comment.