diff --git a/Cargo.lock b/Cargo.lock index 81f19668c24b7..f77c31a458df5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2167,6 +2167,7 @@ version = "0.1.0" dependencies = [ "html5ever", "regex", + "urlencoding", ] [[package]] @@ -3248,6 +3249,7 @@ dependencies = [ "rustc_driver_impl", "rustc_public", "rustc_public_bridge", + "rustc_windows_rc", "tikv-jemalloc-sys", ] @@ -3623,6 +3625,7 @@ name = "rustc_driver" version = "0.0.0" dependencies = [ "rustc_driver_impl", + "rustc_windows_rc", ] [[package]] @@ -4718,6 +4721,13 @@ dependencies = [ "semver", ] +[[package]] +name = "rustc_windows_rc" +version = "0.0.0" +dependencies = [ + "cc", +] + [[package]] name = "rustdoc" version = "0.0.0" @@ -5825,6 +5835,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 3ca752354466f..e3214d1ab9cec 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -33,3 +33,8 @@ llvm = ['rustc_driver_impl/llvm'] max_level_info = ['rustc_driver_impl/max_level_info'] rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts'] # tidy-alphabetical-end + +[build-dependencies] +# tidy-alphabetical-start +rustc_windows_rc = { path = "../rustc_windows_rc" } +# tidy-alphabetical-end diff --git a/compiler/rustc/build.rs b/compiler/rustc/build.rs index 8b7d28d2b8aa6..9b5def53e3cbf 100644 --- a/compiler/rustc/build.rs +++ b/compiler/rustc/build.rs @@ -1,4 +1,6 @@ -use std::env; +use std::{env, path}; + +use rustc_windows_rc::{VersionInfoFileType, compile_windows_resource_file}; fn main() { let target_os = env::var("CARGO_CFG_TARGET_OS"); @@ -13,6 +15,18 @@ fn main() { // Add a manifest file to rustc.exe. fn set_windows_exe_options() { + set_windows_resource(); + set_windows_manifest(); +} + +fn set_windows_resource() { + let stem = path::PathBuf::from("rustc_main_resource"); + let file_description = "rustc"; + let res_file = compile_windows_resource_file(&stem, file_description, VersionInfoFileType::App); + println!("cargo:rustc-link-arg={}", res_file.display()); +} + +fn set_windows_manifest() { static WINDOWS_MANIFEST_FILE: &str = "Windows Manifest.xml"; let mut manifest = env::current_dir().unwrap(); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index caa3369f413a8..1790e21225269 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -103,16 +103,17 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, array_type: Ty<'tcx>, + span: Span, ) -> DINodeCreationResult<'ll> { let ty::Array(element_type, len) = array_type.kind() else { bug!("build_fixed_size_array_di_node() called with non-ty::Array type `{:?}`", array_type) }; - let element_type_di_node = type_di_node(cx, *element_type); + let element_type_di_node = spanned_type_di_node(cx, *element_type, span); return_if_di_node_created_in_meantime!(cx, unique_type_id); - let (size, align) = cx.size_and_align_of(array_type); + let (size, align) = cx.spanned_size_and_align_of(array_type, span); let upper_bound = len .try_to_target_usize(cx.tcx) @@ -447,7 +448,7 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>( build_basic_type_di_node(cx, t) } ty::Tuple(elements) if elements.is_empty() => build_basic_type_di_node(cx, t), - ty::Array(..) => build_fixed_size_array_di_node(cx, unique_type_id, t), + ty::Array(..) => build_fixed_size_array_di_node(cx, unique_type_id, t, span), ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id), ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id), ty::Foreign(..) => build_foreign_type_di_node(cx, t, unique_type_id), diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 4e7096da502d0..53218340414e5 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -7,6 +7,7 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt}; +use rustc_span::{DUMMY_SP, Span}; use tracing::debug; use crate::common::*; @@ -149,7 +150,11 @@ impl<'a, 'tcx> CodegenCx<'a, 'tcx> { } pub(crate) fn size_and_align_of(&self, ty: Ty<'tcx>) -> (Size, Align) { - let layout = self.layout_of(ty); + self.spanned_size_and_align_of(ty, DUMMY_SP) + } + + pub(crate) fn spanned_size_and_align_of(&self, ty: Ty<'tcx>, span: Span) -> (Size, Align) { + let layout = self.spanned_layout_of(ty, span); (layout.size, layout.align.abi) } } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 2dfbc58164323..5462163f4ffd3 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -9,7 +9,7 @@ ar_archive_writer = "0.5" bitflags = "2.4.1" bstr = "1.11.3" # `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version -# per crate", so if you change this, you need to also change it in `rustc_llvm`. +# per crate", so if you change this, you need to also change it in `rustc_llvm` and `rustc_windows_rc`. cc = "=1.2.16" itertools = "0.12" pathdiff = "0.2.0" diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 2c1e5087e1c6b..6ec85648d6d88 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1501,8 +1501,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `get_bytes_mut` will clear the provenance, which is correct, // since we don't want to keep any provenance at the target. // This will also error if copying partial provenance is not supported. - let provenance = - src_alloc.provenance().prepare_copy(src_range, dest_offset, num_copies, self); + let provenance = src_alloc + .provenance() + .prepare_copy(src_range, dest_offset, num_copies, self) + .map_err(|e| e.to_interp_error(src_alloc_id))?; // Prepare a copy of the initialization mask. let init = src_alloc.init_mask().prepare_copy(src_range); diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index e3ee83512952a..5190971982763 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -10,3 +10,8 @@ crate-type = ["dylib"] # tidy-alphabetical-start rustc_driver_impl = { path = "../rustc_driver_impl" } # tidy-alphabetical-end + +[build-dependencies] +# tidy-alphabetical-start +rustc_windows_rc = { path = "../rustc_windows_rc" } +# tidy-alphabetical-end diff --git a/compiler/rustc_driver/build.rs b/compiler/rustc_driver/build.rs new file mode 100644 index 0000000000000..ba44fe7a86ed9 --- /dev/null +++ b/compiler/rustc_driver/build.rs @@ -0,0 +1,21 @@ +use std::{env, path}; + +use rustc_windows_rc::{VersionInfoFileType, compile_windows_resource_file}; + +fn main() { + let target_os = env::var("CARGO_CFG_TARGET_OS"); + let target_env = env::var("CARGO_CFG_TARGET_ENV"); + if Ok("windows") == target_os.as_deref() && Ok("msvc") == target_env.as_deref() { + set_windows_dll_options(); + } else { + // Avoid rerunning the build script every time. + println!("cargo:rerun-if-changed=build.rs"); + } +} + +fn set_windows_dll_options() { + let stem = path::PathBuf::from("rustc_driver_resource"); + let file_description = "rustc_driver"; + let res_file = compile_windows_resource_file(&stem, file_description, VersionInfoFileType::Dll); + println!("cargo:rustc-link-arg={}", res_file.display()); +} diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index cd352ce3d0f4f..0dfd1b13df502 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -11,7 +11,7 @@ libc = "0.2.73" [build-dependencies] # tidy-alphabetical-start # `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version -# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa`. +# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa` and `rustc_windows_rc`. cc = "=1.2.16" # tidy-alphabetical-end diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 225ab8c846d82..9d21d0d22e3e5 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -254,7 +254,10 @@ fn main() { println!("cargo:rustc-link-lib=kstat"); } - if (target.starts_with("arm") && !target.contains("freebsd") && !target.contains("ohos")) + if (target.starts_with("arm") + && !target.starts_with("arm64") + && !target.contains("freebsd") + && !target.contains("ohos")) || target.starts_with("mips-") || target.starts_with("mipsel-") || target.starts_with("powerpc-") diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 2ea92a39d4828..67962813ae437 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -724,6 +724,11 @@ impl Allocation } // If we get here, we have to check per-byte provenance, and join them together. let prov = 'prov: { + if !Prov::OFFSET_IS_ADDR { + // FIXME(#146291): We need to ensure that we don't mix different pointers with + // the same provenance. + return Err(AllocError::ReadPartialPointer(range.start)); + } // Initialize with first fragment. Must have index 0. let Some((mut joint_prov, 0)) = self.provenance.get_byte(range.start, cx) else { break 'prov None; diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index dbbd95408c8ab..720e58d7aa0eb 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -11,6 +11,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use tracing::trace; use super::{AllocRange, CtfeProvenance, Provenance, alloc_range}; +use crate::mir::interpret::{AllocError, AllocResult}; /// Stores the provenance information of pointers stored in memory. #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -137,6 +138,11 @@ impl ProvenanceMap { let Some(bytes) = self.bytes.as_deref_mut() else { return true; }; + if !Prov::OFFSET_IS_ADDR { + // FIXME(#146291): We need to ensure that we don't mix different pointers with + // the same provenance. + return false; + } let ptr_size = cx.data_layout().pointer_size(); while let Some((offset, (prov, _))) = bytes.iter().next().copied() { // Check if this fragment starts a pointer. @@ -285,7 +291,7 @@ impl ProvenanceMap { dest: Size, count: u64, cx: &impl HasDataLayout, - ) -> ProvenanceCopy { + ) -> AllocResult> { let shift_offset = move |idx, offset| { // compute offset for current repetition let dest_offset = dest + src.size * idx; // `Size` operations @@ -363,6 +369,12 @@ impl ProvenanceMap { } trace!("byte provenances: {bytes:?}"); + if !bytes.is_empty() && !Prov::OFFSET_IS_ADDR { + // FIXME(#146291): We need to ensure that we don't mix different pointers with + // the same provenance. + return Err(AllocError::ReadPartialPointer(src.start)); + } + // And again a buffer for the new list on the target side. let mut dest_bytes = Vec::with_capacity(bytes.len() * (count as usize)); for i in 0..count { @@ -373,7 +385,7 @@ impl ProvenanceMap { dest_bytes_box = Some(dest_bytes.into_boxed_slice()); } - ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box } + Ok(ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box }) } /// Applies a provenance copy. diff --git a/compiler/rustc_windows_rc/Cargo.toml b/compiler/rustc_windows_rc/Cargo.toml new file mode 100644 index 0000000000000..080acd35c38cb --- /dev/null +++ b/compiler/rustc_windows_rc/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rustc_windows_rc" +version = "0.0.0" +edition = "2024" + +[dependencies] +#tidy-alphabetical-start +# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version +# per crate", so if you change this, you need to also change it in `rustc_llvm` and `rustc_codegen_ssa`. +cc = "=1.2.16" +#tidy-alphabetical-end diff --git a/compiler/rustc_windows_rc/rustc.rc.in b/compiler/rustc_windows_rc/rustc.rc.in new file mode 100644 index 0000000000000..10a00b9bd4e32 --- /dev/null +++ b/compiler/rustc_windows_rc/rustc.rc.in @@ -0,0 +1,40 @@ +// A template for the rustc_driver and rustc Windows resource files. +// This file is processed by the build script to produce rustc.rc and rustc_driver.rc +// with the appropriate version information filled in. + +// All the strings are in UTF-8 +#pragma code_page(65001) + +#define RUSTC_FILEDESCRIPTION_STR "@RUSTC_FILEDESCRIPTION_STR@" +#define RUSTC_FILETYPE @RUSTC_FILETYPE@ +#define RUSTC_FILEVERSION_QUAD @RUSTC_FILEVERSION_QUAD@ +#define RUSTC_FILEVERSION_STR "@RUSTC_FILEVERSION_STR@" + +#define RUSTC_PRODUCTNAME_STR "@RUSTC_PRODUCTNAME_STR@" +#define RUSTC_PRODUCTVERSION_QUAD @RUSTC_PRODUCTVERSION_QUAD@ +#define RUSTC_PRODUCTVERSION_STR "@RUSTC_PRODUCTVERSION_STR@" + + +1 VERSIONINFO +FILEVERSION RUSTC_FILEVERSION_QUAD +PRODUCTVERSION RUSTC_PRODUCTVERSION_QUAD +FILEOS 0x00040004 +FILETYPE RUSTC_FILETYPE +FILESUBTYPE 0 +FILEFLAGSMASK 0x3f +FILEFLAGS 0x0 +{ + BLOCK "StringFileInfo" + { + BLOCK "000004b0" + { + VALUE "FileDescription", RUSTC_FILEDESCRIPTION_STR + VALUE "FileVersion", RUSTC_FILEVERSION_STR + VALUE "ProductVersion", RUSTC_PRODUCTVERSION_STR + VALUE "ProductName", RUSTC_PRODUCTNAME_STR + } + } + BLOCK "VarFileInfo" { + VALUE "Translation", 0x0, 0x04b0 + } +} diff --git a/compiler/rustc_windows_rc/src/lib.rs b/compiler/rustc_windows_rc/src/lib.rs new file mode 100644 index 0000000000000..caa5e5ef27656 --- /dev/null +++ b/compiler/rustc_windows_rc/src/lib.rs @@ -0,0 +1,158 @@ +//! A build script dependency to create a Windows resource file for the compiler +//! +//! Uses values from the `CFG_VERSION` and `CFG_RELEASE` environment variables +//! to set the product and file version information in the Windows resource file. +use std::{env, ffi, fs, path, process}; + +use cc::windows_registry; + +/// The template for the Windows resource file. +const RESOURCE_TEMPLATE: &str = include_str!("../rustc.rc.in"); + +/// A subset of the possible values for the `FILETYPE` field in a Windows resource file +/// +/// See the `dwFileType` member of [VS_FIXEDFILEINFO](https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo#members) +#[derive(Debug, Clone, Copy)] +#[repr(u32)] +pub enum VersionInfoFileType { + /// `VFT_APP` - The file is an application. + App = 0x00000001, + /// `VFT_DLL` - The file is a dynamic link library. + Dll = 0x00000002, +} + +/// Create and compile a Windows resource file with the product and file version information for the rust compiler. +/// +/// Returns the path to the compiled resource file +/// +/// Does not emit any cargo directives, the caller is responsible for that. +pub fn compile_windows_resource_file( + file_stem: &path::Path, + file_description: &str, + filetype: VersionInfoFileType, +) -> path::PathBuf { + let mut resources_dir = path::PathBuf::from(env::var_os("OUT_DIR").unwrap()); + resources_dir.push("resources"); + fs::create_dir_all(&resources_dir).unwrap(); + + let resource_compiler = + find_resource_compiler(&env::var("CARGO_CFG_TARGET_ARCH").unwrap()).expect("found rc.exe"); + + let rc_path = resources_dir.join(file_stem.with_extension("rc")); + + write_resource_script_file(&rc_path, file_description, filetype); + + let res_path = resources_dir.join(file_stem.with_extension("res")); + + let status = process::Command::new(resource_compiler) + .arg("/fo") + .arg(&res_path) + .arg(&rc_path) + .status() + .expect("can execute resource compiler"); + assert!(status.success(), "rc.exe failed with status {}", status); + assert!( + res_path.try_exists().unwrap_or(false), + "resource file {} was not created", + res_path.display() + ); + res_path +} + +/// Writes a Windows resource script file for the rust compiler with the product and file version information +/// into `rc_path` +fn write_resource_script_file( + rc_path: &path::Path, + file_description: &str, + filetype: VersionInfoFileType, +) { + let mut resource_script = RESOURCE_TEMPLATE.to_string(); + + // Set the string product and file version to the same thing as `rustc --version` + let descriptive_version = env::var("CFG_VERSION").unwrap_or("unknown".to_string()); + + // Set the product name to "Rust Compiler" or "Rust Compiler (nightly)" etc + let product_name = product_name(env::var("CFG_RELEASE_CHANNEL").unwrap()); + + // For the numeric version we need `major,minor,patch,build`. + // Extract them from `CFG_RELEASE` which is "major.minor.patch" and a "-dev", "-nightly" or similar suffix + let cfg_release = env::var("CFG_RELEASE").unwrap(); + // remove the suffix, if present and parse into [`ResourceVersion`] + let version = parse_version(cfg_release.split("-").next().unwrap_or("0.0.0")) + .expect("valid CFG_RELEASE version"); + + resource_script = resource_script + .replace("@RUSTC_FILEDESCRIPTION_STR@", file_description) + .replace("@RUSTC_FILETYPE@", &format!("{}", filetype as u32)) + .replace("@RUSTC_FILEVERSION_QUAD@", &version.to_quad_string()) + .replace("@RUSTC_FILEVERSION_STR@", &descriptive_version) + .replace("@RUSTC_PRODUCTNAME_STR@", &product_name) + .replace("@RUSTC_PRODUCTVERSION_QUAD@", &version.to_quad_string()) + .replace("@RUSTC_PRODUCTVERSION_STR@", &descriptive_version); + + fs::write(&rc_path, resource_script) + .unwrap_or_else(|_| panic!("failed to write resource file {}", rc_path.display())); +} + +fn product_name(channel: String) -> String { + format!( + "Rust Compiler{}", + if channel == "stable" { "".to_string() } else { format!(" ({})", channel) } + ) +} + +/// Windows resources store versions as four 16-bit integers. +struct ResourceVersion { + major: u16, + minor: u16, + patch: u16, + build: u16, +} + +impl ResourceVersion { + /// Format the version as a comma-separated string of four integers + /// as expected by Windows resource scripts for the `FILEVERSION` and `PRODUCTVERSION` fields. + fn to_quad_string(&self) -> String { + format!("{},{},{},{}", self.major, self.minor, self.patch, self.build) + } +} + +/// Parse a string in the format "major.minor.patch" into a [`ResourceVersion`]. +/// The build is set to 0. +/// Returns `None` if the version string is not in the expected format. +fn parse_version(version: &str) -> Option { + let mut parts = version.split('.'); + let major = parts.next()?.parse::().ok()?; + let minor = parts.next()?.parse::().ok()?; + let patch = parts.next()?.parse::().ok()?; + if parts.next().is_some() { + None + } else { + Some(ResourceVersion { major, minor, patch, build: 0 }) + } +} + +/// Find the Windows SDK resource compiler `rc.exe` for the given architecture or target triple. +/// Returns `None` if the tool could not be found. +fn find_resource_compiler(arch_or_target: &str) -> Option { + find_windows_sdk_tool(arch_or_target, "rc.exe") +} + +/// Find a Windows SDK tool for the given architecture or target triple. +/// Returns `None` if the tool could not be found. +fn find_windows_sdk_tool(arch_or_target: &str, tool_name: &str) -> Option { + // windows_registry::find_tool can only find MSVC tools, not Windows SDK tools, but + // cc does include the Windows SDK tools in the PATH environment of MSVC tools. + + let msvc_linker = windows_registry::find_tool(arch_or_target, "link.exe")?; + let path = &msvc_linker.env().iter().find(|(k, _)| k == "PATH")?.1; + find_tool_in_path(tool_name, path) +} + +/// Find a tool in the directories in a given PATH-like string. +fn find_tool_in_path>(tool_name: &str, path: P) -> Option { + env::split_paths(path.as_ref()).find_map(|p| { + let tool_path = p.join(tool_name); + if tool_path.try_exists().unwrap_or(false) { Some(tool_path) } else { None } + }) +} diff --git a/library/core/src/iter/traits/accum.rs b/library/core/src/iter/traits/accum.rs index 73122369b41dd..3b805139ded17 100644 --- a/library/core/src/iter/traits/accum.rs +++ b/library/core/src/iter/traits/accum.rs @@ -203,7 +203,7 @@ macro_rules! float_sum_product { integer_sum_product! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } saturating_integer_sum_product! { u8 u16 u32 u64 u128 usize } -float_sum_product! { f32 f64 } +float_sum_product! { f16 f32 f64 f128 } #[stable(feature = "iter_arith_traits_result", since = "1.16.0")] impl Sum> for Result diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index c306011bdda39..86a68e18b0af4 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -252,47 +252,16 @@ pub use crate::macros::cfg_select; #[macro_use] mod internal_macros; -#[path = "num/shells/int_macros.rs"] -#[macro_use] -mod int_macros; - -#[rustc_diagnostic_item = "i128_legacy_mod"] -#[path = "num/shells/i128.rs"] -pub mod i128; -#[rustc_diagnostic_item = "i16_legacy_mod"] -#[path = "num/shells/i16.rs"] -pub mod i16; -#[rustc_diagnostic_item = "i32_legacy_mod"] -#[path = "num/shells/i32.rs"] -pub mod i32; -#[rustc_diagnostic_item = "i64_legacy_mod"] -#[path = "num/shells/i64.rs"] -pub mod i64; -#[rustc_diagnostic_item = "i8_legacy_mod"] -#[path = "num/shells/i8.rs"] -pub mod i8; -#[rustc_diagnostic_item = "isize_legacy_mod"] -#[path = "num/shells/isize.rs"] -pub mod isize; - -#[rustc_diagnostic_item = "u128_legacy_mod"] -#[path = "num/shells/u128.rs"] -pub mod u128; -#[rustc_diagnostic_item = "u16_legacy_mod"] -#[path = "num/shells/u16.rs"] -pub mod u16; -#[rustc_diagnostic_item = "u32_legacy_mod"] -#[path = "num/shells/u32.rs"] -pub mod u32; -#[rustc_diagnostic_item = "u64_legacy_mod"] -#[path = "num/shells/u64.rs"] -pub mod u64; -#[rustc_diagnostic_item = "u8_legacy_mod"] -#[path = "num/shells/u8.rs"] -pub mod u8; -#[rustc_diagnostic_item = "usize_legacy_mod"] -#[path = "num/shells/usize.rs"] -pub mod usize; +#[path = "num/shells/legacy_int_modules.rs"] +mod legacy_int_modules; +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(clippy::useless_attribute)] // FIXME false positive (https://github.com/rust-lang/rust-clippy/issues/15636) +#[allow(deprecated_in_future)] +pub use legacy_int_modules::{i8, i16, i32, i64, isize, u8, u16, u32, u64, usize}; +#[stable(feature = "i128", since = "1.26.0")] +#[allow(clippy::useless_attribute)] // FIXME false positive (https://github.com/rust-lang/rust-clippy/issues/15636) +#[allow(deprecated_in_future)] +pub use legacy_int_modules::{i128, u128}; #[path = "num/f128.rs"] pub mod f128; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index ccf41dfb01dd1..3f58fc448aa68 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1017,6 +1017,7 @@ pub(crate) mod builtin { )] #[allow_internal_unstable(fmt_internals)] #[rustc_builtin_macro] + #[doc(hidden)] #[macro_export] macro_rules! format_args_nl { ($fmt:expr) => {{ /* compiler built-in */ }}; diff --git a/library/core/src/num/shells/i128.rs b/library/core/src/num/shells/i128.rs deleted file mode 100644 index b3b3d3b4875ab..0000000000000 --- a/library/core/src/num/shells/i128.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`i128` primitive type][i128]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "i128", since = "1.26.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `i128`" -)] - -int_module! { i128, #[stable(feature = "i128", since="1.26.0")] } diff --git a/library/core/src/num/shells/i16.rs b/library/core/src/num/shells/i16.rs deleted file mode 100644 index 70a452e193983..0000000000000 --- a/library/core/src/num/shells/i16.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`i16` primitive type][i16]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `i16`" -)] - -int_module! { i16 } diff --git a/library/core/src/num/shells/i32.rs b/library/core/src/num/shells/i32.rs deleted file mode 100644 index c30849e2591c3..0000000000000 --- a/library/core/src/num/shells/i32.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`i32` primitive type][i32]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `i32`" -)] - -int_module! { i32 } diff --git a/library/core/src/num/shells/i64.rs b/library/core/src/num/shells/i64.rs deleted file mode 100644 index 77d95d712506b..0000000000000 --- a/library/core/src/num/shells/i64.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`i64` primitive type][i64]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `i64`" -)] - -int_module! { i64 } diff --git a/library/core/src/num/shells/i8.rs b/library/core/src/num/shells/i8.rs deleted file mode 100644 index 516ba8cdef3bf..0000000000000 --- a/library/core/src/num/shells/i8.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`i8` primitive type][i8]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `i8`" -)] - -int_module! { i8 } diff --git a/library/core/src/num/shells/int_macros.rs b/library/core/src/num/shells/int_macros.rs deleted file mode 100644 index 8ae9b7abae3bd..0000000000000 --- a/library/core/src/num/shells/int_macros.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![doc(hidden)] - -macro_rules! int_module { - ($T:ident) => (int_module!($T, #[stable(feature = "rust1", since = "1.0.0")]);); - ($T:ident, #[$attr:meta]) => ( - #[doc = concat!( - "The smallest value that can be represented by this integer type. Use ", - "[`", stringify!($T), "::MIN", "`] instead." - )] - /// - /// # Examples - /// - /// ```rust - /// // deprecated way - #[doc = concat!("let min = std::", stringify!($T), "::MIN;")] - /// - /// // intended way - #[doc = concat!("let min = ", stringify!($T), "::MIN;")] - /// ``` - /// - #[$attr] - #[deprecated(since = "TBD", note = "replaced by the `MIN` associated constant on this type")] - #[rustc_diagnostic_item = concat!(stringify!($T), "_legacy_const_min")] - pub const MIN: $T = $T::MIN; - - #[doc = concat!( - "The largest value that can be represented by this integer type. Use ", - "[`", stringify!($T), "::MAX", "`] instead." - )] - /// - /// # Examples - /// - /// ```rust - /// // deprecated way - #[doc = concat!("let max = std::", stringify!($T), "::MAX;")] - /// - /// // intended way - #[doc = concat!("let max = ", stringify!($T), "::MAX;")] - /// ``` - /// - #[$attr] - #[deprecated(since = "TBD", note = "replaced by the `MAX` associated constant on this type")] - #[rustc_diagnostic_item = concat!(stringify!($T), "_legacy_const_max")] - pub const MAX: $T = $T::MAX; - ) -} diff --git a/library/core/src/num/shells/isize.rs b/library/core/src/num/shells/isize.rs deleted file mode 100644 index 828f7345bafbe..0000000000000 --- a/library/core/src/num/shells/isize.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`isize` primitive type][isize]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `isize`" -)] - -int_module! { isize } diff --git a/library/core/src/num/shells/legacy_int_modules.rs b/library/core/src/num/shells/legacy_int_modules.rs new file mode 100644 index 0000000000000..6b4f253911157 --- /dev/null +++ b/library/core/src/num/shells/legacy_int_modules.rs @@ -0,0 +1,71 @@ +#![doc(hidden)] + +macro_rules! legacy_int_module { + ($T:ident) => (legacy_int_module!($T, #[stable(feature = "rust1", since = "1.0.0")]);); + ($T:ident, #[$attr:meta]) => ( + #[$attr] + #[deprecated( + since = "TBD", + note = "all constants in this module replaced by associated constants on the type" + )] + #[rustc_diagnostic_item = concat!(stringify!($T), "_legacy_mod")] + pub mod $T { + #![doc = concat!("Redundant constants module for the [`", stringify!($T), "` primitive type][", stringify!($T), "].")] + //! + //! New code should use the associated constants directly on the primitive type. + + #[doc = concat!( + "The smallest value that can be represented by this integer type. Use ", + "[`", stringify!($T), "::MIN", "`] instead." + )] + /// + /// # Examples + /// + /// ```rust + /// // deprecated way + #[doc = concat!("let min = std::", stringify!($T), "::MIN;")] + /// + /// // intended way + #[doc = concat!("let min = ", stringify!($T), "::MIN;")] + /// ``` + /// + #[$attr] + #[deprecated(since = "TBD", note = "replaced by the `MIN` associated constant on this type")] + #[rustc_diagnostic_item = concat!(stringify!($T), "_legacy_const_min")] + pub const MIN: $T = $T::MIN; + + #[doc = concat!( + "The largest value that can be represented by this integer type. Use ", + "[`", stringify!($T), "::MAX", "`] instead." + )] + /// + /// # Examples + /// + /// ```rust + /// // deprecated way + #[doc = concat!("let max = std::", stringify!($T), "::MAX;")] + /// + /// // intended way + #[doc = concat!("let max = ", stringify!($T), "::MAX;")] + /// ``` + /// + #[$attr] + #[deprecated(since = "TBD", note = "replaced by the `MAX` associated constant on this type")] + #[rustc_diagnostic_item = concat!(stringify!($T), "_legacy_const_max")] + pub const MAX: $T = $T::MAX; + } + ) +} + +legacy_int_module! { i128, #[stable(feature = "i128", since = "1.26.0")] } +legacy_int_module! { i16 } +legacy_int_module! { i32 } +legacy_int_module! { i64 } +legacy_int_module! { i8 } +legacy_int_module! { isize } +legacy_int_module! { u128, #[stable(feature = "i128", since = "1.26.0")] } +legacy_int_module! { u16 } +legacy_int_module! { u32 } +legacy_int_module! { u64 } +legacy_int_module! { u8 } +legacy_int_module! { usize } diff --git a/library/core/src/num/shells/u128.rs b/library/core/src/num/shells/u128.rs deleted file mode 100644 index b1e30e3843525..0000000000000 --- a/library/core/src/num/shells/u128.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`u128` primitive type][u128]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "i128", since = "1.26.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `u128`" -)] - -int_module! { u128, #[stable(feature = "i128", since="1.26.0")] } diff --git a/library/core/src/num/shells/u16.rs b/library/core/src/num/shells/u16.rs deleted file mode 100644 index 7394977e5078a..0000000000000 --- a/library/core/src/num/shells/u16.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`u16` primitive type][u16]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `u16`" -)] - -int_module! { u16 } diff --git a/library/core/src/num/shells/u32.rs b/library/core/src/num/shells/u32.rs deleted file mode 100644 index 4c84274e752ec..0000000000000 --- a/library/core/src/num/shells/u32.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`u32` primitive type][u32]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `u32`" -)] - -int_module! { u32 } diff --git a/library/core/src/num/shells/u64.rs b/library/core/src/num/shells/u64.rs deleted file mode 100644 index 47a95c6820f2f..0000000000000 --- a/library/core/src/num/shells/u64.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`u64` primitive type][u64]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `u64`" -)] - -int_module! { u64 } diff --git a/library/core/src/num/shells/u8.rs b/library/core/src/num/shells/u8.rs deleted file mode 100644 index 360baef722869..0000000000000 --- a/library/core/src/num/shells/u8.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`u8` primitive type][u8]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `u8`" -)] - -int_module! { u8 } diff --git a/library/core/src/num/shells/usize.rs b/library/core/src/num/shells/usize.rs deleted file mode 100644 index 44c24dfc2cf58..0000000000000 --- a/library/core/src/num/shells/usize.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`usize` primitive type][usize]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `usize`" -)] - -int_module! { usize } diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 6b94088cb5679..625024373ef47 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1348,6 +1348,40 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// assert_eq!(x, [7, 8, 3, 4]); /// assert_eq!(y, [1, 2, 9]); /// ``` +/// +/// # Const evaluation limitations +/// +/// If this function is invoked during const-evaluation, the current implementation has a small (and +/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y` +/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function may +/// fail to evaluate (similar to a panic during const-evaluation). This behavior may change in the +/// future. +/// +/// The limitation is illustrated by the following example: +/// +/// ``` +/// use std::mem::size_of; +/// use std::ptr; +/// +/// const { unsafe { +/// const PTR_SIZE: usize = size_of::<*const i32>(); +/// let mut data1 = [0u8; PTR_SIZE]; +/// let mut data2 = [0u8; PTR_SIZE]; +/// // Store a pointer in `data1`. +/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42); +/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks. +/// // This call will fail, because the pointer in `data1` crosses the boundary +/// // between several of the 1-byte chunks that are being swapped here. +/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE); +/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size +/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between +/// // two chunks. +/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1); +/// // Read the pointer from `data2` and dereference it. +/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned(); +/// assert!(*ptr == 42); +/// } } +/// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] #[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "1.88.0")] @@ -1376,7 +1410,9 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { const_eval_select!( @capture[T] { x: *mut T, y: *mut T, count: usize }: if const { - // At compile-time we don't need all the special code below. + // At compile-time we want to always copy this in chunks of `T`, to ensure that if there + // are pointers inside `T` we will copy them in one go rather than trying to copy a part + // of a pointer (which would not work). // SAFETY: Same preconditions as this function unsafe { swap_nonoverlapping_const(x, y, count) } } else { diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index c13fb96a67f92..f8774833d0a54 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -936,12 +936,13 @@ fn test_const_swap_ptr() { assert!(*s1.0.ptr == 666); assert!(*s2.0.ptr == 1); - // Swap them back, byte-for-byte + // Swap them back, again as an array. + // FIXME(#146291): we should be swapping back at type `u8` but that currently does not work. unsafe { ptr::swap_nonoverlapping( - ptr::from_mut(&mut s1).cast::(), - ptr::from_mut(&mut s2).cast::(), - size_of::(), + ptr::from_mut(&mut s1).cast::(), + ptr::from_mut(&mut s2).cast::(), + 1, ); } diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 8c11501558069..2dbdc8a4e026e 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -31,6 +31,7 @@ pub mod process; pub mod random; pub mod stdio; pub mod sync; +pub mod thread; pub mod thread_local; // FIXME(117276): remove this, move feature implementations into individual diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index fb8d69b73759d..3ddf6e5acb02e 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -25,7 +25,6 @@ pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; pub fn unsupported() -> crate::io::Result { diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs index 57247cffad3f2..b8c4d7740c4e1 100644 --- a/library/std/src/sys/pal/sgx/abi/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/mod.rs @@ -67,7 +67,7 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64 let tls_guard = unsafe { tls.activate() }; if secondary { - let join_notifier = super::thread::Thread::entry(); + let join_notifier = crate::sys::thread::Thread::entry(); drop(tls_guard); drop(join_notifier); diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 4a297b6823f20..9a33873af581f 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -13,7 +13,6 @@ mod libunwind_integration; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod thread_parking; pub mod time; pub mod waitqueue; diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 0011cf256df74..9ca6dc581183d 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -10,10 +10,8 @@ pub mod itron { pub mod error; pub mod spin; pub mod task; - pub mod thread; pub mod thread_parking; pub mod time; - use super::unsupported; } // `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as @@ -22,7 +20,7 @@ pub(crate) mod error; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub use self::itron::{thread, thread_parking}; +pub use self::itron::thread_parking; pub mod time; // SAFETY: must be called only once during runtime initialization. diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index c7b1777725858..dd0155265da63 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -9,7 +9,6 @@ pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; #[allow(non_upper_case_globals)] #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 8911a2ee5194d..ebd311db1e12c 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -17,7 +17,6 @@ pub mod helpers; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[cfg(test)] diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs deleted file mode 100644 index 47a48008c76da..0000000000000 --- a/library/std/src/sys/pal/uefi/thread.rs +++ /dev/null @@ -1,66 +0,0 @@ -use super::unsupported; -use crate::ffi::CStr; -use crate::io; -use crate::num::NonZero; -use crate::ptr::NonNull; -use crate::time::{Duration, Instant}; - -pub struct Thread(!); - -pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - _stack: usize, - _name: Option<&str>, - _p: Box, - ) -> io::Result { - unsupported() - } - - pub fn yield_now() { - // do nothing - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - let boot_services: NonNull = - crate::os::uefi::env::boot_services().expect("can't sleep").cast(); - let mut dur_ms = dur.as_micros(); - // ceil up to the nearest microsecond - if dur.subsec_nanos() % 1000 > 0 { - dur_ms += 1; - } - - while dur_ms > 0 { - let ms = crate::cmp::min(dur_ms, usize::MAX as u128); - let _ = unsafe { ((*boot_services.as_ptr()).stall)(ms as usize) }; - dur_ms -= ms; - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - - pub fn join(self) { - self.0 - } -} - -pub(crate) fn current_os_id() -> Option { - None -} - -pub fn available_parallelism() -> io::Result> { - // UEFI is single threaded - Ok(NonZero::new(1).unwrap()) -} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index ac5c823a1bf6d..dd1059fe04a2d 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -17,7 +17,6 @@ pub mod os; pub mod pipe; pub mod stack_overflow; pub mod sync; -pub mod thread; pub mod thread_parking; pub mod time; @@ -55,7 +54,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // thread-id for the main thread and so renaming the main thread will rename the // process and we only want to enable this on platforms we've tested. if cfg!(target_vendor = "apple") { - thread::Thread::set_name(&c"main"); + crate::sys::thread::set_name(c"main"); } unsafe fn sanitize_standard_fds() { diff --git a/library/std/src/sys/pal/wasip1/mod.rs b/library/std/src/sys/pal/wasip1/mod.rs index 61dd1c3f98b10..ae5da3c1f77be 100644 --- a/library/std/src/sys/pal/wasip1/mod.rs +++ b/library/std/src/sys/pal/wasip1/mod.rs @@ -20,7 +20,6 @@ pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/pal/wasip1/thread.rs b/library/std/src/sys/pal/wasip1/thread.rs deleted file mode 100644 index e062b49bd7a29..0000000000000 --- a/library/std/src/sys/pal/wasip1/thread.rs +++ /dev/null @@ -1,214 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::ffi::CStr; -use crate::num::NonZero; -use crate::time::{Duration, Instant}; -use crate::{io, mem}; - -cfg_select! { - target_feature = "atomics" => { - use crate::cmp; - use crate::ptr; - use crate::sys::os; - // Add a few symbols not in upstream `libc` just yet. - mod libc { - pub use crate::ffi; - pub use libc::*; - - // defined in wasi-libc - // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 - #[repr(C)] - union pthread_attr_union { - __i: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], - __vi: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], - __s: [ffi::c_ulong; if size_of::() == 8 { 7 } else { 9 }], - } - - #[repr(C)] - pub struct pthread_attr_t { - __u: pthread_attr_union, - } - - #[allow(non_camel_case_types)] - pub type pthread_t = *mut ffi::c_void; - - pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84; - - unsafe extern "C" { - pub fn pthread_create( - native: *mut pthread_t, - attr: *const pthread_attr_t, - f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void, - value: *mut ffi::c_void, - ) -> ffi::c_int; - pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int; - pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int; - pub fn pthread_attr_setstacksize( - attr: *mut pthread_attr_t, - stack_size: libc::size_t, - ) -> ffi::c_int; - pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int; - pub fn pthread_detach(thread: pthread_t) -> ffi::c_int; - } - } - - pub struct Thread { - id: libc::pthread_t, - } - - impl Drop for Thread { - fn drop(&mut self) { - let ret = unsafe { libc::pthread_detach(self.id) }; - debug_assert_eq!(ret, 0); - } - } - } - _ => { - pub struct Thread(!); - } -} - -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - cfg_select! { - target_feature = "atomics" => { - pub unsafe fn new(stack: usize, _name: Option<&str>, p: Box) -> io::Result { - let p = Box::into_raw(Box::new(p)); - let mut native: libc::pthread_t = unsafe { mem::zeroed() }; - let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; - assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0); - - let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); - - match unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) } { - 0 => {} - n => { - assert_eq!(n, libc::EINVAL); - // EINVAL means |stack_size| is either too small or not a - // multiple of the system page size. Because it's definitely - // >= PTHREAD_STACK_MIN, it must be an alignment issue. - // Round up to the nearest page and try again. - let page_size = os::page_size(); - let stack_size = - (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); - assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0); - } - }; - - let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) }; - // Note: if the thread creation fails and this assert fails, then p will - // be leaked. However, an alternative design could cause double-free - // which is clearly worse. - assert_eq!(unsafe {libc::pthread_attr_destroy(&mut attr) }, 0); - - return if ret != 0 { - // The thread failed to start and as a result p was not consumed. Therefore, it is - // safe to reconstruct the box so that it gets deallocated. - unsafe { drop(Box::from_raw(p)); } - Err(io::Error::from_raw_os_error(ret)) - } else { - Ok(Thread { id: native }) - }; - - extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { - unsafe { - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - } - ptr::null_mut() - } - } - } - _ => { - pub unsafe fn new(_stack: usize, _name: Option<&str>, _p: Box) -> io::Result { - crate::sys::unsupported() - } - } - } - - pub fn yield_now() { - let ret = unsafe { wasi::sched_yield() }; - debug_assert_eq!(ret, Ok(())); - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - let mut nanos = dur.as_nanos(); - while nanos > 0 { - const USERDATA: wasi::Userdata = 0x0123_45678; - - let clock = wasi::SubscriptionClock { - id: wasi::CLOCKID_MONOTONIC, - timeout: u64::try_from(nanos).unwrap_or(u64::MAX), - precision: 0, - flags: 0, - }; - nanos -= u128::from(clock.timeout); - - let in_ = wasi::Subscription { - userdata: USERDATA, - u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, - }; - unsafe { - let mut event: wasi::Event = mem::zeroed(); - let res = wasi::poll_oneoff(&in_, &mut event, 1); - match (res, event) { - ( - Ok(1), - wasi::Event { - userdata: USERDATA, - error: wasi::ERRNO_SUCCESS, - type_: wasi::EVENTTYPE_CLOCK, - .. - }, - ) => {} - _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), - } - } - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - - pub fn join(self) { - cfg_select! { - target_feature = "atomics" => { - let id = mem::ManuallyDrop::new(self).id; - let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - if ret != 0 { - rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } - } - _ => { - self.0 - } - } - } -} - -pub(crate) fn current_os_id() -> Option { - None -} - -pub fn available_parallelism() -> io::Result> { - cfg_select! { - target_feature = "atomics" => { - match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { - -1 => Err(io::Error::last_os_error()), - cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT), - } - } - _ => crate::sys::unsupported(), - } -} diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 5f3fb6d6ddfbf..c1d89da2677c9 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -14,7 +14,6 @@ pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/pal/wasip2/thread.rs b/library/std/src/sys/pal/wasip2/thread.rs deleted file mode 100644 index ad52918f15a13..0000000000000 --- a/library/std/src/sys/pal/wasip2/thread.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::ffi::CStr; -use crate::io; -use crate::num::NonZero; -use crate::time::{Duration, Instant}; - -pub struct Thread(!); - -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; - -impl Thread { - pub unsafe fn new( - _stack: usize, - _name: Option<&str>, - _p: Box, - ) -> io::Result { - // Note that unlike WASIp1 even if the wasm `atomics` feature is enabled - // there is no support for threads, not even experimentally, not even in - // wasi-libc. Thus this is unconditionally unsupported. - crate::sys::unsupported() - } - - pub fn yield_now() { - // no API for this in WASIp2, but there's also no threads, so that's - // sort of expected. - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - // Sleep in increments of `u64::MAX` nanoseconds until the `dur` is - // entirely drained. - let mut remaining = dur.as_nanos(); - while remaining > 0 { - let amt = u64::try_from(remaining).unwrap_or(u64::MAX); - wasip2::clocks::monotonic_clock::subscribe_duration(amt).block(); - remaining -= u128::from(amt); - } - } - - pub fn sleep_until(deadline: Instant) { - match u64::try_from(deadline.into_inner().as_duration().as_nanos()) { - // If the point in time we're sleeping to fits within a 64-bit - // number of nanoseconds then directly use `subscribe_instant`. - Ok(deadline) => { - wasip2::clocks::monotonic_clock::subscribe_instant(deadline).block(); - } - // ... otherwise we're sleeping for 500+ years relative to the - // "start" of what the system is using as a clock so speed/accuracy - // is not so much of a concern. Use `sleep` instead. - Err(_) => { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - } - } - - pub fn join(self) { - self.0 - } -} - -pub(crate) fn current_os_id() -> Option { - None -} - -pub fn available_parallelism() -> io::Result> { - crate::sys::unsupported() -} diff --git a/library/std/src/sys/pal/wasip2/time.rs b/library/std/src/sys/pal/wasip2/time.rs index f1f6839774b91..980070e7b851e 100644 --- a/library/std/src/sys/pal/wasip2/time.rs +++ b/library/std/src/sys/pal/wasip2/time.rs @@ -25,7 +25,7 @@ impl Instant { Some(Instant(self.0.checked_sub(*other)?)) } - pub(super) fn as_duration(&self) -> &Duration { + pub(crate) fn as_duration(&self) -> &Duration { &self.0 } } diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs deleted file mode 100644 index 42a7dbdf8b8b0..0000000000000 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::ffi::CStr; -use crate::io; -use crate::num::NonZero; -use crate::sys::unsupported; -use crate::time::{Duration, Instant}; - -pub struct Thread(!); - -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - _stack: usize, - _name: Option<&str>, - _p: Box, - ) -> io::Result { - unsupported() - } - - pub fn yield_now() {} - - pub fn set_name(_name: &CStr) {} - - pub fn sleep(dur: Duration) { - #[cfg(target_arch = "wasm32")] - use core::arch::wasm32 as wasm; - #[cfg(target_arch = "wasm64")] - use core::arch::wasm64 as wasm; - - use crate::cmp; - - // Use an atomic wait to block the current thread artificially with a - // timeout listed. Note that we should never be notified (return value - // of 0) or our comparison should never fail (return value of 1) so we - // should always only resume execution through a timeout (return value - // 2). - let mut nanos = dur.as_nanos(); - while nanos > 0 { - let amt = cmp::min(i64::MAX as u128, nanos); - let mut x = 0; - let val = unsafe { wasm::memory_atomic_wait32(&mut x, 0, amt as i64) }; - debug_assert_eq!(val, 2); - nanos -= amt; - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - - pub fn join(self) {} -} - -pub(crate) fn current_os_id() -> Option { - None -} - -pub fn available_parallelism() -> io::Result> { - unsupported() -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } -} diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 346c9ff88c963..a20cd0e9ac776 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -23,18 +23,9 @@ pub mod pipe; #[path = "../unsupported/time.rs"] pub mod time; -cfg_select! { - target_feature = "atomics" => { - #[path = "atomics/futex.rs"] - pub mod futex; - #[path = "atomics/thread.rs"] - pub mod thread; - } - _ => { - #[path = "../unsupported/thread.rs"] - pub mod thread; - } -} +#[cfg(target_feature = "atomics")] +#[path = "atomics/futex.rs"] +pub mod futex; #[path = "../unsupported/common.rs"] #[deny(unsafe_op_in_unsafe_fn)] diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 3b6a86cbc8f41..3357946b8f71d 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -20,7 +20,6 @@ pub mod futex; pub mod handle; pub mod os; pub mod pipe; -pub mod thread; pub mod time; cfg_select! { not(target_vendor = "uwp") => { @@ -48,9 +47,9 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { unsafe { stack_overflow::init(); - // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already + // Normally, `thread::spawn` will call `set_name` but since this thread already // exists, we have to call it ourselves. - thread::Thread::set_name_wide(wide_str!("main")); + crate::sys::thread::set_name_wide(wide_str!("main")); } } diff --git a/library/std/src/sys/pal/windows/time.rs b/library/std/src/sys/pal/windows/time.rs index a948c07e0a31e..f8f9a9fd81809 100644 --- a/library/std/src/sys/pal/windows/time.rs +++ b/library/std/src/sys/pal/windows/time.rs @@ -232,7 +232,7 @@ mod perf_counter { } /// A timer you can wait on. -pub(super) struct WaitableTimer { +pub(crate) struct WaitableTimer { handle: c::HANDLE, } impl WaitableTimer { diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index 042c4ff862ff6..e673157e0eb55 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -5,7 +5,6 @@ use crate::os::xous::ffi::exit; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/platform_version/darwin/public_extern.rs b/library/std/src/sys/platform_version/darwin/public_extern.rs index 967cdb4920fd4..c0848d94798f2 100644 --- a/library/std/src/sys/platform_version/darwin/public_extern.rs +++ b/library/std/src/sys/platform_version/darwin/public_extern.rs @@ -77,6 +77,10 @@ use super::{current_version, pack_i32_os_version}; // NOTE: This symbol has a workaround in the compiler's symbol mangling to avoid mangling it, while // still not exposing it from non-cdylib (like `#[no_mangle]` would). #[rustc_std_internal_symbol] +// NOTE: Making this a weak symbol might not be entirely the right solution for this, `compiler_rt` +// doesn't do that, it instead makes the symbol have "hidden" visibility. But since this is placed +// in `libstd`, which might be used as a dylib, we cannot do the same here. +#[linkage = "weak"] // extern "C" is correct, Clang assumes the function cannot unwind: // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/clang/lib/CodeGen/CGObjC.cpp#L3980 // @@ -145,6 +149,7 @@ pub(super) extern "C" fn __isPlatformVersionAtLeast( /// Old entry point for availability. Used when compiling with older Clang versions. // SAFETY: Same as for `__isPlatformVersionAtLeast`. #[rustc_std_internal_symbol] +#[linkage = "weak"] pub(super) extern "C" fn __isOSVersionAtLeast(major: i32, minor: i32, subminor: i32) -> i32 { let version = pack_i32_os_version(major, minor, subminor); (version <= current_version()) as i32 diff --git a/library/std/src/sys/platform_version/darwin/tests.rs b/library/std/src/sys/platform_version/darwin/tests.rs index 76dc4482c9835..eecd58ec79e35 100644 --- a/library/std/src/sys/platform_version/darwin/tests.rs +++ b/library/std/src/sys/platform_version/darwin/tests.rs @@ -28,6 +28,9 @@ fn compare_against_sw_vers() { let subminor: i32 = sw_vers.next().unwrap_or("0").parse().unwrap(); assert_eq!(sw_vers.count(), 0); + // Test directly against the lookup + assert_eq!(lookup_version().get(), pack_os_version(major as _, minor as _, subminor as _)); + // Current version is available assert_eq!(__isOSVersionAtLeast(major, minor, subminor), 1); @@ -40,9 +43,6 @@ fn compare_against_sw_vers() { assert_eq!(__isOSVersionAtLeast(major, minor, subminor + 1), 0); assert_eq!(__isOSVersionAtLeast(major, minor + 1, subminor), 0); assert_eq!(__isOSVersionAtLeast(major + 1, minor, subminor), 0); - - // Test directly against the lookup - assert_eq!(lookup_version().get(), pack_os_version(major as _, minor as _, subminor as _)); } #[test] diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/thread/hermit.rs similarity index 67% rename from library/std/src/sys/pal/hermit/thread.rs rename to library/std/src/sys/thread/hermit.rs index cc4734b681908..4d9f3b114c2a0 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/thread/hermit.rs @@ -1,10 +1,5 @@ -#![allow(dead_code)] - -use super::hermit_abi; -use crate::ffi::CStr; -use crate::mem::ManuallyDrop; use crate::num::NonZero; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{io, ptr}; pub type Tid = hermit_abi::Tid; @@ -68,57 +63,30 @@ impl Thread { } } - #[inline] - pub fn yield_now() { - unsafe { - hermit_abi::yield_now(); - } - } - - #[inline] - pub fn set_name(_name: &CStr) { - // nope - } - - #[inline] - pub fn sleep(dur: Duration) { - let micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; - let micros = u64::try_from(micros).unwrap_or(u64::MAX); - - unsafe { - hermit_abi::usleep(micros); - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { unsafe { let _ = hermit_abi::join(self.tid); } } +} - #[inline] - pub fn id(&self) -> Tid { - self.tid - } - - #[inline] - pub fn into_id(self) -> Tid { - ManuallyDrop::new(self).tid - } +pub fn available_parallelism() -> io::Result> { + unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) } } -pub(crate) fn current_os_id() -> Option { - None +#[inline] +pub fn sleep(dur: Duration) { + let micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; + let micros = u64::try_from(micros).unwrap_or(u64::MAX); + + unsafe { + hermit_abi::usleep(micros); + } } -pub fn available_parallelism() -> io::Result> { - unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) } +#[inline] +pub fn yield_now() { + unsafe { + hermit_abi::yield_now(); + } } diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs new file mode 100644 index 0000000000000..6bb7fc1a20e2f --- /dev/null +++ b/library/std/src/sys/thread/mod.rs @@ -0,0 +1,152 @@ +cfg_select! { + target_os = "hermit" => { + mod hermit; + pub use hermit::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{current_os_id, set_name}; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::{Thread, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + + // SGX should protect in-enclave data from outside attackers, so there + // must not be any data leakage to the OS, particularly no 1-1 mapping + // between SGX thread names and OS thread names. Hence `set_name` is + // intentionally a no-op. + // + // Note that the internally visible SGX thread name is already provided + // by the platform-agnostic Rust thread code. This can be observed in + // the [`std::thread::tests::test_named_thread`] test, which succeeds + // as-is with the SGX target. + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, set_name}; + } + target_os = "solid_asp3" => { + mod solid; + pub use solid::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, current_os_id, set_name}; + } + target_os = "teeos" => { + mod teeos; + pub use teeos::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, current_os_id, set_name}; + } + target_os = "uefi" => { + mod uefi; + pub use uefi::{available_parallelism, sleep}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{Thread, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + target_family = "unix" => { + mod unix; + pub use unix::{Thread, available_parallelism, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[cfg(not(any( + target_env = "newlib", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox", + target_os = "hurd", + target_os = "aix", + )))] + pub use unix::set_name; + #[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "fuchsia", + target_os = "vxworks", + ))] + pub use unix::sleep_until; + #[expect(dead_code)] + mod unsupported; + #[cfg(any( + target_env = "newlib", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox", + target_os = "hurd", + target_os = "aix", + ))] + pub use unsupported::set_name; + } + all(target_os = "wasi", target_env = "p1") => { + mod wasip1; + pub use wasip1::{DEFAULT_MIN_STACK_SIZE, sleep, yield_now}; + #[cfg(target_feature = "atomics")] + pub use wasip1::{Thread, available_parallelism}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{current_os_id, set_name}; + #[cfg(not(target_feature = "atomics"))] + pub use unsupported::{Thread, available_parallelism}; + } + all(target_os = "wasi", target_env = "p2") => { + mod wasip2; + pub use wasip2::{sleep, sleep_until}; + #[expect(dead_code)] + mod unsupported; + // Note that unlike WASIp1 even if the wasm `atomics` feature is enabled + // there is no support for threads, not even experimentally, not even in + // wasi-libc. Thus this is unconditionally unsupported. + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + all(target_family = "wasm", target_feature = "atomics") => { + mod wasm; + pub use wasm::sleep; + + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + target_os = "windows" => { + mod windows; + pub use windows::{Thread, available_parallelism, current_os_id, set_name, set_name_wide, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + target_os = "xous" => { + mod xous; + pub use xous::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{current_os_id, set_name}; + } + _ => { + mod unsupported; + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + } +} + +#[cfg(not(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "fuchsia", + target_os = "vxworks", + all(target_os = "wasi", target_env = "p2"), +)))] +pub fn sleep_until(deadline: crate::time::Instant) { + use crate::time::Instant; + + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + sleep(delay); + } +} diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/thread/sgx.rs similarity index 71% rename from library/std/src/sys/pal/sgx/thread.rs rename to library/std/src/sys/thread/sgx.rs index 1f613badcd704..f20ef7d86b9c7 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/thread/sgx.rs @@ -1,11 +1,8 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? -use super::abi::{thread, usercalls}; -use super::unsupported; -use crate::ffi::CStr; use crate::io; -use crate::num::NonZero; -use crate::time::{Duration, Instant}; +use crate::sys::pal::abi::{thread, usercalls}; +use crate::time::Duration; pub struct Thread(task_queue::JoinHandle); @@ -108,51 +105,27 @@ impl Thread { Ok(Thread(handle)) } - pub(super) fn entry() -> JoinNotifier { + pub(crate) fn entry() -> JoinNotifier { let mut pending_tasks = task_queue::lock(); let task = rtunwrap!(Some, pending_tasks.pop()); drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary task.run() } - pub fn yield_now() { - let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO)); - rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); - } - - /// SGX should protect in-enclave data from the outside (attacker), - /// so there should be no data leakage to the OS, - /// and therefore also no 1-1 mapping between SGX thread names and OS thread names. - /// - /// This is why the method is intentionally No-Op. - pub fn set_name(_name: &CStr) { - // Note that the internally visible SGX thread name is already provided - // by the platform-agnostic (target-agnostic) Rust thread code. - // This can be observed in the [`std::thread::tests::test_named_thread`] test, - // which succeeds as-is with the SGX target. - } - - pub fn sleep(dur: Duration) { - usercalls::wait_timeout(0, dur, || true); - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { self.0.wait(); } } -pub(crate) fn current_os_id() -> Option { +pub fn current_os_id() -> Option { Some(thread::current().addr().get() as u64) } -pub fn available_parallelism() -> io::Result> { - unsupported() +pub fn sleep(dur: Duration) { + usercalls::wait_timeout(0, dur, || true); +} + +pub fn yield_now() { + let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO)); + rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); } diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/thread/solid.rs similarity index 94% rename from library/std/src/sys/pal/itron/thread.rs rename to library/std/src/sys/thread/solid.rs index 4e14cb3cbcaad..46a84faa80225 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/thread/solid.rs @@ -1,16 +1,14 @@ //! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and //! `exd_tsk` are available. -use super::error::{ItronError, expect_success, expect_success_aborting}; -use super::time::dur2reltims; -use super::{abi, task}; use crate::cell::UnsafeCell; -use crate::ffi::CStr; use crate::mem::ManuallyDrop; -use crate::num::NonZero; use crate::ptr::NonNull; use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; -use crate::time::{Duration, Instant}; +use crate::sys::pal::itron::error::{ItronError, expect_success, expect_success_aborting}; +use crate::sys::pal::itron::time::dur2reltims; +use crate::sys::pal::itron::{abi, task}; +use crate::time::Duration; use crate::{hint, io}; pub struct Thread { @@ -195,28 +193,6 @@ impl Thread { Ok(Self { p_inner, task: new_task }) } - pub fn yield_now() { - expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq"); - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - for timeout in dur2reltims(dur) { - expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk"); - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { // Safety: `ThreadInner` is alive at this point let inner = unsafe { self.p_inner.as_ref() }; @@ -361,10 +337,12 @@ unsafe fn terminate_and_delete_current_task() -> ! { unsafe { crate::hint::unreachable_unchecked() }; } -pub(crate) fn current_os_id() -> Option { - None +pub fn yield_now() { + expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq"); } -pub fn available_parallelism() -> io::Result> { - super::unsupported() +pub fn sleep(dur: Duration) { + for timeout in dur2reltims(dur) { + expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk"); + } } diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/thread/teeos.rs similarity index 73% rename from library/std/src/sys/pal/teeos/thread.rs rename to library/std/src/sys/thread/teeos.rs index 1812d11e692e8..cad100395c9f8 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/thread/teeos.rs @@ -1,12 +1,18 @@ -use crate::ffi::CStr; use crate::mem::{self, ManuallyDrop}; -use crate::num::NonZero; use crate::sys::os; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{cmp, io, ptr}; pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024; +unsafe extern "C" { + safe fn TEE_Wait(timeout: u32) -> u32; +} + +fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { + libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") +} + pub struct Thread { id: libc::pthread_t, } @@ -16,10 +22,6 @@ pub struct Thread { unsafe impl Send for Thread {} unsafe impl Sync for Thread {} -unsafe extern "C" { - pub fn TEE_Wait(timeout: u32) -> u32; -} - impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new( @@ -74,7 +76,7 @@ impl Thread { } else { // The new thread will start running earliest after the next yield. // We add a yield here, so that the user does not have to. - Thread::yield_now(); + yield_now(); Ok(Thread { id: native }) }; @@ -91,36 +93,6 @@ impl Thread { } } - pub fn yield_now() { - let ret = unsafe { libc::sched_yield() }; - debug_assert_eq!(ret, 0); - } - - /// This does not do anything on teeos - pub fn set_name(_name: &CStr) { - // Both pthread_setname_np and prctl are not available to the TA, - // so we can't implement this currently. If the need arises please - // contact the teeos rustzone team. - } - - /// only main thread could wait for sometime in teeos - pub fn sleep(dur: Duration) { - let sleep_millis = dur.as_millis(); - let final_sleep: u32 = - if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 }; - unsafe { - let _ = TEE_Wait(final_sleep); - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - /// must join, because no pthread_detach supported pub fn join(self) { let id = self.into_id(); @@ -128,10 +100,6 @@ impl Thread { assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); } - pub fn id(&self) -> libc::pthread_t { - self.id - } - pub fn into_id(self) -> libc::pthread_t { ManuallyDrop::new(self).id } @@ -144,16 +112,15 @@ impl Drop for Thread { } } -pub(crate) fn current_os_id() -> Option { - None -} - -// Note: Both `sched_getaffinity` and `sysconf` are available but not functional on -// teeos, so this function always returns an Error! -pub fn available_parallelism() -> io::Result> { - Err(io::Error::UNKNOWN_THREAD_COUNT) +pub fn yield_now() { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); } -fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { - libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") +/// only main thread could wait for sometime in teeos +pub fn sleep(dur: Duration) { + let sleep_millis = dur.as_millis(); + let final_sleep: u32 = + if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 }; + TEE_Wait(final_sleep); } diff --git a/library/std/src/sys/thread/uefi.rs b/library/std/src/sys/thread/uefi.rs new file mode 100644 index 0000000000000..94f67d7ace2b8 --- /dev/null +++ b/library/std/src/sys/thread/uefi.rs @@ -0,0 +1,25 @@ +use crate::io; +use crate::num::NonZero; +use crate::ptr::NonNull; +use crate::time::Duration; + +pub fn available_parallelism() -> io::Result> { + // UEFI is single threaded + Ok(NonZero::new(1).unwrap()) +} + +pub fn sleep(dur: Duration) { + let boot_services: NonNull = + crate::os::uefi::env::boot_services().expect("can't sleep").cast(); + let mut dur_ms = dur.as_micros(); + // ceil up to the nearest microsecond + if dur.subsec_nanos() % 1000 > 0 { + dur_ms += 1; + } + + while dur_ms > 0 { + let ms = crate::cmp::min(dur_ms, usize::MAX as u128); + let _ = unsafe { ((*boot_services.as_ptr()).stall)(ms as usize) }; + dur_ms -= ms; + } +} diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/thread/unix.rs similarity index 73% rename from library/std/src/sys/pal/unix/thread.rs rename to library/std/src/sys/thread/unix.rs index 3389b8c0c8a55..18ae8ddd3aae7 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/thread/unix.rs @@ -6,7 +6,7 @@ use crate::sys::weak::dlsym; #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))] use crate::sys::weak::weak; use crate::sys::{os, stack_overflow}; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{cmp, io, ptr}; #[cfg(not(any( target_os = "l4re", @@ -121,273 +121,6 @@ impl Thread { } } - pub fn yield_now() { - let ret = unsafe { libc::sched_yield() }; - debug_assert_eq!(ret, 0); - } - - #[cfg(target_os = "android")] - pub fn set_name(name: &CStr) { - const PR_SET_NAME: libc::c_int = 15; - unsafe { - let res = libc::prctl( - PR_SET_NAME, - name.as_ptr(), - 0 as libc::c_ulong, - 0 as libc::c_ulong, - 0 as libc::c_ulong, - ); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, 0); - } - } - - #[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "nuttx", - target_os = "cygwin" - ))] - pub fn set_name(name: &CStr) { - unsafe { - cfg_select! { - any(target_os = "linux", target_os = "cygwin") => { - // Linux and Cygwin limits the allowed length of the name. - const TASK_COMM_LEN: usize = 16; - let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); - } - _ => { - // FreeBSD, DragonFly BSD and NuttX do not enforce length limits. - } - }; - // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, - // FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0. - let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, 0); - } - } - - #[cfg(target_os = "openbsd")] - pub fn set_name(name: &CStr) { - unsafe { - libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); - } - } - - #[cfg(target_vendor = "apple")] - pub fn set_name(name: &CStr) { - unsafe { - let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); - let res = libc::pthread_setname_np(name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, 0); - } - } - - #[cfg(target_os = "netbsd")] - pub fn set_name(name: &CStr) { - unsafe { - let res = libc::pthread_setname_np( - libc::pthread_self(), - c"%s".as_ptr(), - name.as_ptr() as *mut libc::c_void, - ); - debug_assert_eq!(res, 0); - } - } - - #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] - pub fn set_name(name: &CStr) { - weak!( - fn pthread_setname_np( - thread: libc::pthread_t, - name: *const libc::c_char, - ) -> libc::c_int; - ); - - if let Some(f) = pthread_setname_np.get() { - #[cfg(target_os = "nto")] - const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize; - #[cfg(any(target_os = "solaris", target_os = "illumos"))] - const THREAD_NAME_MAX: usize = 32; - - let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name); - let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; - debug_assert_eq!(res, 0); - } - } - - #[cfg(target_os = "fuchsia")] - pub fn set_name(name: &CStr) { - use super::fuchsia::*; - unsafe { - zx_object_set_property( - zx_thread_self(), - ZX_PROP_NAME, - name.as_ptr() as *const libc::c_void, - name.to_bytes().len(), - ); - } - } - - #[cfg(target_os = "haiku")] - pub fn set_name(name: &CStr) { - unsafe { - let thread_self = libc::find_thread(ptr::null_mut()); - let res = libc::rename_thread(thread_self, name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, libc::B_OK); - } - } - - #[cfg(target_os = "vxworks")] - pub fn set_name(name: &CStr) { - let mut name = truncate_cstr::<{ (libc::VX_TASK_RENAME_LENGTH - 1) as usize }>(name); - let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; - debug_assert_eq!(res, libc::OK); - } - - #[cfg(any( - target_env = "newlib", - target_os = "l4re", - target_os = "emscripten", - target_os = "redox", - target_os = "hurd", - target_os = "aix", - ))] - pub fn set_name(_name: &CStr) { - // Newlib and Emscripten have no way to set a thread name. - } - - #[cfg(not(target_os = "espidf"))] - pub fn sleep(dur: Duration) { - let mut secs = dur.as_secs(); - let mut nsecs = dur.subsec_nanos() as _; - - // If we're awoken with a signal then the return value will be -1 and - // nanosleep will fill in `ts` with the remaining time. - unsafe { - while secs > 0 || nsecs > 0 { - let mut ts = libc::timespec { - tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t, - tv_nsec: nsecs, - }; - secs -= ts.tv_sec as u64; - let ts_ptr = &raw mut ts; - if libc::nanosleep(ts_ptr, ts_ptr) == -1 { - assert_eq!(os::errno(), libc::EINTR); - secs += ts.tv_sec as u64; - nsecs = ts.tv_nsec; - } else { - nsecs = 0; - } - } - } - } - - #[cfg(target_os = "espidf")] - pub fn sleep(dur: Duration) { - // ESP-IDF does not have `nanosleep`, so we use `usleep` instead. - // As per the documentation of `usleep`, it is expected to support - // sleep times as big as at least up to 1 second. - // - // ESP-IDF does support almost up to `u32::MAX`, but due to a potential integer overflow in its - // `usleep` implementation - // (https://github.com/espressif/esp-idf/blob/d7ca8b94c852052e3bc33292287ef4dd62c9eeb1/components/newlib/time.c#L210), - // we limit the sleep time to the maximum one that would not cause the underlying `usleep` implementation to overflow - // (`portTICK_PERIOD_MS` can be anything between 1 to 1000, and is 10 by default). - const MAX_MICROS: u32 = u32::MAX - 1_000_000 - 1; - - // Add any nanoseconds smaller than a microsecond as an extra microsecond - // so as to comply with the `std::thread::sleep` contract which mandates - // implementations to sleep for _at least_ the provided `dur`. - // We can't overflow `micros` as it is a `u128`, while `Duration` is a pair of - // (`u64` secs, `u32` nanos), where the nanos are strictly smaller than 1 second - // (i.e. < 1_000_000_000) - let mut micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; - - while micros > 0 { - let st = if micros > MAX_MICROS as u128 { MAX_MICROS } else { micros as u32 }; - unsafe { - libc::usleep(st); - } - - micros -= st as u128; - } - } - - // Any unix that has clock_nanosleep - // If this list changes update the MIRI chock_nanosleep shim - #[cfg(any( - target_os = "freebsd", - target_os = "netbsd", - target_os = "linux", - target_os = "android", - target_os = "solaris", - target_os = "illumos", - target_os = "dragonfly", - target_os = "hurd", - target_os = "fuchsia", - target_os = "vxworks", - ))] - pub fn sleep_until(deadline: Instant) { - let Some(ts) = deadline.into_inner().into_timespec().to_timespec() else { - // The deadline is further in the future then can be passed to - // clock_nanosleep. We have to use Self::sleep instead. This might - // happen on 32 bit platforms, especially closer to 2038. - let now = Instant::now(); - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - return; - }; - - unsafe { - // When we get interrupted (res = EINTR) call clock_nanosleep again - loop { - let res = libc::clock_nanosleep( - super::time::Instant::CLOCK_ID, - libc::TIMER_ABSTIME, - &ts, - core::ptr::null_mut(), // not required with TIMER_ABSTIME - ); - - if res == 0 { - break; - } else { - assert_eq!( - res, - libc::EINTR, - "timespec is in range, - clockid is valid and kernel should support it" - ); - } - } - } - } - - // Any unix that does not have clock_nanosleep - #[cfg(not(any( - target_os = "freebsd", - target_os = "netbsd", - target_os = "linux", - target_os = "android", - target_os = "solaris", - target_os = "illumos", - target_os = "dragonfly", - target_os = "hurd", - target_os = "fuchsia", - target_os = "vxworks", - )))] - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { let id = self.into_id(); let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; @@ -410,67 +143,6 @@ impl Drop for Thread { } } -pub(crate) fn current_os_id() -> Option { - // Most Unix platforms have a way to query an integer ID of the current thread, all with - // slightly different spellings. - // - // The OS thread ID is used rather than `pthread_self` so as to match what will be displayed - // for process inspection (debuggers, trace, `top`, etc.). - cfg_select! { - // Most platforms have a function returning a `pid_t` or int, which is an `i32`. - any(target_os = "android", target_os = "linux") => { - use crate::sys::weak::syscall; - - // `libc::gettid` is only available on glibc 2.30+, but the syscall is available - // since Linux 2.4.11. - syscall!(fn gettid() -> libc::pid_t;); - - // SAFETY: FFI call with no preconditions. - let id: libc::pid_t = unsafe { gettid() }; - Some(id as u64) - } - target_os = "nto" => { - // SAFETY: FFI call with no preconditions. - let id: libc::pid_t = unsafe { libc::gettid() }; - Some(id as u64) - } - target_os = "openbsd" => { - // SAFETY: FFI call with no preconditions. - let id: libc::pid_t = unsafe { libc::getthrid() }; - Some(id as u64) - } - target_os = "freebsd" => { - // SAFETY: FFI call with no preconditions. - let id: libc::c_int = unsafe { libc::pthread_getthreadid_np() }; - Some(id as u64) - } - target_os = "netbsd" => { - // SAFETY: FFI call with no preconditions. - let id: libc::lwpid_t = unsafe { libc::_lwp_self() }; - Some(id as u64) - } - any(target_os = "illumos", target_os = "solaris") => { - // On Illumos and Solaris, the `pthread_t` is the same as the OS thread ID. - // SAFETY: FFI call with no preconditions. - let id: libc::pthread_t = unsafe { libc::pthread_self() }; - Some(id as u64) - } - target_vendor = "apple" => { - // Apple allows querying arbitrary thread IDs, `thread=NULL` queries the current thread. - let mut id = 0u64; - // SAFETY: `thread_id` is a valid pointer, no other preconditions. - let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) }; - if status == 0 { - Some(id) - } else { - None - } - } - // Other platforms don't have an OS thread ID or don't have a way to access it. - _ => None, - } -} - #[cfg(any( target_os = "linux", target_os = "nto", @@ -668,6 +340,301 @@ pub fn available_parallelism() -> io::Result> { } } +pub fn current_os_id() -> Option { + // Most Unix platforms have a way to query an integer ID of the current thread, all with + // slightly different spellings. + // + // The OS thread ID is used rather than `pthread_self` so as to match what will be displayed + // for process inspection (debuggers, trace, `top`, etc.). + cfg_select! { + // Most platforms have a function returning a `pid_t` or int, which is an `i32`. + any(target_os = "android", target_os = "linux") => { + use crate::sys::weak::syscall; + + // `libc::gettid` is only available on glibc 2.30+, but the syscall is available + // since Linux 2.4.11. + syscall!(fn gettid() -> libc::pid_t;); + + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { gettid() }; + Some(id as u64) + } + target_os = "nto" => { + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { libc::gettid() }; + Some(id as u64) + } + target_os = "openbsd" => { + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { libc::getthrid() }; + Some(id as u64) + } + target_os = "freebsd" => { + // SAFETY: FFI call with no preconditions. + let id: libc::c_int = unsafe { libc::pthread_getthreadid_np() }; + Some(id as u64) + } + target_os = "netbsd" => { + // SAFETY: FFI call with no preconditions. + let id: libc::lwpid_t = unsafe { libc::_lwp_self() }; + Some(id as u64) + } + any(target_os = "illumos", target_os = "solaris") => { + // On Illumos and Solaris, the `pthread_t` is the same as the OS thread ID. + // SAFETY: FFI call with no preconditions. + let id: libc::pthread_t = unsafe { libc::pthread_self() }; + Some(id as u64) + } + target_vendor = "apple" => { + // Apple allows querying arbitrary thread IDs, `thread=NULL` queries the current thread. + let mut id = 0u64; + // SAFETY: `thread_id` is a valid pointer, no other preconditions. + let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) }; + if status == 0 { + Some(id) + } else { + None + } + } + // Other platforms don't have an OS thread ID or don't have a way to access it. + _ => None, + } +} + +#[cfg(target_os = "android")] +pub fn set_name(name: &CStr) { + const PR_SET_NAME: libc::c_int = 15; + unsafe { + let res = libc::prctl( + PR_SET_NAME, + name.as_ptr(), + 0 as libc::c_ulong, + 0 as libc::c_ulong, + 0 as libc::c_ulong, + ); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); + } +} + +#[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "nuttx", + target_os = "cygwin" +))] +pub fn set_name(name: &CStr) { + unsafe { + cfg_select! { + any(target_os = "linux", target_os = "cygwin") => { + // Linux and Cygwin limits the allowed length of the name. + const TASK_COMM_LEN: usize = 16; + let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + } + _ => { + // FreeBSD, DragonFly BSD and NuttX do not enforce length limits. + } + }; + // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, + // FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0. + let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); + } +} + +#[cfg(target_os = "openbsd")] +pub fn set_name(name: &CStr) { + unsafe { + libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); + } +} + +#[cfg(target_vendor = "apple")] +pub fn set_name(name: &CStr) { + unsafe { + let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); + let res = libc::pthread_setname_np(name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); + } +} + +#[cfg(target_os = "netbsd")] +pub fn set_name(name: &CStr) { + unsafe { + let res = libc::pthread_setname_np( + libc::pthread_self(), + c"%s".as_ptr(), + name.as_ptr() as *mut libc::c_void, + ); + debug_assert_eq!(res, 0); + } +} + +#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] +pub fn set_name(name: &CStr) { + weak!( + fn pthread_setname_np(thread: libc::pthread_t, name: *const libc::c_char) -> libc::c_int; + ); + + if let Some(f) = pthread_setname_np.get() { + #[cfg(target_os = "nto")] + const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize; + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + const THREAD_NAME_MAX: usize = 32; + + let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name); + let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; + debug_assert_eq!(res, 0); + } +} + +#[cfg(target_os = "fuchsia")] +pub fn set_name(name: &CStr) { + use super::fuchsia::*; + unsafe { + zx_object_set_property( + zx_thread_self(), + ZX_PROP_NAME, + name.as_ptr() as *const libc::c_void, + name.to_bytes().len(), + ); + } +} + +#[cfg(target_os = "haiku")] +pub fn set_name(name: &CStr) { + unsafe { + let thread_self = libc::find_thread(ptr::null_mut()); + let res = libc::rename_thread(thread_self, name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, libc::B_OK); + } +} + +#[cfg(target_os = "vxworks")] +pub fn set_name(name: &CStr) { + let mut name = truncate_cstr::<{ (libc::VX_TASK_RENAME_LENGTH - 1) as usize }>(name); + let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; + debug_assert_eq!(res, libc::OK); +} + +#[cfg(not(target_os = "espidf"))] +pub fn sleep(dur: Duration) { + let mut secs = dur.as_secs(); + let mut nsecs = dur.subsec_nanos() as _; + + // If we're awoken with a signal then the return value will be -1 and + // nanosleep will fill in `ts` with the remaining time. + unsafe { + while secs > 0 || nsecs > 0 { + let mut ts = libc::timespec { + tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t, + tv_nsec: nsecs, + }; + secs -= ts.tv_sec as u64; + let ts_ptr = &raw mut ts; + if libc::nanosleep(ts_ptr, ts_ptr) == -1 { + assert_eq!(os::errno(), libc::EINTR); + secs += ts.tv_sec as u64; + nsecs = ts.tv_nsec; + } else { + nsecs = 0; + } + } + } +} + +#[cfg(target_os = "espidf")] +pub fn sleep(dur: Duration) { + // ESP-IDF does not have `nanosleep`, so we use `usleep` instead. + // As per the documentation of `usleep`, it is expected to support + // sleep times as big as at least up to 1 second. + // + // ESP-IDF does support almost up to `u32::MAX`, but due to a potential integer overflow in its + // `usleep` implementation + // (https://github.com/espressif/esp-idf/blob/d7ca8b94c852052e3bc33292287ef4dd62c9eeb1/components/newlib/time.c#L210), + // we limit the sleep time to the maximum one that would not cause the underlying `usleep` implementation to overflow + // (`portTICK_PERIOD_MS` can be anything between 1 to 1000, and is 10 by default). + const MAX_MICROS: u32 = u32::MAX - 1_000_000 - 1; + + // Add any nanoseconds smaller than a microsecond as an extra microsecond + // so as to comply with the `std::thread::sleep` contract which mandates + // implementations to sleep for _at least_ the provided `dur`. + // We can't overflow `micros` as it is a `u128`, while `Duration` is a pair of + // (`u64` secs, `u32` nanos), where the nanos are strictly smaller than 1 second + // (i.e. < 1_000_000_000) + let mut micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; + + while micros > 0 { + let st = if micros > MAX_MICROS as u128 { MAX_MICROS } else { micros as u32 }; + unsafe { + libc::usleep(st); + } + + micros -= st as u128; + } +} + +// Any unix that has clock_nanosleep +// If this list changes update the MIRI chock_nanosleep shim +#[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "fuchsia", + target_os = "vxworks", +))] +pub fn sleep_until(deadline: crate::time::Instant) { + use crate::time::Instant; + + let Some(ts) = deadline.into_inner().into_timespec().to_timespec() else { + // The deadline is further in the future then can be passed to + // clock_nanosleep. We have to use Self::sleep instead. This might + // happen on 32 bit platforms, especially closer to 2038. + let now = Instant::now(); + if let Some(delay) = deadline.checked_duration_since(now) { + sleep(delay); + } + return; + }; + + unsafe { + // When we get interrupted (res = EINTR) call clock_nanosleep again + loop { + let res = libc::clock_nanosleep( + crate::sys::time::Instant::CLOCK_ID, + libc::TIMER_ABSTIME, + &ts, + core::ptr::null_mut(), // not required with TIMER_ABSTIME + ); + + if res == 0 { + break; + } else { + assert_eq!( + res, + libc::EINTR, + "timespec is in range, + clockid is valid and kernel should support it" + ); + } + } + } +} + +pub fn yield_now() { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); +} + #[cfg(any(target_os = "android", target_os = "linux"))] mod cgroups { //! Currently not covered diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/thread/unsupported.rs similarity index 54% rename from library/std/src/sys/pal/unsupported/thread.rs rename to library/std/src/sys/thread/unsupported.rs index 34d9b5ec70c6b..a5001efa3b405 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/thread/unsupported.rs @@ -1,8 +1,7 @@ -use super::unsupported; use crate::ffi::CStr; use crate::io; use crate::num::NonZero; -use crate::time::{Duration, Instant}; +use crate::time::Duration; pub struct Thread(!); @@ -15,23 +14,7 @@ impl Thread { _name: Option<&str>, _p: Box, ) -> io::Result { - unsupported() - } - - pub fn yield_now() { - // do nothing - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(_dur: Duration) { - panic!("can't sleep"); - } - - pub fn sleep_until(_deadline: Instant) { - panic!("can't sleep"); + Err(io::Error::UNSUPPORTED_PLATFORM) } pub fn join(self) { @@ -39,10 +22,22 @@ impl Thread { } } -pub(crate) fn current_os_id() -> Option { +pub fn available_parallelism() -> io::Result> { + Err(io::Error::UNKNOWN_THREAD_COUNT) +} + +pub fn current_os_id() -> Option { None } -pub fn available_parallelism() -> io::Result> { - unsupported() +pub fn yield_now() { + // do nothing +} + +pub fn set_name(_name: &CStr) { + // nope +} + +pub fn sleep(_dur: Duration) { + panic!("can't sleep"); } diff --git a/library/std/src/sys/thread/wasip1.rs b/library/std/src/sys/thread/wasip1.rs new file mode 100644 index 0000000000000..83001fad49c81 --- /dev/null +++ b/library/std/src/sys/thread/wasip1.rs @@ -0,0 +1,185 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +#[cfg(target_feature = "atomics")] +use crate::io; +use crate::mem; +#[cfg(target_feature = "atomics")] +use crate::num::NonZero; +#[cfg(target_feature = "atomics")] +use crate::sys::os; +use crate::time::Duration; +#[cfg(target_feature = "atomics")] +use crate::{cmp, ptr}; + +// Add a few symbols not in upstream `libc` just yet. +#[cfg(target_feature = "atomics")] +mod libc { + pub use libc::*; + + pub use crate::ffi; + + // defined in wasi-libc + // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 + #[repr(C)] + union pthread_attr_union { + __i: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], + __vi: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], + __s: [ffi::c_ulong; if size_of::() == 8 { 7 } else { 9 }], + } + + #[repr(C)] + pub struct pthread_attr_t { + __u: pthread_attr_union, + } + + #[allow(non_camel_case_types)] + pub type pthread_t = *mut ffi::c_void; + + pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84; + + unsafe extern "C" { + pub fn pthread_create( + native: *mut pthread_t, + attr: *const pthread_attr_t, + f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void, + value: *mut ffi::c_void, + ) -> ffi::c_int; + pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int; + pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int; + pub fn pthread_attr_setstacksize( + attr: *mut pthread_attr_t, + stack_size: libc::size_t, + ) -> ffi::c_int; + pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int; + pub fn pthread_detach(thread: pthread_t) -> ffi::c_int; + } +} + +#[cfg(target_feature = "atomics")] +pub struct Thread { + id: libc::pthread_t, +} + +#[cfg(target_feature = "atomics")] +impl Drop for Thread { + fn drop(&mut self) { + let ret = unsafe { libc::pthread_detach(self.id) }; + debug_assert_eq!(ret, 0); + } +} + +pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; + +#[cfg(target_feature = "atomics")] +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { + let p = Box::into_raw(Box::new(p)); + let mut native: libc::pthread_t = unsafe { mem::zeroed() }; + let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; + assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0); + + let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); + + match unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) } { + 0 => {} + n => { + assert_eq!(n, libc::EINVAL); + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + let page_size = os::page_size(); + let stack_size = + (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); + assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0); + } + }; + + let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) }; + // Note: if the thread creation fails and this assert fails, then p will + // be leaked. However, an alternative design could cause double-free + // which is clearly worse. + assert_eq!(unsafe { libc::pthread_attr_destroy(&mut attr) }, 0); + + return if ret != 0 { + // The thread failed to start and as a result p was not consumed. Therefore, it is + // safe to reconstruct the box so that it gets deallocated. + unsafe { + drop(Box::from_raw(p)); + } + Err(io::Error::from_raw_os_error(ret)) + } else { + Ok(Thread { id: native }) + }; + + extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { + unsafe { + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); + } + ptr::null_mut() + } + } + + pub fn join(self) { + let id = mem::ManuallyDrop::new(self).id; + let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; + if ret != 0 { + rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); + } + } +} + +#[cfg(target_feature = "atomics")] +pub fn available_parallelism() -> io::Result> { + match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { + -1 => Err(io::Error::last_os_error()), + cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT), + } +} + +pub fn yield_now() { + let ret = unsafe { wasi::sched_yield() }; + debug_assert_eq!(ret, Ok(())); +} + +pub fn sleep(dur: Duration) { + let mut nanos = dur.as_nanos(); + while nanos > 0 { + const USERDATA: wasi::Userdata = 0x0123_45678; + + let clock = wasi::SubscriptionClock { + id: wasi::CLOCKID_MONOTONIC, + timeout: u64::try_from(nanos).unwrap_or(u64::MAX), + precision: 0, + flags: 0, + }; + nanos -= u128::from(clock.timeout); + + let in_ = wasi::Subscription { + userdata: USERDATA, + u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, + }; + unsafe { + let mut event: wasi::Event = mem::zeroed(); + let res = wasi::poll_oneoff(&in_, &mut event, 1); + match (res, event) { + ( + Ok(1), + wasi::Event { + userdata: USERDATA, + error: wasi::ERRNO_SUCCESS, + type_: wasi::EVENTTYPE_CLOCK, + .. + }, + ) => {} + _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), + } + } + } +} diff --git a/library/std/src/sys/thread/wasip2.rs b/library/std/src/sys/thread/wasip2.rs new file mode 100644 index 0000000000000..420cad2a5e4ab --- /dev/null +++ b/library/std/src/sys/thread/wasip2.rs @@ -0,0 +1,32 @@ +use crate::time::{Duration, Instant}; + +pub fn sleep(dur: Duration) { + // Sleep in increments of `u64::MAX` nanoseconds until the `dur` is + // entirely drained. + let mut remaining = dur.as_nanos(); + while remaining > 0 { + let amt = u64::try_from(remaining).unwrap_or(u64::MAX); + wasip2::clocks::monotonic_clock::subscribe_duration(amt).block(); + remaining -= u128::from(amt); + } +} + +pub fn sleep_until(deadline: Instant) { + match u64::try_from(deadline.into_inner().as_duration().as_nanos()) { + // If the point in time we're sleeping to fits within a 64-bit + // number of nanoseconds then directly use `subscribe_instant`. + Ok(deadline) => { + wasip2::clocks::monotonic_clock::subscribe_instant(deadline).block(); + } + // ... otherwise we're sleeping for 500+ years relative to the + // "start" of what the system is using as a clock so speed/accuracy + // is not so much of a concern. Use `sleep` instead. + Err(_) => { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + sleep(delay); + } + } + } +} diff --git a/library/std/src/sys/thread/wasm.rs b/library/std/src/sys/thread/wasm.rs new file mode 100644 index 0000000000000..e843bc992ba1b --- /dev/null +++ b/library/std/src/sys/thread/wasm.rs @@ -0,0 +1,23 @@ +use crate::cmp; +use crate::time::Duration; + +pub fn sleep(dur: Duration) { + #[cfg(target_arch = "wasm32")] + use core::arch::wasm32 as wasm; + #[cfg(target_arch = "wasm64")] + use core::arch::wasm64 as wasm; + + // Use an atomic wait to block the current thread artificially with a + // timeout listed. Note that we should never be notified (return value + // of 0) or our comparison should never fail (return value of 1) so we + // should always only resume execution through a timeout (return value + // 2). + let mut nanos = dur.as_nanos(); + while nanos > 0 { + let amt = cmp::min(i64::MAX as u128, nanos); + let mut x = 0; + let val = unsafe { wasm::memory_atomic_wait32(&mut x, 0, amt as i64) }; + debug_assert_eq!(val, 2); + nanos -= amt; + } +} diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/thread/windows.rs similarity index 66% rename from library/std/src/sys/pal/windows/thread.rs rename to library/std/src/sys/thread/windows.rs index b0e38220a2d3b..a5640c51c4a5d 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/thread/windows.rs @@ -1,14 +1,14 @@ use core::ffi::c_void; -use super::time::WaitableTimer; -use super::to_u16s; use crate::ffi::CStr; use crate::num::NonZero; use crate::os::windows::io::{AsRawHandle, HandleOrNull}; use crate::sys::handle::Handle; +use crate::sys::pal::time::WaitableTimer; +use crate::sys::pal::{dur2timeout, to_u16s}; use crate::sys::{c, stack_overflow}; use crate::sys_common::FromInner; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{io, ptr}; pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -62,24 +62,6 @@ impl Thread { } } - pub fn set_name(name: &CStr) { - if let Ok(utf8) = name.to_str() { - if let Ok(utf16) = to_u16s(utf8) { - unsafe { - // SAFETY: the vec returned by `to_u16s` ends with a zero value - Self::set_name_wide(&utf16) - } - }; - }; - } - - /// # Safety - /// - /// `name` must end with a zero value - pub unsafe fn set_name_wide(name: &[u16]) { - unsafe { c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()) }; - } - pub fn join(self) { let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; if rc == c::WAIT_FAILED { @@ -87,37 +69,6 @@ impl Thread { } } - pub fn yield_now() { - // This function will return 0 if there are no other threads to execute, - // but this also means that the yield was useless so this isn't really a - // case that needs to be worried about. - unsafe { - c::SwitchToThread(); - } - } - - pub fn sleep(dur: Duration) { - fn high_precision_sleep(dur: Duration) -> Result<(), ()> { - let timer = WaitableTimer::high_resolution()?; - timer.set(dur)?; - timer.wait() - } - // Attempt to use high-precision sleep (Windows 10, version 1803+). - // On error fallback to the standard `Sleep` function. - // Also preserves the zero duration behavior of `Sleep`. - if dur.is_zero() || high_precision_sleep(dur).is_err() { - unsafe { c::Sleep(super::dur2timeout(dur)) } - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn handle(&self) -> &Handle { &self.handle } @@ -127,14 +78,6 @@ impl Thread { } } -pub(crate) fn current_os_id() -> Option { - // SAFETY: FFI call with no preconditions. - let id: u32 = unsafe { c::GetCurrentThreadId() }; - - // A return value of 0 indicates failed lookup. - if id == 0 { None } else { Some(id.into()) } -} - pub fn available_parallelism() -> io::Result> { let res = unsafe { let mut sysinfo: c::SYSTEM_INFO = crate::mem::zeroed(); @@ -146,3 +89,52 @@ pub fn available_parallelism() -> io::Result> { cpus => Ok(unsafe { NonZero::new_unchecked(cpus) }), } } + +pub fn current_os_id() -> Option { + // SAFETY: FFI call with no preconditions. + let id: u32 = unsafe { c::GetCurrentThreadId() }; + + // A return value of 0 indicates failed lookup. + if id == 0 { None } else { Some(id.into()) } +} + +pub fn set_name(name: &CStr) { + if let Ok(utf8) = name.to_str() { + if let Ok(utf16) = to_u16s(utf8) { + unsafe { + // SAFETY: the vec returned by `to_u16s` ends with a zero value + set_name_wide(&utf16) + } + }; + }; +} + +/// # Safety +/// +/// `name` must end with a zero value +pub unsafe fn set_name_wide(name: &[u16]) { + unsafe { c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()) }; +} + +pub fn sleep(dur: Duration) { + fn high_precision_sleep(dur: Duration) -> Result<(), ()> { + let timer = WaitableTimer::high_resolution()?; + timer.set(dur)?; + timer.wait() + } + // Attempt to use high-precision sleep (Windows 10, version 1803+). + // On error fallback to the standard `Sleep` function. + // Also preserves the zero duration behavior of `Sleep`. + if dur.is_zero() || high_precision_sleep(dur).is_err() { + unsafe { c::Sleep(dur2timeout(dur)) } + } +} + +pub fn yield_now() { + // This function will return 0 if there are no other threads to execute, + // but this also means that the yield was useless so this isn't really a + // case that needs to be worried about. + unsafe { + c::SwitchToThread(); + } +} diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/thread/xous.rs similarity index 79% rename from library/std/src/sys/pal/xous/thread.rs rename to library/std/src/sys/thread/xous.rs index 92803c94c6e70..133e15a0928c6 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/thread/xous.rs @@ -1,6 +1,5 @@ use core::arch::asm; -use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::os::xous::ffi::{ @@ -8,7 +7,7 @@ use crate::os::xous::ffi::{ map_memory, update_memory_flags, }; use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; -use crate::time::{Duration, Instant}; +use crate::time::Duration; pub struct Thread { tid: ThreadId, @@ -110,46 +109,29 @@ impl Thread { Ok(Thread { tid }) } - pub fn yield_now() { - do_yield(); - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - // Because the sleep server works on units of `usized milliseconds`, split - // the messages up into these chunks. This means we may run into issues - // if you try to sleep a thread for more than 49 days on a 32-bit system. - let mut millis = dur.as_millis(); - while millis > 0 { - let sleep_duration = - if millis > (usize::MAX as _) { usize::MAX } else { millis as usize }; - blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into()) - .expect("failed to send message to ticktimer server"); - millis -= sleep_duration as u128; - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { join_thread(self.tid).unwrap(); } } -pub(crate) fn current_os_id() -> Option { - None -} - pub fn available_parallelism() -> io::Result> { // We're unicore right now. Ok(unsafe { NonZero::new_unchecked(1) }) } + +pub fn yield_now() { + do_yield(); +} + +pub fn sleep(dur: Duration) { + // Because the sleep server works on units of `usized milliseconds`, split + // the messages up into these chunks. This means we may run into issues + // if you try to sleep a thread for more than 49 days on a 32-bit system. + let mut millis = dur.as_millis(); + while millis > 0 { + let sleep_duration = if millis > (usize::MAX as _) { usize::MAX } else { millis as usize }; + blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into()) + .expect("failed to send message to ticktimer server"); + millis -= sleep_duration as u128; + } +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index b6059c28cec7d..97b506c1b8fe3 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -550,7 +550,7 @@ impl Builder { } if let Some(name) = their_thread.cname() { - imp::Thread::set_name(name); + imp::set_name(name); } let f = f.into_inner(); @@ -763,7 +763,7 @@ where /// [`Mutex`]: crate::sync::Mutex #[stable(feature = "rust1", since = "1.0.0")] pub fn yield_now() { - imp::Thread::yield_now() + imp::yield_now() } /// Determines whether the current thread is unwinding because of panic. @@ -884,7 +884,7 @@ pub fn sleep_ms(ms: u32) { /// ``` #[stable(feature = "thread_sleep", since = "1.4.0")] pub fn sleep(dur: Duration) { - imp::Thread::sleep(dur) + imp::sleep(dur) } /// Puts the current thread to sleep until the specified deadline has passed. @@ -983,7 +983,7 @@ pub fn sleep(dur: Duration) { /// ``` #[unstable(feature = "thread_sleep_until", issue = "113752")] pub fn sleep_until(deadline: Instant) { - imp::Thread::sleep_until(deadline) + imp::sleep_until(deadline) } /// Used to ensure that `park` and `park_timeout` do not unwind, as that can diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 89a0ab7711ee2..229adf714598b 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1805,7 +1805,7 @@ mod snapshot { insta::assert_snapshot!( ctx.config("check") .path("compiler") - .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (75 crates)"); } #[test] @@ -1831,7 +1831,7 @@ mod snapshot { ctx.config("check") .path("compiler") .stage(1) - .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (75 crates)"); } #[test] @@ -1845,7 +1845,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [check] rustc 1 -> rustc 2 (74 crates) + [check] rustc 1 -> rustc 2 (75 crates) "); } @@ -1861,7 +1861,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [check] rustc 1 -> std 1 - [check] rustc 1 -> rustc 2 (74 crates) + [check] rustc 1 -> rustc 2 (75 crates) [check] rustc 1 -> rustc 2 [check] rustc 1 -> Rustdoc 2 [check] rustc 1 -> rustc_codegen_cranelift 2 @@ -1957,7 +1957,7 @@ mod snapshot { ctx.config("check") .paths(&["library", "compiler"]) .args(&args) - .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (75 crates)"); } #[test] diff --git a/src/doc/nomicon b/src/doc/nomicon index 57ed447366056..f17a018b99894 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 57ed4473660565d9357fcae176b358d7e8724ebf +Subproject commit f17a018b9989430967d1c58e9a12c51169abc744 diff --git a/src/doc/reference b/src/doc/reference index 89f67b3c1b904..b3ce60628c6f5 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 89f67b3c1b904cbcd9ed55e443d6fc67c8ca2769 +Subproject commit b3ce60628c6f55ab8ff3dba9f3d20203df1c0dee diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index ad27f82c18464..dd26bc8e726dc 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit ad27f82c18464525c761a4a8db2e01785da59e1f +Subproject commit dd26bc8e726dc2e73534c8972d4dccd1bed7495f diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index feafb41dc9905..0e06361024b9f 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -578,7 +578,7 @@ pub(super) fn write_code( } fn write_footer(out: &mut String, playground_button: Option<&str>) { - write_str(out, format_args_nl!("{}", playground_button.unwrap_or_default())); + write_str(out, format_args!("{}", playground_button.unwrap_or_default())); } /// How a span of text is classified. Mostly corresponds to token kinds. diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index f62eba4b3c195..9871066b9eb51 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -10,7 +10,6 @@ #![feature(box_patterns)] #![feature(debug_closure_helpers)] #![feature(file_buffered)] -#![feature(format_args_nl)] #![feature(if_let_guard)] #![feature(iter_advance_by)] #![feature(iter_intersperse)] diff --git a/src/tools/linkchecker/Cargo.toml b/src/tools/linkchecker/Cargo.toml index fb5bff3fe63ff..f0886e31b243f 100644 --- a/src/tools/linkchecker/Cargo.toml +++ b/src/tools/linkchecker/Cargo.toml @@ -10,3 +10,4 @@ path = "main.rs" [dependencies] regex = "1" html5ever = "0.29.0" +urlencoding = "2.1.3" diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 1dc45728c90cd..e07a0784cdb3a 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -232,18 +232,7 @@ enum FileEntry { type Cache = HashMap; fn small_url_encode(s: &str) -> String { - s.replace('<', "%3C") - .replace('>', "%3E") - .replace(' ', "%20") - .replace('?', "%3F") - .replace('\'', "%27") - .replace('&', "%26") - .replace(',', "%2C") - .replace(':', "%3A") - .replace(';', "%3B") - .replace('[', "%5B") - .replace(']', "%5D") - .replace('\"', "%22") + urlencoding::encode(s).to_string() } impl Checker { diff --git a/src/tools/linkchecker/tests/valid/inner/bar.html b/src/tools/linkchecker/tests/valid/inner/bar.html index 4b500d78b76e4..6ffda259c40eb 100644 --- a/src/tools/linkchecker/tests/valid/inner/bar.html +++ b/src/tools/linkchecker/tests/valid/inner/bar.html @@ -3,5 +3,8 @@

Bar

+ +

Bar

+ diff --git a/src/tools/linkchecker/tests/valid/inner/foo.html b/src/tools/linkchecker/tests/valid/inner/foo.html index 3c6a7483bcd46..f30bf71820519 100644 --- a/src/tools/linkchecker/tests/valid/inner/foo.html +++ b/src/tools/linkchecker/tests/valid/inner/foo.html @@ -8,7 +8,15 @@
external links not validated Redirect + + + + +

Local

+ +

Local

+ diff --git a/src/tools/linkchecker/tests/valid/inner/redir-target.html b/src/tools/linkchecker/tests/valid/inner/redir-target.html index bd59884a01ecf..ac1dec6d5b4aa 100644 --- a/src/tools/linkchecker/tests/valid/inner/redir-target.html +++ b/src/tools/linkchecker/tests/valid/inner/redir-target.html @@ -1,5 +1,8 @@

Redir

+ + +

Redir

diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr index bcec5557a3fa4..c7c1a769cdaa5 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _ = note: inside closure at tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr index 51533cd17a8fb..ae8b2b936a71f 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu = note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr index 63fc7ce346e0c..dfa9a6e258310 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu = note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr index d8a3e058da933..47a0ebdcfef82 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: trying to join a detached thread - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here @@ -7,7 +7,7 @@ LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle( = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr index 079f01c0e5493..3b1181c92fd9a 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr @@ -9,13 +9,13 @@ LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJ = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr index 70de94f56bac3..6eefa2da1d819 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0 = note: inside closure at tests/fail-dep/concurrency/windows_join_self.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr index 4f3a56fef8262..6dd6c36ab6560 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr index 5045badaa870f..3154197b95ed4 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr index 3713c8da392b7..1af44c107ff85 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr @@ -5,13 +5,13 @@ error: the evaluated program deadlocked = note: BACKTRACE on thread `unnamed-ID`: error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr index 47fc889b00106..b85470225c692 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr index 99d242ec7dafa..bbd2ab1c4d8de 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr index f766500d331da..c3cc206817315 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/tests/ui/consts/const-eval/ptr_fragments.rs b/tests/ui/consts/const-eval/ptr_fragments.rs index 04dcbe55590e9..7dc5870f89e2b 100644 --- a/tests/ui/consts/const-eval/ptr_fragments.rs +++ b/tests/ui/consts/const-eval/ptr_fragments.rs @@ -1,5 +1,6 @@ //! Test that various operations involving pointer fragments work as expected. //@ run-pass +//@ ignore-test: disabled due to use std::mem::{self, MaybeUninit, transmute}; use std::ptr; diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs index e2f3f51b08638..aed33d7ed8d31 100644 --- a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs +++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs @@ -1,4 +1,5 @@ //! Test that we properly error when there is a pointer fragment in the final value. +//@ ignore-test: disabled due to use std::{mem::{self, MaybeUninit}, ptr}; diff --git a/tests/ui/consts/const-eval/ptr_fragments_mixed.rs b/tests/ui/consts/const-eval/ptr_fragments_mixed.rs new file mode 100644 index 0000000000000..79a42820f501e --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments_mixed.rs @@ -0,0 +1,28 @@ +//! This mixes fragments from different pointers to the same allocarion, in a way +//! that we should not accept. See . +static A: u8 = 123; + +const HALF_PTR: usize = std::mem::size_of::<*const ()>() / 2; + +const fn mix_ptr() -> *const u8 { + unsafe { + let x: *const u8 = &raw const A; + let mut y = x.wrapping_add(usize::MAX / 4); + core::ptr::copy_nonoverlapping( + (&raw const x).cast::(), + (&raw mut y).cast::(), + HALF_PTR, + ); + y + } +} + +const APTR: *const u8 = mix_ptr(); //~ERROR: unable to read parts of a pointer + +fn main() { + let a = APTR; + println!("{a:p}"); + let b = mix_ptr(); + println!("{b:p}"); + assert_eq!(a, b); +} diff --git a/tests/ui/consts/const-eval/ptr_fragments_mixed.stderr b/tests/ui/consts/const-eval/ptr_fragments_mixed.stderr new file mode 100644 index 0000000000000..9e991ab7a7d38 --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments_mixed.stderr @@ -0,0 +1,23 @@ +error[E0080]: unable to read parts of a pointer from memory at ALLOC0 + --> $DIR/ptr_fragments_mixed.rs:20:25 + | +LL | const APTR: *const u8 = mix_ptr(); + | ^^^^^^^^^ evaluation of `APTR` failed inside this call + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported +note: inside `mix_ptr` + --> $DIR/ptr_fragments_mixed.rs:11:9 + | +LL | / core::ptr::copy_nonoverlapping( +LL | | (&raw const x).cast::(), +LL | | (&raw mut y).cast::(), +LL | | HALF_PTR, +LL | | ); + | |_________^ +note: inside `std::ptr::copy_nonoverlapping::` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/read_partial_ptr.rs b/tests/ui/consts/const-eval/read_partial_ptr.rs index bccef9c0bc6ca..c153e274e41f6 100644 --- a/tests/ui/consts/const-eval/read_partial_ptr.rs +++ b/tests/ui/consts/const-eval/read_partial_ptr.rs @@ -1,4 +1,5 @@ //! Ensure we error when trying to load from a pointer whose provenance has been messed with. +//@ ignore-test: disabled due to const PARTIAL_OVERWRITE: () = { let mut p = &42; diff --git a/tests/ui/limits/huge-array-simple-64.stderr b/tests/ui/limits/huge-array-simple-64.full-debuginfo.stderr similarity index 83% rename from tests/ui/limits/huge-array-simple-64.stderr rename to tests/ui/limits/huge-array-simple-64.full-debuginfo.stderr index 46df288d4f78a..8ce93ab1884ea 100644 --- a/tests/ui/limits/huge-array-simple-64.stderr +++ b/tests/ui/limits/huge-array-simple-64.full-debuginfo.stderr @@ -1,5 +1,5 @@ error: values of the type `[u8; 2305843011361177600]` are too big for the target architecture - --> $DIR/huge-array-simple-64.rs:7:9 + --> $DIR/huge-array-simple-64.rs:12:9 | LL | let _fat: [u8; (1<<61)+(1<<31)] = | ^^^^ diff --git a/tests/ui/limits/huge-array-simple-64.no-debuginfo.stderr b/tests/ui/limits/huge-array-simple-64.no-debuginfo.stderr new file mode 100644 index 0000000000000..8ce93ab1884ea --- /dev/null +++ b/tests/ui/limits/huge-array-simple-64.no-debuginfo.stderr @@ -0,0 +1,8 @@ +error: values of the type `[u8; 2305843011361177600]` are too big for the target architecture + --> $DIR/huge-array-simple-64.rs:12:9 + | +LL | let _fat: [u8; (1<<61)+(1<<31)] = + | ^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/limits/huge-array-simple-64.rs b/tests/ui/limits/huge-array-simple-64.rs index d2838e0d41e70..0c000940062fc 100644 --- a/tests/ui/limits/huge-array-simple-64.rs +++ b/tests/ui/limits/huge-array-simple-64.rs @@ -1,3 +1,8 @@ +// FIXME(#61117): Remove revisions once x86_64-gnu-debug CI job sets rust.debuginfo-level-tests=2 +// NOTE: The .stderr for both revisions shall be identical. +//@ revisions: no-debuginfo full-debuginfo +//@[no-debuginfo] compile-flags: -Cdebuginfo=0 +//@[full-debuginfo] compile-flags: -Cdebuginfo=2 //@ build-fail //@ ignore-32bit diff --git a/tests/ui/limits/huge-array.stderr b/tests/ui/limits/huge-array.full-debuginfo.stderr similarity index 87% rename from tests/ui/limits/huge-array.stderr rename to tests/ui/limits/huge-array.full-debuginfo.stderr index ce0c0d650c2f8..0a9c8c67ce9a4 100644 --- a/tests/ui/limits/huge-array.stderr +++ b/tests/ui/limits/huge-array.full-debuginfo.stderr @@ -1,5 +1,5 @@ error: values of the type `[[u8; 1518599999]; 1518600000]` are too big for the target architecture - --> $DIR/huge-array.rs:4:9 + --> $DIR/huge-array.rs:9:9 | LL | let s: [T; 1518600000] = [t; 1518600000]; | ^ diff --git a/tests/ui/limits/huge-array.no-debuginfo.stderr b/tests/ui/limits/huge-array.no-debuginfo.stderr new file mode 100644 index 0000000000000..0a9c8c67ce9a4 --- /dev/null +++ b/tests/ui/limits/huge-array.no-debuginfo.stderr @@ -0,0 +1,8 @@ +error: values of the type `[[u8; 1518599999]; 1518600000]` are too big for the target architecture + --> $DIR/huge-array.rs:9:9 + | +LL | let s: [T; 1518600000] = [t; 1518600000]; + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/limits/huge-array.rs b/tests/ui/limits/huge-array.rs index 97cfd1ff8fb2c..0cf5902769472 100644 --- a/tests/ui/limits/huge-array.rs +++ b/tests/ui/limits/huge-array.rs @@ -1,3 +1,8 @@ +// FIXME(#61117): Remove revisions once x86_64-gnu-debug CI job sets rust.debuginfo-level-tests=2 +// NOTE: The .stderr for both revisions shall be identical. +//@ revisions: no-debuginfo full-debuginfo +//@[no-debuginfo] compile-flags: -Cdebuginfo=0 +//@[full-debuginfo] compile-flags: -Cdebuginfo=2 //@ build-fail fn generic(t: T) { diff --git a/tests/ui/limits/issue-15919-64.stderr b/tests/ui/limits/issue-15919-64.full-debuginfo.stderr similarity index 85% rename from tests/ui/limits/issue-15919-64.stderr rename to tests/ui/limits/issue-15919-64.full-debuginfo.stderr index cd443f2065bbd..54434675d2506 100644 --- a/tests/ui/limits/issue-15919-64.stderr +++ b/tests/ui/limits/issue-15919-64.full-debuginfo.stderr @@ -1,5 +1,5 @@ error: values of the type `[usize; usize::MAX]` are too big for the target architecture - --> $DIR/issue-15919-64.rs:5:9 + --> $DIR/issue-15919-64.rs:10:9 | LL | let x = [0usize; 0xffff_ffff_ffff_ffff]; | ^ diff --git a/tests/ui/limits/issue-15919-64.no-debuginfo.stderr b/tests/ui/limits/issue-15919-64.no-debuginfo.stderr new file mode 100644 index 0000000000000..54434675d2506 --- /dev/null +++ b/tests/ui/limits/issue-15919-64.no-debuginfo.stderr @@ -0,0 +1,8 @@ +error: values of the type `[usize; usize::MAX]` are too big for the target architecture + --> $DIR/issue-15919-64.rs:10:9 + | +LL | let x = [0usize; 0xffff_ffff_ffff_ffff]; + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/limits/issue-15919-64.rs b/tests/ui/limits/issue-15919-64.rs index 7e6200882a966..c1eee1d95ba17 100644 --- a/tests/ui/limits/issue-15919-64.rs +++ b/tests/ui/limits/issue-15919-64.rs @@ -1,3 +1,8 @@ +// FIXME(#61117): Remove revisions once x86_64-gnu-debug CI job sets rust.debuginfo-level-tests=2 +// NOTE: The .stderr for both revisions shall be identical. +//@ revisions: no-debuginfo full-debuginfo +//@[no-debuginfo] compile-flags: -Cdebuginfo=0 +//@[full-debuginfo] compile-flags: -Cdebuginfo=2 //@ build-fail //@ ignore-32bit