Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

target_os = "custom": selecting/swapping platform-specific parts of the libstd at runtime #115506

Closed
wants to merge 12 commits into from
3 changes: 3 additions & 0 deletions library/std/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::env;

fn main() {
let is_custom_os = || env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("custom");

println!("cargo:rerun-if-changed=build.rs");
let target = env::var("TARGET").expect("TARGET was not set");
if target.contains("freebsd") {
Expand Down Expand Up @@ -40,6 +42,7 @@ fn main() {
|| target.contains("xous")
|| target.contains("hurd")
|| target.contains("uefi")
|| is_custom_os()
// See src/bootstrap/synthetic_targets.rs
|| env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()
{
Expand Down
1 change: 1 addition & 0 deletions library/std/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,7 @@ pub mod consts {
/// - dragonfly
/// - netbsd
/// - openbsd
/// - custom
/// - solaris
/// - android
/// - windows
Expand Down
310 changes: 310 additions & 0 deletions library/std/src/os/custom/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
//! Platform-specific parts of the standard library
//! that can be plugged-in at runtime.
//!
//! Using these modules, code can set an implementation for
//! each platform-specific part of the standard library at
//! runtime. It does so via the `set_impl` functions.
//!
//! This is primarily geared toward experimental platforms such
//! as new kernels and bare-bones environments, where you might
//! want to use the standard library without recompiling
//! everything or adding support upstream.
//!
//! # Initial state
//!
//! Initially, as no implementation has been defined, most
//! of these parts panic with this message:
//!
//! ```text
//! std::os::thread::IMPL has not been initialized at this point.
//! ```
//!
//! There are three exceptions:
//!
//! - There is a default allocator, allowing for 16KiB of heap use.
//! It implements the sequential fit algorithm.
//! - Initially, locking primitives are functional and behave as spinlocks.
//! - Before `os::set_impl` has been called, any call to `abort_internal`
//! will result in an infinite loop.
//! - Before `thread::set_impl` has been called, `None` is used where
//! `current_thread_id` would be called.
//!
//! These should be set/changed as soon as possible, as they are used internally.
//!
//! # To do
//!
//! - thread parking support
//! - thread-safe Once support
//! - invocation arguments (returns an empty iterator at the moment)
//! - `pipe::read2`
//!

#![unstable(issue = "none", feature = "std_internals")]

#[doc(hidden)]
#[macro_export]
macro_rules! custom_os_impl {
($module:ident, $method:ident $(, $arg:expr)*) => {{
let errmsg = concat!(
"std::os::custom::", stringify!($module), "::IMPL",
" has not been initialized at this point",
);

let rwlock = &crate::os::custom::$module::IMPL;
let reader = rwlock.read().expect("poisoned lock");
let some_impl = reader.as_ref().expect(errmsg);

some_impl.$method($($arg,)*)
}};
}

macro_rules! static_rwlock_box_impl {
($api:ident) => {
pub(crate) static IMPL: RwLock<Option<Box<dyn $api>>> = RwLock::new(None);

/// Sets the implementation singleton
///
/// This parameter takes a `transition` closure responsible
/// for properly transitioning from the previous implementation
/// to a new one.
///
/// Initially, there is no implementation; the first time this is called,
/// the closure parameter will be `None`.
///
/// Removing an implementation (i.e. setting the internal singleton to `None`)
/// is intentionally not allowed.
pub fn set_impl<F: FnOnce(Option<Box<dyn $api>>) -> Box<dyn $api>>(transition: F) {
let mut writer = IMPL.write().expect("poisoned lock");
let maybe_impl = core::mem::replace(&mut *writer, None);
let new_impl = transition(maybe_impl);
*writer = Some(new_impl);
}
};
}

/// Platform-specific allocator
pub mod alloc {
use crate::alloc::GlobalAlloc;
use crate::sync::RwLock;

static_rwlock_box_impl!(Allocator);

/// Platform-specific allocator
pub trait Allocator: GlobalAlloc + Send + Sync {}
}

/// Platform-specific interface to a filesystem
pub mod fs {
use crate::io;
use crate::path::{Path, PathBuf};
use crate::sync::RwLock;

#[doc(inline)]
pub use crate::sys::fs::{
DirEntry, File, FileApi, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
ReadDir, ReadDirApi,
};

static_rwlock_box_impl!(FilesystemInterface);

/// Platform-specific interface to a filesystem
pub trait FilesystemInterface: Send + Sync {
fn open(&self, path: &Path, opts: &OpenOptions) -> io::Result<File>;
fn mkdir(&self, p: &Path) -> io::Result<()>;
fn read_dir(&self, p: &Path) -> io::Result<ReadDir>;
fn unlink(&self, p: &Path) -> io::Result<()>;
fn rename(&self, old: &Path, new: &Path) -> io::Result<()>;
fn set_perm(&self, p: &Path, perm: FilePermissions) -> io::Result<()>;
fn rmdir(&self, p: &Path) -> io::Result<()>;
fn remove_dir_all(&self, path: &Path) -> io::Result<()>;
fn try_exists(&self, path: &Path) -> io::Result<bool>;
fn readlink(&self, p: &Path) -> io::Result<PathBuf>;
fn symlink(&self, original: &Path, link: &Path) -> io::Result<()>;
fn link(&self, src: &Path, dst: &Path) -> io::Result<()>;
fn stat(&self, p: &Path) -> io::Result<FileAttr>;
fn lstat(&self, p: &Path) -> io::Result<FileAttr>;
fn canonicalize(&self, p: &Path) -> io::Result<PathBuf>;
fn copy(&self, from: &Path, to: &Path) -> io::Result<u64>;
}
}

/// Platform-specific management of `AtomicU32`-based futexes
pub mod futex {
use crate::sync::{atomic::AtomicU32, RwLock};
use crate::time::Duration;

// by default (None) => spinlock
static_rwlock_box_impl!(FutexManager);

/// Platform-specific management of `AtomicU32`-based futexes
pub trait FutexManager: Send + Sync {
/// Wait for a futex_wake operation to wake us.
///
/// Returns directly if the futex doesn't hold the expected value.
///
/// Returns false on timeout, and true in all other cases.
fn futex_wait(&self, futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool;

/// Wake up one thread that's blocked on futex_wait on this futex.
///
/// Returns true if this actually woke up such a thread,
/// or false if no thread was waiting on this futex.
///
/// On some platforms, this always returns false.
fn futex_wake(&self, futex: &AtomicU32) -> bool;

/// Wake up all threads that are waiting on futex_wait on this futex.
fn futex_wake_all(&self, futex: &AtomicU32);
}
}

/// Platform-specific interface to a network
pub mod net {
use crate::io;
use crate::net::SocketAddr;
use crate::sync::RwLock;
use crate::time::Duration;

#[doc(inline)]
pub use crate::sys::net::{
LookupHost, TcpListener, TcpListenerApi, TcpStream, TcpStreamApi, UdpSocket, UdpSocketApi,
};

static_rwlock_box_impl!(NetworkInterface);

/// Platform-specific interface to a network
pub trait NetworkInterface: Send + Sync {
fn tcp_connect(
&self,
addr: &SocketAddr,
timeout: Option<Duration>,
) -> io::Result<TcpStream>;
fn tcp_bind(&self, addr: &SocketAddr) -> io::Result<TcpListener>;
fn udp_bind(&self, addr: &SocketAddr) -> io::Result<UdpSocket>;
fn lookup_str(&self, v: &str) -> io::Result<LookupHost>;
fn lookup_tuple(&self, v: (&str, u16)) -> io::Result<LookupHost>;
}
}

/// Platform-specific interface to the running operating system
pub mod os {
use crate::ffi::{OsStr, OsString};
use crate::io;
use crate::path::{Path, PathBuf};
use crate::sync::RwLock;

#[doc(inline)]
pub use crate::sys::os::{Env, JoinPathsError, SplitPaths, Variable};

static_rwlock_box_impl!(Os);

/// Platform-specific interface to the running operating system
pub trait Os: Send + Sync {
fn errno(&self) -> i32;
fn error_string(&self, errno: i32) -> String;

fn current_exe(&self) -> io::Result<PathBuf>;
fn env(&self) -> Env;
fn get_env(&self, variable: &OsStr) -> Option<OsString>;
fn set_env(&self, variable: &OsStr, value: &OsStr) -> io::Result<()>;
fn unset_env(&self, variable: &OsStr) -> io::Result<()>;
fn env_path_delim(&self) -> &'static str;

fn getcwd(&self) -> io::Result<PathBuf>;
fn temp_dir(&self) -> PathBuf;
fn home_dir(&self) -> Option<PathBuf>;
fn chdir(&self, path: &Path) -> io::Result<()>;

fn exit(&self, code: i32) -> !;
fn get_pid(&self) -> u32;

fn decode_error_kind(&self, errno: i32) -> crate::io::ErrorKind;
fn is_interrupted(&self, errno: i32) -> bool;
fn hashmap_random_keys(&self) -> (u64, u64);
}
}

/// Platform-specific management of processes
pub mod process {
use crate::io;
use crate::sync::RwLock;

#[doc(inline)]
pub use crate::sys_common::process::{CommandEnv, CommandEnvs};

#[doc(inline)]
pub use crate::sys::process::{Command, ExitStatus, Process, ProcessApi, Stdio, StdioPipes};

static_rwlock_box_impl!(ProcessManager);

/// Platform-specific management of processes
pub trait ProcessManager: Send + Sync {
fn spawn(&self, command: &Command) -> io::Result<(Process, StdioPipes)>;
}
}

/// Platform-specific standard IO interface
pub mod stdio {
use crate::io;
use crate::sync::RwLock;

static_rwlock_box_impl!(StdioInterface);

/// Platform-specific standard IO interface
pub trait StdioInterface: Send + Sync {
fn read_stdin(&self, buf: &mut [u8]) -> io::Result<usize>;
fn write_stdout(&self, buf: &[u8]) -> io::Result<usize>;
fn flush_stdout(&self) -> io::Result<()>;
fn write_stderr(&self, buf: &[u8]) -> io::Result<usize>;
fn flush_stderr(&self) -> io::Result<()>;
fn is_ebadf(&self, err: &io::Error) -> bool;
fn panic_output(&self) -> Option<Vec<u8>>;
}
}

/// Platform-specific management of threads
pub mod thread {
use crate::ffi::CStr;
use crate::io;
use crate::num::NonZeroUsize;
use crate::sync::RwLock;
use crate::time::Duration;

#[doc(inline)]
pub use crate::sys::thread::{Thread, ThreadApi};

static_rwlock_box_impl!(ThreadManager);

pub type ThreadId = usize;

/// Platform-specific management of threads
pub trait ThreadManager: Send + Sync {
/// unsafe: see thread::Builder::spawn_unchecked for safety requirements
unsafe fn new(&self, stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread>;
fn yield_now(&self);
fn set_name(&self, name: &CStr);
fn sleep(&self, dur: Duration);
fn join(&self, thread: &Thread);
fn available_parallelism(&self) -> io::Result<NonZeroUsize>;

/// Must return None for the initial thread
fn current_thread_id(&self) -> Option<ThreadId>;

// todo: thread parking
}
}

/// Platform-specific timer interface
pub mod time {
use crate::sync::RwLock;

pub use crate::sys::time::{Instant, SystemTime};

static_rwlock_box_impl!(Timer);

/// Platform-specific timer interface
pub trait Timer: Send + Sync {
fn now_instant(&self) -> Instant;
fn now_systime(&self) -> SystemTime;
}
}
42 changes: 42 additions & 0 deletions library/std/src/os/fake_unix_windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! This is a doc-build trick artefact. Check the upstream documentation.

/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod process {
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod ExitStatusExt {}
}

/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod fs {
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod PermissionsExt {}
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod symlink {}
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod symlink_file {}
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod symlink_dir {}
}

/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod ffi {
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod OsStrExt {
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod encode_wide {}
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod from_bytes {}
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod as_bytes {}
}

/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod OsStringExt {
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod from_wide {}
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod from_vec {}
/// This is a doc-build trick artefact. Check the upstream documentation.
pub mod into_vec {}
}
}
Loading
Loading