Compare commits

...

4 Commits

Author SHA1 Message Date
72989d86a8 Add more from the std 2026-03-17 22:51:36 +01:00
9958b23c89 Add more from the std 2026-03-17 21:33:34 +01:00
56ad115e58 Add a small part of the real rust std 2026-03-17 20:13:39 +01:00
56a00d0403 Sync computers 2026-03-17 18:29:00 +01:00
85 changed files with 17211 additions and 258 deletions

View File

@@ -1,6 +1,6 @@
[workspace]
resolver = "3"
members = ["crates/bytes-struct","crates/io","crates/os-std", "crates/shared", "user/*"]
members = ["crates/bytes-struct","crates/io","crates/std", "crates/shared", "user/*"]
[package]
name = "kernel-rust"

View File

@@ -1 +0,0 @@
pub use io::SeekFrom;

View File

@@ -1,62 +0,0 @@
#![no_std]
extern crate alloc;
pub mod io;
pub mod prelude;
pub use shared::fs;
pub use shared::syscall;
#[macro_export]
macro_rules! custom_std_setup {
() => {
use $crate::prelude::*;
extern crate alloc;
struct GlobalAllocator;
#[global_allocator]
static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
unsafe impl core::alloc::GlobalAlloc for GlobalAllocator {
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
$crate::syscall::alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
$crate::syscall::dealloc(ptr, layout)
}
}
#[panic_handler]
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
// TODO print
loop {}
}
#[unsafe(no_mangle)]
pub extern "C" fn _start() {
main()
}
};
}
#[macro_export]
macro_rules! print {
($($args:expr),*) => {
$crate::syscall::write_string_temp(&format!($($args),*))
};
}
#[macro_export]
macro_rules! println {
() => {
$crate::print!("");
// $crate::print!("\n\r");
};
($($args:expr),*) => {
$crate::print!($($args),*);
// $crate::println!();
};
}

View File

@@ -1,5 +0,0 @@
pub use crate::print;
pub use crate::println;
pub use alloc::format;
pub use alloc::string::String;
pub use alloc::vec;

View File

@@ -1,3 +1,7 @@
use io::{IoBase, Read, Write};
use crate::syscall;
#[derive(Debug)]
pub struct File {
fd: u64,
@@ -6,7 +10,7 @@ pub struct File {
impl File {
/// # Safety
/// The file descriptor must be valid
pub unsafe fn new(fd: u64) -> Self {
pub unsafe fn from_raw_fd(fd: u64) -> Self {
Self { fd }
}
@@ -14,3 +18,23 @@ impl File {
self.fd
}
}
impl IoBase for File {
type Error = ();
}
impl Read for File {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
Ok(syscall::read(self.as_fd(), buf) as usize)
}
}
impl Write for File {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
Ok(syscall::write(self.as_fd(), buf) as usize)
}
fn flush(&mut self) -> Result<(), Self::Error> {
todo!()
}
}

View File

@@ -10,9 +10,12 @@ pub enum SysCall {
Read = 0,
Write = 1,
Open = 2,
Close = 3,
Seek = 8,
Alloc = 40,
Dealloc = 41,
Spawn = 58,
ExecVE = 59,
Exit = 60,
NanoSleep = 101,
WriteIntTemp = 998,
@@ -26,9 +29,12 @@ impl From<u64> for SysCall {
0 => SysCall::Read,
1 => SysCall::Write,
2 => SysCall::Open,
3 => SysCall::Close,
8 => SysCall::Seek,
40 => SysCall::Alloc,
41 => SysCall::Dealloc,
58 => SysCall::Spawn,
59 => SysCall::ExecVE,
60 => SysCall::Exit,
101 => SysCall::NanoSleep,
998 => SysCall::WriteIntTemp,
@@ -143,30 +149,53 @@ pub fn open<P: AsRef<Path>>(path: P) -> File {
let ptr = path_str.as_ptr();
let size = path_str.len();
let (fd, ..) = syscall!(SysCall::Open, ptr as u64, size as u64);
File::new(fd)
File::from_raw_fd(fd)
}
}
pub fn write(file: &mut File, buf: &[u8]) {
pub fn close(file_descriptor: u64) {
unsafe {
syscall!(SysCall::Close, file_descriptor);
}
}
pub fn write(file_descriptor: u64, buf: &[u8]) -> u64 {
unsafe {
let ptr = buf.as_ptr();
let size = buf.len();
syscall!(SysCall::Write, file.as_fd(), ptr as u64, size as u64);
let (len, ..) = syscall!(SysCall::Write, file_descriptor, ptr as u64, size as u64);
len
}
}
pub fn read(file: &mut File, buf: &mut [u8]) {
pub fn read(file_descriptor: u64, buf: &mut [u8]) -> u64 {
unsafe {
let ptr = buf.as_ptr();
let size = buf.len();
syscall!(SysCall::Read, file.as_fd(), ptr as u64, size as u64);
let (len, ..) = syscall!(SysCall::Read, file_descriptor, ptr as u64, size as u64);
len
}
}
pub fn seek(file: &mut File, seek: SeekFrom) {
pub fn seek(file_descriptor: u64, seek: SeekFrom) {
unsafe {
let (discriminant, value) = match seek {
SeekFrom::Start(v) => (0, v),
SeekFrom::End(v) => (1, v as u64),
SeekFrom::Current(v) => (2, v as u64),
};
syscall!(SysCall::Seek, file.as_fd(), discriminant, value);
syscall!(SysCall::Seek, file_descriptor, discriminant, value);
}
}
pub fn spawn<P: AsRef<Path>>(path: P) {
unsafe {
let path_str = path.as_ref().as_str();
let ptr = path_str.as_ptr();
let size = path_str.len();
syscall!(SysCall::Spawn, ptr as u64, size as u64);
}
}
pub fn execve<P: AsRef<Path>>(path: P) {
unsafe {
let path_str = path.as_ref().as_str();
let ptr = path_str.as_ptr();
let size = path_str.len();
syscall!(SysCall::ExecVE, ptr as u64, size as u64);
}
}

View File

@@ -1,5 +1,5 @@
[package]
name = "os-std"
name = "std"
version = "0.1.0"
edition = "2024"

490
crates/std/src/alloc.rs Normal file
View File

@@ -0,0 +1,490 @@
//! Memory allocation APIs.
//!
//! In a given program, the standard library has one “global” memory allocator
//! that is used for example by `Box<T>` and `Vec<T>`.
//!
//! Currently the default global allocator is unspecified. Libraries, however,
//! like `cdylib`s and `staticlib`s are guaranteed to use the [`System`] by
//! default.
//!
//! # The `#[global_allocator]` attribute
//!
//! This attribute allows configuring the choice of global allocator.
//! You can use this to implement a completely custom global allocator
//! to route all[^system-alloc] default allocation requests to a custom object.
//!
//! ```rust
//! use std::alloc::{GlobalAlloc, System, Layout};
//!
//! struct MyAllocator;
//!
//! unsafe impl GlobalAlloc for MyAllocator {
//! unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
//! unsafe { System.alloc(layout) }
//! }
//!
//! unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
//! unsafe { System.dealloc(ptr, layout) }
//! }
//! }
//!
//! #[global_allocator]
//! static GLOBAL: MyAllocator = MyAllocator;
//!
//! fn main() {
//! // This `Vec` will allocate memory through `GLOBAL` above
//! let mut v = Vec::new();
//! v.push(1);
//! }
//! ```
//!
//! The attribute is used on a `static` item whose type implements the
//! [`GlobalAlloc`] trait. This type can be provided by an external library:
//!
//! ```rust,ignore (demonstrates crates.io usage)
//! use jemallocator::Jemalloc;
//!
//! #[global_allocator]
//! static GLOBAL: Jemalloc = Jemalloc;
//!
//! fn main() {}
//! ```
//!
//! The `#[global_allocator]` can only be used once in a crate
//! or its recursive dependencies.
//!
//! [^system-alloc]: Note that the Rust standard library internals may still
//! directly call [`System`] when necessary (for example for the runtime
//! support typically required to implement a global allocator, see [re-entrance] on [`GlobalAlloc`]
//! for more details).
//!
//! [re-entrance]: trait.GlobalAlloc.html#re-entrance
#![deny(unsafe_op_in_unsafe_fn)]
#![stable(feature = "alloc_module", since = "1.28.0")]
use core::ptr::NonNull;
use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
use core::{hint, mem, ptr};
#[stable(feature = "alloc_module", since = "1.28.0")]
#[doc(inline)]
pub use alloc_crate::alloc::*;
/// The default memory allocator provided by the operating system.
///
/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows,
/// plus related functions. However, it is not valid to mix use of the backing
/// system allocator with `System`, as this implementation may include extra
/// work, such as to serve alignment requests greater than the alignment
/// provided directly by the backing system allocator.
///
/// This type implements the [`GlobalAlloc`] trait. Currently the default
/// global allocator is unspecified. Libraries, however, like `cdylib`s and
/// `staticlib`s are guaranteed to use the [`System`] by default and as such
/// work as if they had this definition:
///
/// ```rust
/// use std::alloc::System;
///
/// #[global_allocator]
/// static A: System = System;
///
/// fn main() {
/// let a = Box::new(4); // Allocates from the system allocator.
/// println!("{a}");
/// }
/// ```
///
/// You can also define your own wrapper around `System` if you'd like, such as
/// keeping track of the number of all bytes allocated:
///
/// ```rust
/// use std::alloc::{System, GlobalAlloc, Layout};
/// use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
///
/// struct Counter;
///
/// static ALLOCATED: AtomicUsize = AtomicUsize::new(0);
///
/// unsafe impl GlobalAlloc for Counter {
/// unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
/// let ret = unsafe { System.alloc(layout) };
/// if !ret.is_null() {
/// ALLOCATED.fetch_add(layout.size(), Relaxed);
/// }
/// ret
/// }
///
/// unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
/// unsafe { System.dealloc(ptr, layout); }
/// ALLOCATED.fetch_sub(layout.size(), Relaxed);
/// }
/// }
///
/// #[global_allocator]
/// static A: Counter = Counter;
///
/// fn main() {
/// println!("allocated bytes before main: {}", ALLOCATED.load(Relaxed));
/// }
/// ```
///
/// It can also be used directly to allocate memory independently of whatever
/// global allocator has been selected for a Rust program. For example if a Rust
/// program opts in to using jemalloc as the global allocator, `System` will
/// still allocate memory using `malloc` and `HeapAlloc`.
#[stable(feature = "alloc_system_type", since = "1.28.0")]
#[derive(Debug, Default, Copy, Clone)]
pub struct System;
impl System {
#[inline]
fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocError> {
match layout.size() {
0 => Ok(NonNull::slice_from_raw_parts(layout.dangling_ptr(), 0)),
// SAFETY: `layout` is non-zero in size,
size => unsafe {
let raw_ptr = if zeroed {
GlobalAlloc::alloc_zeroed(self, layout)
} else {
GlobalAlloc::alloc(self, layout)
};
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
Ok(NonNull::slice_from_raw_parts(ptr, size))
},
}
}
// SAFETY: Same as `Allocator::grow`
#[inline]
unsafe fn grow_impl(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
zeroed: bool,
) -> Result<NonNull<[u8]>, AllocError> {
debug_assert!(
new_layout.size() >= old_layout.size(),
"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
);
match old_layout.size() {
0 => self.alloc_impl(new_layout, zeroed),
// SAFETY: `new_size` is non-zero as `new_size` is greater than or equal to `old_size`
// as required by safety conditions and the `old_size == 0` case was handled in the
// previous match arm. Other conditions must be upheld by the caller
old_size if old_layout.align() == new_layout.align() => unsafe {
let new_size = new_layout.size();
// `realloc` probably checks for `new_size >= old_layout.size()` or something similar.
hint::assert_unchecked(new_size >= old_layout.size());
let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
if zeroed {
raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
}
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
},
// SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`,
// both the old and new memory allocation are valid for reads and writes for `old_size`
// bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
// `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
// for `dealloc` must be upheld by the caller.
old_size => unsafe {
let new_ptr = self.alloc_impl(new_layout, zeroed)?;
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_size);
Allocator::deallocate(self, ptr, old_layout);
Ok(new_ptr)
},
}
}
}
// The Allocator impl checks the layout size to be non-zero and forwards to the GlobalAlloc impl,
// which is in `std::sys::*::alloc`.
#[unstable(feature = "allocator_api", issue = "32838")]
unsafe impl Allocator for System {
#[inline]
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
self.alloc_impl(layout, false)
}
#[inline]
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
self.alloc_impl(layout, true)
}
#[inline]
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
if layout.size() != 0 {
// SAFETY: `layout` is non-zero in size,
// other conditions must be upheld by the caller
unsafe { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) }
}
}
#[inline]
unsafe fn grow(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
// SAFETY: all conditions must be upheld by the caller
unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
}
#[inline]
unsafe fn grow_zeroed(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
// SAFETY: all conditions must be upheld by the caller
unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
}
#[inline]
unsafe fn shrink(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
debug_assert!(
new_layout.size() <= old_layout.size(),
"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
);
match new_layout.size() {
// SAFETY: conditions must be upheld by the caller
0 => unsafe {
Allocator::deallocate(self, ptr, old_layout);
Ok(NonNull::slice_from_raw_parts(new_layout.dangling_ptr(), 0))
},
// SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller
new_size if old_layout.align() == new_layout.align() => unsafe {
// `realloc` probably checks for `new_size <= old_layout.size()` or something similar.
hint::assert_unchecked(new_size <= old_layout.size());
let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size);
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
},
// SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`,
// both the old and new memory allocation are valid for reads and writes for `new_size`
// bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
// `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
// for `dealloc` must be upheld by the caller.
new_size => unsafe {
let new_ptr = Allocator::allocate(self, new_layout)?;
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size);
Allocator::deallocate(self, ptr, old_layout);
Ok(new_ptr)
},
}
}
}
static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
/// Registers a custom allocation error hook, replacing any that was previously registered.
///
/// The allocation error hook is invoked when an infallible memory allocation fails — that is,
/// as a consequence of calling [`handle_alloc_error`] — before the runtime aborts.
///
/// The allocation error hook is a global resource. [`take_alloc_error_hook`] may be used to
/// retrieve a previously registered hook and wrap or discard it.
///
/// # What the provided `hook` function should expect
///
/// The hook function is provided with a [`Layout`] struct which contains information
/// about the allocation that failed.
///
/// The hook function may choose to panic or abort; in the event that it returns normally, this
/// will cause an immediate abort.
///
/// Since [`take_alloc_error_hook`] is a safe function that allows retrieving the hook, the hook
/// function must be _sound_ to call even if no memory allocations were attempted.
///
/// # The default hook
///
/// The default hook, used if [`set_alloc_error_hook`] is never called, prints a message to
/// standard error (and then returns, causing the runtime to abort the process).
/// Compiler options may cause it to panic instead, and the default behavior may be changed
/// to panicking in future versions of Rust.
///
/// # Examples
///
/// ```
/// #![feature(alloc_error_hook)]
///
/// use std::alloc::{Layout, set_alloc_error_hook};
///
/// fn custom_alloc_error_hook(layout: Layout) {
/// panic!("memory allocation of {} bytes failed", layout.size());
/// }
///
/// set_alloc_error_hook(custom_alloc_error_hook);
/// ```
#[unstable(feature = "alloc_error_hook", issue = "51245")]
pub fn set_alloc_error_hook(hook: fn(Layout)) {
HOOK.store(hook as *mut (), Ordering::Release);
}
// /// Unregisters the current allocation error hook, returning it.
// ///
// /// *See also the function [`set_alloc_error_hook`].*
// ///
// /// If no custom hook is registered, the default hook will be returned.
// #[unstable(feature = "alloc_error_hook", issue = "51245")]
// pub fn take_alloc_error_hook() -> fn(Layout) {
// let hook = HOOK.swap(ptr::null_mut(), Ordering::Acquire);
// if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }
// }
// #[optimize(size)]
// fn default_alloc_error_hook(layout: Layout) {
// if cfg!(panic = "immediate-abort") {
// return;
// }
// // This is the default path taken on OOM, and the only path taken on stable with std.
// // Crucially, it does *not* call any user-defined code, and therefore users do not have to
// // worry about allocation failure causing reentrancy issues. That makes it different from
// // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error
// // handler that is called when there is no `#[alloc_error_handler]`), which triggers a
// // regular panic and thus can invoke a user-defined panic hook, executing arbitrary
// // user-defined code.
// static PREV_ALLOC_FAILURE: AtomicBool = AtomicBool::new(false);
// if PREV_ALLOC_FAILURE.swap(true, Ordering::Relaxed) {
// // Don't try to print a backtrace if a previous alloc error happened. This likely means
// // there is not enough memory to print a backtrace, although it could also mean that two
// // threads concurrently run out of memory.
// rtprintpanic!(
// "memory allocation of {} bytes failed\nskipping backtrace printing to avoid potential recursion\n",
// layout.size()
// );
// return;
// } else {
// rtprintpanic!("memory allocation of {} bytes failed\n", layout.size());
// }
// let Some(mut out) = crate::sys::stdio::panic_output() else {
// return;
// };
// // Use a lock to prevent mixed output in multithreading context.
// // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows.
// // Make sure to not take this lock until after checking PREV_ALLOC_FAILURE to avoid deadlocks
// // when there is too little memory to print a backtrace.
// let mut lock = crate::sys::backtrace::lock();
// match crate::panic::get_backtrace_style() {
// Some(crate::panic::BacktraceStyle::Short) => {
// drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Short))
// }
// Some(crate::panic::BacktraceStyle::Full) => {
// drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Full))
// }
// Some(crate::panic::BacktraceStyle::Off) => {
// use crate::io::Write;
// let _ = writeln!(
// out,
// "note: run with `RUST_BACKTRACE=1` environment variable to display a \
// backtrace"
// );
// if cfg!(miri) {
// let _ = writeln!(
// out,
// "note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \
// for the environment variable to have an effect"
// );
// }
// }
// // If backtraces aren't supported or are forced-off, do nothing.
// None => {}
// }
// }
// #[cfg(not(test))]
// #[doc(hidden)]
// #[alloc_error_handler]
// #[unstable(feature = "alloc_internals", issue = "none")]
// pub fn rust_oom(layout: Layout) -> ! {
// crate::sys::backtrace::__rust_end_short_backtrace(|| {
// let hook = HOOK.load(Ordering::Acquire);
// let hook: fn(Layout) =
// if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } };
// hook(layout);
// crate::process::abort()
// })
// }
#[cfg(not(test))]
#[doc(hidden)]
#[allow(unused_attributes)]
#[unstable(feature = "alloc_internals", issue = "none")]
pub mod __default_lib_allocator {
use super::{GlobalAlloc, Layout, System};
// These magic symbol names are used as a fallback for implementing the
// `__rust_alloc` etc symbols (see `src/liballoc/alloc.rs`) when there is
// no `#[global_allocator]` attribute.
// for symbol names src/librustc_ast/expand/allocator.rs
// for signatures src/librustc_allocator/lib.rs
// linkage directives are provided as part of the current compiler allocator
// ABI
#[rustc_std_internal_symbol]
pub unsafe extern "C" fn __rdl_alloc(size: usize, align: usize) -> *mut u8 {
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
// `GlobalAlloc::alloc`.
unsafe {
let layout = Layout::from_size_align_unchecked(size, align);
System.alloc(layout)
}
}
#[rustc_std_internal_symbol]
pub unsafe extern "C" fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) {
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
// `GlobalAlloc::dealloc`.
unsafe { System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) }
}
#[rustc_std_internal_symbol]
pub unsafe extern "C" fn __rdl_realloc(
ptr: *mut u8,
old_size: usize,
align: usize,
new_size: usize,
) -> *mut u8 {
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
// `GlobalAlloc::realloc`.
unsafe {
let old_layout = Layout::from_size_align_unchecked(old_size, align);
System.realloc(ptr, old_layout, new_size)
}
}
#[rustc_std_internal_symbol]
pub unsafe extern "C" fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
// SAFETY: see the guarantees expected by `Layout::from_size_align` and
// `GlobalAlloc::alloc_zeroed`.
unsafe {
let layout = Layout::from_size_align_unchecked(size, align);
System.alloc_zeroed(layout)
}
}
}

4
crates/std/src/error.rs Normal file
View File

@@ -0,0 +1,4 @@
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::error::Error;
#[unstable(feature = "error_generic_member_access", issue = "99301")]
pub use core::error::{Request, request_ref, request_value};

View File

@@ -0,0 +1,14 @@
//! [`CStr`], [`CString`], and related types.
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
pub use alloc_crate::ffi::c_str::FromVecWithNulError;
#[stable(feature = "cstring_into", since = "1.7.0")]
pub use alloc_crate::ffi::c_str::IntoStringError;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::ffi::c_str::{CString, NulError};
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::ffi::c_str::CStr;
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
pub use core::ffi::c_str::FromBytesUntilNulError;
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
pub use core::ffi::c_str::FromBytesWithNulError;

207
crates/std/src/ffi/mod.rs Normal file
View File

@@ -0,0 +1,207 @@
//! Utilities related to FFI bindings.
//!
//! This module provides utilities to handle data across non-Rust
//! interfaces, like other programming languages and the underlying
//! operating system. It is mainly of use for FFI (Foreign Function
//! Interface) bindings and code that needs to exchange C-like strings
//! with other languages.
//!
//! # Overview
//!
//! Rust represents owned strings with the [`String`] type, and
//! borrowed slices of strings with the [`str`] primitive. Both are
//! always in UTF-8 encoding, and may contain nul bytes in the middle,
//! i.e., if you look at the bytes that make up the string, there may
//! be a `\0` among them. Both `String` and `str` store their length
//! explicitly; there are no nul terminators at the end of strings
//! like in C.
//!
//! C strings are different from Rust strings:
//!
//! * **Encodings** - Rust strings are UTF-8, but C strings may use
//! other encodings. If you are using a string from C, you should
//! check its encoding explicitly, rather than just assuming that it
//! is UTF-8 like you can do in Rust.
//!
//! * **Character size** - C strings may use `char` or `wchar_t`-sized
//! characters; please **note** that C's `char` is different from Rust's.
//! The C standard leaves the actual sizes of those types open to
//! interpretation, but defines different APIs for strings made up of
//! each character type. Rust strings are always UTF-8, so different
//! Unicode characters will be encoded in a variable number of bytes
//! each. The Rust type [`char`] represents a '[Unicode scalar
//! value]', which is similar to, but not the same as, a '[Unicode
//! code point]'.
//!
//! * **Nul terminators and implicit string lengths** - Often, C
//! strings are nul-terminated, i.e., they have a `\0` character at the
//! end. The length of a string buffer is not stored, but has to be
//! calculated; to compute the length of a string, C code must
//! manually call a function like `strlen()` for `char`-based strings,
//! or `wcslen()` for `wchar_t`-based ones. Those functions return
//! the number of characters in the string excluding the nul
//! terminator, so the buffer length is really `len+1` characters.
//! Rust strings don't have a nul terminator; their length is always
//! stored and does not need to be calculated. While in Rust
//! accessing a string's length is an *O*(1) operation (because the
//! length is stored); in C it is an *O*(*n*) operation because the
//! length needs to be computed by scanning the string for the nul
//! terminator.
//!
//! * **Internal nul characters** - When C strings have a nul
//! terminator character, this usually means that they cannot have nul
//! characters in the middle — a nul character would essentially
//! truncate the string. Rust strings *can* have nul characters in
//! the middle, because nul does not have to mark the end of the
//! string in Rust.
//!
//! # Representations of non-Rust strings
//!
//! [`CString`] and [`CStr`] are useful when you need to transfer
//! UTF-8 strings to and from languages with a C ABI, like Python.
//!
//! * **From Rust to C:** [`CString`] represents an owned, C-friendly
//! string: it is nul-terminated, and has no internal nul characters.
//! Rust code can create a [`CString`] out of a normal string (provided
//! that the string doesn't have nul characters in the middle), and
//! then use a variety of methods to obtain a raw <code>\*mut [u8]</code> that can
//! then be passed as an argument to functions which use the C
//! conventions for strings.
//!
//! * **From C to Rust:** [`CStr`] represents a borrowed C string; it
//! is what you would use to wrap a raw <code>\*const [u8]</code> that you got from
//! a C function. A [`CStr`] is guaranteed to be a nul-terminated array
//! of bytes. Once you have a [`CStr`], you can convert it to a Rust
//! <code>&[str]</code> if it's valid UTF-8, or lossily convert it by adding
//! replacement characters.
//!
//! [`OsString`] and [`OsStr`] are useful when you need to transfer
//! strings to and from the operating system itself, or when capturing
//! the output of external commands. Conversions between [`OsString`],
//! [`OsStr`] and Rust strings work similarly to those for [`CString`]
//! and [`CStr`].
//!
//! * [`OsString`] losslessly represents an owned platform string. However, this
//! representation is not necessarily in a form native to the platform.
//! In the Rust standard library, various APIs that transfer strings to/from the operating
//! system use [`OsString`] instead of plain strings. For example,
//! [`env::var_os()`] is used to query environment variables; it
//! returns an <code>[Option]<[OsString]></code>. If the environment variable
//! exists you will get a <code>[Some]\(os_string)</code>, which you can
//! *then* try to convert to a Rust string. This yields a [`Result`], so that
//! your code can detect errors in case the environment variable did
//! not in fact contain valid Unicode data.
//!
//! * [`OsStr`] losslessly represents a borrowed reference to a platform string.
//! However, this representation is not necessarily in a form native to the platform.
//! It can be converted into a UTF-8 Rust string slice in a similar way to
//! [`OsString`].
//!
//! # Conversions
//!
//! ## On Unix
//!
//! On Unix, [`OsStr`] implements the
//! <code>std::os::unix::ffi::[OsStrExt][unix.OsStrExt]</code> trait, which
//! augments it with two methods, [`from_bytes`] and [`as_bytes`].
//! These do inexpensive conversions from and to byte slices.
//!
//! Additionally, on Unix [`OsString`] implements the
//! <code>std::os::unix::ffi::[OsStringExt][unix.OsStringExt]</code> trait,
//! which provides [`from_vec`] and [`into_vec`] methods that consume
//! their arguments, and take or produce vectors of [`u8`].
//!
//! ## On Windows
//!
//! An [`OsStr`] can be losslessly converted to a native Windows string. And
//! a native Windows string can be losslessly converted to an [`OsString`].
//!
//! On Windows, [`OsStr`] implements the
//! <code>std::os::windows::ffi::[OsStrExt][windows.OsStrExt]</code> trait,
//! which provides an [`encode_wide`] method. This provides an
//! iterator that can be [`collect`]ed into a vector of [`u16`]. After a nul
//! characters is appended, this is the same as a native Windows string.
//!
//! Additionally, on Windows [`OsString`] implements the
//! <code>std::os::windows:ffi::[OsStringExt][windows.OsStringExt]</code>
//! trait, which provides a [`from_wide`] method to convert a native Windows
//! string (without the terminating nul character) to an [`OsString`].
//!
//! ## Other platforms
//!
//! Many other platforms provide their own extension traits in a
//! `std::os::*::ffi` module.
//!
//! ## On all platforms
//!
//! On all platforms, [`OsStr`] consists of a sequence of bytes that is encoded as a superset of
//! UTF-8; see [`OsString`] for more details on its encoding on different platforms.
//!
//! For limited, inexpensive conversions from and to bytes, see [`OsStr::as_encoded_bytes`] and
//! [`OsStr::from_encoded_bytes_unchecked`].
//!
//! For basic string processing, see [`OsStr::slice_encoded_bytes`].
//!
//! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value
//! [Unicode code point]: https://www.unicode.org/glossary/#code_point
//! [`env::set_var()`]: crate::env::set_var "env::set_var"
//! [`env::var_os()`]: crate::env::var_os "env::var_os"
//! [unix.OsStringExt]: crate::os::unix::ffi::OsStringExt "os::unix::ffi::OsStringExt"
//! [`from_vec`]: crate::os::unix::ffi::OsStringExt::from_vec "os::unix::ffi::OsStringExt::from_vec"
//! [`into_vec`]: crate::os::unix::ffi::OsStringExt::into_vec "os::unix::ffi::OsStringExt::into_vec"
//! [unix.OsStrExt]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt"
//! [`from_bytes`]: crate::os::unix::ffi::OsStrExt::from_bytes "os::unix::ffi::OsStrExt::from_bytes"
//! [`as_bytes`]: crate::os::unix::ffi::OsStrExt::as_bytes "os::unix::ffi::OsStrExt::as_bytes"
//! [`OsStrExt`]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt"
//! [windows.OsStrExt]: crate::os::windows::ffi::OsStrExt "os::windows::ffi::OsStrExt"
//! [`encode_wide`]: crate::os::windows::ffi::OsStrExt::encode_wide "os::windows::ffi::OsStrExt::encode_wide"
//! [`collect`]: crate::iter::Iterator::collect "iter::Iterator::collect"
//! [windows.OsStringExt]: crate::os::windows::ffi::OsStringExt "os::windows::ffi::OsStringExt"
//! [`from_wide`]: crate::os::windows::ffi::OsStringExt::from_wide "os::windows::ffi::OsStringExt::from_wide"
#![stable(feature = "rust1", since = "1.0.0")]
#[stable(feature = "c_str_module", since = "1.88.0")]
pub mod c_str;
#[stable(feature = "core_c_void", since = "1.30.0")]
pub use core::ffi::c_void;
#[unstable(
feature = "c_variadic",
reason = "the `c_variadic` feature has not been properly tested on \
all supported platforms",
issue = "44930"
)]
pub use core::ffi::{VaArgSafe, VaList};
#[stable(feature = "core_ffi_c", since = "1.64.0")]
pub use core::ffi::{
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
c_ulong, c_ulonglong, c_ushort,
};
#[unstable(feature = "c_size_t", issue = "88345")]
pub use core::ffi::{c_ptrdiff_t, c_size_t, c_ssize_t};
#[doc(inline)]
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
pub use self::c_str::FromBytesUntilNulError;
#[doc(inline)]
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
pub use self::c_str::FromBytesWithNulError;
#[doc(inline)]
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
pub use self::c_str::FromVecWithNulError;
#[doc(inline)]
#[stable(feature = "cstring_into", since = "1.7.0")]
pub use self::c_str::IntoStringError;
#[doc(inline)]
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::c_str::NulError;
#[doc(inline)]
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::c_str::{CStr, CString};
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(inline)]
pub use self::os_str::{OsStr, OsString};
#[stable(feature = "os_str_display", since = "1.87.0")]
pub mod os_str;

1845
crates/std/src/ffi/os_str.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,311 @@
use super::*;
use crate::mem::MaybeUninit;
use crate::ptr;
#[test]
fn test_os_string_with_capacity() {
let os_string = OsString::with_capacity(0);
assert_eq!(0, os_string.inner.into_inner().capacity());
let os_string = OsString::with_capacity(10);
assert_eq!(10, os_string.inner.into_inner().capacity());
let mut os_string = OsString::with_capacity(0);
os_string.push("abc");
assert!(os_string.inner.into_inner().capacity() >= 3);
}
#[test]
fn test_os_string_clear() {
let mut os_string = OsString::from("abc");
assert_eq!(3, os_string.inner.as_inner().len());
os_string.clear();
assert_eq!(&os_string, "");
assert_eq!(0, os_string.inner.as_inner().len());
}
#[test]
fn test_os_string_leak() {
let os_string = OsString::from("have a cake");
let (len, cap) = (os_string.len(), os_string.capacity());
let leaked = os_string.leak();
assert_eq!(leaked.as_encoded_bytes(), b"have a cake");
unsafe { drop(String::from_raw_parts(leaked as *mut OsStr as _, len, cap)) }
}
#[test]
fn test_os_string_capacity() {
let os_string = OsString::with_capacity(0);
assert_eq!(0, os_string.capacity());
let os_string = OsString::with_capacity(10);
assert_eq!(10, os_string.capacity());
let mut os_string = OsString::with_capacity(0);
os_string.push("abc");
assert!(os_string.capacity() >= 3);
}
#[test]
fn test_os_string_reserve() {
let mut os_string = OsString::new();
assert_eq!(os_string.capacity(), 0);
os_string.reserve(2);
assert!(os_string.capacity() >= 2);
for _ in 0..16 {
os_string.push("a");
}
assert!(os_string.capacity() >= 16);
os_string.reserve(16);
assert!(os_string.capacity() >= 32);
os_string.push("a");
os_string.reserve(16);
assert!(os_string.capacity() >= 33)
}
#[test]
fn test_os_string_reserve_exact() {
let mut os_string = OsString::new();
assert_eq!(os_string.capacity(), 0);
os_string.reserve_exact(2);
assert!(os_string.capacity() >= 2);
for _ in 0..16 {
os_string.push("a");
}
assert!(os_string.capacity() >= 16);
os_string.reserve_exact(16);
assert!(os_string.capacity() >= 32);
os_string.push("a");
os_string.reserve_exact(16);
assert!(os_string.capacity() >= 33)
}
#[test]
fn test_os_string_join() {
let strings = [OsStr::new("hello"), OsStr::new("dear"), OsStr::new("world")];
assert_eq!("hello", strings[..1].join(OsStr::new(" ")));
assert_eq!("hello dear world", strings.join(OsStr::new(" ")));
assert_eq!("hellodearworld", strings.join(OsStr::new("")));
assert_eq!("hello.\n dear.\n world", strings.join(OsStr::new(".\n ")));
assert_eq!("dear world", strings[1..].join(&OsString::from(" ")));
let strings_abc = [OsString::from("a"), OsString::from("b"), OsString::from("c")];
assert_eq!("a b c", strings_abc.join(OsStr::new(" ")));
}
#[test]
fn test_os_string_default() {
let os_string: OsString = Default::default();
assert_eq!("", &os_string);
}
#[test]
fn test_os_str_is_empty() {
let mut os_string = OsString::new();
assert!(os_string.is_empty());
os_string.push("abc");
assert!(!os_string.is_empty());
os_string.clear();
assert!(os_string.is_empty());
}
#[test]
fn test_os_str_len() {
let mut os_string = OsString::new();
assert_eq!(0, os_string.len());
os_string.push("abc");
assert_eq!(3, os_string.len());
os_string.clear();
assert_eq!(0, os_string.len());
}
#[test]
fn test_os_str_default() {
let os_str: &OsStr = Default::default();
assert_eq!("", os_str);
}
#[test]
fn into_boxed() {
let orig = "Hello, world!";
let os_str = OsStr::new(orig);
let boxed: Box<OsStr> = Box::from(os_str);
let os_string = os_str.to_owned().into_boxed_os_str().into_os_string();
assert_eq!(os_str, &*boxed);
assert_eq!(&*boxed, &*os_string);
assert_eq!(&*os_string, os_str);
}
#[test]
fn boxed_default() {
let boxed = <Box<OsStr>>::default();
assert!(boxed.is_empty());
}
#[test]
fn test_os_str_clone_into() {
let mut os_string = OsString::with_capacity(123);
os_string.push("hello");
let os_str = OsStr::new("bonjour");
os_str.clone_into(&mut os_string);
assert_eq!(os_str, os_string);
assert!(os_string.capacity() >= 123);
}
#[test]
fn into_rc() {
let orig = "Hello, world!";
let os_str = OsStr::new(orig);
let rc: Rc<OsStr> = Rc::from(os_str);
let arc: Arc<OsStr> = Arc::from(os_str);
assert_eq!(&*rc, os_str);
assert_eq!(&*arc, os_str);
let rc2: Rc<OsStr> = Rc::from(os_str.to_owned());
let arc2: Arc<OsStr> = Arc::from(os_str.to_owned());
assert_eq!(&*rc2, os_str);
assert_eq!(&*arc2, os_str);
}
#[test]
fn slice_encoded_bytes() {
let os_str = OsStr::new("123θგ🦀");
// ASCII
let digits = os_str.slice_encoded_bytes(..3);
assert_eq!(digits, "123");
let three = os_str.slice_encoded_bytes(2..3);
assert_eq!(three, "3");
// 2-byte UTF-8
let theta = os_str.slice_encoded_bytes(3..5);
assert_eq!(theta, "θ");
// 3-byte UTF-8
let gani = os_str.slice_encoded_bytes(5..8);
assert_eq!(gani, "");
// 4-byte UTF-8
let crab = os_str.slice_encoded_bytes(8..);
assert_eq!(crab, "🦀");
}
#[test]
#[should_panic]
fn slice_out_of_bounds() {
let crab = OsStr::new("🦀");
let _ = crab.slice_encoded_bytes(..5);
}
#[test]
#[should_panic]
fn slice_mid_char() {
let crab = OsStr::new("🦀");
let _ = crab.slice_encoded_bytes(..2);
}
#[cfg(unix)]
#[test]
#[should_panic(expected = "byte index 1 is not an OsStr boundary")]
fn slice_invalid_data() {
use crate::os::unix::ffi::OsStrExt;
let os_string = OsStr::from_bytes(b"\xFF\xFF");
let _ = os_string.slice_encoded_bytes(1..);
}
#[cfg(unix)]
#[test]
#[should_panic(expected = "byte index 1 is not an OsStr boundary")]
fn slice_partial_utf8() {
use crate::os::unix::ffi::{OsStrExt, OsStringExt};
let part_crab = OsStr::from_bytes(&"🦀".as_bytes()[..3]);
let mut os_string = OsString::from_vec(vec![0xFF]);
os_string.push(part_crab);
let _ = os_string.slice_encoded_bytes(1..);
}
#[cfg(unix)]
#[test]
fn slice_invalid_edge() {
use crate::os::unix::ffi::{OsStrExt, OsStringExt};
let os_string = OsStr::from_bytes(b"a\xFFa");
assert_eq!(os_string.slice_encoded_bytes(..1), "a");
assert_eq!(os_string.slice_encoded_bytes(1..), OsStr::from_bytes(b"\xFFa"));
assert_eq!(os_string.slice_encoded_bytes(..2), OsStr::from_bytes(b"a\xFF"));
assert_eq!(os_string.slice_encoded_bytes(2..), "a");
let os_string = OsStr::from_bytes(&"abc🦀".as_bytes()[..6]);
assert_eq!(os_string.slice_encoded_bytes(..3), "abc");
assert_eq!(os_string.slice_encoded_bytes(3..), OsStr::from_bytes(b"\xF0\x9F\xA6"));
let mut os_string = OsString::from_vec(vec![0xFF]);
os_string.push("🦀");
assert_eq!(os_string.slice_encoded_bytes(..1), OsStr::from_bytes(b"\xFF"));
assert_eq!(os_string.slice_encoded_bytes(1..), "🦀");
}
#[cfg(windows)]
#[test]
#[should_panic(expected = "byte index 3 lies between surrogate codepoints")]
fn slice_between_surrogates() {
use crate::os::windows::ffi::OsStringExt;
let os_string = OsString::from_wide(&[0xD800, 0xD800]);
assert_eq!(os_string.as_encoded_bytes(), &[0xED, 0xA0, 0x80, 0xED, 0xA0, 0x80]);
let _ = os_string.slice_encoded_bytes(..3);
}
#[cfg(windows)]
#[test]
fn slice_surrogate_edge() {
use crate::os::windows::ffi::OsStringExt;
let surrogate = OsString::from_wide(&[0xD800]);
let mut pre_crab = surrogate.clone();
pre_crab.push("🦀");
assert_eq!(pre_crab.slice_encoded_bytes(..3), surrogate);
assert_eq!(pre_crab.slice_encoded_bytes(3..), "🦀");
let mut post_crab = OsString::from("🦀");
post_crab.push(&surrogate);
assert_eq!(post_crab.slice_encoded_bytes(..4), "🦀");
assert_eq!(post_crab.slice_encoded_bytes(4..), surrogate);
}
#[test]
fn clone_to_uninit() {
let a = OsStr::new("hello.txt");
let mut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<OsStr>(a)];
unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) };
assert_eq!(a.as_encoded_bytes(), unsafe { storage.assume_init_ref() });
let mut b: Box<OsStr> = OsStr::new("world.exe").into();
assert_eq!(size_of_val::<OsStr>(a), size_of_val::<OsStr>(&b));
assert_ne!(a, &*b);
unsafe { a.clone_to_uninit(ptr::from_mut::<OsStr>(&mut b).cast()) };
assert_eq!(a, &*b);
}
#[test]
fn debug() {
let s = "'single quotes'";
assert_eq!(format!("{:?}", OsStr::new(s)), format!("{:?}", s));
}

3532
crates/std/src/fs.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,91 @@
//! Generic hashing support.
//!
//! This module provides a generic way to compute the [hash] of a value.
//! Hashes are most commonly used with [`HashMap`] and [`HashSet`].
//!
//! [hash]: https://en.wikipedia.org/wiki/Hash_function
//! [`HashMap`]: ../../std/collections/struct.HashMap.html
//! [`HashSet`]: ../../std/collections/struct.HashSet.html
//!
//! The simplest way to make a type hashable is to use `#[derive(Hash)]`:
//!
//! # Examples
//!
//! ```rust
//! use std::hash::{DefaultHasher, Hash, Hasher};
//!
//! #[derive(Hash)]
//! struct Person {
//! id: u32,
//! name: String,
//! phone: u64,
//! }
//!
//! let person1 = Person {
//! id: 5,
//! name: "Janet".to_string(),
//! phone: 555_666_7777,
//! };
//! let person2 = Person {
//! id: 5,
//! name: "Bob".to_string(),
//! phone: 555_666_7777,
//! };
//!
//! assert!(calculate_hash(&person1) != calculate_hash(&person2));
//!
//! fn calculate_hash<T: Hash>(t: &T) -> u64 {
//! let mut s = DefaultHasher::new();
//! t.hash(&mut s);
//! s.finish()
//! }
//! ```
//!
//! If you need more control over how a value is hashed, you need to implement
//! the [`Hash`] trait:
//!
//! ```rust
//! use std::hash::{DefaultHasher, Hash, Hasher};
//!
//! struct Person {
//! id: u32,
//! # #[allow(dead_code)]
//! name: String,
//! phone: u64,
//! }
//!
//! impl Hash for Person {
//! fn hash<H: Hasher>(&self, state: &mut H) {
//! self.id.hash(state);
//! self.phone.hash(state);
//! }
//! }
//!
//! let person1 = Person {
//! id: 5,
//! name: "Janet".to_string(),
//! phone: 555_666_7777,
//! };
//! let person2 = Person {
//! id: 5,
//! name: "Bob".to_string(),
//! phone: 555_666_7777,
//! };
//!
//! assert_eq!(calculate_hash(&person1), calculate_hash(&person2));
//!
//! fn calculate_hash<T: Hash>(t: &T) -> u64 {
//! let mut s = DefaultHasher::new();
//! t.hash(&mut s);
//! s.finish()
//! }
//! ```
#![stable(feature = "rust1", since = "1.0.0")]
pub(crate) mod random;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::hash::*;
#[stable(feature = "std_hash_exports", since = "1.76.0")]
pub use self::random::{DefaultHasher, RandomState};

View File

@@ -0,0 +1,159 @@
//! This module exists to isolate [`RandomState`] and [`DefaultHasher`] outside of the
//! [`collections`] module without actually publicly exporting them, so that parts of that
//! implementation can more easily be moved to the [`alloc`] crate.
//!
//! Although its items are public and contain stability attributes, they can't actually be accessed
//! outside this crate.
//!
//! [`collections`]: crate::collections
use super::{BuildHasher, Hasher, SipHasher13};
use crate::cell::Cell;
use crate::fmt;
use crate::sys::random::hashmap_random_keys;
/// `RandomState` is the default state for [`HashMap`] types.
///
/// A particular instance `RandomState` will create the same instances of
/// [`Hasher`], but the hashers created by two different `RandomState`
/// instances are unlikely to produce the same result for the same values.
///
/// [`HashMap`]: crate::collections::HashMap
///
/// # Examples
///
/// ```
/// use std::collections::HashMap;
/// use std::hash::RandomState;
///
/// let s = RandomState::new();
/// let mut map = HashMap::with_hasher(s);
/// map.insert(1, 2);
/// ```
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
#[derive(Clone)]
pub struct RandomState {
k0: u64,
k1: u64,
}
impl RandomState {
/// Constructs a new `RandomState` that is initialized with random keys.
///
/// # Examples
///
/// ```
/// use std::hash::RandomState;
///
/// let s = RandomState::new();
/// ```
#[inline]
#[allow(deprecated)]
// rand
#[must_use]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
pub fn new() -> RandomState {
// Historically this function did not cache keys from the OS and instead
// simply always called `rand::thread_rng().gen()` twice. In #31356 it
// was discovered, however, that because we re-seed the thread-local RNG
// from the OS periodically that this can cause excessive slowdown when
// many hash maps are created on a thread. To solve this performance
// trap we cache the first set of randomly generated keys per-thread.
//
// Later in #36481 it was discovered that exposing a deterministic
// iteration order allows a form of DOS attack. To counter that we
// increment one of the seeds on every RandomState creation, giving
// every corresponding HashMap a different iteration order.
thread_local!(static KEYS: Cell<(u64, u64)> = {
Cell::new(hashmap_random_keys())
});
KEYS.with(|keys| {
let (k0, k1) = keys.get();
keys.set((k0.wrapping_add(1), k1));
RandomState { k0, k1 }
})
}
}
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
impl BuildHasher for RandomState {
type Hasher = DefaultHasher;
#[inline]
fn build_hasher(&self) -> DefaultHasher {
DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1))
}
}
/// The default [`Hasher`] used by [`RandomState`].
///
/// The internal algorithm is not specified, and so it and its hashes should
/// not be relied upon over releases.
#[derive(Clone, Debug)]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
pub struct DefaultHasher(SipHasher13);
impl DefaultHasher {
/// Creates a new `DefaultHasher`.
///
/// This hasher is not guaranteed to be the same as all other
/// `DefaultHasher` instances, but is the same as all other `DefaultHasher`
/// instances created through `new` or `default`.
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
#[inline]
#[rustc_const_unstable(feature = "const_default", issue = "143894")]
#[must_use]
pub const fn new() -> DefaultHasher {
DefaultHasher(SipHasher13::new_with_keys(0, 0))
}
}
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
#[rustc_const_unstable(feature = "const_default", issue = "143894")]
impl const Default for DefaultHasher {
/// Creates a new `DefaultHasher` using [`new`].
/// See its documentation for more.
///
/// [`new`]: DefaultHasher::new
#[inline]
fn default() -> DefaultHasher {
DefaultHasher::new()
}
}
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
impl Hasher for DefaultHasher {
// The underlying `SipHasher13` doesn't override the other
// `write_*` methods, so it's ok not to forward them here.
#[inline]
fn write(&mut self, msg: &[u8]) {
self.0.write(msg)
}
#[inline]
fn write_str(&mut self, s: &str) {
self.0.write_str(s);
}
#[inline]
fn finish(&self) -> u64 {
self.0.finish()
}
}
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
impl Default for RandomState {
/// Constructs a new `RandomState`.
#[inline]
fn default() -> RandomState {
RandomState::new()
}
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl fmt::Debug for RandomState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RandomState").finish_non_exhaustive()
}
}

71
crates/std/src/io.rs Normal file
View File

@@ -0,0 +1,71 @@
use crate::fs::File;
use io::IoBase;
pub use io::Read;
pub use io::Seek;
pub use io::SeekFrom;
pub use io::Write;
pub struct Stdin;
impl IoBase for Stdin {
type Error = ();
}
impl Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
unsafe { File::from_raw_fd(0).read(buf) }
}
}
pub fn stdin() -> Stdin {
Stdin
}
pub type Result<T> = core::result::Result<T, Error>;
pub(super) struct Repr();
// Part took from the real std
#[rustc_macro_transparency = "semiopaque"]
#[unstable(feature = "io_const_error", issue = "133448")]
#[allow_internal_unstable(hint_must_use, io_const_error_internals)]
pub macro const_error($kind:expr, $message:expr $(,)?) {
crate::io::Error::new()
}
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Error {
repr: Repr,
}
impl Error {
pub const fn new() -> Self {
Self { repr: Repr() }
}
}
#[allow(dead_code)]
impl Error {
pub(crate) const INVALID_UTF8: Self =
const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8");
pub(crate) const READ_EXACT_EOF: Self =
const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer");
pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!(
ErrorKind::NotFound,
"the number of hardware threads is not known for the target platform",
);
pub(crate) const UNSUPPORTED_PLATFORM: Self =
const_error!(ErrorKind::Unsupported, "operation not supported on this platform");
pub(crate) const WRITE_ALL_EOF: Self =
const_error!(ErrorKind::WriteZero, "failed to write whole buffer");
pub(crate) const ZERO_TIMEOUT: Self =
const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout");
pub(crate) const NO_ADDRESSES: Self =
const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses");
}

314
crates/std/src/lib.rs Normal file
View File

@@ -0,0 +1,314 @@
#![unstable(feature = "custom_std", issue = "none")]
#![no_std]
//
// Lints:
#![warn(deprecated_in_future)]
// #![warn(missing_docs)]
// #![warn(missing_debug_implementations)]
#![allow(explicit_outlives_requirements)]
#![allow(unused_lifetimes)]
#![allow(internal_features)]
#![deny(fuzzy_provenance_casts)]
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(rustdoc::redundant_explicit_links)]
#![warn(rustdoc::unescaped_backticks)]
// Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind`
#![deny(ffi_unwind_calls)]
// std may use features in a platform-specific way
#![allow(unused_features)]
//
// Features:
#![cfg_attr(
test,
feature(internal_output_capture, print_internals, update_panic_count, rt)
)]
#![cfg_attr(
all(target_vendor = "fortanix", target_env = "sgx"),
feature(slice_index_methods, coerce_unsized, sgx_platform)
)]
#![cfg_attr(all(test, target_os = "uefi"), feature(uefi_std))]
#![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))]
#![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))]
//
// Language features:
// tidy-alphabetical-start
#![feature(alloc_error_handler)]
#![feature(allocator_internals)]
#![feature(allow_internal_unsafe)]
#![feature(allow_internal_unstable)]
#![feature(asm_experimental_arch)]
#![feature(autodiff)]
#![feature(cfg_sanitizer_cfi)]
#![feature(cfg_target_thread_local)]
#![feature(cfi_encoding)]
#![feature(const_default)]
#![feature(const_trait_impl)]
#![feature(core_float_math)]
#![feature(decl_macro)]
#![feature(deprecated_suggestion)]
#![feature(doc_cfg)]
#![feature(doc_masked)]
#![feature(doc_notable_trait)]
#![feature(dropck_eyepatch)]
#![feature(f16)]
#![feature(f128)]
#![feature(ffi_const)]
#![feature(formatting_options)]
#![feature(funnel_shifts)]
#![feature(if_let_guard)]
#![feature(intra_doc_pointers)]
#![feature(iter_advance_by)]
#![feature(iter_next_chunk)]
#![feature(lang_items)]
#![feature(link_cfg)]
#![feature(linkage)]
#![feature(macro_metavar_expr_concat)]
#![feature(maybe_uninit_fill)]
#![feature(min_specialization)]
#![feature(must_not_suspend)]
#![feature(needs_panic_runtime)]
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(optimize_attribute)]
#![feature(prelude_import)]
#![feature(rustc_attrs)]
#![feature(rustdoc_internals)]
#![feature(staged_api)]
#![feature(stmt_expr_attributes)]
#![feature(strict_provenance_lints)]
#![feature(thread_local)]
#![feature(try_blocks)]
#![feature(try_trait_v2)]
#![feature(type_alias_impl_trait)]
// tidy-alphabetical-end
//
// Library features (core):
// tidy-alphabetical-start
#![feature(bstr)]
#![feature(bstr_internals)]
#![feature(cast_maybe_uninit)]
#![feature(cfg_select)]
#![feature(char_internals)]
#![feature(clone_to_uninit)]
#![feature(const_convert)]
#![feature(core_intrinsics)]
#![feature(core_io_borrowed_buf)]
#![feature(drop_guard)]
#![feature(duration_constants)]
#![feature(error_generic_member_access)]
#![feature(error_iter)]
#![feature(exact_size_is_empty)]
#![feature(exclusive_wrapper)]
#![feature(extend_one)]
#![feature(float_algebraic)]
// #![feature(float_gamma)]
#![feature(float_minimum_maximum)]
#![feature(fmt_internals)]
#![feature(fn_ptr_trait)]
#![feature(generic_atomic)]
#![feature(hasher_prefixfree_extras)]
#![feature(hashmap_internals)]
#![feature(hint_must_use)]
#![feature(int_from_ascii)]
#![feature(ip)]
#![feature(maybe_uninit_array_assume_init)]
#![feature(panic_can_unwind)]
#![feature(panic_internals)]
#![feature(pin_coerce_unsized_trait)]
#![feature(pointer_is_aligned_to)]
#![feature(portable_simd)]
#![feature(ptr_as_uninit)]
#![feature(ptr_mask)]
#![feature(random)]
#![feature(slice_internals)]
#![feature(slice_ptr_get)]
#![feature(slice_range)]
#![feature(slice_split_once)]
#![feature(std_internals)]
#![feature(str_internals)]
#![feature(sync_unsafe_cell)]
#![feature(temporary_niche_types)]
#![feature(ub_checks)]
#![feature(used_with_arg)]
// tidy-alphabetical-end
//
// Library features (alloc):
// tidy-alphabetical-start
#![feature(alloc_layout_extra)]
#![feature(allocator_api)]
#![feature(clone_from_ref)]
#![feature(get_mut_unchecked)]
#![feature(map_try_insert)]
#![feature(slice_concat_trait)]
#![feature(thin_box)]
#![feature(try_reserve_kind)]
#![feature(try_with_capacity)]
#![feature(unique_rc_arc)]
#![feature(wtf8_internals)]
// tidy-alphabetical-end
//
// Library features (unwind):
// tidy-alphabetical-start
// #![feature(panic_unwind)]
// tidy-alphabetical-end
//
// Library features (std_detect):
// tidy-alphabetical-start
// #![feature(stdarch_internal)]
// tidy-alphabetical-end
//
// Only for re-exporting:
// tidy-alphabetical-start
#![feature(assert_matches)]
#![feature(async_iterator)]
#![feature(c_variadic)]
#![feature(cfg_accessible)]
#![feature(cfg_eval)]
#![feature(concat_bytes)]
#![feature(const_format_args)]
#![feature(custom_test_frameworks)]
#![feature(edition_panic)]
#![feature(format_args_nl)]
#![feature(log_syntax)]
#![feature(test)]
#![feature(trace_macros)]
// tidy-alphabetical-end
//
// Only used in tests/benchmarks:
//
// Only for const-ness:
// tidy-alphabetical-start
#![feature(io_const_error)]
// tidy-alphabetical-end
//
#![feature(c_size_t, unsafe_binders)]
#![allow(clippy::doc_lazy_continuation, clippy::all)]
#![allow(stable_features, incomplete_features, unexpected_cfgs)]
#![allow(unused)]
#[macro_use]
extern crate alloc as alloc_crate;
pub use core::any;
pub use core::array;
pub use core::async_iter;
pub use core::cell;
pub use core::char;
pub use core::clone;
pub use core::cmp;
pub use core::convert;
pub use core::default;
pub use core::future;
pub use core::hint;
#[allow(deprecated, deprecated_in_future)]
pub use core::i8;
#[allow(deprecated, deprecated_in_future)]
pub use core::i16;
#[allow(deprecated, deprecated_in_future)]
pub use core::i32;
#[allow(deprecated, deprecated_in_future)]
pub use core::i64;
#[allow(deprecated, deprecated_in_future)]
pub use core::i128;
pub use core::intrinsics;
#[allow(deprecated, deprecated_in_future)]
pub use core::isize;
pub use core::iter;
pub use core::marker;
pub use core::mem;
pub use core::ops;
pub use core::option;
pub use core::pin;
pub use core::ptr;
pub use core::range;
pub use core::result;
#[allow(deprecated, deprecated_in_future)]
pub use core::u8;
#[allow(deprecated, deprecated_in_future)]
pub use core::u16;
#[allow(deprecated, deprecated_in_future)]
pub use core::u32;
#[allow(deprecated, deprecated_in_future)]
pub use core::u64;
#[allow(deprecated, deprecated_in_future)]
pub use core::u128;
pub use core::unsafe_binder;
#[allow(deprecated, deprecated_in_future)]
pub use core::usize;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::borrow;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::boxed;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::fmt;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::format;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::rc;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::slice;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::str;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::string;
#[stable(feature = "rust1", since = "1.0.0")]
pub use alloc_crate::vec;
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
pub use core::{
assert, cfg, column, compile_error, concat, const_format_args, env, file, format_args,
format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env,
stringify, trace_macros,
};
pub mod ffi;
pub mod hash;
pub mod io;
// pub mod fs;
pub mod error;
pub mod num;
pub mod path;
pub mod prelude;
pub mod process;
#[macro_use]
pub mod rt;
pub mod alloc;
pub mod sync;
pub mod sys;
pub mod thread;
pub mod time;
#[prelude_import]
#[allow(unused_imports)]
pub use prelude::rust_2024::*;
#[allow(unused)]
mod sealed {
/// This trait being unreachable from outside the crate
/// prevents outside implementations of our extension traits.
/// This allows adding more trait methods in the future.
#[unstable(feature = "sealed", issue = "none")]
pub trait Sealed {}
}
pub use shared::fs;
pub use shared::syscall;
#[macro_export]
macro_rules! print {
($($args:expr),*) => {
$crate::syscall::write_string_temp(&format!($($args),*))
};
}
#[macro_export]
macro_rules! println {
() => {
$crate::print!("");
// $crate::print!("\n\r");
};
($($args:expr),*) => {
$crate::print!($($args),*);
// $crate::println!();
};
}

28
crates/std/src/num/mod.rs Normal file
View File

@@ -0,0 +1,28 @@
//! Additional functionality for numerics.
//!
//! This module provides some extra types that are useful when doing numerical
//! work. See the individual documentation for each piece for more information.
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
#[stable(feature = "int_error_matching", since = "1.55.0")]
pub use core::num::IntErrorKind;
#[stable(feature = "generic_nonzero", since = "1.79.0")]
pub use core::num::NonZero;
#[stable(feature = "saturating_int_impl", since = "1.74.0")]
pub use core::num::Saturating;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::num::Wrapping;
#[unstable(
feature = "nonzero_internals",
reason = "implementation detail which may disappear or be replaced at any time",
issue = "none"
)]
pub use core::num::ZeroablePrimitive;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError};
#[stable(feature = "signed_nonzero", since = "1.34.0")]
pub use core::num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize};
#[stable(feature = "nonzero", since = "1.28.0")]
pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize};

4047
crates/std/src/path.rs Normal file

File diff suppressed because it is too large Load Diff

181
crates/std/src/prelude.rs Normal file
View File

@@ -0,0 +1,181 @@
pub mod rust_2024 {
pub use crate::print;
pub use crate::println;
pub use alloc::format;
pub use alloc::vec;
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(no_inline)]
pub use crate::borrow::ToOwned;
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(no_inline)]
pub use crate::boxed::Box;
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(no_inline)]
pub use crate::string::{String, ToString};
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(no_inline)]
pub use crate::vec::Vec;
// Re-exported built-in macros and traits
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
#[doc(no_inline)]
#[expect(deprecated)]
pub use core::prelude::v1::{
Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, assert, assert_eq,
assert_ne, cfg, column, compile_error, concat, debug_assert, debug_assert_eq,
debug_assert_ne, env, file, format_args, include, include_bytes, include_str, line,
matches, module_path, option_env, stringify, todo, r#try, unimplemented, unreachable,
write, writeln,
};
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
#[doc(no_inline)]
pub use crate::thread_local;
#[stable(feature = "cfg_select", since = "1.95.0")]
#[doc(no_inline)]
pub use core::prelude::v1::cfg_select;
#[unstable(
feature = "concat_bytes",
issue = "87555",
reason = "`concat_bytes` is not stable enough for use and is subject to change"
)]
#[doc(no_inline)]
pub use core::prelude::v1::concat_bytes;
#[unstable(feature = "const_format_args", issue = "none")]
#[doc(no_inline)]
pub use core::prelude::v1::const_format_args;
#[unstable(
feature = "log_syntax",
issue = "29598",
reason = "`log_syntax!` is not stable enough for use and is subject to change"
)]
#[doc(no_inline)]
pub use core::prelude::v1::log_syntax;
#[unstable(
feature = "trace_macros",
issue = "29598",
reason = "`trace_macros` is not stable enough for use and is subject to change"
)]
#[doc(no_inline)]
pub use core::prelude::v1::trace_macros;
// Do not `doc(no_inline)` so that they become doc items on their own
// (no public module for them to be re-exported from).
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
pub use core::prelude::v1::{
alloc_error_handler, bench, derive, global_allocator, test, test_case,
};
#[unstable(feature = "derive_const", issue = "118304")]
pub use core::prelude::v1::derive_const;
// Do not `doc(no_inline)` either.
#[unstable(
feature = "cfg_accessible",
issue = "64797",
reason = "`cfg_accessible` is not fully implemented"
)]
pub use core::prelude::v1::cfg_accessible;
// Do not `doc(no_inline)` either.
#[unstable(
feature = "cfg_eval",
issue = "82679",
reason = "`cfg_eval` is a recently implemented feature"
)]
pub use core::prelude::v1::cfg_eval;
// Do not `doc(no_inline)` either.
#[unstable(
feature = "type_ascription",
issue = "23416",
reason = "placeholder syntax for type ascription"
)]
pub use core::prelude::v1::type_ascribe;
// Do not `doc(no_inline)` either.
#[unstable(
feature = "deref_patterns",
issue = "87121",
reason = "placeholder syntax for deref patterns"
)]
pub use core::prelude::v1::deref;
// Do not `doc(no_inline)` either.
#[unstable(
feature = "type_alias_impl_trait",
issue = "63063",
reason = "`type_alias_impl_trait` has open design concerns"
)]
pub use core::prelude::v1::define_opaque;
#[unstable(feature = "extern_item_impls", issue = "125418")]
pub use core::prelude::v1::{eii, unsafe_eii};
#[unstable(feature = "eii_internals", issue = "none")]
pub use core::prelude::v1::eii_declaration;
#[stable(feature = "prelude_2021", since = "1.55.0")]
#[doc(no_inline)]
pub use core::prelude::rust_2021::*;
#[stable(feature = "prelude_2024", since = "1.85.0")]
#[doc(no_inline)]
pub use core::prelude::rust_2024::*;
#[stable(feature = "rust1", since = "1.0.0")]
#[doc(no_inline)]
pub use crate::convert::{AsMut, AsRef, From, Into};
extern crate alloc;
struct GlobalAllocator;
#[core::prelude::v1::global_allocator]
static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
unsafe impl core::alloc::GlobalAlloc for GlobalAllocator {
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
crate::syscall::alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
crate::syscall::dealloc(ptr, layout)
}
}
#[panic_handler]
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
// TODO print
loop {}
}
/// # Safety
/// `argc` and `argv` are passed by the kernel
#[unsafe(no_mangle)]
pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize {
unsafe extern "Rust" {
fn main(argc: isize, argv: *const *const u8) -> isize;
}
unsafe { main(argc, argv) }
}
#[lang = "start"]
pub fn lang_start<T: crate::process::Termination + 'static>(
main: fn() -> T,
argc: isize,
argv: *const *const u8,
_sigpipe: u8,
) -> isize {
println!("{}", argc);
println!("{:?}", argv);
main().report().to_isize()
}
}

29
crates/std/src/process.rs Normal file
View File

@@ -0,0 +1,29 @@
pub struct ExitCode(isize);
impl ExitCode {
pub const SUCCESS: ExitCode = ExitCode(0);
pub fn to_isize(self) -> isize {
self.0
}
}
#[lang = "termination"]
pub trait Termination {
/// Is called to get the representation of the value as status code.
/// This status code is returned to the operating system.
fn report(self) -> ExitCode;
}
impl Termination for () {
#[inline]
fn report(self) -> ExitCode {
ExitCode::SUCCESS
}
}
impl Termination for isize {
#[inline]
fn report(self) -> ExitCode {
ExitCode(self)
}
}

6
crates/std/src/rt.rs Normal file
View File

@@ -0,0 +1,6 @@
macro_rules! rtabort {
($($t:tt)*) => {{}};
}
macro_rules! rtprintpanic {
($($t:tt)*) => {{}};
}

14
crates/std/src/sync.rs Normal file
View File

@@ -0,0 +1,14 @@
pub mod once {
use crate::sys::sync as sys;
pub struct OnceState {
pub(crate) inner: sys::OnceState,
}
/// Used for the internal implementation of `sys::sync::once` on different platforms and the
/// [`LazyLock`](crate::sync::LazyLock) implementation.
pub(crate) enum OnceExclusiveState {
Incomplete,
Poisoned,
Complete,
}
}
pub use once::OnceState;

View File

@@ -0,0 +1,113 @@
#![forbid(unsafe_op_in_unsafe_fn)]
use crate::alloc::{GlobalAlloc, Layout, System};
use crate::ptr;
// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values.
#[allow(dead_code)]
const MIN_ALIGN: usize = if cfg!(any(
all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")),
all(target_arch = "xtensa", target_os = "espidf"),
)) {
// The allocator on the esp-idf and zkvm platforms guarantees 4 byte alignment.
4
} else if cfg!(any(
target_arch = "x86",
target_arch = "arm",
target_arch = "m68k",
target_arch = "csky",
target_arch = "loongarch32",
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "sparc",
target_arch = "wasm32",
target_arch = "hexagon",
target_arch = "riscv32",
target_arch = "xtensa",
)) {
8
} else if cfg!(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm64ec",
target_arch = "loongarch64",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "s390x",
target_arch = "sparc64",
target_arch = "riscv64",
target_arch = "wasm64",
)) {
16
} else {
panic!("add a value for MIN_ALIGN")
};
#[allow(dead_code)]
unsafe fn realloc_fallback(
alloc: &System,
ptr: *mut u8,
old_layout: Layout,
new_size: usize,
) -> *mut u8 {
// SAFETY: Docs for GlobalAlloc::realloc require this to be valid
unsafe {
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
let new_ptr = GlobalAlloc::alloc(alloc, new_layout);
if !new_ptr.is_null() {
let size = usize::min(old_layout.size(), new_size);
ptr::copy_nonoverlapping(ptr, new_ptr, size);
GlobalAlloc::dealloc(alloc, ptr, old_layout);
}
new_ptr
}
}
cfg_select! {
any(
target_family = "unix",
target_os = "wasi",
target_os = "teeos",
target_os = "trusty",
) => {
mod unix;
}
target_os = "windows" => {
mod windows;
}
target_os = "hermit" => {
mod hermit;
}
target_os = "motor" => {
mod motor;
}
all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx;
}
target_os = "solid_asp3" => {
mod solid;
}
target_os = "uefi" => {
mod uefi;
}
target_os = "vexos" => {
mod vexos;
}
target_family = "wasm" => {
mod wasm;
}
target_os = "xous" => {
mod xous;
}
target_os = "zkvm" => {
mod zkvm;
}
target_os = "survos" => {
mod survos;
}
}

View File

@@ -0,0 +1,14 @@
use crate::alloc::{GlobalAlloc, Layout, System};
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
unsafe { crate::syscall::alloc(layout) }
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { crate::syscall::dealloc(ptr, layout) }
}
}

114
crates/std/src/sys/cmath.rs Normal file
View File

@@ -0,0 +1,114 @@
#![cfg(not(test))]
// These symbols are all defined by `libm`,
// or by `compiler-builtins` on unsupported platforms.
unsafe extern "C" {
pub safe fn acos(n: f64) -> f64;
pub safe fn asin(n: f64) -> f64;
pub safe fn atan(n: f64) -> f64;
pub safe fn atan2(a: f64, b: f64) -> f64;
pub safe fn cosh(n: f64) -> f64;
pub safe fn expm1(n: f64) -> f64;
pub safe fn expm1f(n: f32) -> f32;
#[cfg_attr(target_env = "msvc", link_name = "_hypot")]
pub safe fn hypot(x: f64, y: f64) -> f64;
#[cfg_attr(target_env = "msvc", link_name = "_hypotf")]
pub safe fn hypotf(x: f32, y: f32) -> f32;
pub safe fn log1p(n: f64) -> f64;
pub safe fn log1pf(n: f32) -> f32;
pub safe fn sinh(n: f64) -> f64;
pub safe fn tan(n: f64) -> f64;
pub safe fn tanh(n: f64) -> f64;
pub safe fn tgamma(n: f64) -> f64;
pub safe fn tgammaf(n: f32) -> f32;
pub safe fn lgamma_r(n: f64, s: &mut i32) -> f64;
#[cfg(not(target_os = "aix"))]
pub safe fn lgammaf_r(n: f32, s: &mut i32) -> f32;
pub safe fn erf(n: f64) -> f64;
pub safe fn erff(n: f32) -> f32;
pub safe fn erfc(n: f64) -> f64;
pub safe fn erfcf(n: f32) -> f32;
pub safe fn acosf128(n: f128) -> f128;
pub safe fn asinf128(n: f128) -> f128;
pub safe fn atanf128(n: f128) -> f128;
pub safe fn atan2f128(a: f128, b: f128) -> f128;
pub safe fn cbrtf128(n: f128) -> f128;
pub safe fn coshf128(n: f128) -> f128;
pub safe fn expm1f128(n: f128) -> f128;
pub safe fn hypotf128(x: f128, y: f128) -> f128;
pub safe fn log1pf128(n: f128) -> f128;
pub safe fn sinhf128(n: f128) -> f128;
pub safe fn tanf128(n: f128) -> f128;
pub safe fn tanhf128(n: f128) -> f128;
pub safe fn tgammaf128(n: f128) -> f128;
pub safe fn lgammaf128_r(n: f128, s: &mut i32) -> f128;
pub safe fn erff128(n: f128) -> f128;
pub safe fn erfcf128(n: f128) -> f128;
}
cfg_select! {
all(target_os = "windows", target_env = "msvc", target_arch = "x86") => {
// On 32-bit x86 MSVC these functions aren't defined, so we just define shims
// which promote everything to f64, perform the calculation, and then demote
// back to f32. While not precisely correct should be "correct enough" for now.
#[inline]
pub fn acosf(n: f32) -> f32 {
f64::acos(n as f64) as f32
}
#[inline]
pub fn asinf(n: f32) -> f32 {
f64::asin(n as f64) as f32
}
#[inline]
pub fn atan2f(n: f32, b: f32) -> f32 {
f64::atan2(n as f64, b as f64) as f32
}
#[inline]
pub fn atanf(n: f32) -> f32 {
f64::atan(n as f64) as f32
}
#[inline]
pub fn coshf(n: f32) -> f32 {
f64::cosh(n as f64) as f32
}
#[inline]
pub fn sinhf(n: f32) -> f32 {
f64::sinh(n as f64) as f32
}
#[inline]
pub fn tanf(n: f32) -> f32 {
f64::tan(n as f64) as f32
}
#[inline]
pub fn tanhf(n: f32) -> f32 {
f64::tanh(n as f64) as f32
}
}
_ => {
unsafe extern "C" {
pub safe fn acosf(n: f32) -> f32;
pub safe fn asinf(n: f32) -> f32;
pub safe fn atan2f(a: f32, b: f32) -> f32;
pub safe fn atanf(n: f32) -> f32;
pub safe fn coshf(n: f32) -> f32;
pub safe fn sinhf(n: f32) -> f32;
pub safe fn tanf(n: f32) -> f32;
pub safe fn tanhf(n: f32) -> f32;
}
}
}
// On AIX, we don't have lgammaf_r only the f64 version, so we can
// use the f64 version lgamma_r
#[cfg(target_os = "aix")]
pub fn lgammaf_r(n: f32, s: &mut i32) -> f32 {
lgamma_r(n.into(), s) as f32
}

View File

@@ -0,0 +1,62 @@
//! The configure builtins provides runtime support compiler-builtin features
//! which require dynamic initialization to work as expected, e.g. aarch64
//! outline-atomics.
/// Enable LSE atomic operations at startup, if supported.
///
/// Linker sections are based on what [`ctor`] does, with priorities to run slightly before user
/// code:
///
/// - Apple uses the section `__mod_init_func`, `mod_init_funcs` is needed to set
/// `S_MOD_INIT_FUNC_POINTERS`. There doesn't seem to be a way to indicate priorities.
/// - Windows uses `.CRT$XCT`, which is run before user constructors (these should use `.CRT$XCU`).
/// - ELF uses `.init_array` with a priority of 90, which runs before our `ARGV_INIT_ARRAY`
/// initializer (priority 99). Both are within the 0-100 implementation-reserved range, per docs
/// for the [`prio-ctor-dtor`] warning, and this matches compiler-rt's `CONSTRUCTOR_PRIORITY`.
///
/// To save startup time, the initializer is only run if outline atomic routines from
/// compiler-builtins may be used. If LSE is known to be available then the calls are never
/// emitted, and if we build the C intrinsics then it has its own initializer using the symbol
/// `__aarch64_have_lse_atomics`.
///
/// Initialization is done in a global constructor to so we get the same behavior regardless of
/// whether Rust's `init` is used, or if we are in a `dylib` or `no_main` situation (as opposed
/// to doing it as part of pre-main startup). This also matches C implementations.
///
/// Ideally `core` would have something similar, but detecting the CPU features requires the
/// auxiliary vector from the OS. We do the initialization in `std` rather than as part of
/// `compiler-builtins` because a builtins->std dependency isn't possible, and inlining parts of
/// `std-detect` would be much messier.
///
/// [`ctor`]: https://github.com/mmastrac/rust-ctor/blob/63382b833ddcbfb8b064f4e86bfa1ed4026ff356/shared/src/macros/mod.rs#L522-L534
/// [`prio-ctor-dtor`]: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
#[cfg(all(
target_arch = "aarch64",
target_feature = "outline-atomics",
not(target_feature = "lse"),
not(feature = "compiler-builtins-c"),
))]
#[used]
#[cfg_attr(target_vendor = "apple", unsafe(link_section = "__DATA,__mod_init_func,mod_init_funcs"))]
#[cfg_attr(target_os = "windows", unsafe(link_section = ".CRT$XCT"))]
#[cfg_attr(
not(any(target_vendor = "apple", target_os = "windows")),
unsafe(link_section = ".init_array.90")
)]
static RUST_LSE_INIT: extern "C" fn() = {
extern "C" fn init_lse() {
use crate::arch;
// This is provided by compiler-builtins::aarch64_outline_atomics.
unsafe extern "C" {
fn __rust_enable_lse();
}
if arch::is_aarch64_feature_detected!("lse") {
unsafe {
__rust_enable_lse();
}
}
}
init_lse
};

31
crates/std/src/sys/env/common.rs vendored Normal file
View File

@@ -0,0 +1,31 @@
use crate::ffi::OsString;
use crate::{fmt, vec};
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
}
impl Env {
pub(super) fn new(env: Vec<(OsString, OsString)>) -> Self {
Env { iter: env.into_iter() }
}
}
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter.as_slice()).finish()
}
}
impl !Send for Env {}
impl !Sync for Env {}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}

62
crates/std/src/sys/env/mod.rs vendored Normal file
View File

@@ -0,0 +1,62 @@
//! Platform-dependent environment variables abstraction.
#![forbid(unsafe_op_in_unsafe_fn)]
#[cfg(any(
target_family = "unix",
target_os = "hermit",
target_os = "motor",
all(target_vendor = "fortanix", target_env = "sgx"),
target_os = "solid_asp3",
target_os = "uefi",
target_os = "wasi",
target_os = "xous",
))]
mod common;
cfg_select! {
target_family = "unix" => {
mod unix;
pub use unix::*;
}
target_family = "windows" => {
mod windows;
pub use windows::*;
}
target_os = "hermit" => {
mod hermit;
pub use hermit::*;
}
target_os = "motor" => {
mod motor;
pub use motor::*;
}
all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx;
pub use sgx::*;
}
target_os = "solid_asp3" => {
mod solid;
pub use solid::*;
}
target_os = "uefi" => {
mod uefi;
pub use uefi::*;
}
target_os = "wasi" => {
mod wasi;
pub use wasi::*;
}
target_os = "xous" => {
mod xous;
pub use xous::*;
}
target_os = "zkvm" => {
mod zkvm;
pub use zkvm::*;
}
_ => {
mod unsupported;
pub use unsupported::*;
}
}

33
crates/std/src/sys/env/unsupported.rs vendored Normal file
View File

@@ -0,0 +1,33 @@
use crate::ffi::{OsStr, OsString};
use crate::{fmt, io};
pub struct Env(!);
impl fmt::Debug for Env {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
}
}
impl Iterator for Env {
type Item = (OsString, OsString);
fn next(&mut self) -> Option<(OsString, OsString)> {
self.0
}
}
pub fn env() -> Env {
panic!("not supported on this platform")
}
pub fn getenv(_: &OsStr) -> Option<OsString> {
None
}
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
}
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
}

View File

@@ -0,0 +1,426 @@
//! Constants associated with each target.
// Replaces the #[else] gate with #[cfg(not(any(…)))] of all the other gates.
// This ensures that they must be mutually exclusive and do not have precedence
// like cfg_select!.
macro cfg_unordered(
$(#[cfg($cfg:meta)] $os:item)*
#[else] $fallback:item
) {
$(#[cfg($cfg)] $os)*
#[cfg(not(any($($cfg),*)))] $fallback
}
// Keep entries sorted alphabetically and mutually exclusive.
cfg_unordered! {
#[cfg(target_os = "aix")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "aix";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".a";
pub const DLL_EXTENSION: &str = "a";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "android")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "android";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "cygwin")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "cygwin";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = ".dll";
pub const DLL_EXTENSION: &str = "dll";
pub const EXE_SUFFIX: &str = ".exe";
pub const EXE_EXTENSION: &str = "exe";
}
#[cfg(target_os = "dragonfly")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "dragonfly";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "emscripten")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "emscripten";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = ".js";
pub const EXE_EXTENSION: &str = "js";
}
#[cfg(target_os = "espidf")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "espidf";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "freebsd")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "freebsd";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "fuchsia")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "fuchsia";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "haiku")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "haiku";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "hermit")]
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "hermit";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = "";
pub const DLL_EXTENSION: &str = "";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "horizon")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "horizon";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = ".elf";
pub const EXE_EXTENSION: &str = "elf";
}
#[cfg(target_os = "hurd")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "hurd";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "illumos")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "illumos";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "ios")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "ios";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".dylib";
pub const DLL_EXTENSION: &str = "dylib";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "l4re")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "l4re";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "linux")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "linux";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "macos")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "macos";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".dylib";
pub const DLL_EXTENSION: &str = "dylib";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "netbsd")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "netbsd";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "nto")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "nto";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "nuttx")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "nuttx";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "openbsd")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "openbsd";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "redox")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "redox";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "rtems")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "rtems";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = ".sgxs";
pub const DLL_EXTENSION: &str = "sgxs";
pub const EXE_SUFFIX: &str = ".sgxs";
pub const EXE_EXTENSION: &str = "sgxs";
}
#[cfg(target_os = "solaris")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "solaris";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "solid_asp3")]
pub mod os {
pub const FAMILY: &str = "itron";
pub const OS: &str = "solid";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "tvos")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "tvos";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".dylib";
pub const DLL_EXTENSION: &str = "dylib";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "uefi")]
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "uefi";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = "";
pub const DLL_EXTENSION: &str = "";
pub const EXE_SUFFIX: &str = ".efi";
pub const EXE_EXTENSION: &str = "efi";
}
#[cfg(target_os = "vexos")]
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "vexos";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = "";
pub const DLL_EXTENSION: &str = "";
pub const EXE_SUFFIX: &str = ".bin";
pub const EXE_EXTENSION: &str = "bin";
}
#[cfg(target_os = "visionos")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "visionos";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".dylib";
pub const DLL_EXTENSION: &str = "dylib";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "vita")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "vita";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = ".elf";
pub const EXE_EXTENSION: &str = "elf";
}
#[cfg(target_os = "vxworks")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "vxworks";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(all(target_family = "wasm", not(any(target_os = "emscripten", target_os = "linux"))))]
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = ".wasm";
pub const DLL_EXTENSION: &str = "wasm";
pub const EXE_SUFFIX: &str = ".wasm";
pub const EXE_EXTENSION: &str = "wasm";
}
#[cfg(target_os = "watchos")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "watchos";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".dylib";
pub const DLL_EXTENSION: &str = "dylib";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "windows")]
pub mod os {
pub const FAMILY: &str = "windows";
pub const OS: &str = "windows";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = ".dll";
pub const DLL_EXTENSION: &str = "dll";
pub const EXE_SUFFIX: &str = ".exe";
pub const EXE_EXTENSION: &str = "exe";
}
#[cfg(target_os = "zkvm")]
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = ".elf";
pub const DLL_EXTENSION: &str = "elf";
pub const EXE_SUFFIX: &str = ".elf";
pub const EXE_EXTENSION: &str = "elf";
}
// The fallback when none of the other gates match.
#[else]
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = "";
pub const DLL_EXTENSION: &str = "";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
}

143
crates/std/src/sys/exit.rs Normal file
View File

@@ -0,0 +1,143 @@
cfg_select! {
target_os = "linux" => {
/// Mitigation for <https://github.com/rust-lang/rust/issues/126600>
///
/// On glibc, `libc::exit` has been observed to not always be thread-safe.
/// It is currently unclear whether that is a glibc bug or allowed by the standard.
/// To mitigate this problem, we ensure that only one
/// Rust thread calls `libc::exit` (or returns from `main`) by calling this function before
/// calling `libc::exit` (or returning from `main`).
///
/// Technically, this is not enough to ensure soundness, since other code directly calling
/// `libc::exit` will still race with this.
///
/// *This function does not itself call `libc::exit`.* This is so it can also be used
/// to guard returning from `main`.
///
/// This function will return only the first time it is called in a process.
///
/// * If it is called again on the same thread as the first call, it will abort.
/// * If it is called again on a different thread, it will wait in a loop
/// (waiting for the process to exit).
pub fn unique_thread_exit() {
use crate::ffi::c_int;
use crate::ptr;
use crate::sync::atomic::AtomicPtr;
use crate::sync::atomic::Ordering::{Acquire, Relaxed};
static EXITING_THREAD_ID: AtomicPtr<c_int> = AtomicPtr::new(ptr::null_mut());
// We use the address of `errno` as a cheap and safe way to identify
// threads. As the C standard mandates that `errno` must have thread
// storage duration, we can rely on its address not changing over the
// lifetime of the thread. Additionally, accesses to `errno` are
// async-signal-safe, so this function is available in all imaginable
// circumstances.
let this_thread_id = crate::sys::io::errno_location();
match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) {
Ok(_) => {
// This is the first thread to call `unique_thread_exit`,
// and this is the first time it is called. Continue exiting.
}
Err(exiting_thread_id) if exiting_thread_id == this_thread_id => {
// This is the first thread to call `unique_thread_exit`,
// but this is the second time it is called.
// Abort the process.
core::panicking::panic_nounwind("std::process::exit called re-entrantly")
}
Err(_) => {
// This is not the first thread to call `unique_thread_exit`.
// Pause until the process exits.
loop {
// Safety: libc::pause is safe to call.
unsafe { libc::pause(); }
}
}
}
}
}
_ => {
/// Mitigation for <https://github.com/rust-lang/rust/issues/126600>
///
/// Mitigation is ***NOT*** implemented on this platform, either because this platform
/// is not affected, or because mitigation is not yet implemented for this platform.
#[cfg_attr(any(test, doctest), expect(dead_code))]
pub fn unique_thread_exit() {
// Mitigation not required on platforms where `exit` is thread-safe.
}
}
}
pub fn exit(code: i32) -> ! {
cfg_select! {
target_os = "hermit" => {
unsafe { hermit_abi::exit(code) }
}
target_os = "linux" => {
unsafe {
unique_thread_exit();
libc::exit(code)
}
}
target_os = "motor" => {
moto_rt::process::exit(code)
}
all(target_vendor = "fortanix", target_env = "sgx") => {
crate::sys::pal::abi::exit_with_code(code as _)
}
target_os = "solid_asp3" => {
rtabort!("exit({}) called", code)
}
target_os = "teeos" => {
let _ = code;
panic!("TA should not call `exit`")
}
target_os = "uefi" => {
use r_efi::base::Status;
use crate::os::uefi::env;
if let (Some(boot_services), Some(handle)) =
(env::boot_services(), env::try_image_handle())
{
let boot_services = boot_services.cast::<r_efi::efi::BootServices>();
let _ = unsafe {
((*boot_services.as_ptr()).exit)(
handle.as_ptr(),
Status::from_usize(code as usize),
0,
crate::ptr::null_mut(),
)
};
}
crate::intrinsics::abort()
}
any(
target_family = "unix",
target_os = "wasi",
) => {
unsafe { libc::exit(code as crate::ffi::c_int) }
}
target_os = "vexos" => {
let _ = code;
unsafe {
vex_sdk::vexSystemExitRequest();
loop {
vex_sdk::vexTasksRun();
}
}
}
target_os = "windows" => {
unsafe { crate::sys::pal::c::ExitProcess(code as u32) }
}
target_os = "xous" => {
crate::os::xous::ffi::exit(code as u32)
}
_ => {
let _ = code;
crate::intrinsics::abort()
}
}
}

View File

@@ -0,0 +1,81 @@
#![allow(dead_code)] // not used on all platforms
use crate::io::{self, Error, ErrorKind};
use crate::path::{Path, PathBuf};
use crate::sys::fs::{File, OpenOptions};
use crate::sys::helpers::ignore_notfound;
use crate::{fmt, fs};
pub(crate) const NOT_FILE_ERROR: Error = io::const_error!(
ErrorKind::InvalidInput,
"the source path is neither a regular file nor a symlink to a regular file",
);
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
let mut reader = fs::File::open(from)?;
let metadata = reader.metadata()?;
if !metadata.is_file() {
return Err(NOT_FILE_ERROR);
}
let mut writer = fs::File::create(to)?;
let perm = metadata.permissions();
let ret = io::copy(&mut reader, &mut writer)?;
writer.set_permissions(perm)?;
Ok(ret)
}
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
let filetype = fs::symlink_metadata(path)?.file_type();
if filetype.is_symlink() { fs::remove_file(path) } else { remove_dir_all_recursive(path) }
}
fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
for child in fs::read_dir(path)? {
let result: io::Result<()> = try {
let child = child?;
if child.file_type()?.is_dir() {
remove_dir_all_recursive(&child.path())?;
} else {
fs::remove_file(&child.path())?;
}
};
// ignore internal NotFound errors to prevent race conditions
if let Err(err) = &result
&& err.kind() != io::ErrorKind::NotFound
{
return result;
}
}
ignore_notfound(fs::remove_dir(path))
}
pub fn exists(path: &Path) -> io::Result<bool> {
match fs::metadata(path) {
Ok(_) => Ok(true),
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
Err(error) => Err(error),
}
}
pub struct Dir {
path: PathBuf,
}
impl Dir {
pub fn open(path: &Path, _opts: &OpenOptions) -> io::Result<Self> {
path.canonicalize().map(|path| Self { path })
}
pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
File::open(&self.path.join(path), &opts)
}
}
impl fmt::Debug for Dir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Dir").field("path", &self.path).finish()
}
}

View File

@@ -0,0 +1,173 @@
#![deny(unsafe_op_in_unsafe_fn)]
use crate::io;
use crate::path::{Path, PathBuf};
pub mod common;
cfg_select! {
any(target_family = "unix", target_os = "wasi") => {
mod unix;
use unix as imp;
#[cfg(not(target_os = "wasi"))]
pub use unix::{chown, fchown, lchown, mkfifo};
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
pub use unix::chroot;
#[cfg(not(target_os = "wasi"))]
pub(crate) use unix::debug_assert_fd_is_open;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(super) use unix::CachedFileMetadata;
use crate::sys::helpers::run_path_with_cstr as with_native_path;
}
target_os = "windows" => {
mod windows;
use windows as imp;
pub use windows::{symlink_inner, junction_point};
use crate::sys::path::with_native_path;
}
target_os = "hermit" => {
mod hermit;
use hermit as imp;
}
target_os = "motor" => {
mod motor;
use motor as imp;
}
target_os = "solid_asp3" => {
mod solid;
use solid as imp;
}
target_os = "uefi" => {
mod uefi;
use uefi as imp;
}
target_os = "vexos" => {
mod vexos;
use vexos as imp;
}
_ => {
mod unsupported;
use unsupported as imp;
}
}
// FIXME: Replace this with platform-specific path conversion functions.
#[cfg(not(any(target_family = "unix", target_os = "windows", target_os = "wasi")))]
#[inline]
pub fn with_native_path<T>(path: &Path, f: &dyn Fn(&Path) -> io::Result<T>) -> io::Result<T> {
f(path)
}
pub use imp::{
Dir, DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
ReadDir,
};
pub fn read_dir(path: &Path) -> io::Result<ReadDir> {
// FIXME: use with_native_path on all platforms
imp::readdir(path)
}
pub fn remove_file(path: &Path) -> io::Result<()> {
with_native_path(path, &imp::unlink)
}
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
with_native_path(old, &|old| with_native_path(new, &|new| imp::rename(old, new)))
}
pub fn remove_dir(path: &Path) -> io::Result<()> {
with_native_path(path, &imp::rmdir)
}
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
// FIXME: use with_native_path on all platforms
#[cfg(not(windows))]
return imp::remove_dir_all(path);
#[cfg(windows)]
with_native_path(path, &imp::remove_dir_all)
}
pub fn read_link(path: &Path) -> io::Result<PathBuf> {
with_native_path(path, &imp::readlink)
}
pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
// FIXME: use with_native_path on all platforms
#[cfg(windows)]
return imp::symlink(original, link);
#[cfg(not(windows))]
with_native_path(original, &|original| {
with_native_path(link, &|link| imp::symlink(original, link))
})
}
pub fn hard_link(original: &Path, link: &Path) -> io::Result<()> {
with_native_path(original, &|original| {
with_native_path(link, &|link| imp::link(original, link))
})
}
pub fn metadata(path: &Path) -> io::Result<FileAttr> {
with_native_path(path, &imp::stat)
}
pub fn symlink_metadata(path: &Path) -> io::Result<FileAttr> {
with_native_path(path, &imp::lstat)
}
pub fn set_permissions(path: &Path, perm: FilePermissions) -> io::Result<()> {
with_native_path(path, &|path| imp::set_perm(path, perm.clone()))
}
#[cfg(all(unix, not(target_os = "vxworks")))]
pub fn set_permissions_nofollow(path: &Path, perm: crate::fs::Permissions) -> io::Result<()> {
use crate::fs::OpenOptions;
let mut options = OpenOptions::new();
// ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it.
// Their filesystems do not have symbolic links, so no special handling is required.
#[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
{
use crate::os::unix::fs::OpenOptionsExt;
options.custom_flags(libc::O_NOFOLLOW);
}
options.open(path)?.set_permissions(perm)
}
#[cfg(any(not(unix), target_os = "vxworks"))]
pub fn set_permissions_nofollow(_path: &Path, _perm: crate::fs::Permissions) -> io::Result<()> {
crate::unimplemented!(
"`set_permissions_nofollow` is currently only implemented on Unix platforms"
)
}
pub fn canonicalize(path: &Path) -> io::Result<PathBuf> {
with_native_path(path, &imp::canonicalize)
}
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
// FIXME: use with_native_path on all platforms
#[cfg(not(windows))]
return imp::copy(from, to);
#[cfg(windows)]
with_native_path(from, &|from| with_native_path(to, &|to| imp::copy(from, to)))
}
pub fn exists(path: &Path) -> io::Result<bool> {
// FIXME: use with_native_path on all platforms
#[cfg(not(windows))]
return imp::exists(path);
#[cfg(windows)]
with_native_path(path, &imp::exists)
}
pub fn set_times(path: &Path, times: FileTimes) -> io::Result<()> {
with_native_path(path, &|path| imp::set_times(path, times.clone()))
}
pub fn set_times_nofollow(path: &Path, times: FileTimes) -> io::Result<()> {
with_native_path(path, &|path| imp::set_times_nofollow(path, times.clone()))
}

View File

@@ -0,0 +1,362 @@
use crate::ffi::OsString;
use crate::fmt;
use crate::fs::TryLockError;
use crate::hash::{Hash, Hasher};
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
use crate::path::{Path, PathBuf};
pub use crate::sys::fs::common::Dir;
use crate::sys::time::SystemTime;
use crate::sys::unsupported;
pub struct File(!);
pub struct FileAttr(!);
pub struct ReadDir(!);
pub struct DirEntry(!);
#[derive(Clone, Debug)]
pub struct OpenOptions {}
#[derive(Copy, Clone, Debug, Default)]
pub struct FileTimes {}
pub struct FilePermissions(!);
pub struct FileType(!);
#[derive(Debug)]
pub struct DirBuilder {}
impl FileAttr {
pub fn size(&self) -> u64 {
self.0
}
pub fn perm(&self) -> FilePermissions {
self.0
}
pub fn file_type(&self) -> FileType {
self.0
}
pub fn modified(&self) -> io::Result<SystemTime> {
self.0
}
pub fn accessed(&self) -> io::Result<SystemTime> {
self.0
}
pub fn created(&self) -> io::Result<SystemTime> {
self.0
}
}
impl Clone for FileAttr {
fn clone(&self) -> FileAttr {
self.0
}
}
impl FilePermissions {
pub fn readonly(&self) -> bool {
self.0
}
pub fn set_readonly(&mut self, _readonly: bool) {
self.0
}
}
impl Clone for FilePermissions {
fn clone(&self) -> FilePermissions {
self.0
}
}
impl PartialEq for FilePermissions {
fn eq(&self, _other: &FilePermissions) -> bool {
self.0
}
}
impl Eq for FilePermissions {}
impl fmt::Debug for FilePermissions {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
}
}
impl FileTimes {
pub fn set_accessed(&mut self, _t: SystemTime) {}
pub fn set_modified(&mut self, _t: SystemTime) {}
}
impl FileType {
pub fn is_dir(&self) -> bool {
self.0
}
pub fn is_file(&self) -> bool {
self.0
}
pub fn is_symlink(&self) -> bool {
self.0
}
}
impl Clone for FileType {
fn clone(&self) -> FileType {
self.0
}
}
impl Copy for FileType {}
impl PartialEq for FileType {
fn eq(&self, _other: &FileType) -> bool {
self.0
}
}
impl Eq for FileType {}
impl Hash for FileType {
fn hash<H: Hasher>(&self, _h: &mut H) {
self.0
}
}
impl fmt::Debug for FileType {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
}
}
impl fmt::Debug for ReadDir {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
}
}
impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
self.0
}
}
impl DirEntry {
pub fn path(&self) -> PathBuf {
self.0
}
pub fn file_name(&self) -> OsString {
self.0
}
pub fn metadata(&self) -> io::Result<FileAttr> {
self.0
}
pub fn file_type(&self) -> io::Result<FileType> {
self.0
}
}
impl OpenOptions {
pub fn new() -> OpenOptions {
OpenOptions {}
}
pub fn read(&mut self, _read: bool) {}
pub fn write(&mut self, _write: bool) {}
pub fn append(&mut self, _append: bool) {}
pub fn truncate(&mut self, _truncate: bool) {}
pub fn create(&mut self, _create: bool) {}
pub fn create_new(&mut self, _create_new: bool) {}
}
impl File {
pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
unsupported()
}
pub fn file_attr(&self) -> io::Result<FileAttr> {
self.0
}
pub fn fsync(&self) -> io::Result<()> {
self.0
}
pub fn datasync(&self) -> io::Result<()> {
self.0
}
pub fn lock(&self) -> io::Result<()> {
self.0
}
pub fn lock_shared(&self) -> io::Result<()> {
self.0
}
pub fn try_lock(&self) -> Result<(), TryLockError> {
self.0
}
pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
self.0
}
pub fn unlock(&self) -> io::Result<()> {
self.0
}
pub fn truncate(&self, _size: u64) -> io::Result<()> {
self.0
}
pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
self.0
}
pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0
}
pub fn is_read_vectored(&self) -> bool {
self.0
}
pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
self.0
}
pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
self.0
}
pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.0
}
pub fn is_write_vectored(&self) -> bool {
self.0
}
pub fn flush(&self) -> io::Result<()> {
self.0
}
pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
self.0
}
pub fn size(&self) -> Option<io::Result<u64>> {
self.0
}
pub fn tell(&self) -> io::Result<u64> {
self.0
}
pub fn duplicate(&self) -> io::Result<File> {
self.0
}
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
self.0
}
pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
self.0
}
}
impl DirBuilder {
pub fn new() -> DirBuilder {
DirBuilder {}
}
pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
unsupported()
}
}
impl fmt::Debug for File {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
}
}
pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
unsupported()
}
pub fn unlink(_p: &Path) -> io::Result<()> {
unsupported()
}
pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
unsupported()
}
pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
match perm.0 {}
}
pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> {
unsupported()
}
pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> {
unsupported()
}
pub fn rmdir(_p: &Path) -> io::Result<()> {
unsupported()
}
pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
unsupported()
}
pub fn exists(_path: &Path) -> io::Result<bool> {
unsupported()
}
pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
unsupported()
}
pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
unsupported()
}
pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
unsupported()
}
pub fn stat(_p: &Path) -> io::Result<FileAttr> {
unsupported()
}
pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
unsupported()
}
pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
unsupported()
}
pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
unsupported()
}

36
crates/std/src/sys/mod.rs Normal file
View File

@@ -0,0 +1,36 @@
pub mod cmath;
pub mod configure_builtins;
pub mod env;
pub mod env_consts;
pub mod exit;
pub mod os_str;
pub mod path;
pub mod sync;
pub mod thread;
pub mod time;
pub mod random;
pub mod thread_local;
pub mod alloc;
// pub mod fs;
/// A trait for viewing representations from std types.
#[cfg_attr(not(target_os = "linux"), allow(unused))]
pub(crate) trait AsInner<Inner: ?Sized> {
fn as_inner(&self) -> &Inner;
}
/// A trait for viewing representations from std types.
#[cfg_attr(not(target_os = "linux"), allow(unused))]
pub(crate) trait AsInnerMut<Inner: ?Sized> {
fn as_inner_mut(&mut self) -> &mut Inner;
}
/// A trait for extracting representations from std types.
pub(crate) trait IntoInner<Inner> {
fn into_inner(self) -> Inner;
}
/// A trait for creating std types from internal representations.
pub(crate) trait FromInner<Inner> {
fn from_inner(inner: Inner) -> Self;
}

View File

@@ -0,0 +1,363 @@
//! The underlying OsString/OsStr implementation on Unix and many other
//! systems: just a `Vec<u8>`/`[u8]`.
use core::clone::CloneToUninit;
use crate::borrow::Cow;
use alloc_crate::bstr::ByteStr;
use alloc_crate::collections::TryReserveError;
use crate::rc::Rc;
use alloc_crate::sync::Arc;
use crate::sys::{AsInner, FromInner, IntoInner};
use crate::{fmt, mem, str};
#[cfg(test)]
mod tests;
#[derive(Hash)]
#[repr(transparent)]
pub struct Buf {
pub inner: Vec<u8>,
}
#[repr(transparent)]
pub struct Slice {
pub inner: [u8],
}
impl IntoInner<Vec<u8>> for Buf {
fn into_inner(self) -> Vec<u8> {
self.inner
}
}
impl FromInner<Vec<u8>> for Buf {
fn from_inner(inner: Vec<u8>) -> Self {
Buf { inner }
}
}
impl AsInner<[u8]> for Buf {
#[inline]
fn as_inner(&self) -> &[u8] {
&self.inner
}
}
impl fmt::Debug for Buf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_slice(), f)
}
}
impl fmt::Display for Buf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.as_slice(), f)
}
}
impl fmt::Debug for Slice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.inner.utf8_chunks().debug(), f)
}
}
impl fmt::Display for Slice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(ByteStr::new(&self.inner), f)
}
}
impl Clone for Buf {
#[inline]
fn clone(&self) -> Self {
Buf { inner: self.inner.clone() }
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.inner.clone_from(&source.inner)
}
}
impl Buf {
#[inline]
pub fn into_encoded_bytes(self) -> Vec<u8> {
self.inner
}
#[inline]
pub unsafe fn from_encoded_bytes_unchecked(s: Vec<u8>) -> Self {
Self { inner: s }
}
#[inline]
pub fn into_string(self) -> Result<String, Buf> {
String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() })
}
#[inline]
pub const fn from_string(s: String) -> Buf {
Buf { inner: s.into_bytes() }
}
#[inline]
pub fn with_capacity(capacity: usize) -> Buf {
Buf { inner: Vec::with_capacity(capacity) }
}
#[inline]
pub fn clear(&mut self) {
self.inner.clear()
}
#[inline]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[inline]
pub fn push_slice(&mut self, s: &Slice) {
self.inner.extend_from_slice(&s.inner)
}
#[inline]
pub fn push_str(&mut self, s: &str) {
self.inner.extend_from_slice(s.as_bytes());
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.inner.reserve(additional)
}
#[inline]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
self.inner.try_reserve(additional)
}
#[inline]
pub fn reserve_exact(&mut self, additional: usize) {
self.inner.reserve_exact(additional)
}
#[inline]
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
self.inner.try_reserve_exact(additional)
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.inner.shrink_to_fit()
}
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.inner.shrink_to(min_capacity)
}
#[inline]
pub fn as_slice(&self) -> &Slice {
// SAFETY: Slice is just a wrapper for [u8],
// and self.inner.as_slice() returns &[u8].
// Therefore, transmuting &[u8] to &Slice is safe.
unsafe { mem::transmute(self.inner.as_slice()) }
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut Slice {
// SAFETY: Slice is just a wrapper for [u8],
// and self.inner.as_mut_slice() returns &mut [u8].
// Therefore, transmuting &mut [u8] to &mut Slice is safe.
unsafe { mem::transmute(self.inner.as_mut_slice()) }
}
#[inline]
pub fn leak<'a>(self) -> &'a mut Slice {
unsafe { mem::transmute(self.inner.leak()) }
}
#[inline]
pub fn into_box(self) -> Box<Slice> {
unsafe { mem::transmute(self.inner.into_boxed_slice()) }
}
#[inline]
pub fn from_box(boxed: Box<Slice>) -> Buf {
let inner: Box<[u8]> = unsafe { mem::transmute(boxed) };
Buf { inner: inner.into_vec() }
}
#[inline]
pub fn into_arc(&self) -> Arc<Slice> {
self.as_slice().into_arc()
}
#[inline]
pub fn into_rc(&self) -> Rc<Slice> {
self.as_slice().into_rc()
}
/// Provides plumbing to `Vec::truncate` without giving full mutable access
/// to the `Vec`.
///
/// # Safety
///
/// The length must be at an `OsStr` boundary, according to
/// `Slice::check_public_boundary`.
#[inline]
pub unsafe fn truncate_unchecked(&mut self, len: usize) {
self.inner.truncate(len);
}
/// Provides plumbing to `Vec::extend_from_slice` without giving full
/// mutable access to the `Vec`.
///
/// # Safety
///
/// The slice must be valid for the platform encoding (as described in
/// `OsStr::from_encoded_bytes_unchecked`). This encoding has no safety
/// requirements.
#[inline]
pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) {
self.inner.extend_from_slice(other);
}
}
impl Slice {
#[inline]
pub fn as_encoded_bytes(&self) -> &[u8] {
&self.inner
}
#[inline]
pub unsafe fn from_encoded_bytes_unchecked(s: &[u8]) -> &Slice {
unsafe { mem::transmute(s) }
}
#[track_caller]
#[inline]
pub fn check_public_boundary(&self, index: usize) {
if index == 0 || index == self.inner.len() {
return;
}
if index < self.inner.len()
&& (self.inner[index - 1].is_ascii() || self.inner[index].is_ascii())
{
return;
}
slow_path(&self.inner, index);
/// We're betting that typical splits will involve an ASCII character.
///
/// Putting the expensive checks in a separate function generates notably
/// better assembly.
#[track_caller]
#[inline(never)]
fn slow_path(bytes: &[u8], index: usize) {
let (before, after) = bytes.split_at(index);
// UTF-8 takes at most 4 bytes per codepoint, so we don't
// need to check more than that.
let after = after.get(..4).unwrap_or(after);
match str::from_utf8(after) {
Ok(_) => return,
Err(err) if err.valid_up_to() != 0 => return,
Err(_) => (),
}
for len in 2..=4.min(index) {
let before = &before[index - len..];
if str::from_utf8(before).is_ok() {
return;
}
}
panic!("byte index {index} is not an OsStr boundary");
}
}
#[inline]
pub fn from_str(s: &str) -> &Slice {
unsafe { Slice::from_encoded_bytes_unchecked(s.as_bytes()) }
}
#[inline]
pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> {
str::from_utf8(&self.inner)
}
#[inline]
pub fn to_string_lossy(&self) -> Cow<'_, str> {
String::from_utf8_lossy(&self.inner)
}
#[inline]
pub fn to_owned(&self) -> Buf {
Buf { inner: self.inner.to_vec() }
}
#[inline]
pub fn clone_into(&self, buf: &mut Buf) {
self.inner.clone_into(&mut buf.inner)
}
#[inline]
pub fn empty_box() -> Box<Slice> {
let boxed: Box<[u8]> = Default::default();
unsafe { mem::transmute(boxed) }
}
#[inline]
pub fn into_arc(&self) -> Arc<Slice> {
let arc: Arc<[u8]> = Arc::from(&self.inner);
unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) }
}
#[inline]
pub fn into_rc(&self) -> Rc<Slice> {
let rc: Rc<[u8]> = Rc::from(&self.inner);
unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) }
}
#[inline]
pub fn make_ascii_lowercase(&mut self) {
self.inner.make_ascii_lowercase()
}
#[inline]
pub fn make_ascii_uppercase(&mut self) {
self.inner.make_ascii_uppercase()
}
#[inline]
pub fn to_ascii_lowercase(&self) -> Buf {
Buf { inner: self.inner.to_ascii_lowercase() }
}
#[inline]
pub fn to_ascii_uppercase(&self) -> Buf {
Buf { inner: self.inner.to_ascii_uppercase() }
}
#[inline]
pub fn is_ascii(&self) -> bool {
self.inner.is_ascii()
}
#[inline]
pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
self.inner.eq_ignore_ascii_case(&other.inner)
}
}
#[unstable(feature = "clone_to_uninit", issue = "126799")]
unsafe impl CloneToUninit for Slice {
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
unsafe fn clone_to_uninit(&self, dst: *mut u8) {
// SAFETY: we're just a transparent wrapper around [u8]
unsafe { self.inner.clone_to_uninit(dst) }
}
}

View File

@@ -0,0 +1,17 @@
use super::*;
#[test]
fn slice_debug_output() {
let input = unsafe { Slice::from_encoded_bytes_unchecked(b"\xF0hello,\tworld") };
let expected = r#""\xF0hello,\tworld""#;
let output = format!("{input:?}");
assert_eq!(output, expected);
}
#[test]
fn display() {
assert_eq!("Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye", unsafe {
Slice::from_encoded_bytes_unchecked(b"Hello\xC0\x80 There\xE6\x83 Goodbye").to_string()
},);
}

View File

@@ -0,0 +1,16 @@
#![forbid(unsafe_op_in_unsafe_fn)]
cfg_select! {
any(target_os = "windows", target_os = "uefi") => {
mod wtf8;
pub use wtf8::{Buf, Slice};
}
any(target_os = "motor") => {
mod utf8;
pub use utf8::{Buf, Slice};
}
_ => {
mod bytes;
pub use bytes::{Buf, Slice};
}
}

View File

@@ -0,0 +1,28 @@
cfg_select! {
target_os = "windows" => {
mod windows;
mod windows_prefix;
pub use windows::*;
}
all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx;
pub use sgx::*;
}
target_os = "solid_asp3" => {
mod unsupported_backslash;
pub use unsupported_backslash::*;
}
target_os = "uefi" => {
mod uefi;
pub use uefi::*;
}
target_os = "cygwin" => {
mod cygwin;
mod windows_prefix;
pub use cygwin::*;
}
_ => {
mod unix;
pub use unix::*;
}
}

View File

@@ -0,0 +1,72 @@
use crate::ffi::OsStr;
use crate::path::{Path, PathBuf, Prefix};
use crate::{env, io};
#[inline]
pub fn is_sep_byte(b: u8) -> bool {
b == b'/'
}
#[inline]
pub fn is_verbatim_sep(b: u8) -> bool {
b == b'/'
}
#[inline]
pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
None
}
pub const HAS_PREFIXES: bool = false;
pub const MAIN_SEP_STR: &str = "/";
pub const MAIN_SEP: char = '/';
/// Make a POSIX path absolute without changing its semantics.
pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
// This is mostly a wrapper around collecting `Path::components`, with
// exceptions made where this conflicts with the POSIX specification.
// See 4.13 Pathname Resolution, IEEE Std 1003.1-2017
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
// Get the components, skipping the redundant leading "." component if it exists.
let mut components = path.strip_prefix(".").unwrap_or(path).components();
let path_os = path.as_os_str().as_encoded_bytes();
let mut normalized = if path.is_absolute() {
// "If a pathname begins with two successive <slash> characters, the
// first component following the leading <slash> characters may be
// interpreted in an implementation-defined manner, although more than
// two leading <slash> characters shall be treated as a single <slash>
// character."
if path_os.starts_with(b"//") && !path_os.starts_with(b"///") {
components.next();
PathBuf::from("//")
} else {
PathBuf::new()
}
} else {
// env::current_dir()?
todo!()
};
normalized.extend(components);
// "Interfaces using pathname resolution may specify additional constraints
// when a pathname that does not name an existing directory contains at
// least one non- <slash> character and contains one or more trailing
// <slash> characters".
// A trailing <slash> is also meaningful if "a symbolic link is
// encountered during pathname resolution".
if path_os.ends_with(b"/") {
normalized.push("");
}
Ok(normalized)
}
pub(crate) fn is_absolute(path: &Path) -> bool {
if cfg!(any(unix, target_os = "hermit", target_os = "wasi", target_os = "motor")) {
path.has_root()
} else {
path.has_root() && path.prefix().is_some()
}
}

View File

@@ -0,0 +1,134 @@
cfg_select! {
// Tier 1
any(target_os = "linux", target_os = "android") => {
mod linux;
pub use linux::{fill_bytes, hashmap_random_keys};
}
target_os = "windows" => {
mod windows;
pub use windows::fill_bytes;
}
target_vendor = "apple" => {
mod apple;
pub use apple::fill_bytes;
// Others, in alphabetical ordering.
}
any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "haiku",
target_os = "illumos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "rtems",
target_os = "solaris",
target_os = "vita",
target_os = "nuttx",
) => {
mod arc4random;
pub use arc4random::fill_bytes;
}
target_os = "emscripten" => {
mod getentropy;
pub use getentropy::fill_bytes;
}
target_os = "espidf" => {
mod espidf;
pub use espidf::fill_bytes;
}
target_os = "fuchsia" => {
mod fuchsia;
pub use fuchsia::fill_bytes;
}
target_os = "hermit" => {
mod hermit;
pub use hermit::fill_bytes;
}
any(target_os = "horizon", target_os = "cygwin") => {
// FIXME(horizon): add arc4random_buf to shim-3ds
mod getrandom;
pub use getrandom::fill_bytes;
}
any(
target_os = "aix",
target_os = "hurd",
target_os = "l4re",
target_os = "nto",
) => {
mod unix_legacy;
pub use unix_legacy::fill_bytes;
}
target_os = "redox" => {
mod redox;
pub use redox::fill_bytes;
}
target_os = "motor" => {
mod motor;
pub use motor::fill_bytes;
}
all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx;
pub use sgx::fill_bytes;
}
target_os = "solid_asp3" => {
mod solid;
pub use solid::fill_bytes;
}
target_os = "teeos" => {
mod teeos;
pub use teeos::fill_bytes;
}
target_os = "trusty" => {
mod trusty;
pub use trusty::fill_bytes;
}
target_os = "uefi" => {
mod uefi;
pub use uefi::fill_bytes;
}
target_os = "vxworks" => {
mod vxworks;
pub use vxworks::fill_bytes;
}
all(target_os = "wasi", target_env = "p1") => {
mod wasip1;
pub use wasip1::fill_bytes;
}
all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => {
mod wasip2;
pub use wasip2::{fill_bytes, hashmap_random_keys};
}
target_os = "zkvm" => {
mod zkvm;
pub use zkvm::fill_bytes;
}
any(
all(target_family = "wasm", target_os = "unknown"),
target_os = "xous",
target_os = "vexos",
target_os = "survos",
) => {
// FIXME: finally remove std support for wasm32-unknown-unknown
// FIXME: add random data generation to xous
mod unsupported;
pub use unsupported::{fill_bytes, hashmap_random_keys};
}
_ => {}
}
#[cfg(not(any(
target_os = "linux",
target_os = "android",
all(target_family = "wasm", target_os = "unknown"),
all(target_os = "wasi", not(target_env = "p1")),
target_os = "xous",
target_os = "vexos",
target_os = "survos",
)))]
pub fn hashmap_random_keys() -> (u64, u64) {
let mut buf = [0; 16];
fill_bytes(&mut buf);
let k1 = u64::from_ne_bytes(buf[..8].try_into().unwrap());
let k2 = u64::from_ne_bytes(buf[8..].try_into().unwrap());
(k1, k2)
}

View File

@@ -0,0 +1,15 @@
use crate::ptr;
pub fn fill_bytes(_: &mut [u8]) {
panic!("this target does not support random data generation");
}
pub fn hashmap_random_keys() -> (u64, u64) {
// Use allocation addresses for a bit of randomness. This isn't
// particularly secure, but there isn't really an alternative.
let stack = 0u8;
let heap = Box::new(0u8);
let k1 = ptr::from_ref(&stack).addr() as u64;
let k2 = ptr::from_ref(&*heap).addr() as u64;
(k1, k2)
}

View File

@@ -0,0 +1,44 @@
cfg_select! {
any(
all(target_os = "windows", not(target_vendor="win7")),
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "motor",
target_os = "fuchsia",
all(target_family = "wasm", target_feature = "atomics"),
target_os = "hermit",
) => {
mod futex;
pub use futex::Condvar;
}
any(
target_family = "unix",
target_os = "teeos",
) => {
mod pthread;
pub use pthread::Condvar;
}
all(target_os = "windows", target_vendor = "win7") => {
mod windows7;
pub use windows7::Condvar;
}
all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx;
pub use sgx::Condvar;
}
target_os = "solid_asp3" => {
mod itron;
pub use itron::Condvar;
}
target_os = "xous" => {
mod xous;
pub use xous::Condvar;
}
_ => {
mod no_threads;
pub use no_threads::Condvar;
}
}

View File

@@ -0,0 +1,27 @@
use crate::sys::sync::Mutex;
use crate::thread::sleep;
use crate::time::Duration;
pub struct Condvar {}
impl Condvar {
#[inline]
pub const fn new() -> Condvar {
Condvar {}
}
#[inline]
pub fn notify_one(&self) {}
#[inline]
pub fn notify_all(&self) {}
pub unsafe fn wait(&self, _mutex: &Mutex) {
panic!("condvar wait not supported")
}
pub unsafe fn wait_timeout(&self, _mutex: &Mutex, dur: Duration) -> bool {
sleep(dur);
false
}
}

View File

@@ -0,0 +1,9 @@
mod condvar;
mod mutex;
mod once;
mod rwlock;
pub use condvar::Condvar;
pub use mutex::Mutex;
pub use once::{Once, OnceState};
pub use rwlock::RwLock;

View File

@@ -0,0 +1,47 @@
cfg_select! {
any(
all(target_os = "windows", not(target_vendor = "win7")),
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "openbsd",
target_os = "motor",
target_os = "dragonfly",
all(target_family = "wasm", target_feature = "atomics"),
target_os = "hermit",
) => {
mod futex;
pub use futex::Mutex;
}
target_os = "fuchsia" => {
mod fuchsia;
pub use fuchsia::Mutex;
}
any(
target_family = "unix",
target_os = "teeos",
) => {
mod pthread;
pub use pthread::Mutex;
}
all(target_os = "windows", target_vendor = "win7") => {
mod windows7;
pub use windows7::{Mutex, raw};
}
all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx;
pub use sgx::Mutex;
}
target_os = "solid_asp3" => {
mod itron;
pub use itron::Mutex;
}
target_os = "xous" => {
mod xous;
pub use xous::Mutex;
}
_ => {
mod no_threads;
pub use no_threads::Mutex;
}
}

View File

@@ -0,0 +1,31 @@
use crate::cell::Cell;
pub struct Mutex {
// This platform has no threads, so we can use a Cell here.
locked: Cell<bool>,
}
unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {} // no threads on this platform
impl Mutex {
#[inline]
pub const fn new() -> Mutex {
Mutex { locked: Cell::new(false) }
}
#[inline]
pub fn lock(&self) {
assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex");
}
#[inline]
pub unsafe fn unlock(&self) {
self.locked.set(false);
}
#[inline]
pub fn try_lock(&self) -> bool {
self.locked.replace(true) == false
}
}

View File

@@ -0,0 +1,40 @@
// A "once" is a relatively simple primitive, and it's also typically provided
// by the OS as well (see `pthread_once` or `InitOnceExecuteOnce`). The OS
// primitives, however, tend to have surprising restrictions, such as the Unix
// one doesn't allow an argument to be passed to the function.
//
// As a result, we end up implementing it ourselves in the standard library.
// This also gives us the opportunity to optimize the implementation a bit which
// should help the fast path on call sites.
cfg_select! {
any(
all(target_os = "windows", not(target_vendor="win7")),
target_os = "linux",
target_os = "android",
all(target_arch = "wasm32", target_feature = "atomics"),
target_os = "freebsd",
target_os = "motor",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "fuchsia",
target_os = "hermit",
) => {
mod futex;
pub use futex::{Once, OnceState};
}
any(
windows,
target_family = "unix",
all(target_vendor = "fortanix", target_env = "sgx"),
target_os = "solid_asp3",
target_os = "xous",
) => {
mod queue;
pub use queue::{Once, OnceState};
}
_ => {
mod no_threads;
pub use no_threads::{Once, OnceState};
}
}

View File

@@ -0,0 +1,114 @@
use crate::cell::Cell;
use crate::sync as public;
use crate::sync::once::OnceExclusiveState;
pub struct Once {
state: Cell<State>,
}
pub struct OnceState {
poisoned: bool,
set_state_to: Cell<State>,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum State {
Incomplete,
Poisoned,
Running,
Complete,
}
struct CompletionGuard<'a> {
state: &'a Cell<State>,
set_state_on_drop_to: State,
}
impl<'a> Drop for CompletionGuard<'a> {
fn drop(&mut self) {
self.state.set(self.set_state_on_drop_to);
}
}
// Safety: threads are not supported on this platform.
unsafe impl Sync for Once {}
impl Once {
#[inline]
pub const fn new() -> Once {
Once { state: Cell::new(State::Incomplete) }
}
#[inline]
pub fn is_completed(&self) -> bool {
self.state.get() == State::Complete
}
#[inline]
pub(crate) fn state(&mut self) -> OnceExclusiveState {
match self.state.get() {
State::Incomplete => OnceExclusiveState::Incomplete,
State::Poisoned => OnceExclusiveState::Poisoned,
State::Complete => OnceExclusiveState::Complete,
_ => unreachable!("invalid Once state"),
}
}
#[inline]
pub(crate) fn set_state(&mut self, new_state: OnceExclusiveState) {
self.state.set(match new_state {
OnceExclusiveState::Incomplete => State::Incomplete,
OnceExclusiveState::Poisoned => State::Poisoned,
OnceExclusiveState::Complete => State::Complete,
});
}
#[cold]
#[track_caller]
pub fn wait(&self, _ignore_poisoning: bool) {
panic!("not implementable on this target");
}
#[cold]
#[track_caller]
pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) {
let state = self.state.get();
match state {
State::Poisoned if !ignore_poisoning => {
// Panic to propagate the poison.
panic!("Once instance has previously been poisoned");
}
State::Incomplete | State::Poisoned => {
self.state.set(State::Running);
// `guard` will set the new state on drop.
let mut guard =
CompletionGuard { state: &self.state, set_state_on_drop_to: State::Poisoned };
// Run the function, letting it know if we're poisoned or not.
let f_state = public::OnceState {
inner: OnceState {
poisoned: state == State::Poisoned,
set_state_to: Cell::new(State::Complete),
},
};
f(&f_state);
guard.set_state_on_drop_to = f_state.inner.set_state_to.get();
}
State::Running => {
panic!("one-time initialization may not be performed recursively");
}
State::Complete => {}
}
}
}
impl OnceState {
#[inline]
pub fn is_poisoned(&self) -> bool {
self.poisoned
}
#[inline]
pub fn poison(&self) {
self.set_state_to.set(State::Poisoned)
}
}

View File

@@ -0,0 +1,35 @@
cfg_select! {
any(
all(target_os = "windows", not(target_vendor = "win7")),
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "fuchsia",
all(target_family = "wasm", target_feature = "atomics"),
target_os = "hermit",
target_os = "motor",
) => {
mod futex;
pub use futex::RwLock;
}
any(
target_family = "unix",
all(target_os = "windows", target_vendor = "win7"),
all(target_vendor = "fortanix", target_env = "sgx"),
target_os = "xous",
target_os = "teeos",
) => {
mod queue;
pub use queue::RwLock;
}
target_os = "solid_asp3" => {
mod solid;
pub use solid::RwLock;
}
_ => {
mod no_threads;
pub use no_threads::RwLock;
}
}

View File

@@ -0,0 +1,69 @@
use crate::cell::Cell;
pub struct RwLock {
// This platform has no threads, so we can use a Cell here.
mode: Cell<isize>,
}
unsafe impl Send for RwLock {}
unsafe impl Sync for RwLock {} // no threads on this platform
impl RwLock {
#[inline]
pub const fn new() -> RwLock {
RwLock { mode: Cell::new(0) }
}
#[inline]
pub fn read(&self) {
let m = self.mode.get();
if m >= 0 {
self.mode.set(m + 1);
} else {
rtabort!("rwlock locked for writing");
}
}
#[inline]
pub fn try_read(&self) -> bool {
let m = self.mode.get();
if m >= 0 {
self.mode.set(m + 1);
true
} else {
false
}
}
#[inline]
pub fn write(&self) {
if self.mode.replace(-1) != 0 {
rtabort!("rwlock locked for reading")
}
}
#[inline]
pub fn try_write(&self) -> bool {
if self.mode.get() == 0 {
self.mode.set(-1);
true
} else {
false
}
}
#[inline]
pub unsafe fn read_unlock(&self) {
self.mode.set(self.mode.get() - 1);
}
#[inline]
pub unsafe fn write_unlock(&self) {
assert_eq!(self.mode.replace(0), -1);
}
#[inline]
pub unsafe fn downgrade(&self) {
assert_eq!(self.mode.replace(1), -1);
}
}

View File

@@ -0,0 +1,154 @@
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};
}
target_os = "motor" => {
mod motor;
pub use motor::*;
}
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};
}
any(target_family = "unix", target_os = "wasi") => {
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",
target_os = "wasi",
)))]
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 = "vxworks",
target_os = "wasi",
target_vendor = "apple",
))]
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",
target_os = "wasi",
))]
pub use unsupported::set_name;
}
target_os = "vexos" => {
mod vexos;
pub use vexos::{sleep, sleep_until, yield_now};
#[expect(dead_code)]
mod unsupported;
pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, 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 = "vxworks",
target_os = "wasi",
target_vendor = "apple",
target_os = "motor",
target_os = "vexos"
)))]
pub fn sleep_until(deadline: crate::time::Instant) {
use crate::time::Instant;
// The clock source used for `sleep` might not be the same used for `Instant`.
// Since this function *must not* return before the deadline, we recheck the
// time after every call to `sleep`. See #149935 for an example of this
// occurring on older Windows systems.
while let Some(delay) = deadline.checked_duration_since(Instant::now()) {
// Sleep for the estimated time remaining until the deadline.
//
// If your system has a better way of estimating the delay time or
// provides a way to sleep until an absolute time, specialize this
// function for your system.
sleep(delay);
}
}

View File

@@ -0,0 +1,46 @@
use crate::ffi::CStr;
use crate::io;
use crate::num::NonZero;
use crate::thread::ThreadInit;
use crate::time::Duration;
// Silence dead code warnings for the otherwise unused ThreadInit::init() call.
#[expect(dead_code)]
fn dummy_init_call(init: Box<ThreadInit>) {
drop(init.init());
}
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, _init: Box<ThreadInit>) -> io::Result<Thread> {
Err(io::Error::UNSUPPORTED_PLATFORM)
}
pub fn join(self) {
self.0
}
}
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
Err(io::Error::UNKNOWN_THREAD_COUNT)
}
pub fn current_os_id() -> Option<u64> {
None
}
pub fn yield_now() {
// do nothing
}
pub fn set_name(_name: &CStr) {
// nope
}
pub fn sleep(_dur: Duration) {
panic!("can't sleep");
}

View File

@@ -0,0 +1,227 @@
//! Implementation of the `thread_local` macro.
//!
//! There are three different thread-local implementations:
//! * Some targets lack threading support, and hence have only one thread, so
//! the TLS data is stored in a normal `static`.
//! * Some targets support TLS natively via the dynamic linker and C runtime.
//! * On some targets, the OS provides a library-based TLS implementation. The
//! TLS data is heap-allocated and referenced using a TLS key.
//!
//! Each implementation provides a macro which generates the `LocalKey` `const`
//! used to reference the TLS variable, along with the necessary helper structs
//! to track the initialization/destruction state of the variable.
//!
//! Additionally, this module contains abstractions for the OS interfaces used
//! for these implementations.
#![cfg_attr(test, allow(unused))]
#![doc(hidden)]
#![forbid(unsafe_op_in_unsafe_fn)]
#![unstable(
feature = "thread_local_internals",
reason = "internal details of the thread_local macro",
issue = "none"
)]
cfg_select! {
any(
all(target_family = "wasm", not(target_feature = "atomics")),
target_os = "uefi",
target_os = "zkvm",
target_os = "trusty",
target_os = "vexos",
target_os = "survos",
) => {
mod no_threads;
pub use no_threads::{EagerStorage, LazyStorage, thread_local_inner};
pub(crate) use no_threads::{LocalPointer, local_pointer};
}
target_thread_local => {
mod native;
pub use native::{EagerStorage, LazyStorage, thread_local_inner};
pub(crate) use native::{LocalPointer, local_pointer};
}
_ => {
mod os;
pub use os::{Storage, thread_local_inner, value_align};
pub(crate) use os::{LocalPointer, local_pointer};
}
}
/// The native TLS implementation needs a way to register destructors for its data.
/// This module contains platform-specific implementations of that register.
///
/// It turns out however that most platforms don't have a way to register a
/// destructor for each variable. On these platforms, we keep track of the
/// destructors ourselves and register (through the [`guard`] module) only a
/// single callback that runs all of the destructors in the list.
#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))]
pub(crate) mod destructors {
cfg_select! {
any(
target_os = "linux",
target_os = "android",
target_os = "fuchsia",
target_os = "redox",
target_os = "hurd",
target_os = "netbsd",
target_os = "dragonfly"
) => {
mod linux_like;
mod list;
pub(super) use linux_like::register;
pub(super) use list::run;
}
_ => {
mod list;
pub(super) use list::register;
pub(crate) use list::run;
}
}
}
/// This module provides a way to schedule the execution of the destructor list
/// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable`
/// should ensure that these functions are called at the right times.
pub(crate) mod guard {
cfg_select! {
all(target_thread_local, target_vendor = "apple") => {
mod apple;
pub(crate) use apple::enable;
}
target_os = "windows" => {
mod windows;
pub(crate) use windows::enable;
}
any(
all(target_family = "wasm", not(
all(target_os = "wasi", target_env = "p1", target_feature = "atomics")
)),
target_os = "uefi",
target_os = "zkvm",
target_os = "trusty",
target_os = "vexos",
) => {
pub(crate) fn enable() {
// FIXME: Right now there is no concept of "thread exit" on
// wasm, but this is likely going to show up at some point in
// the form of an exported symbol that the wasm runtime is going
// to be expected to call. For now we just leak everything, but
// if such a function starts to exist it will probably need to
// iterate the destructor list with these functions:
#[cfg(all(target_family = "wasm", target_feature = "atomics"))]
#[allow(unused)]
use super::destructors::run;
#[allow(unused)]
use crate::rt::thread_cleanup;
}
}
any(
target_os = "hermit",
target_os = "xous",
) => {
// `std` is the only runtime, so it just calls the destructor functions
// itself when the time comes.
pub(crate) fn enable() {}
}
target_os = "solid_asp3" => {
mod solid;
pub(crate) use solid::enable;
}
target_os = "survos" => {
// todo
pub(crate) fn enable() {}
}
_ => {
mod key;
pub(crate) use key::enable;
}
}
}
/// `const`-creatable TLS keys.
///
/// Most OSs without native TLS will provide a library-based way to create TLS
/// storage. For each TLS variable, we create a key, which can then be used to
/// reference an entry in a thread-local table. This then associates each key
/// with a pointer which we can get and set to store our data.
pub(crate) mod key {
cfg_select! {
any(
all(
not(target_vendor = "apple"),
not(target_family = "wasm"),
target_family = "unix",
),
all(not(target_thread_local), target_vendor = "apple"),
target_os = "teeos",
all(target_os = "wasi", target_env = "p1", target_feature = "atomics"),
) => {
mod racy;
mod unix;
#[cfg(test)]
mod tests;
pub(super) use racy::LazyKey;
pub(super) use unix::{Key, set};
#[cfg(any(not(target_thread_local), test))]
pub(super) use unix::get;
use unix::{create, destroy};
}
all(not(target_thread_local), target_os = "windows") => {
#[cfg(test)]
mod tests;
mod windows;
pub(super) use windows::{Key, LazyKey, get, run_dtors, set};
}
all(target_vendor = "fortanix", target_env = "sgx") => {
mod racy;
mod sgx;
#[cfg(test)]
mod tests;
pub(super) use racy::LazyKey;
pub(super) use sgx::{Key, get, set};
use sgx::{create, destroy};
}
target_os = "xous" => {
mod racy;
#[cfg(test)]
mod tests;
mod xous;
pub(super) use racy::LazyKey;
pub(crate) use xous::destroy_tls;
pub(super) use xous::{Key, get, set};
use xous::{create, destroy};
}
target_os = "motor" => {
mod racy;
#[cfg(test)]
mod tests;
pub(super) use racy::LazyKey;
pub(super) use moto_rt::tls::{Key, get, set};
use moto_rt::tls::{create, destroy};
}
_ => {}
}
}
/// Run a callback in a scenario which must not unwind (such as a `extern "C"
/// fn` declared in a user crate). If the callback unwinds anyway, then
/// `rtabort` with a message about thread local panicking on drop.
#[inline]
#[allow(dead_code)]
fn abort_on_dtor_unwind(f: impl FnOnce()) {
// Using a guard like this is lower cost.
let guard = DtorUnwindGuard;
f();
core::mem::forget(guard);
struct DtorUnwindGuard;
impl Drop for DtorUnwindGuard {
#[inline]
fn drop(&mut self) {
// This is not terribly descriptive, but it doesn't need to be as we'll
// already have printed a panic message at this point.
rtabort!("thread local panicked on drop");
}
}
}

View File

@@ -0,0 +1,150 @@
//! On some targets like wasm there's no threads, so no need to generate
//! thread locals and we can instead just use plain statics!
use crate::cell::{Cell, UnsafeCell};
use crate::mem::MaybeUninit;
use crate::ptr;
#[doc(hidden)]
#[allow_internal_unstable(thread_local_internals)]
#[allow_internal_unsafe]
#[unstable(feature = "thread_local_internals", issue = "none")]
#[rustc_macro_transparency = "semiopaque"]
pub macro thread_local_inner {
// used to generate the `LocalKey` value for const-initialized thread locals
(@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{
const __RUST_STD_INTERNAL_INIT: $t = $init;
// NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed.
unsafe {
$crate::thread::LocalKey::new(|_| {
$(#[$align_attr])*
static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::EagerStorage<$t> =
$crate::thread::local_impl::EagerStorage { value: __RUST_STD_INTERNAL_INIT };
&__RUST_STD_INTERNAL_VAL.value
})
}
}},
// used to generate the `LocalKey` value for `thread_local!`
(@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{
#[inline]
fn __rust_std_internal_init_fn() -> $t { $init }
unsafe {
$crate::thread::LocalKey::new(|__rust_std_internal_init| {
$(#[$align_attr])*
static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::LazyStorage<$t> = $crate::thread::local_impl::LazyStorage::new();
__RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, __rust_std_internal_init_fn)
})
}
}},
}
#[allow(missing_debug_implementations)]
#[repr(transparent)] // Required for correctness of `#[rustc_align_static]`
pub struct EagerStorage<T> {
pub value: T,
}
// SAFETY: the target doesn't have threads.
unsafe impl<T> Sync for EagerStorage<T> {}
#[derive(Clone, Copy, PartialEq, Eq)]
enum State {
Initial,
Alive,
Destroying,
}
#[allow(missing_debug_implementations)]
#[repr(C)]
pub struct LazyStorage<T> {
// This field must be first, for correctness of `#[rustc_align_static]`
value: UnsafeCell<MaybeUninit<T>>,
state: Cell<State>,
}
impl<T> LazyStorage<T> {
pub const fn new() -> LazyStorage<T> {
LazyStorage {
value: UnsafeCell::new(MaybeUninit::uninit()),
state: Cell::new(State::Initial),
}
}
/// Gets a pointer to the TLS value, potentially initializing it with the
/// provided parameters.
///
/// The resulting pointer may not be used after reentrant inialialization
/// has occurred.
#[inline]
pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
if self.state.get() == State::Alive {
self.value.get() as *const T
} else {
self.initialize(i, f)
}
}
#[cold]
fn initialize(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
let value = i.and_then(Option::take).unwrap_or_else(f);
// Destroy the old value if it is initialized
// FIXME(#110897): maybe panic on recursive initialization.
if self.state.get() == State::Alive {
self.state.set(State::Destroying);
// Safety: we check for no initialization during drop below
unsafe {
ptr::drop_in_place(self.value.get() as *mut T);
}
self.state.set(State::Initial);
}
// Guard against initialization during drop
if self.state.get() == State::Destroying {
panic!("Attempted to initialize thread-local while it is being dropped");
}
unsafe {
self.value.get().write(MaybeUninit::new(value));
}
self.state.set(State::Alive);
self.value.get() as *const T
}
}
// SAFETY: the target doesn't have threads.
unsafe impl<T> Sync for LazyStorage<T> {}
#[rustc_macro_transparency = "semiopaque"]
pub(crate) macro local_pointer {
() => {},
($vis:vis static $name:ident; $($rest:tt)*) => {
$vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
$crate::sys::thread_local::local_pointer! { $($rest)* }
},
}
pub(crate) struct LocalPointer {
p: Cell<*mut ()>,
}
impl LocalPointer {
pub const fn __new() -> LocalPointer {
LocalPointer { p: Cell::new(ptr::null_mut()) }
}
pub fn get(&self) -> *mut () {
self.p.get()
}
pub fn set(&self, p: *mut ()) {
self.p.set(p)
}
}
// SAFETY: the target doesn't have threads.
unsafe impl Sync for LocalPointer {}

View File

@@ -0,0 +1,289 @@
use super::key::{Key, LazyKey, get, set};
use super::{abort_on_dtor_unwind, guard};
use crate::alloc::{self, GlobalAlloc, Layout, System};
use crate::cell::Cell;
use crate::marker::PhantomData;
use crate::mem::ManuallyDrop;
use crate::ops::Deref;
use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use crate::ptr::{self, NonNull};
#[doc(hidden)]
#[allow_internal_unstable(thread_local_internals)]
#[allow_internal_unsafe]
#[unstable(feature = "thread_local_internals", issue = "none")]
#[rustc_macro_transparency = "semiopaque"]
pub macro thread_local_inner {
// NOTE: we cannot import `Storage` or `LocalKey` with a `use` because that can shadow user
// provided type or type alias with a matching name. Please update the shadowing test in
// `tests/thread.rs` if these types are renamed.
// used to generate the `LocalKey` value for `thread_local!`.
(@key $t:ty, $($(#[$($align_attr:tt)*])+)?, $init:expr) => {{
#[inline]
fn __rust_std_internal_init_fn() -> $t { $init }
// NOTE: this cannot import `LocalKey` or `Storage` with a `use` because that can shadow
// user provided type or type alias with a matching name. Please update the shadowing test
// in `tests/thread.rs` if these types are renamed.
unsafe {
$crate::thread::LocalKey::new(|__rust_std_internal_init| {
static __RUST_STD_INTERNAL_VAL: $crate::thread::local_impl::Storage<$t, {
$({
// Ensure that attributes have valid syntax
// and that the proper feature gate is enabled
$(#[$($align_attr)*])+
#[allow(unused)]
static DUMMY: () = ();
})?
#[allow(unused_mut)]
let mut final_align = $crate::thread::local_impl::value_align::<$t>();
$($($crate::thread::local_impl::thread_local_inner!(@align final_align, $($align_attr)*);)+)?
final_align
}>
= $crate::thread::local_impl::Storage::new();
__RUST_STD_INTERNAL_VAL.get(__rust_std_internal_init, __rust_std_internal_init_fn)
})
}
}},
// process a single `rustc_align_static` attribute
(@align $final_align:ident, rustc_align_static($($align:tt)*) $(, $($attr_rest:tt)+)?) => {
let new_align: $crate::primitive::usize = $($align)*;
if new_align > $final_align {
$final_align = new_align;
}
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
},
// process a single `cfg_attr` attribute
// by translating it into a `cfg`ed block and recursing.
// https://doc.rust-lang.org/reference/conditional-compilation.html#railroad-ConfigurationPredicate
(@align $final_align:ident, cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
#[cfg(true)]
{
$crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
}
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
},
(@align $final_align:ident, cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
#[cfg(false)]
{
$crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
}
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
},
(@align $final_align:ident, cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
#[cfg($cfg_pred)]
{
$crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
}
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
},
}
/// Use a regular global static to store this key; the state provided will then be
/// thread-local.
/// INVARIANT: ALIGN must be a valid alignment, and no less than `value_align::<T>`.
#[allow(missing_debug_implementations)]
pub struct Storage<T, const ALIGN: usize> {
key: LazyKey,
marker: PhantomData<Cell<T>>,
}
unsafe impl<T, const ALIGN: usize> Sync for Storage<T, ALIGN> {}
#[repr(C)]
struct Value<T: 'static> {
// This field must be first, for correctness of `#[rustc_align_static]`
value: T,
// INVARIANT: if this value is stored under a TLS key, `key` must be that `key`.
key: Key,
}
pub const fn value_align<T: 'static>() -> usize {
crate::mem::align_of::<Value<T>>()
}
/// Equivalent to `Box<Value<T>, System>`, but potentially over-aligned.
struct AlignedSystemBox<T: 'static, const ALIGN: usize> {
ptr: NonNull<Value<T>>,
}
impl<T: 'static, const ALIGN: usize> AlignedSystemBox<T, ALIGN> {
#[inline]
fn new(v: Value<T>) -> Self {
let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap();
// We use the System allocator here to avoid interfering with a potential
// Global allocator using thread-local storage.
let ptr: *mut Value<T> = (unsafe { System.alloc(layout) }).cast();
let Some(ptr) = NonNull::new(ptr) else {
alloc::handle_alloc_error(layout);
};
unsafe { ptr.write(v) };
Self { ptr }
}
#[inline]
fn into_raw(b: Self) -> *mut Value<T> {
let md = ManuallyDrop::new(b);
md.ptr.as_ptr()
}
#[inline]
unsafe fn from_raw(ptr: *mut Value<T>) -> Self {
Self { ptr: unsafe { NonNull::new_unchecked(ptr) } }
}
}
impl<T: 'static, const ALIGN: usize> Deref for AlignedSystemBox<T, ALIGN> {
type Target = Value<T>;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*(self.ptr.as_ptr()) }
}
}
impl<T: 'static, const ALIGN: usize> Drop for AlignedSystemBox<T, ALIGN> {
#[inline]
fn drop(&mut self) {
let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap();
unsafe {
let unwind_result = catch_unwind(AssertUnwindSafe(|| self.ptr.drop_in_place()));
System.dealloc(self.ptr.as_ptr().cast(), layout);
if let Err(payload) = unwind_result {
resume_unwind(payload);
}
}
}
}
impl<T: 'static, const ALIGN: usize> Storage<T, ALIGN> {
pub const fn new() -> Storage<T, ALIGN> {
Storage { key: LazyKey::new(Some(destroy_value::<T, ALIGN>)), marker: PhantomData }
}
/// Gets a pointer to the TLS value, potentially initializing it with the
/// provided parameters. If the TLS variable has been destroyed, a null
/// pointer is returned.
///
/// The resulting pointer may not be used after reentrant inialialization
/// or thread destruction has occurred.
pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
let key = self.key.force();
let ptr = unsafe { get(key) as *mut Value<T> };
if ptr.addr() > 1 {
// SAFETY: the check ensured the pointer is safe (its destructor
// is not running) + it is coming from a trusted source (self).
unsafe { &(*ptr).value }
} else {
// SAFETY: trivially correct.
unsafe { Self::try_initialize(key, ptr, i, f) }
}
}
/// # Safety
/// * `key` must be the result of calling `self.key.force()`
/// * `ptr` must be the current value associated with `key`.
unsafe fn try_initialize(
key: Key,
ptr: *mut Value<T>,
i: Option<&mut Option<T>>,
f: impl FnOnce() -> T,
) -> *const T {
if ptr.addr() == 1 {
// destructor is running
return ptr::null();
}
let value = AlignedSystemBox::<T, ALIGN>::new(Value {
value: i.and_then(Option::take).unwrap_or_else(f),
key,
});
let ptr = AlignedSystemBox::into_raw(value);
// SAFETY:
// * key came from a `LazyKey` and is thus correct.
// * `ptr` is a correct pointer that can be destroyed by the key destructor.
// * the value is stored under the key that it contains.
let old = unsafe {
let old = get(key) as *mut Value<T>;
set(key, ptr as *mut u8);
old
};
if !old.is_null() {
// If the variable was recursively initialized, drop the old value.
// SAFETY: We cannot be inside a `LocalKey::with` scope, as the
// initializer has already returned and the next scope only starts
// after we return the pointer. Therefore, there can be no references
// to the old value.
drop(unsafe { AlignedSystemBox::<T, ALIGN>::from_raw(old) });
}
// SAFETY: We just created this value above.
unsafe { &(*ptr).value }
}
}
unsafe extern "C" fn destroy_value<T: 'static, const ALIGN: usize>(ptr: *mut u8) {
// SAFETY:
//
// The OS TLS ensures that this key contains a null value when this
// destructor starts to run. We set it back to a sentinel value of 1 to
// ensure that any future calls to `get` for this thread will return
// `None`.
//
// Note that to prevent an infinite loop we reset it back to null right
// before we return from the destructor ourselves.
abort_on_dtor_unwind(|| {
let ptr = unsafe { AlignedSystemBox::<T, ALIGN>::from_raw(ptr as *mut Value<T>) };
let key = ptr.key;
// SAFETY: `key` is the TLS key `ptr` was stored under.
unsafe { set(key, ptr::without_provenance_mut(1)) };
drop(ptr);
// SAFETY: `key` is the TLS key `ptr` was stored under.
unsafe { set(key, ptr::null_mut()) };
// Make sure that the runtime cleanup will be performed
// after the next round of TLS destruction.
guard::enable();
});
}
#[rustc_macro_transparency = "semiopaque"]
pub(crate) macro local_pointer {
() => {},
($vis:vis static $name:ident; $($rest:tt)*) => {
$vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
$crate::sys::thread_local::local_pointer! { $($rest)* }
},
}
pub(crate) struct LocalPointer {
key: LazyKey,
}
impl LocalPointer {
pub const fn __new() -> LocalPointer {
LocalPointer { key: LazyKey::new(None) }
}
pub fn get(&'static self) -> *mut () {
unsafe { get(self.key.force()) as *mut () }
}
pub fn set(&'static self, p: *mut ()) {
unsafe { set(self.key.force(), p as *mut u8) }
}
}

View File

@@ -0,0 +1,53 @@
cfg_select! {
target_os = "hermit" => {
mod hermit;
use hermit as imp;
}
target_os = "motor" => {
use moto_rt::time as imp;
}
all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx;
use sgx as imp;
}
target_os = "solid_asp3" => {
mod solid;
use solid as imp;
}
target_os = "uefi" => {
mod uefi;
use uefi as imp;
}
any(
target_os = "teeos",
target_family = "unix",
target_os = "wasi",
) => {
mod unix;
use unix as imp;
}
target_os = "vexos" => {
mod vexos;
#[expect(unused)]
mod unsupported;
mod imp {
pub use super::vexos::Instant;
pub use super::unsupported::{SystemTime, UNIX_EPOCH};
}
}
target_os = "windows" => {
mod windows;
use windows as imp;
}
target_os = "xous" => {
mod xous;
use xous as imp;
}
_ => {
mod unsupported;
use unsupported as imp;
}
}
pub use imp::{Instant, SystemTime, UNIX_EPOCH};

View File

@@ -0,0 +1,49 @@
use crate::time::Duration;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant(Duration);
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct SystemTime(Duration);
pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
impl Instant {
pub fn now() -> Instant {
panic!("time not implemented on this platform")
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.0.checked_sub(other.0)
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_add(*other)?))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_sub(*other)?))
}
}
impl SystemTime {
pub const MAX: SystemTime = SystemTime(Duration::MAX);
pub const MIN: SystemTime = SystemTime(Duration::ZERO);
pub fn now() -> SystemTime {
panic!("time not implemented on this platform")
}
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0)
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime(self.0.checked_add(*other)?))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime(self.0.checked_sub(*other)?))
}
}

24
crates/std/src/thread.rs Normal file
View File

@@ -0,0 +1,24 @@
pub mod local;
pub use crate::sys::thread::*;
pub use local::LocalKey;
// Implementation details used by the thread_local!{} macro.
#[doc(hidden)]
#[unstable(feature = "thread_local_internals", issue = "none")]
pub mod local_impl {
pub use super::local::thread_local_process_attrs;
pub use crate::sys::thread_local::*;
}
pub struct ThreadInit {
pub handle: Thread,
pub rust_start: Box<dyn FnOnce() + Send>,
}
impl ThreadInit {
/// Initialize the 'current thread' mechanism on this thread, returning the
/// Rust entry point.
pub fn init(self: Box<Self>) -> Box<dyn FnOnce() + Send> {
todo!()
}
}

View File

@@ -0,0 +1,865 @@
//! Thread local storage
#![unstable(feature = "thread_local_internals", issue = "none")]
use crate::cell::{Cell, RefCell};
use crate::error::Error;
use crate::fmt;
/// A thread local storage (TLS) key which owns its contents.
///
/// This key uses the fastest implementation available on the target platform.
/// It is instantiated with the [`thread_local!`] macro and the
/// primary method is the [`with`] method, though there are helpers to make
/// working with [`Cell`] types easier.
///
/// The [`with`] method yields a reference to the contained value which cannot
/// outlive the current thread or escape the given closure.
///
/// [`thread_local!`]: crate::thread_local
///
/// # Initialization and Destruction
///
/// Initialization is dynamically performed on the first call to a setter (e.g.
/// [`with`]) within a thread, and values that implement [`Drop`] get
/// destructed when a thread exits. Some platform-specific caveats apply, which
/// are explained below.
/// Note that, should the destructor panic, the whole process will be [aborted].
/// On platforms where initialization requires memory allocation, this is
/// performed directly through [`System`], allowing the [global allocator]
/// to make use of thread local storage.
///
/// A `LocalKey`'s initializer cannot recursively depend on itself. Using a
/// `LocalKey` in this way may cause panics, aborts, or infinite recursion on
/// the first call to `with`.
///
/// [`System`]: crate::alloc::System
/// [global allocator]: crate::alloc
/// [aborted]: crate::process::abort
///
/// # Single-thread Synchronization
///
/// Though there is no potential race with other threads, it is still possible to
/// obtain multiple references to the thread-local data in different places on
/// the call stack. For this reason, only shared (`&T`) references may be obtained.
///
/// To allow obtaining an exclusive mutable reference (`&mut T`), typically a
/// [`Cell`] or [`RefCell`] is used (see the [`std::cell`] for more information
/// on how exactly this works). To make this easier there are specialized
/// implementations for [`LocalKey<Cell<T>>`] and [`LocalKey<RefCell<T>>`].
///
/// [`std::cell`]: `crate::cell`
/// [`LocalKey<Cell<T>>`]: struct.LocalKey.html#impl-LocalKey<Cell<T>>
/// [`LocalKey<RefCell<T>>`]: struct.LocalKey.html#impl-LocalKey<RefCell<T>>
///
///
/// # Examples
///
/// ```
/// use std::cell::Cell;
/// use std::thread;
///
/// // explicit `const {}` block enables more efficient initialization
/// thread_local!(static FOO: Cell<u32> = const { Cell::new(1) });
///
/// assert_eq!(FOO.get(), 1);
/// FOO.set(2);
///
/// // each thread starts out with the initial value of 1
/// let t = thread::spawn(move || {
/// assert_eq!(FOO.get(), 1);
/// FOO.set(3);
/// });
///
/// // wait for the thread to complete and bail out on panic
/// t.join().unwrap();
///
/// // we retain our original value of 2 despite the child thread
/// assert_eq!(FOO.get(), 2);
/// ```
///
/// # Platform-specific behavior
///
/// Note that a "best effort" is made to ensure that destructors for types
/// stored in thread local storage are run, but not all platforms can guarantee
/// that destructors will be run for all types in thread local storage. For
/// example, there are a number of known caveats where destructors are not run:
///
/// 1. On Unix systems when pthread-based TLS is being used, destructors will
/// not be run for TLS values on the main thread when it exits. Note that the
/// application will exit immediately after the main thread exits as well.
/// 2. On all platforms it's possible for TLS to re-initialize other TLS slots
/// during destruction. Some platforms ensure that this cannot happen
/// infinitely by preventing re-initialization of any slot that has been
/// destroyed, but not all platforms have this guard. Those platforms that do
/// not guard typically have a synthetic limit after which point no more
/// destructors are run.
/// 3. When the process exits on Windows systems, TLS destructors may only be
/// run on the thread that causes the process to exit. This is because the
/// other threads may be forcibly terminated.
///
/// ## Synchronization in thread-local destructors
///
/// On Windows, synchronization operations (such as [`JoinHandle::join`]) in
/// thread local destructors are prone to deadlocks and so should be avoided.
/// This is because the [loader lock] is held while a destructor is run. The
/// lock is acquired whenever a thread starts or exits or when a DLL is loaded
/// or unloaded. Therefore these events are blocked for as long as a thread
/// local destructor is running.
///
/// [loader lock]: https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
/// [`JoinHandle::join`]: crate::thread::JoinHandle::join
/// [`with`]: LocalKey::with
#[cfg_attr(not(test), rustc_diagnostic_item = "LocalKey")]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct LocalKey<T: 'static> {
// This outer `LocalKey<T>` type is what's going to be stored in statics,
// but actual data inside will sometimes be tagged with #[thread_local].
// It's not valid for a true static to reference a #[thread_local] static,
// so we get around that by exposing an accessor through a layer of function
// indirection (this thunk).
//
// Note that the thunk is itself unsafe because the returned lifetime of the
// slot where data lives, `'static`, is not actually valid. The lifetime
// here is actually slightly shorter than the currently running thread!
//
// Although this is an extra layer of indirection, it should in theory be
// trivially devirtualizable by LLVM because the value of `inner` never
// changes and the constant should be readonly within a crate. This mainly
// only runs into problems when TLS statics are exported across crates.
inner: fn(Option<&mut Option<T>>) -> *const T,
}
#[stable(feature = "std_debug", since = "1.16.0")]
impl<T: 'static> fmt::Debug for LocalKey<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LocalKey").finish_non_exhaustive()
}
}
#[doc(hidden)]
#[allow_internal_unstable(thread_local_internals)]
#[unstable(feature = "thread_local_internals", issue = "none")]
#[rustc_macro_transparency = "semiopaque"]
pub macro thread_local_process_attrs {
// Parse `cfg_attr` to figure out whether it's a `rustc_align_static`.
// Each `cfg_attr` can have zero or more attributes on the RHS, and can be nested.
// finished parsing the `cfg_attr`, it had no `rustc_align_static`
(
[] [$(#[$($prev_other_attrs:tt)*])*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
[$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs_ret)*] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),*)]];
$($rest)*
);
),
// finished parsing the `cfg_attr`, it had nothing but `rustc_align_static`
(
[$(#[$($prev_align_attrs:tt)*])+] [];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
[$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)*];
$($rest)*
);
),
// finished parsing the `cfg_attr`, it had a mix of `rustc_align_static` and other attrs
(
[$(#[$($prev_align_attrs:tt)*])+] [$(#[$($prev_other_attrs:tt)*])+];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
[$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),+)]];
$($rest)*
);
),
// it's a `rustc_align_static`
(
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [rustc_align_static($($align_static_args:tt)*) $(, $($attr_rhs:tt)*)?] };
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)* #[rustc_align_static($($align_static_args)*)]] [$($prev_other_attrs)*];
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
$($rest)*
);
),
// it's a nested `cfg_attr(true, ...)`; recurse into RHS
(
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
$($rest)*
);
),
// it's a nested `cfg_attr(false, ...)`; recurse into RHS
(
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
$($rest)*
);
),
// it's a nested `cfg_attr(..., ...)`; recurse into RHS
(
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr($cfg_lhs:meta, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: ($cfg_lhs), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
$($rest)*
);
),
// it's some other attribute
(
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [$meta:meta $(, $($attr_rhs:tt)*)?] };
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)*] [$($prev_other_attrs)* #[$meta]];
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
$($rest)*
);
),
// Separate attributes into `rustc_align_static` and everything else:
// `rustc_align_static` attribute
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[rustc_align_static $($attr_rest:tt)*] $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)* #[rustc_align_static $($attr_rest)*]] [$($prev_other_attrs)*];
$($rest)*
);
),
// `cfg_attr(true, ...)` attribute; parse it
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(true, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
$($rest)*
);
),
// `cfg_attr(false, ...)` attribute; parse it
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(false, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
$($rest)*
);
),
// `cfg_attr(..., ...)` attribute; parse it
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: ($cfg_pred), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
$($rest)*
);
),
// doc comment not followed by any other attributes; process it all at once to avoid blowing recursion limit
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; $(#[doc $($doc_rhs:tt)*])+ $vis:vis static $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)*] [$($prev_other_attrs)* $(#[doc $($doc_rhs)*])+];
$vis static $($rest)*
);
),
// 8 lines of doc comment; process them all at once to avoid blowing recursion limit
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
#[doc $($doc_rhs_1:tt)*] #[doc $($doc_rhs_2:tt)*] #[doc $($doc_rhs_3:tt)*] #[doc $($doc_rhs_4:tt)*]
#[doc $($doc_rhs_5:tt)*] #[doc $($doc_rhs_6:tt)*] #[doc $($doc_rhs_7:tt)*] #[doc $($doc_rhs_8:tt)*]
$($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)*] [$($prev_other_attrs)*
#[doc $($doc_rhs_1)*] #[doc $($doc_rhs_2)*] #[doc $($doc_rhs_3)*] #[doc $($doc_rhs_4)*]
#[doc $($doc_rhs_5)*] #[doc $($doc_rhs_6)*] #[doc $($doc_rhs_7)*] #[doc $($doc_rhs_8)*]];
$($rest)*
);
),
// other attribute
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[$($attr:tt)*] $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)*] [$($prev_other_attrs)* #[$($attr)*]];
$($rest)*
);
),
// Delegate to `thread_local_inner` once attributes are fully categorized:
// process `const` declaration and recurse
([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = const $init:block $(; $($($rest:tt)+)?)?) => (
$($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> =
$crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, const $init);
$($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)?
),
// process non-`const` declaration and recurse
([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = $init:expr $(; $($($rest:tt)+)?)?) => (
$($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> =
$crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, $init);
$($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)?
),
}
/// Declare a new thread local storage key of type [`std::thread::LocalKey`].
///
/// # Syntax
///
/// The macro wraps any number of static declarations and makes them thread local.
/// Publicity and attributes for each static are allowed. Example:
///
/// ```
/// use std::cell::{Cell, RefCell};
///
/// thread_local! {
/// pub static FOO: Cell<u32> = const { Cell::new(1) };
///
/// static BAR: RefCell<Vec<f32>> = RefCell::new(vec![1.0, 2.0]);
/// }
///
/// assert_eq!(FOO.get(), 1);
/// BAR.with_borrow(|v| assert_eq!(v[1], 2.0));
/// ```
///
/// Note that only shared references (`&T`) to the inner data may be obtained, so a
/// type such as [`Cell`] or [`RefCell`] is typically used to allow mutating access.
///
/// This macro supports a special `const {}` syntax that can be used
/// when the initialization expression can be evaluated as a constant.
/// This can enable a more efficient thread local implementation that
/// can avoid lazy initialization. For types that do not
/// [need to be dropped][crate::mem::needs_drop], this can enable an
/// even more efficient implementation that does not need to
/// track any additional state.
///
/// ```
/// use std::cell::RefCell;
///
/// thread_local! {
/// pub static FOO: RefCell<Vec<u32>> = const { RefCell::new(Vec::new()) };
/// }
///
/// FOO.with_borrow(|v| assert_eq!(v.len(), 0));
/// ```
///
/// See [`LocalKey` documentation][`std::thread::LocalKey`] for more
/// information.
///
/// [`std::thread::LocalKey`]: crate::thread::LocalKey
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")]
#[allow_internal_unstable(thread_local_internals)]
macro_rules! thread_local {
() => {};
($($tt:tt)+) => {
$crate::thread::local_impl::thread_local_process_attrs!([] []; $($tt)+);
};
}
/// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with).
#[stable(feature = "thread_local_try_with", since = "1.26.0")]
#[non_exhaustive]
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct AccessError;
#[stable(feature = "thread_local_try_with", since = "1.26.0")]
impl fmt::Debug for AccessError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AccessError").finish()
}
}
#[stable(feature = "thread_local_try_with", since = "1.26.0")]
impl fmt::Display for AccessError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt("already destroyed", f)
}
}
#[stable(feature = "thread_local_try_with", since = "1.26.0")]
impl Error for AccessError {}
// This ensures the panicking code is outlined from `with` for `LocalKey`.
#[cfg_attr(not(panic = "immediate-abort"), inline(never))]
#[track_caller]
#[cold]
fn panic_access_error(err: AccessError) -> ! {
panic!("cannot access a Thread Local Storage value during or after destruction: {err:?}")
}
impl<T: 'static> LocalKey<T> {
#[doc(hidden)]
#[unstable(
feature = "thread_local_internals",
reason = "recently added to create a key",
issue = "none"
)]
pub const unsafe fn new(inner: fn(Option<&mut Option<T>>) -> *const T) -> LocalKey<T> {
LocalKey { inner }
}
/// Acquires a reference to the value in this TLS key.
///
/// This will lazily initialize the value if this thread has not referenced
/// this key yet.
///
/// # Panics
///
/// This function will `panic!()` if the key currently has its
/// destructor running, and it **may** panic if the destructor has
/// previously been run for this thread.
///
/// # Examples
///
/// ```
/// thread_local! {
/// pub static STATIC: String = String::from("I am");
/// }
///
/// assert_eq!(
/// STATIC.with(|original_value| format!("{original_value} initialized")),
/// "I am initialized",
/// );
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with<F, R>(&'static self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
match self.try_with(f) {
Ok(r) => r,
Err(err) => panic_access_error(err),
}
}
/// Acquires a reference to the value in this TLS key.
///
/// This will lazily initialize the value if this thread has not referenced
/// this key yet. If the key has been destroyed (which may happen if this is called
/// in a destructor), this function will return an [`AccessError`].
///
/// # Panics
///
/// This function will still `panic!()` if the key is uninitialized and the
/// key's initializer panics.
///
/// # Examples
///
/// ```
/// thread_local! {
/// pub static STATIC: String = String::from("I am");
/// }
///
/// assert_eq!(
/// STATIC.try_with(|original_value| format!("{original_value} initialized")),
/// Ok(String::from("I am initialized")),
/// );
/// ```
#[stable(feature = "thread_local_try_with", since = "1.26.0")]
#[inline]
pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>
where
F: FnOnce(&T) -> R,
{
let thread_local = unsafe { (self.inner)(None).as_ref().ok_or(AccessError)? };
Ok(f(thread_local))
}
/// Acquires a reference to the value in this TLS key, initializing it with
/// `init` if it wasn't already initialized on this thread.
///
/// If `init` was used to initialize the thread local variable, `None` is
/// passed as the first argument to `f`. If it was already initialized,
/// `Some(init)` is passed to `f`.
///
/// # Panics
///
/// This function will panic if the key currently has its destructor
/// running, and it **may** panic if the destructor has previously been run
/// for this thread.
fn initialize_with<F, R>(&'static self, init: T, f: F) -> R
where
F: FnOnce(Option<T>, &T) -> R,
{
let mut init = Some(init);
let reference = unsafe {
match (self.inner)(Some(&mut init)).as_ref() {
Some(r) => r,
None => panic_access_error(AccessError),
}
};
f(init, reference)
}
}
impl<T: 'static> LocalKey<Cell<T>> {
/// Sets or initializes the contained value.
///
/// Unlike the other methods, this will *not* run the lazy initializer of
/// the thread local. Instead, it will be directly initialized with the
/// given value if it wasn't initialized yet.
///
/// # Panics
///
/// Panics if the key currently has its destructor running,
/// and it **may** panic if the destructor has previously been run for this thread.
///
/// # Examples
///
/// ```
/// use std::cell::Cell;
///
/// thread_local! {
/// static X: Cell<i32> = panic!("!");
/// }
///
/// // Calling X.get() here would result in a panic.
///
/// X.set(123); // But X.set() is fine, as it skips the initializer above.
///
/// assert_eq!(X.get(), 123);
/// ```
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
pub fn set(&'static self, value: T) {
self.initialize_with(Cell::new(value), |value, cell| {
if let Some(value) = value {
// The cell was already initialized, so `value` wasn't used to
// initialize it. So we overwrite the current value with the
// new one instead.
cell.set(value.into_inner());
}
});
}
/// Returns a copy of the contained value.
///
/// This will lazily initialize the value if this thread has not referenced
/// this key yet.
///
/// # Panics
///
/// Panics if the key currently has its destructor running,
/// and it **may** panic if the destructor has previously been run for this thread.
///
/// # Examples
///
/// ```
/// use std::cell::Cell;
///
/// thread_local! {
/// static X: Cell<i32> = const { Cell::new(1) };
/// }
///
/// assert_eq!(X.get(), 1);
/// ```
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
pub fn get(&'static self) -> T
where
T: Copy,
{
self.with(Cell::get)
}
/// Takes the contained value, leaving `Default::default()` in its place.
///
/// This will lazily initialize the value if this thread has not referenced
/// this key yet.
///
/// # Panics
///
/// Panics if the key currently has its destructor running,
/// and it **may** panic if the destructor has previously been run for this thread.
///
/// # Examples
///
/// ```
/// use std::cell::Cell;
///
/// thread_local! {
/// static X: Cell<Option<i32>> = const { Cell::new(Some(1)) };
/// }
///
/// assert_eq!(X.take(), Some(1));
/// assert_eq!(X.take(), None);
/// ```
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
pub fn take(&'static self) -> T
where
T: Default,
{
self.with(Cell::take)
}
/// Replaces the contained value, returning the old value.
///
/// This will lazily initialize the value if this thread has not referenced
/// this key yet.
///
/// # Panics
///
/// Panics if the key currently has its destructor running,
/// and it **may** panic if the destructor has previously been run for this thread.
///
/// # Examples
///
/// ```
/// use std::cell::Cell;
///
/// thread_local! {
/// static X: Cell<i32> = const { Cell::new(1) };
/// }
///
/// assert_eq!(X.replace(2), 1);
/// assert_eq!(X.replace(3), 2);
/// ```
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
#[rustc_confusables("swap")]
pub fn replace(&'static self, value: T) -> T {
self.with(|cell| cell.replace(value))
}
/// Updates the contained value using a function.
///
/// # Examples
///
/// ```
/// #![feature(local_key_cell_update)]
/// use std::cell::Cell;
///
/// thread_local! {
/// static X: Cell<i32> = const { Cell::new(5) };
/// }
///
/// X.update(|x| x + 1);
/// assert_eq!(X.get(), 6);
/// ```
#[unstable(feature = "local_key_cell_update", issue = "143989")]
pub fn update(&'static self, f: impl FnOnce(T) -> T)
where
T: Copy,
{
self.with(|cell| cell.update(f))
}
}
impl<T: 'static> LocalKey<RefCell<T>> {
/// Acquires a reference to the contained value.
///
/// This will lazily initialize the value if this thread has not referenced
/// this key yet.
///
/// # Panics
///
/// Panics if the value is currently mutably borrowed.
///
/// Panics if the key currently has its destructor running,
/// and it **may** panic if the destructor has previously been run for this thread.
///
/// # Examples
///
/// ```
/// use std::cell::RefCell;
///
/// thread_local! {
/// static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
/// }
///
/// X.with_borrow(|v| assert!(v.is_empty()));
/// ```
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
pub fn with_borrow<F, R>(&'static self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
self.with(|cell| f(&cell.borrow()))
}
/// Acquires a mutable reference to the contained value.
///
/// This will lazily initialize the value if this thread has not referenced
/// this key yet.
///
/// # Panics
///
/// Panics if the value is currently borrowed.
///
/// Panics if the key currently has its destructor running,
/// and it **may** panic if the destructor has previously been run for this thread.
///
/// # Examples
///
/// ```
/// use std::cell::RefCell;
///
/// thread_local! {
/// static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
/// }
///
/// X.with_borrow_mut(|v| v.push(1));
///
/// X.with_borrow(|v| assert_eq!(*v, vec![1]));
/// ```
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
pub fn with_borrow_mut<F, R>(&'static self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
self.with(|cell| f(&mut cell.borrow_mut()))
}
/// Sets or initializes the contained value.
///
/// Unlike the other methods, this will *not* run the lazy initializer of
/// the thread local. Instead, it will be directly initialized with the
/// given value if it wasn't initialized yet.
///
/// # Panics
///
/// Panics if the value is currently borrowed.
///
/// Panics if the key currently has its destructor running,
/// and it **may** panic if the destructor has previously been run for this thread.
///
/// # Examples
///
/// ```
/// use std::cell::RefCell;
///
/// thread_local! {
/// static X: RefCell<Vec<i32>> = panic!("!");
/// }
///
/// // Calling X.with() here would result in a panic.
///
/// X.set(vec![1, 2, 3]); // But X.set() is fine, as it skips the initializer above.
///
/// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));
/// ```
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
pub fn set(&'static self, value: T) {
self.initialize_with(RefCell::new(value), |value, cell| {
if let Some(value) = value {
// The cell was already initialized, so `value` wasn't used to
// initialize it. So we overwrite the current value with the
// new one instead.
*cell.borrow_mut() = value.into_inner();
}
});
}
/// Takes the contained value, leaving `Default::default()` in its place.
///
/// This will lazily initialize the value if this thread has not referenced
/// this key yet.
///
/// # Panics
///
/// Panics if the value is currently borrowed.
///
/// Panics if the key currently has its destructor running,
/// and it **may** panic if the destructor has previously been run for this thread.
///
/// # Examples
///
/// ```
/// use std::cell::RefCell;
///
/// thread_local! {
/// static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
/// }
///
/// X.with_borrow_mut(|v| v.push(1));
///
/// let a = X.take();
///
/// assert_eq!(a, vec![1]);
///
/// X.with_borrow(|v| assert!(v.is_empty()));
/// ```
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
pub fn take(&'static self) -> T
where
T: Default,
{
self.with(RefCell::take)
}
/// Replaces the contained value, returning the old value.
///
/// # Panics
///
/// Panics if the value is currently borrowed.
///
/// Panics if the key currently has its destructor running,
/// and it **may** panic if the destructor has previously been run for this thread.
///
/// # Examples
///
/// ```
/// use std::cell::RefCell;
///
/// thread_local! {
/// static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
/// }
///
/// let prev = X.replace(vec![1, 2, 3]);
/// assert!(prev.is_empty());
///
/// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));
/// ```
#[stable(feature = "local_key_cell_methods", since = "1.73.0")]
#[rustc_confusables("swap")]
pub fn replace(&'static self, value: T) -> T {
self.with(|cell| cell.replace(value))
}
}

859
crates/std/src/time.rs Normal file
View File

@@ -0,0 +1,859 @@
//! Temporal quantification.
//!
//! # Examples
//!
//! There are multiple ways to create a new [`Duration`]:
//!
//! ```
//! # use std::time::Duration;
//! let five_seconds = Duration::from_secs(5);
//! assert_eq!(five_seconds, Duration::from_millis(5_000));
//! assert_eq!(five_seconds, Duration::from_micros(5_000_000));
//! assert_eq!(five_seconds, Duration::from_nanos(5_000_000_000));
//!
//! let ten_seconds = Duration::from_secs(10);
//! let seven_nanos = Duration::from_nanos(7);
//! let total = ten_seconds + seven_nanos;
//! assert_eq!(total, Duration::new(10, 7));
//! ```
//!
//! Using [`Instant`] to calculate how long a function took to run:
//!
//! ```ignore (incomplete)
//! let now = Instant::now();
//!
//! // Calling a slow function, it may take a while
//! slow_function();
//!
//! let elapsed_time = now.elapsed();
//! println!("Running slow_function() took {} seconds.", elapsed_time.as_secs());
//! ```
#![stable(feature = "time", since = "1.3.0")]
#[stable(feature = "time", since = "1.3.0")]
pub use core::time::Duration;
#[stable(feature = "duration_checked_float", since = "1.66.0")]
pub use core::time::TryFromFloatSecsError;
use crate::error::Error;
use crate::fmt;
use crate::ops::{Add, AddAssign, Sub, SubAssign};
use crate::sys::{FromInner, IntoInner, time};
/// A measurement of a monotonically nondecreasing clock.
/// Opaque and useful only with [`Duration`].
///
/// Instants are always guaranteed, barring [platform bugs], to be no less than any previously
/// measured instant when created, and are often useful for tasks such as measuring
/// benchmarks or timing how long an operation takes.
///
/// Note, however, that instants are **not** guaranteed to be **steady**. In other
/// words, each tick of the underlying clock might not be the same length (e.g.
/// some seconds may be longer than others). An instant may jump forwards or
/// experience time dilation (slow down or speed up), but it will never go
/// backwards.
/// As part of this non-guarantee it is also not specified whether system suspends count as
/// elapsed time or not. The behavior varies across platforms and Rust versions.
///
/// Instants are opaque types that can only be compared to one another. There is
/// no method to get "the number of seconds" from an instant. Instead, it only
/// allows measuring the duration between two instants (or comparing two
/// instants).
///
/// The size of an `Instant` struct may vary depending on the target operating
/// system.
///
/// Example:
///
/// ```no_run
/// use std::time::{Duration, Instant};
/// use std::thread::sleep;
///
/// fn main() {
/// let now = Instant::now();
///
/// // we sleep for 2 seconds
/// sleep(Duration::new(2, 0));
/// // it prints '2'
/// println!("{}", now.elapsed().as_secs());
/// }
/// ```
///
/// [platform bugs]: Instant#monotonicity
///
/// # OS-specific behaviors
///
/// An `Instant` is a wrapper around system-specific types and it may behave
/// differently depending on the underlying operating system. For example,
/// the following snippet is fine on Linux but panics on macOS:
///
/// ```no_run
/// use std::time::{Instant, Duration};
///
/// let now = Instant::now();
/// let days_per_10_millennia = 365_2425;
/// let solar_seconds_per_day = 60 * 60 * 24;
/// let millennium_in_solar_seconds = 31_556_952_000;
/// assert_eq!(millennium_in_solar_seconds, days_per_10_millennia * solar_seconds_per_day / 10);
///
/// let duration = Duration::new(millennium_in_solar_seconds, 0);
/// println!("{:?}", now + duration);
/// ```
///
/// For cross-platform code, you can comfortably use durations of up to around one hundred years.
///
/// # Underlying System calls
///
/// The following system calls are [currently] being used by `now()` to find out
/// the current time:
///
/// | Platform | System call |
/// |-----------|----------------------------------------------------------------------|
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
/// | UNIX | [clock_gettime] with `CLOCK_MONOTONIC` |
/// | Darwin | [clock_gettime] with `CLOCK_UPTIME_RAW` |
/// | VXWorks | [clock_gettime] with `CLOCK_MONOTONIC` |
/// | SOLID | `get_tim` |
/// | WASI | [__wasi_clock_time_get] with `monotonic` |
/// | Windows | [QueryPerformanceCounter] |
///
/// [currently]: crate::io#platform-specific-behavior
/// [QueryPerformanceCounter]: https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter
/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time
/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode
/// [__wasi_clock_time_get]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get
/// [clock_gettime]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html
///
/// **Disclaimer:** These system calls might change over time.
///
/// > Note: mathematical operations like [`add`] may panic if the underlying
/// > structure cannot represent the new point in time.
///
/// [`add`]: Instant::add
///
/// ## Monotonicity
///
/// On all platforms `Instant` will try to use an OS API that guarantees monotonic behavior
/// if available, which is the case for all [tier 1] platforms.
/// In practice such guarantees are under rare circumstances broken by hardware, virtualization
/// or operating system bugs. To work around these bugs and platforms not offering monotonic clocks
/// [`duration_since`], [`elapsed`] and [`sub`] saturate to zero. In older Rust versions this
/// lead to a panic instead. [`checked_duration_since`] can be used to detect and handle situations
/// where monotonicity is violated, or `Instant`s are subtracted in the wrong order.
///
/// This workaround obscures programming errors where earlier and later instants are accidentally
/// swapped. For this reason future Rust versions may reintroduce panics.
///
/// [tier 1]: https://doc.rust-lang.org/rustc/platform-support.html
/// [`duration_since`]: Instant::duration_since
/// [`elapsed`]: Instant::elapsed
/// [`sub`]: Instant::sub
/// [`checked_duration_since`]: Instant::checked_duration_since
///
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[stable(feature = "time2", since = "1.8.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Instant")]
pub struct Instant(time::Instant);
/// A measurement of the system clock, useful for talking to
/// external entities like the file system or other processes.
///
/// Distinct from the [`Instant`] type, this time measurement **is not
/// monotonic**. This means that you can save a file to the file system, then
/// save another file to the file system, **and the second file has a
/// `SystemTime` measurement earlier than the first**. In other words, an
/// operation that happens after another operation in real time may have an
/// earlier `SystemTime`!
///
/// Consequently, comparing two `SystemTime` instances to learn about the
/// duration between them returns a [`Result`] instead of an infallible [`Duration`]
/// to indicate that this sort of time drift may happen and needs to be handled.
///
/// Although a `SystemTime` cannot be directly inspected, the [`UNIX_EPOCH`]
/// constant is provided in this module as an anchor in time to learn
/// information about a `SystemTime`. By calculating the duration from this
/// fixed point in time, a `SystemTime` can be converted to a human-readable time,
/// or perhaps some other string representation.
///
/// The size of a `SystemTime` struct may vary depending on the target operating
/// system.
///
/// A `SystemTime` does not count leap seconds.
/// `SystemTime::now()`'s behavior around a leap second
/// is the same as the operating system's wall clock.
/// The precise behavior near a leap second
/// (e.g. whether the clock appears to run slow or fast, or stop, or jump)
/// depends on platform and configuration,
/// so should not be relied on.
///
/// Example:
///
/// ```no_run
/// use std::time::{Duration, SystemTime};
/// use std::thread::sleep;
///
/// fn main() {
/// let now = SystemTime::now();
///
/// // we sleep for 2 seconds
/// sleep(Duration::new(2, 0));
/// match now.elapsed() {
/// Ok(elapsed) => {
/// // it prints '2'
/// println!("{}", elapsed.as_secs());
/// }
/// Err(e) => {
/// // the system clock went backwards!
/// println!("Great Scott! {e:?}");
/// }
/// }
/// }
/// ```
///
/// # Platform-specific behavior
///
/// The precision of `SystemTime` can depend on the underlying OS-specific time format.
/// For example, on Windows the time is represented in 100 nanosecond intervals whereas Linux
/// can represent nanosecond intervals.
///
/// The following system calls are [currently] being used by `now()` to find out
/// the current time:
///
/// | Platform | System call |
/// |-----------|----------------------------------------------------------------------|
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
/// | UNIX | [clock_gettime (Realtime Clock)] |
/// | Darwin | [clock_gettime (Realtime Clock)] |
/// | VXWorks | [clock_gettime (Realtime Clock)] |
/// | SOLID | `SOLID_RTC_ReadTime` |
/// | WASI | [__wasi_clock_time_get (Realtime Clock)] |
/// | Windows | [GetSystemTimePreciseAsFileTime] / [GetSystemTimeAsFileTime] |
///
/// [currently]: crate::io#platform-specific-behavior
/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time
/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode
/// [clock_gettime (Realtime Clock)]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html
/// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get
/// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime
/// [GetSystemTimeAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime
///
/// **Disclaimer:** These system calls might change over time.
///
/// > Note: mathematical operations like [`add`] may panic if the underlying
/// > structure cannot represent the new point in time.
///
/// [`add`]: SystemTime::add
/// [`UNIX_EPOCH`]: SystemTime::UNIX_EPOCH
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[stable(feature = "time2", since = "1.8.0")]
pub struct SystemTime(time::SystemTime);
/// An error returned from the `duration_since` and `elapsed` methods on
/// `SystemTime`, used to learn how far in the opposite direction a system time
/// lies.
///
/// # Examples
///
/// ```no_run
/// use std::thread::sleep;
/// use std::time::{Duration, SystemTime};
///
/// let sys_time = SystemTime::now();
/// sleep(Duration::from_secs(1));
/// let new_sys_time = SystemTime::now();
/// match sys_time.duration_since(new_sys_time) {
/// Ok(_) => {}
/// Err(e) => println!("SystemTimeError difference: {:?}", e.duration()),
/// }
/// ```
#[derive(Clone, Debug)]
#[stable(feature = "time2", since = "1.8.0")]
pub struct SystemTimeError(Duration);
impl Instant {
/// Returns an instant corresponding to "now".
///
/// # Examples
///
/// ```
/// use std::time::Instant;
///
/// let now = Instant::now();
/// ```
#[must_use]
#[stable(feature = "time2", since = "1.8.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "instant_now")]
pub fn now() -> Instant {
Instant(time::Instant::now())
}
/// Returns the amount of time elapsed from another instant to this one,
/// or zero duration if that instant is later than this one.
///
/// # Panics
///
/// Previous Rust versions panicked when `earlier` was later than `self`. Currently this
/// method saturates. Future versions may reintroduce the panic in some circumstances.
/// See [Monotonicity].
///
/// [Monotonicity]: Instant#monotonicity
///
/// # Examples
///
/// ```no_run
/// use std::time::{Duration, Instant};
/// use std::thread::sleep;
///
/// let now = Instant::now();
/// sleep(Duration::new(1, 0));
/// let new_now = Instant::now();
/// println!("{:?}", new_now.duration_since(now));
/// println!("{:?}", now.duration_since(new_now)); // 0ns
/// ```
#[must_use]
#[stable(feature = "time2", since = "1.8.0")]
pub fn duration_since(&self, earlier: Instant) -> Duration {
self.checked_duration_since(earlier).unwrap_or_default()
}
/// Returns the amount of time elapsed from another instant to this one,
/// or None if that instant is later than this one.
///
/// Due to [monotonicity bugs], even under correct logical ordering of the passed `Instant`s,
/// this method can return `None`.
///
/// [monotonicity bugs]: Instant#monotonicity
///
/// # Examples
///
/// ```no_run
/// use std::time::{Duration, Instant};
/// use std::thread::sleep;
///
/// let now = Instant::now();
/// sleep(Duration::new(1, 0));
/// let new_now = Instant::now();
/// println!("{:?}", new_now.checked_duration_since(now));
/// println!("{:?}", now.checked_duration_since(new_now)); // None
/// ```
#[must_use]
#[stable(feature = "checked_duration_since", since = "1.39.0")]
pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
self.0.checked_sub_instant(&earlier.0)
}
/// Returns the amount of time elapsed from another instant to this one,
/// or zero duration if that instant is later than this one.
///
/// # Examples
///
/// ```no_run
/// use std::time::{Duration, Instant};
/// use std::thread::sleep;
///
/// let now = Instant::now();
/// sleep(Duration::new(1, 0));
/// let new_now = Instant::now();
/// println!("{:?}", new_now.saturating_duration_since(now));
/// println!("{:?}", now.saturating_duration_since(new_now)); // 0ns
/// ```
#[must_use]
#[stable(feature = "checked_duration_since", since = "1.39.0")]
pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
self.checked_duration_since(earlier).unwrap_or_default()
}
/// Returns the amount of time elapsed since this instant.
///
/// # Panics
///
/// Previous Rust versions panicked when the current time was earlier than self. Currently this
/// method returns a Duration of zero in that case. Future versions may reintroduce the panic.
/// See [Monotonicity].
///
/// [Monotonicity]: Instant#monotonicity
///
/// # Examples
///
/// ```no_run
/// use std::thread::sleep;
/// use std::time::{Duration, Instant};
///
/// let instant = Instant::now();
/// let three_secs = Duration::from_secs(3);
/// sleep(three_secs);
/// assert!(instant.elapsed() >= three_secs);
/// ```
#[must_use]
#[stable(feature = "time2", since = "1.8.0")]
pub fn elapsed(&self) -> Duration {
Instant::now() - *self
}
/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
#[stable(feature = "time_checked_add", since = "1.34.0")]
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
self.0.checked_add_duration(&duration).map(Instant)
}
/// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
#[stable(feature = "time_checked_add", since = "1.34.0")]
pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
self.0.checked_sub_duration(&duration).map(Instant)
}
// Used by platform specific `sleep_until` implementations such as the one used on Linux.
#[cfg_attr(
not(target_os = "linux"),
allow(unused, reason = "not every platform has a specific `sleep_until`")
)]
pub(crate) fn into_inner(self) -> time::Instant {
self.0
}
}
#[stable(feature = "time2", since = "1.8.0")]
impl Add<Duration> for Instant {
type Output = Instant;
/// # Panics
///
/// This function may panic if the resulting point in time cannot be represented by the
/// underlying data structure. See [`Instant::checked_add`] for a version without panic.
fn add(self, other: Duration) -> Instant {
self.checked_add(other).expect("overflow when adding duration to instant")
}
}
#[stable(feature = "time_augmented_assignment", since = "1.9.0")]
impl AddAssign<Duration> for Instant {
fn add_assign(&mut self, other: Duration) {
*self = *self + other;
}
}
#[stable(feature = "time2", since = "1.8.0")]
impl Sub<Duration> for Instant {
type Output = Instant;
fn sub(self, other: Duration) -> Instant {
self.checked_sub(other).expect("overflow when subtracting duration from instant")
}
}
#[stable(feature = "time_augmented_assignment", since = "1.9.0")]
impl SubAssign<Duration> for Instant {
fn sub_assign(&mut self, other: Duration) {
*self = *self - other;
}
}
#[stable(feature = "time2", since = "1.8.0")]
impl Sub<Instant> for Instant {
type Output = Duration;
/// Returns the amount of time elapsed from another instant to this one,
/// or zero duration if that instant is later than this one.
///
/// # Panics
///
/// Previous Rust versions panicked when `other` was later than `self`. Currently this
/// method saturates. Future versions may reintroduce the panic in some circumstances.
/// See [Monotonicity].
///
/// [Monotonicity]: Instant#monotonicity
fn sub(self, other: Instant) -> Duration {
self.duration_since(other)
}
}
#[stable(feature = "time2", since = "1.8.0")]
impl fmt::Debug for Instant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl SystemTime {
/// An anchor in time which can be used to create new `SystemTime` instances or
/// learn about where in time a `SystemTime` lies.
//
// NOTE! this documentation is duplicated, here and in std::time::UNIX_EPOCH.
// The two copies are not quite identical, because of the difference in naming.
///
/// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with
/// respect to the system clock. Using `duration_since` on an existing
/// `SystemTime` instance can tell how far away from this point in time a
/// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a
/// `SystemTime` instance to represent another fixed point in time.
///
/// `duration_since(UNIX_EPOCH).unwrap().as_secs()` returns
/// the number of non-leap seconds since the start of 1970 UTC.
/// This is a POSIX `time_t` (as a `u64`),
/// and is the same time representation as used in many Internet protocols.
///
/// # Examples
///
/// ```no_run
/// use std::time::SystemTime;
///
/// match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
/// Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()),
/// Err(_) => panic!("SystemTime before UNIX EPOCH!"),
/// }
/// ```
#[stable(feature = "assoc_unix_epoch", since = "1.28.0")]
pub const UNIX_EPOCH: SystemTime = UNIX_EPOCH;
/// Represents the maximum value representable by [`SystemTime`] on this platform.
///
/// This value differs a lot between platforms, but it is always the case
/// that any positive addition of a [`Duration`], whose value is greater
/// than or equal to the time precision of the operating system, to
/// [`SystemTime::MAX`] will fail.
///
/// # Examples
///
/// ```no_run
/// #![feature(time_systemtime_limits)]
/// use std::time::{Duration, SystemTime};
///
/// // Adding zero will change nothing.
/// assert_eq!(SystemTime::MAX.checked_add(Duration::ZERO), Some(SystemTime::MAX));
///
/// // But adding just one second will already fail ...
/// //
/// // Keep in mind that this in fact may succeed, if the Duration is
/// // smaller than the time precision of the operating system, which
/// // happens to be 1ns on most operating systems, with Windows being the
/// // notable exception by using 100ns, hence why this example uses 1s.
/// assert_eq!(SystemTime::MAX.checked_add(Duration::new(1, 0)), None);
///
/// // Utilize this for saturating arithmetic to improve error handling.
/// // In this case, we will use a certificate with a timestamp in the
/// // future as a practical example.
/// let configured_offset = Duration::from_secs(60 * 60 * 24);
/// let valid_after =
/// SystemTime::now()
/// .checked_add(configured_offset)
/// .unwrap_or(SystemTime::MAX);
/// ```
#[unstable(feature = "time_systemtime_limits", issue = "149067")]
pub const MAX: SystemTime = SystemTime(time::SystemTime::MAX);
/// Represents the minimum value representable by [`SystemTime`] on this platform.
///
/// This value differs a lot between platforms, but it is always the case
/// that any positive subtraction of a [`Duration`] from, whose value is
/// greater than or equal to the time precision of the operating system, to
/// [`SystemTime::MIN`] will fail.
///
/// Depending on the platform, this may be either less than or equal to
/// [`SystemTime::UNIX_EPOCH`], depending on whether the operating system
/// supports the representation of timestamps before the Unix epoch or not.
/// However, it is always guaranteed that a [`SystemTime::UNIX_EPOCH`] fits
/// between a [`SystemTime::MIN`] and [`SystemTime::MAX`].
///
/// # Examples
///
/// ```
/// #![feature(time_systemtime_limits)]
/// use std::time::{Duration, SystemTime};
///
/// // Subtracting zero will change nothing.
/// assert_eq!(SystemTime::MIN.checked_sub(Duration::ZERO), Some(SystemTime::MIN));
///
/// // But subtracting just one second will already fail.
/// //
/// // Keep in mind that this in fact may succeed, if the Duration is
/// // smaller than the time precision of the operating system, which
/// // happens to be 1ns on most operating systems, with Windows being the
/// // notable exception by using 100ns, hence why this example uses 1s.
/// assert_eq!(SystemTime::MIN.checked_sub(Duration::new(1, 0)), None);
///
/// // Utilize this for saturating arithmetic to improve error handling.
/// // In this case, we will use a cache expiry as a practical example.
/// let configured_expiry = Duration::from_secs(60 * 3);
/// let expiry_threshold =
/// SystemTime::now()
/// .checked_sub(configured_expiry)
/// .unwrap_or(SystemTime::MIN);
/// ```
#[unstable(feature = "time_systemtime_limits", issue = "149067")]
pub const MIN: SystemTime = SystemTime(time::SystemTime::MIN);
/// Returns the system time corresponding to "now".
///
/// # Examples
///
/// ```
/// use std::time::SystemTime;
///
/// let sys_time = SystemTime::now();
/// ```
#[must_use]
#[stable(feature = "time2", since = "1.8.0")]
pub fn now() -> SystemTime {
SystemTime(time::SystemTime::now())
}
/// Returns the amount of time elapsed from an earlier point in time.
///
/// This function may fail because measurements taken earlier are not
/// guaranteed to always be before later measurements (due to anomalies such
/// as the system clock being adjusted either forwards or backwards).
/// [`Instant`] can be used to measure elapsed time without this risk of failure.
///
/// If successful, <code>[Ok]\([Duration])</code> is returned where the duration represents
/// the amount of time elapsed from the specified measurement to this one.
///
/// Returns an [`Err`] if `earlier` is later than `self`, and the error
/// contains how far from `self` the time is.
///
/// # Examples
///
/// ```no_run
/// use std::time::SystemTime;
///
/// let sys_time = SystemTime::now();
/// let new_sys_time = SystemTime::now();
/// let difference = new_sys_time.duration_since(sys_time)
/// .expect("Clock may have gone backwards");
/// println!("{difference:?}");
/// ```
#[stable(feature = "time2", since = "1.8.0")]
pub fn duration_since(&self, earlier: SystemTime) -> Result<Duration, SystemTimeError> {
self.0.sub_time(&earlier.0).map_err(SystemTimeError)
}
/// Returns the difference from this system time to the
/// current clock time.
///
/// This function may fail as the underlying system clock is susceptible to
/// drift and updates (e.g., the system clock could go backwards), so this
/// function might not always succeed. If successful, <code>[Ok]\([Duration])</code> is
/// returned where the duration represents the amount of time elapsed from
/// this time measurement to the current time.
///
/// To measure elapsed time reliably, use [`Instant`] instead.
///
/// Returns an [`Err`] if `self` is later than the current system time, and
/// the error contains how far from the current system time `self` is.
///
/// # Examples
///
/// ```no_run
/// use std::thread::sleep;
/// use std::time::{Duration, SystemTime};
///
/// let sys_time = SystemTime::now();
/// let one_sec = Duration::from_secs(1);
/// sleep(one_sec);
/// assert!(sys_time.elapsed().unwrap() >= one_sec);
/// ```
#[stable(feature = "time2", since = "1.8.0")]
pub fn elapsed(&self) -> Result<Duration, SystemTimeError> {
SystemTime::now().duration_since(*self)
}
/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
/// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
///
/// In the case that the `duration` is smaller than the time precision of the operating
/// system, `Some(self)` will be returned.
#[stable(feature = "time_checked_add", since = "1.34.0")]
pub fn checked_add(&self, duration: Duration) -> Option<SystemTime> {
self.0.checked_add_duration(&duration).map(SystemTime)
}
/// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
/// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
///
/// In the case that the `duration` is smaller than the time precision of the operating
/// system, `Some(self)` will be returned.
#[stable(feature = "time_checked_add", since = "1.34.0")]
pub fn checked_sub(&self, duration: Duration) -> Option<SystemTime> {
self.0.checked_sub_duration(&duration).map(SystemTime)
}
/// Saturating [`SystemTime`] addition, computing `self + duration`,
/// returning [`SystemTime::MAX`] if overflow occurred.
///
/// In the case that the `duration` is smaller than the time precision of
/// the operating system, `self` will be returned.
#[unstable(feature = "time_saturating_systemtime", issue = "151199")]
pub fn saturating_add(&self, duration: Duration) -> SystemTime {
self.checked_add(duration).unwrap_or(SystemTime::MAX)
}
/// Saturating [`SystemTime`] subtraction, computing `self - duration`,
/// returning [`SystemTime::MIN`] if overflow occurred.
///
/// In the case that the `duration` is smaller than the time precision of
/// the operating system, `self` will be returned.
#[unstable(feature = "time_saturating_systemtime", issue = "151199")]
pub fn saturating_sub(&self, duration: Duration) -> SystemTime {
self.checked_sub(duration).unwrap_or(SystemTime::MIN)
}
/// Saturating computation of time elapsed from an earlier point in time,
/// returning [`Duration::ZERO`] in the case that `earlier` is later or
/// equal to `self`.
///
/// # Examples
///
/// ```no_run
/// #![feature(time_saturating_systemtime)]
/// use std::time::{Duration, SystemTime};
///
/// let now = SystemTime::now();
/// let prev = now.saturating_sub(Duration::new(1, 0));
///
/// // now - prev should return non-zero.
/// assert_eq!(now.saturating_duration_since(prev), Duration::new(1, 0));
/// assert!(now.duration_since(prev).is_ok());
///
/// // prev - now should return zero (and fail with the non-saturating).
/// assert_eq!(prev.saturating_duration_since(now), Duration::ZERO);
/// assert!(prev.duration_since(now).is_err());
///
/// // now - now should return zero (and work with the non-saturating).
/// assert_eq!(now.saturating_duration_since(now), Duration::ZERO);
/// assert!(now.duration_since(now).is_ok());
/// ```
#[unstable(feature = "time_saturating_systemtime", issue = "151199")]
pub fn saturating_duration_since(&self, earlier: SystemTime) -> Duration {
self.duration_since(earlier).unwrap_or(Duration::ZERO)
}
}
#[stable(feature = "time2", since = "1.8.0")]
impl Add<Duration> for SystemTime {
type Output = SystemTime;
/// # Panics
///
/// This function may panic if the resulting point in time cannot be represented by the
/// underlying data structure. See [`SystemTime::checked_add`] for a version without panic.
fn add(self, dur: Duration) -> SystemTime {
self.checked_add(dur).expect("overflow when adding duration to instant")
}
}
#[stable(feature = "time_augmented_assignment", since = "1.9.0")]
impl AddAssign<Duration> for SystemTime {
fn add_assign(&mut self, other: Duration) {
*self = *self + other;
}
}
#[stable(feature = "time2", since = "1.8.0")]
impl Sub<Duration> for SystemTime {
type Output = SystemTime;
fn sub(self, dur: Duration) -> SystemTime {
self.checked_sub(dur).expect("overflow when subtracting duration from instant")
}
}
#[stable(feature = "time_augmented_assignment", since = "1.9.0")]
impl SubAssign<Duration> for SystemTime {
fn sub_assign(&mut self, other: Duration) {
*self = *self - other;
}
}
#[stable(feature = "time2", since = "1.8.0")]
impl fmt::Debug for SystemTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
/// An anchor in time which can be used to create new `SystemTime` instances or
/// learn about where in time a `SystemTime` lies.
//
// NOTE! this documentation is duplicated, here and in SystemTime::UNIX_EPOCH.
// The two copies are not quite identical, because of the difference in naming.
///
/// This constant is defined to be "1970-01-01 00:00:00 UTC" on all systems with
/// respect to the system clock. Using `duration_since` on an existing
/// [`SystemTime`] instance can tell how far away from this point in time a
/// measurement lies, and using `UNIX_EPOCH + duration` can be used to create a
/// [`SystemTime`] instance to represent another fixed point in time.
///
/// `duration_since(UNIX_EPOCH).unwrap().as_secs()` returns
/// the number of non-leap seconds since the start of 1970 UTC.
/// This is a POSIX `time_t` (as a `u64`),
/// and is the same time representation as used in many Internet protocols.
///
/// # Examples
///
/// ```no_run
/// use std::time::{SystemTime, UNIX_EPOCH};
///
/// match SystemTime::now().duration_since(UNIX_EPOCH) {
/// Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()),
/// Err(_) => panic!("SystemTime before UNIX EPOCH!"),
/// }
/// ```
#[stable(feature = "time2", since = "1.8.0")]
pub const UNIX_EPOCH: SystemTime = SystemTime(time::UNIX_EPOCH);
impl SystemTimeError {
/// Returns the positive duration which represents how far forward the
/// second system time was from the first.
///
/// A `SystemTimeError` is returned from the [`SystemTime::duration_since`]
/// and [`SystemTime::elapsed`] methods whenever the second system time
/// represents a point later in time than the `self` of the method call.
///
/// # Examples
///
/// ```no_run
/// use std::thread::sleep;
/// use std::time::{Duration, SystemTime};
///
/// let sys_time = SystemTime::now();
/// sleep(Duration::from_secs(1));
/// let new_sys_time = SystemTime::now();
/// match sys_time.duration_since(new_sys_time) {
/// Ok(_) => {}
/// Err(e) => println!("SystemTimeError difference: {:?}", e.duration()),
/// }
/// ```
#[must_use]
#[stable(feature = "time2", since = "1.8.0")]
pub fn duration(&self) -> Duration {
self.0
}
}
#[stable(feature = "time2", since = "1.8.0")]
impl Error for SystemTimeError {}
#[stable(feature = "time2", since = "1.8.0")]
impl fmt::Display for SystemTimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "second time provided was later than self")
}
}
impl FromInner<time::SystemTime> for SystemTime {
fn from_inner(time: time::SystemTime) -> SystemTime {
SystemTime(time)
}
}
impl IntoInner<time::SystemTime> for SystemTime {
fn into_inner(self) -> time::SystemTime {
self.0
}
}

2
ilm.ld
View File

@@ -5,7 +5,7 @@ OUTPUT_ARCH(riscv)
ENTRY(_start)
MEMORY {
RAM (wxa) : ORIGIN = 0x80000000, LENGTH = 128M
RAM (wxa) : ORIGIN = 0x80000000, LENGTH = 512M
}
SECTIONS {

View File

@@ -2,6 +2,7 @@ release := ""
qemu_flags := ""
cargo_flags := "" + if release != "" { "--release" } else { "" }
bin_path := if release != "" { "target/riscv64/release" } else { "target/riscv64/debug" }
rust_src := `rustc --print sysroot` / "lib/rustlib/src/rust/library/std/src"
default: run
@@ -12,6 +13,67 @@ mount_filesystem:
sync_filesystem:
sync
cp_std path:
@echo "Copying {{ path }}"
@mkdir {{ "crates/std/src" / parent_directory(path) }} -p
@cp {{ rust_src / path }} {{ "crates/std/src" / path }}
@sed -i -f patches.sed {{ "crates/std/src" / path }}
insert_target line path:
@sed -i '{{ line }}a \target_os = "survos",' {{ "crates/std/src" / path }}
update_std:
@# Not copied : sys/mod.rs, io.rs, error.rs, thread.rs, sync.rs
# @just cp_std "process.rs"
# @just cp_std "fs.rs"
@just cp_std "time.rs"
@just cp_std "hash/mod.rs"
@just cp_std "hash/random.rs"
@just cp_std "num/mod.rs"
@just cp_std "ffi/c_str.rs"
@just cp_std "ffi/mod.rs"
@just cp_std "ffi/os_str.rs"
@just cp_std "ffi/os_str/tests.rs"
@just cp_std "thread/local.rs"
@just cp_std "sys/exit.rs"
@just cp_std "sys/env_consts.rs"
@just cp_std "sys/configure_builtins.rs"
@just cp_std "sys/cmath.rs"
@just cp_std "sys/sync/condvar/mod.rs"
@just cp_std "sys/sync/condvar/no_threads.rs"
@just cp_std "sys/sync/mutex/mod.rs"
@just cp_std "sys/sync/mutex/no_threads.rs"
@just cp_std "sys/sync/once/mod.rs"
@just cp_std "sys/sync/once/no_threads.rs"
@just cp_std "sys/sync/rwlock/mod.rs"
@just cp_std "sys/sync/rwlock/no_threads.rs"
@just cp_std "sys/thread/mod.rs"
@just cp_std "sys/thread/unsupported.rs"
@just cp_std "sys/thread_local/no_threads.rs"
@just cp_std "sys/thread_local/os.rs"
@just cp_std "sys/time/mod.rs"
@just cp_std "sys/time/unsupported.rs"
@just cp_std "sys/random/mod.rs"
@just cp_std "sys/random/unsupported.rs"
@just cp_std "sys/env/mod.rs"
@just cp_std "sys/env/common.rs"
@just cp_std "sys/env/unsupported.rs"
@just cp_std "sys/os_str/mod.rs"
@just cp_std "sys/os_str/bytes.rs"
@just cp_std "sys/os_str/bytes/tests.rs"
@just cp_std "sys/path/mod.rs"
@just cp_std "sys/fs/mod.rs"
@just cp_std "sys/fs/common.rs"
@just cp_std "sys/fs/unsupported.rs"
@just insert_target 108 "sys/random/mod.rs"
@just insert_target 125 "sys/random/mod.rs"
@# Copied but edited for the moment
# @just cp_std "sys/thread_local/mod.rs"
# @just cp_std "sys/alloc/mod.rs"
# @just cp_std "alloc.rs"
# @just cp_std "path.rs"
# @just cp_std "sys/path/unix.rs"
build_user_prog prog:
RUSTFLAGS="-C relocation-model=pic -C link-arg=-Tuser.ld -C link-arg=-pie" cargo b {{ cargo_flags }} --package {{ prog }}
riscv64-elf-strip {{ bin_path / prog }}
@@ -34,8 +96,8 @@ qemu := f"qemu-system-riscv64 \
-device bochs-display \
-device virtio-keyboard-pci \
-device virtio-mouse-pci \
-device loader,file=disk.img,addr=0x90000000 \
-bios none -m 512M {{qemu_flags}}"
-device loader,file=disk.img,addr=0xA0000000 \
-bios none -m 1024M {{qemu_flags}}"
# -trace \"virtio*\"
# -d guest_errors,unimp,int"

9
patches.sed Normal file
View File

@@ -0,0 +1,9 @@
s|crate::bstr::ByteStr|alloc_crate::bstr::ByteStr|g
s|crate::collections::TryReserveError|alloc_crate::collections::TryReserveError|g
s|crate::sync::Arc|alloc_crate::sync::Arc|g
s|alloc::ffi|alloc_crate::ffi|g
s|alloc::slice::Join|alloc_crate::slice::Join|g
# /target_os = "xous",/a \ target_os = "survos",
# Ajouter d'autres modifications facilement ici :
# s|ancien_texte|nouveau_texte|g

View File

@@ -6,7 +6,7 @@
"target-endian": "little",
"target-pointer-width": 64,
"arch": "riscv64",
"os": "none",
"os": "survos",
"vendor": "unknown",
"env": "",
"features": "+i,+m,+a,+zicsr",

2
src/drivers.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod keyboard;
pub mod mouse;

67
src/drivers/keyboard.rs Normal file
View File

@@ -0,0 +1,67 @@
use crate::{
keymap::{KeyType, ModifierType, map_keycode},
tty::TTY0,
virtio::{
Virtqueue,
input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver},
},
virtual_fs::{FILE_SYSTEM, VirtualFileSystem},
};
#[derive(Debug, Clone, Copy, Default)]
pub struct KeyboardState {
// ctrl_modifier: bool,
pub shift_modifier: bool,
pub alt_gr_modifier: bool,
}
impl KeyboardState {
pub const fn new() -> Self {
Self {
// ctrl_modifier: false,
shift_modifier: false,
alt_gr_modifier: false,
}
}
}
static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
pub static mut KBD_DRIVER: VirtioPciDriver<KeyboardState> = unsafe {
VirtioPciDriver::new(
|state, event| {
let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap();
kbd_buffer
.write(core::mem::transmute::<
&VirtioInputEvent,
&[u8; size_of::<VirtioInputEvent>()],
>(event))
.unwrap();
if event.is_key() {
let event = event.as_key_event();
#[allow(clippy::single_match)]
match map_keycode(event.code, state) {
KeyType::Ascii(c) if event.value == EventCodeValue::Pressed => {
let mut buf = [0; 4];
let to_send = c.encode_utf8(&mut buf);
for c in to_send.as_bytes() {
TTY0.buffer.borrow_mut().push(*c);
}
}
KeyType::Modifier(ModifierType::Shift) => {
state.shift_modifier = !state.shift_modifier;
}
KeyType::Modifier(ModifierType::AltGr) => {
state.alt_gr_modifier = !state.alt_gr_modifier;
}
_ => {}
}
// println!("event: {:#?}", event);
} else {
// println!("key pressed, {:#?}", event);
}
},
KeyboardState::new(),
&mut KBD_QUEUE,
)
};

38
src/drivers/mouse.rs Normal file
View File

@@ -0,0 +1,38 @@
use crate::{
cursor::{clear_cursor, draw_cursor},
vga::Vga,
virtio::{self, Virtqueue, input::VirtioPciDriver},
};
pub static mut MOUSE_POSITION: (u16, u16) = (0, 0);
static mut MOUSE_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
pub static mut MOUSE_DRIVER: VirtioPciDriver<()> = unsafe {
VirtioPciDriver::new(
|_, event| {
if event.is_relative() {
let event = event.as_relative_event();
clear_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
match event.code {
virtio::input::EventCodeRelative::X => {
MOUSE_POSITION.0 = (MOUSE_POSITION.0 as i32 + event.value) as u16
}
virtio::input::EventCodeRelative::Y => {
MOUSE_POSITION.1 = (MOUSE_POSITION.1 as i32 + event.value) as u16
}
_ => {}
}
draw_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
// println!("mouse moved relatively, {:#?}", event);
} else {
// println!("mouse moved, {:#?}", event);
}
},
(),
&mut MOUSE_QUEUE,
)
};

View File

@@ -12,7 +12,7 @@ use io::{IoBase, Read, Seek, Write};
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
const DISK_ADDR: *const u8 = 0x9000_0000 as *const _;
const DISK_ADDR: *const u8 = 0xA000_0000 as *const _;
#[derive(Debug)]
/// Simple disk backend that reads from a fixed memory region.

View File

@@ -9,9 +9,9 @@ use log::info;
use shared::syscall::SysCall;
use crate::{
KBD_DRIVER, MOUSE_DRIVER,
boot::sbi::{ExtensionID, TimerFunctionID},
clear_csr,
drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER},
process::{ExecutionContext, exit_process, sleep},
read_csr,
riscv::{disable_interrupt, dump_cpu},
@@ -160,6 +160,15 @@ unsafe extern "C" fn supervisor_trap_handler(
unsafe { (*interrupt_state).a[0] = fd as u64 };
}
SysCall::Close => {
let fd = a1;
let mut scheduler = SCHEDULER.lock();
let current_process = scheduler.get_current_process();
let mut vnode = current_process.fd_table[fd as usize].take().unwrap();
vnode.close();
}
SysCall::Write => {
let fd = a1;
let buf =
@@ -168,7 +177,8 @@ unsafe extern "C" fn supervisor_trap_handler(
let mut scheduler = SCHEDULER.lock();
let current_process = scheduler.get_current_process();
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
vnode.write(buf).unwrap();
let res = vnode.write(buf).unwrap();
unsafe { (*interrupt_state).a[0] = res as u64 };
}
SysCall::Read => {
let fd = a1;
@@ -182,6 +192,8 @@ unsafe extern "C" fn supervisor_trap_handler(
if res == 0 && !buf.is_empty() {
loop_syscall(interrupt_state);
scheduler.schedule(&mut interrupt_state);
} else {
unsafe { (*interrupt_state).a[0] = res as u64 };
}
}
SysCall::Seek => {
@@ -197,6 +209,17 @@ unsafe extern "C" fn supervisor_trap_handler(
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
vnode.seek(seek).unwrap();
}
SysCall::Spawn => {
let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
let mut scheduler = SCHEDULER.lock();
scheduler.create_process_from_file(path, 0, core::ptr::null());
}
SysCall::ExecVE => {
// let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
// let mut scheduler = SCHEDULER.lock();
// scheduler.create_process_from_file(path, &[]);
unimplemented!("ExecVE is not implemented")
}
SysCall::Alloc => {
let layout = Layout::from_size_align(a1 as usize, a2 as usize).unwrap();
// Allocate memory and put the pointer in a0

View File

@@ -1,4 +1,4 @@
use crate::KeyboardState;
use crate::drivers::keyboard::KeyboardState;
#[derive(Debug, Clone, Copy)]
#[derive_const(PartialEq, Eq)]

View File

@@ -12,7 +12,8 @@
arbitrary_self_types_pointers,
derive_const,
const_cmp,
const_trait_impl
const_trait_impl,
trait_alias
)]
use core::sync::atomic::AtomicBool;
@@ -22,20 +23,15 @@ use embedded_alloc::LlffHeap as Heap;
use log::info;
use crate::{
cursor::{clear_cursor, draw_cursor},
drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER},
io::init_log,
keymap::{KeyType, ModifierType, map_keycode},
pci::{PciDeviceIterator, scan_virtio_devices},
pci::scan_virtio_devices,
riscv::enable_supervisor_interrupt,
scheduler::{SCHEDULER, idle},
tty::TTY0,
user::{proc2, test},
vga::Vga,
virtio::{
Virtqueue,
input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver, init_plic_pci},
},
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, init_file_system},
virtio::input::init_plic_pci,
virtual_fs::init_file_system,
};
extern crate alloc;
@@ -44,6 +40,7 @@ mod critical_section;
mod cursor;
mod data_structures;
mod draw;
mod drivers;
mod fs;
mod interrupt;
mod io;
@@ -75,97 +72,6 @@ const _: () = assert!(core::mem::size_of::<usize>() == core::mem::size_of::<u64>
#[cfg(not(target_endian = "little"))]
compile_error! {"This kernel implementation assume endianness is little-endian. Some memory access like PCI could not work in big-endian."}
#[derive(Debug, Clone, Copy, Default)]
pub struct KeyboardState {
// ctrl_modifier: bool,
pub shift_modifier: bool,
pub alt_gr_modifier: bool,
}
impl KeyboardState {
pub const fn new() -> Self {
Self {
// ctrl_modifier: false,
shift_modifier: false,
alt_gr_modifier: false,
}
}
}
static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
pub static mut KBD_DRIVER: VirtioPciDriver<KeyboardState> = unsafe {
VirtioPciDriver::new(
|state, event| {
let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap();
kbd_buffer
.write(core::mem::transmute::<
&VirtioInputEvent,
&[u8; size_of::<VirtioInputEvent>()],
>(event))
.unwrap();
if event.is_key() {
let event = event.as_key_event();
#[allow(clippy::single_match)]
match map_keycode(event.code, state) {
KeyType::Ascii(c) if event.value == EventCodeValue::Pressed => {
let mut buf = [0; 4];
let to_send = c.encode_utf8(&mut buf);
for c in to_send.as_bytes() {
TTY0.buffer.borrow_mut().push(*c);
}
}
KeyType::Modifier(ModifierType::Shift) => {
state.shift_modifier = !state.shift_modifier;
}
KeyType::Modifier(ModifierType::AltGr) => {
state.alt_gr_modifier = !state.alt_gr_modifier;
}
_ => {}
}
println!("event: {:#?}", event);
} else {
// println!("key pressed, {:#?}", event);
}
},
KeyboardState::new(),
&mut KBD_QUEUE,
)
};
pub static mut MOUSE_POSITION: (u16, u16) = (0, 0);
static mut MOUSE_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
pub static mut MOUSE_DRIVER: VirtioPciDriver<()> = unsafe {
VirtioPciDriver::new(
|_, event| {
if event.is_relative() {
let event = event.as_relative_event();
clear_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
match event.code {
virtio::input::EventCodeRelative::X => {
MOUSE_POSITION.0 = (MOUSE_POSITION.0 as i32 + event.value) as u16
}
virtio::input::EventCodeRelative::Y => {
MOUSE_POSITION.1 = (MOUSE_POSITION.1 as i32 + event.value) as u16
}
_ => {}
}
draw_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
// println!("mouse moved relatively, {:#?}", event);
} else {
// println!("mouse moved, {:#?}", event);
}
},
(),
&mut MOUSE_QUEUE,
)
};
#[unsafe(no_mangle)]
pub extern "C" fn supervisor_mode_entry() {
unsafe {
@@ -180,19 +86,20 @@ pub extern "C" fn supervisor_mode_entry() {
info!("Hello World !");
// unsafe { Vga.draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) };
SCHEDULER.lock().create_process(Box::new(test), "proc1");
SCHEDULER.lock().create_process(Box::new(proc2), "proc2");
// let binding = Box::leak(Box::new(["coucou".as_bytes()]));
SCHEDULER
.lock()
.create_process(Box::new(test), "proc1", 0, core::ptr::null());
SCHEDULER
.lock()
.create_process(Box::new(proc2), "proc2", 0, core::ptr::null());
SCHEDULER
.lock()
.create_process_from_file("/usr/bin/test_pic");
.create_process_from_file("/usr/bin/test_pic", 0, core::ptr::null());
enable_supervisor_interrupt();
for pci in PciDeviceIterator::new() {
println!("{:x?}", pci.vendor_and_device_id())
}
unsafe {
let (pci_keyboard, pci_mouse) = scan_virtio_devices();
let (pci_keyboard, pci_mouse) = (pci_keyboard.unwrap(), pci_mouse.unwrap());
@@ -214,5 +121,5 @@ pub extern "C" fn supervisor_mode_entry() {
init_plic_pci(pci_keyboard.irq);
init_plic_pci(pci_mouse.irq);
}
idle();
idle(0, core::ptr::null());
}

View File

@@ -19,7 +19,6 @@ use crate::{
riscv::SStatus,
scheduler::{ACTIVE_PID, SCHEDULER, Scheduler},
time::elapsed_time_since_startup,
tty::TTY0,
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, VirtualNode},
};
@@ -66,6 +65,9 @@ pub struct ExecutionContext {
pub mstatus: u64,
}
pub trait ProcessEntryTrait = Fn(isize, *const *const u8);
pub type ProcessEntry = dyn ProcessEntryTrait;
/// Represents a process in the system.
///
/// Each process has its own execution context, stack,
@@ -78,13 +80,13 @@ pub struct Process {
/// Current state of the process.
pub state: ProcessState,
/// Optional entry point for the process code.
pub entry: Option<Box<dyn Fn()>>,
pub entry: Option<Box<ProcessEntry>>,
/// Wake time for sleeping processes.
pub wake_time: Duration,
/// Saved execution context.
pub ctx: ExecutionContext,
/// Process stack.
pub stack: [u64; STACK_SIZE],
pub stack: Box<[u64; STACK_SIZE]>,
/// File descriptor table.
pub fd_table: Vec<Option<Box<dyn VirtualNode>>>,
}
@@ -109,7 +111,7 @@ impl Default for Process {
mepc: core::ptr::null(),
mstatus: 0,
},
stack: [0; _],
stack: unsafe { Box::new_zeroed().assume_init() },
entry: None,
fd_table: Vec::new(),
}
@@ -150,7 +152,12 @@ impl Scheduler {
/// Attempts to open `path`, load its contents into memory and create a new
/// kernel process that will execute the loaded binary. Returns the PID of the
/// created process, or -1 on failure.
pub fn create_process_from_file<T: AsRef<Path>>(&mut self, path: T) -> i64 {
pub fn create_process_from_file<T: AsRef<Path>>(
&mut self,
path: T,
argc: isize,
argv: *const *const u8,
) -> i64 {
let path = path.as_ref();
let name = path.as_str();
@@ -231,25 +238,30 @@ impl Scheduler {
// Entry point
let entry_va = gelf.entry;
let entry_addr = unsafe { base.add((entry_va - min_vaddr) as usize) } as *const u8;
let entry_fn =
unsafe { core::mem::transmute::<*const u8, extern "C" fn()>(entry_addr) };
let wrapper = Box::new(move || {
entry_fn();
});
let entry_fn = unsafe {
core::mem::transmute::<*const u8, extern "C" fn(isize, *const *const u8)>(
entry_addr,
)
};
let wrapper = move |argc, argv| {
entry_fn(argc, argv);
};
println!("Program loaded at : {:x?}", entry_addr);
return self.create_process(wrapper, name);
return self.create_process(wrapper, name, argc, argv);
}
}
// Fallback: treat the file as a raw binary blob and execute in-place
let entry_point = unsafe {
core::mem::transmute::<*const u8, extern "C" fn()>(Vec::leak(content).as_ptr())
core::mem::transmute::<*const u8, extern "C" fn(isize, *const *const u8)>(
Vec::leak(content).as_ptr(),
)
};
let wrapper = move |argc, argv| {
entry_point(argc, argv);
};
let wrapper = Box::new(move || {
entry_point();
});
self.create_process(wrapper, name)
self.create_process(wrapper, name, argc, argv)
}
/// Creates a new process with the specified code and name.
@@ -271,39 +283,48 @@ impl Scheduler {
///
/// The provided `code` function will be executed when the process is first
/// scheduled. Returns the new PID, or -1 if the process table is full.
pub fn create_process<T: Into<String>, F: Fn() + 'static + Send>(
pub fn create_process<T: Into<String>, F: ProcessEntryTrait + 'static + Send>(
&mut self,
code: Box<F>,
code: F,
name: T,
argc: isize,
argv: *const *const u8,
) -> i64 {
// SAFETY: Initializing process in the global table.
// Access is safe because we verified bounds and found a Dead slot.
unsafe {
self.process_table
.insert(self.next_pid, Box::new(Process::default()));
self.process_table.insert(self.next_pid, Process::default());
let process = self.process_table.get_mut(&self.next_pid).unwrap();
// Configure process metadata
process.pid = self.next_pid as i64;
process.name = name.into();
process.state = ProcessState::Activable;
process.entry = Some(code);
process.entry = Some(Box::new(code));
process.fd_table = Vec::new();
FILE_SYSTEM.mount(
format!("/proc/{}/0", process.pid).into(),
Box::new(TTY0.clone()),
);
// FILE_SYSTEM.mount(
// format!("/proc/{}/0", process.pid).into(),
// Box::new(TTY0.clone()),
// );
// FD 0
process.fd_table.push(Some(
FILE_SYSTEM
.open(format!("/proc/{}/0", process.pid).as_ref())
.unwrap(),
));
process
.fd_table
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
// FD 1
process
.fd_table
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
// FD 2
process
.fd_table
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
// Configure execution context
// a0 contains the pointer to the function to execute
process.ctx.a[0] = &raw const *process.entry.as_ref().unwrap_unchecked() as u64;
process.ctx.a[1] = argc as u64;
process.ctx.a[2] = argv as u64;
// mepc points to process_launcher which will call the function
process.ctx.mepc = process_launcher as *const _;
@@ -338,10 +359,15 @@ impl Scheduler {
/// This function is installed into the process `mepc` so that when the new
/// process is scheduled it will run this launcher which calls the user
/// function and ensures the process exits cleanly.
extern "C" fn process_launcher(code: *const Box<dyn Fn()>) {
#[allow(improper_ctypes_definitions)]
extern "C" fn process_launcher(
code: *const Box<ProcessEntry>,
argc: isize,
argv: *const *const u8,
) {
// SAFETY: The code pointer was initialized in create_process
// and points to a valid function.
unsafe { (*code)() };
unsafe { (*code)(argc, argv) };
// If user code didn't exit explicitly, call exit() to clean up the process
exit();

View File

@@ -17,7 +17,7 @@ use crate::{
#[derive(Debug)]
pub struct Scheduler {
pub next_pid: u64,
pub process_table: BTreeMap<u64, Box<Process>>,
pub process_table: BTreeMap<u64, Process>,
}
pub static ACTIVE_PID: AtomicU64 = AtomicU64::new(0);
@@ -30,7 +30,7 @@ pub static SCHEDULER: Mutex<LazyCell<Scheduler>> = Mutex::new(LazyCell::new(|| S
/// Idle loop executed when there is no runnable process.
///
/// Uses the `wfi` instruction to yield the CPU while waiting for interrupts.
pub fn idle() {
pub fn idle(_argc: isize, _argv: *const *const u8) {
loop {
// write_string_temp("idle");
// info!("idle");
@@ -47,7 +47,7 @@ impl Scheduler {
/// it as the active process.
pub fn init(&mut self) {
info!("scheduler init");
self.create_process(Box::new(idle), "idle");
self.create_process(Box::new(idle), "idle", 0, core::ptr::null());
self.process_table.get_mut(&0).unwrap().state = ProcessState::Active;
}
@@ -69,9 +69,7 @@ impl Scheduler {
}
}
let mut current_process_iter = self
.process_table
.range_mut((Bound::Excluded(prev_pid), Bound::Unbounded));
let mut current_process_iter = self.process_table.range_mut(prev_pid + 1..);
ACTIVE_PID.store(
loop {

View File

@@ -6,14 +6,15 @@ use core::time::Duration;
use shared::syscall::{sleep, write_string_temp};
pub fn test() {
pub fn test(_argc: isize, _argv: *const *const u8) {
loop {
// write_string_temp(str::from_utf8(_args[0]).unwrap());
write_string_temp("test");
sleep(Duration::new(2, 0));
}
}
pub fn proc2() {
pub fn proc2(_argc: isize, _argv: *const *const u8) {
loop {
write_string_temp("proc2");
sleep(Duration::new(3, 0));

View File

@@ -22,6 +22,7 @@ pub struct VirtioPciDriver<S, F: Fn(&mut S, &VirtioInputEvent) = fn(&mut S, &Vir
}
#[repr(u16)]
#[allow(unused)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventType {
Sync = 0,
@@ -31,6 +32,7 @@ pub enum EventType {
}
#[repr(u16)]
#[allow(unused)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventCodeRelative {
X = 0,
@@ -39,6 +41,7 @@ pub enum EventCodeRelative {
}
#[repr(u16)]
#[allow(unused)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EventCodeValue {
Released = 0,

View File

@@ -3,7 +3,6 @@ use io::SeekFrom;
use crate::{
draw::{Color, Draw, FONT_HEIGHT, FONT_WIDTH},
println,
vga::{self, Vga},
virtual_fs::{self, FILE_SYSTEM, VirtualFileSystem},
};
@@ -41,7 +40,6 @@ impl VirtualConsole {
}
pub fn write_char(&mut self, c: char) {
println!("char_console: {:?}", c as u64);
let mut last_cursor = self.cursor;
match c {
'\n' => {

View File

@@ -9,6 +9,7 @@ use hashbrown::HashMap;
use io::{IoBase, Read, Seek, Write};
pub mod keyboard;
pub mod null;
pub mod stdin;
pub mod virtual_stdin;
@@ -16,10 +17,12 @@ use crate::{
fs::Disk,
tty::TTY0,
vga::Vga,
virtual_fs::{keyboard::KeyboardBuffer, virtual_stdin::VirtualStdin},
virtual_fs::{keyboard::KeyboardBuffer, null::Null, virtual_stdin::VirtualStdin},
};
pub trait VirtualNode: IoBase<Error = ()> + Read + Write + Seek + Debug {}
pub trait VirtualNode: IoBase<Error = ()> + Read + Write + Seek + Debug {
fn close(&mut self) {}
}
pub trait VirtualFileSystem: Debug {
fn open(&mut self, path: &Path) -> Result<Box<dyn VirtualNode + '_>, ()>;
@@ -62,6 +65,7 @@ pub unsafe fn init_file_system() {
unsafe {
FILE_SYSTEM.mount("/dev/fb0".into(), Box::new(VGAFileSystem));
FILE_SYSTEM.mount("/dev/tty0".into(), Box::new(TTY0.clone()));
FILE_SYSTEM.mount("/dev/null".into(), Box::new(Null));
FILE_SYSTEM.mount(
"/dev/input/keyboard".into(),
Box::new(KeyboardBuffer::new()),

51
src/virtual_fs/null.rs Normal file
View File

@@ -0,0 +1,51 @@
use alloc::boxed::Box;
use io::{IoBase, Read, Seek, Write};
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
#[derive(Debug)]
pub struct Null;
#[derive(Debug)]
pub struct NullNode;
impl VirtualFileSystem for Null {
fn open(
&mut self,
path: &bffs::path::Path,
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, ()> {
if !path.is_empty() {
Err(())
} else {
Ok(Box::new(NullNode))
}
}
}
impl IoBase for NullNode {
type Error = ();
}
impl Read for NullNode {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, Self::Error> {
Ok(0)
}
}
impl Seek for NullNode {
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, Self::Error> {
todo!()
}
}
impl Write for NullNode {
fn write(&mut self, _buf: &[u8]) -> Result<usize, Self::Error> {
todo!()
}
fn flush(&mut self) -> Result<(), Self::Error> {
todo!()
}
}
impl VirtualNode for NullNode {}

7
user/shell/Cargo.toml Normal file
View File

@@ -0,0 +1,7 @@
[package]
name = "shell"
version = "0.1.0"
edition = "2024"
[dependencies]
std = { path = "../../crates/std" }

8
user/shell/src/main.rs Normal file
View File

@@ -0,0 +1,8 @@
#![feature(custom_std)]
fn main() -> isize {
println!(
"Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! "
);
0
}

View File

@@ -4,4 +4,4 @@ version = "0.1.0"
edition = "2024"
[dependencies]
os-std = { path = "../../crates/os-std" }
std = { path = "../../crates/std" }

View File

@@ -1,10 +1,10 @@
#![no_std]
#![no_main]
#![feature(custom_std)]
#![allow(unused)]
// use core::time::Duration;
use os_std::syscall;
os_std::custom_std_setup! {}
use std::{
io::{Read, Write, stdin},
syscall,
};
fn main() {
// let mut input = String::new();
@@ -12,19 +12,13 @@ fn main() {
// syscall::seek(&mut file, SeekFrom::End(-3));
// syscall::write(&mut file, &[255; 6400 * 50]);
// syscall::sleep(Duration::from_secs_f64(2.0));
let mut stdin = syscall::open("/dev/tty0");
let mut file = syscall::open("/dev/tty0");
syscall::close(0);
let mut tty = syscall::open("/dev/tty0");
syscall::spawn("/usr/bin/shell");
loop {
let mut test = [0; 2];
syscall::read(&mut stdin, &mut test);
let len = *test.iter().find(|x| **x == 0).unwrap_or(&1) + 1;
syscall::write(
&mut file,
str::from_utf8(&test[..len as usize]).unwrap().as_bytes(),
);
let len = stdin().read(&mut test).unwrap();
tty.write_all(str::from_utf8(&test[..len as usize]).unwrap().as_bytes())
.unwrap();
}
// println!(
// "Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! {:?}",
// str::from_utf8(&test)
// );
}