diff --git a/Cargo.lock b/Cargo.lock index e49fbde363fb9..70b4399ab5d80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3319,6 +3319,7 @@ dependencies = [ name = "rustc_driver_impl" version = "0.0.0" dependencies = [ + "ctrlc", "libc", "rustc_ast", "rustc_ast_lowering", diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 046d2052968a3..0e88fb77aa894 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -1,8 +1,10 @@ use crate::const_eval::CheckAlignment; use std::borrow::Cow; +use std::sync::atomic::Ordering::Relaxed; use either::{Left, Right}; +use rustc_data_structures::CTRL_C_RECEIVED; use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::mir::interpret::ErrorHandled; @@ -66,7 +68,11 @@ fn eval_body_using_ecx<'mir, 'tcx>( )?; // The main interpreter loop. - while ecx.step()? {} + while ecx.step()? { + if CTRL_C_RECEIVED.load(Relaxed) { + throw_exhaust!(Interrupted); + } + } // Intern the result let intern_kind = if cid.promoted.is_some() { diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 859e384d8b529..30cda015a0571 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -46,6 +46,8 @@ extern crate cfg_if; #[macro_use] extern crate rustc_macros; +use std::sync::atomic::AtomicBool; + pub use rustc_index::static_assert_size; #[inline(never)] @@ -129,3 +131,8 @@ impl Drop for OnDrop { // See comments in src/librustc_middle/lib.rs #[doc(hidden)] pub fn __noop_fix_for_27438() {} + +/// `rustc_driver::main` installs a handler that will set this to `true` if +/// the compiler has been sent a request to shut down, such as by a Ctrl-C. +/// This static is placed here so that it is available to all parts of the compiler. +pub static CTRL_C_RECEIVED: AtomicBool = AtomicBool::new(false); diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 67352c55c9019..405e5215b43d3 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [lib] [dependencies] +ctrlc = "3.3.0" tracing = { version = "0.1.35" } serde_json = "1.0.59" rustc_log = { path = "../rustc_log" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 14888cf4d75c0..0a472388d6c11 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -23,6 +23,7 @@ use rustc_data_structures::profiling::{ get_resident_set_size, print_time_passes_entry, TimePassesFormat, }; use rustc_data_structures::sync::SeqCst; +use rustc_data_structures::CTRL_C_RECEIVED; use rustc_errors::registry::{InvalidErrorCode, Registry}; use rustc_errors::{ DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage, TerminalUrl, @@ -55,6 +56,7 @@ use std::panic::{self, catch_unwind}; use std::path::PathBuf; use std::process::{self, Command, Stdio}; use std::str; +use std::sync::atomic::Ordering::Relaxed; use std::sync::OnceLock; use std::time::Instant; @@ -1410,6 +1412,19 @@ pub fn main() -> ! { signal_handler::install(); let mut callbacks = TimePassesCallbacks::default(); install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); + + ctrlc::set_handler(move || { + // Indicate that we have been signaled to stop. If we were already signaled, exit + // immediately. In our interpreter loop we try to consult this value often, but if for + // whatever reason we don't get to that check or the cleanup we do upon finding that + // this bool has become true takes a long time, the exit here will promptly exit the + // process on the second Ctrl-C. + if CTRL_C_RECEIVED.swap(true, Relaxed) { + std::process::exit(1); + } + }) + .expect("Unable to install ctrlc handler"); + let exit_code = catch_with_exit_code(|| { let args = env::args_os() .enumerate() diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 055d8e9a352bc..e87f30deb4e0b 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -473,6 +473,8 @@ pub enum ResourceExhaustionInfo { MemoryExhausted, /// The address space (of the target) is full. AddressSpaceFull, + /// The compiler got an interrupt signal (a user ran out of patience). + Interrupted, } impl fmt::Display for ResourceExhaustionInfo { @@ -491,6 +493,7 @@ impl fmt::Display for ResourceExhaustionInfo { AddressSpaceFull => { write!(f, "there are no more free addresses in the address space") } + Interrupted => write!(f, "compilation was interrupted"), } } } diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 25c8df43ee26c..9cbbb031c9f4e 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -3,13 +3,14 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; use std::num::TryFromIntError; -use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; +use std::sync::atomic::Ordering::Relaxed; use std::task::Poll; use std::time::{Duration, SystemTime}; use log::trace; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::CTRL_C_RECEIVED; use rustc_hir::def_id::DefId; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::Mutability; @@ -1020,21 +1021,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Run the core interpreter loop. Returns only when an interrupt occurs (an error or program /// termination). fn run_threads(&mut self) -> InterpResult<'tcx, !> { - static SIGNALED: AtomicBool = AtomicBool::new(false); + // In normal rustc, rustc_driver::main installs this handler. But we don't use that + // function, see src/bin/miri.rs. ctrlc::set_handler(move || { - // Indicate that we have ben signaled to stop. If we were already signaled, exit + // Indicate that we have been signaled to stop. If we were already signaled, exit // immediately. In our interpreter loop we try to consult this value often, but if for // whatever reason we don't get to that check or the cleanup we do upon finding that // this bool has become true takes a long time, the exit here will promptly exit the // process on the second Ctrl-C. - if SIGNALED.swap(true, Relaxed) { + if CTRL_C_RECEIVED.swap(true, Relaxed) { std::process::exit(1); } }) - .unwrap(); + .expect("Unable to install ctrlc handler"); let this = self.eval_context_mut(); loop { - if SIGNALED.load(Relaxed) { + if CTRL_C_RECEIVED.load(Relaxed) { this.machine.handle_abnormal_termination(); std::process::exit(1); } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index db2b7910b711c..d85fd03e2a504 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -133,6 +133,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "crossbeam-utils", "crypto-common", "cstr", + "ctrlc", "datafrog", "derive_more", "digest", @@ -184,6 +185,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "memmap2", "memoffset", "miniz_oxide", + "nix", "num_cpus", "object", "odht", @@ -280,6 +282,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "winapi-util", "winapi-x86_64-pc-windows-gnu", "windows", + "windows-sys", "windows-targets", "windows_aarch64_gnullvm", "windows_aarch64_msvc",